@rws-framework/ai-tools 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bin/add-v.sh +10 -0
- package/.bin/emerge.sh +11 -0
- package/.emerge-vis-output/rws-server/emerge-file_result_dependency_graph.graphml +2067 -0
- package/.emerge-vis-output/rws-server/emerge-filesystem_graph.graphml +1505 -0
- package/.emerge-vis-output/rws-server/emerge-statistics-and-metrics.json +1 -0
- package/.emerge-vis-output/rws-server/emerge-statistics-metrics.txt +1147 -0
- package/.emerge-vis-output/rws-server/html/emerge.html +501 -0
- package/.emerge-vis-output/rws-server/html/jsconfig.json +9 -0
- package/.emerge-vis-output/rws-server/html/resources/css/custom.css +211 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_common.js +45 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_data.js +13 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_git.js +1414 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_graph.js +539 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_heatmap.js +220 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_hull.js +180 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_main.js +1003 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_search.js +71 -0
- package/.emerge-vis-output/rws-server/html/resources/js/emerge_ui.js +199 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.css +4124 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.css +4123 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.css +488 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.css +485 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.css +4266 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.css +4257 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.css +10878 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.css +10842 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.min.css +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.min.css.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.js +7075 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.js.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.min.js +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.min.js.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.js +5202 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.js.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.min.js +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.min.js.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.js +5249 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.js.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.min.js +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.min.js.map +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/d3/d3.v7.8.4.min.js +2 -0
- package/.emerge-vis-output/rws-server/html/vendors/d3/d3.v7.min.js +2 -0
- package/.emerge-vis-output/rws-server/html/vendors/dark-mode-switch/css/dark-mode.css +148 -0
- package/.emerge-vis-output/rws-server/html/vendors/dark-mode-switch/js/dark-mode-switch.min.js +1 -0
- package/.emerge-vis-output/rws-server/html/vendors/daterangepicker/daterangepicker.css +410 -0
- package/.emerge-vis-output/rws-server/html/vendors/daterangepicker/daterangepicker.min.js +8 -0
- package/.emerge-vis-output/rws-server/html/vendors/daterangepicker/moment.min.js +7 -0
- package/.emerge-vis-output/rws-server/html/vendors/hull/hull.js +373 -0
- package/.emerge-vis-output/rws-server/html/vendors/jquery/jquery-3.6.0.min.js +2 -0
- package/.emerge-vis-output/rws-server/html/vendors/popper/popper.min.js +6 -0
- package/.emerge-vis-output/rws-server/html/vendors/simpleheat/simpleheat.js +141 -0
- package/.eslintrc.json +53 -0
- package/README.md +862 -0
- package/package.json +49 -0
- package/src/index.ts +22 -0
- package/src/models/convo/ConvoLoader.ts +415 -0
- package/src/models/convo/VectorStore.ts +33 -0
- package/src/models/prompts/_prompt.ts +388 -0
- package/src/services/VectorStoreService.ts +15 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
|
|
2
|
+
// Some JS code is derived/borrowed or heavily inspired from demos/examples by the following people:
|
|
3
|
+
// Mike Bostock - https://github.com/mbostock, https://bost.ocks.org/mike/
|
|
4
|
+
// Tom Roth - https://bl.ocks.org/puzzler10/4438752bb93f45dc5ad5214efaa12e4a
|
|
5
|
+
// Ma'moun othman/@mamounothman - https://stackoverflow.com/questions/61800343/d3-js-version-5-chart-to-pdf
|
|
6
|
+
// Sam Leach/@SamuelLeach - https://gist.github.com/samuelleach/5497403
|
|
7
|
+
// Pranav C Balan - https://stackoverflow.com/questions/41287778/get-all-possible-object-keys-from-a-list-of-objects-javascript-typescript
|
|
8
|
+
// If I missed someone or gave wrong credit, please contact me and I'll update this.
|
|
9
|
+
|
|
10
|
+
const activeNodeLabelColor = hexToRGB("#333333", 0.6)
|
|
11
|
+
const passiveNodeLabelColor = hexToRGB("#333333", 0.2)
|
|
12
|
+
const dmActiveNodeLabelColor = hexToRGB("#DDDDDD", 0.6)
|
|
13
|
+
const dmPassiveNodeLabelColor = hexToRGB("#DDDDDD", 0.2)
|
|
14
|
+
|
|
15
|
+
const activeEdgeColor = hexToRGB("#AAAAAA", 1.0);
|
|
16
|
+
const passiveEdgeColor = hexToRGB("#AAAAAA", 0.2);
|
|
17
|
+
const dmActiveEdgeColor = hexToRGB("#888888", 0.8)
|
|
18
|
+
const dmPassiveEdgeColor = hexToRGB("#888888", 0.2)
|
|
19
|
+
|
|
20
|
+
const toolTipMetricItemTextColor = hexToRGB("#333333", 0.7);
|
|
21
|
+
const toolTipMetricItemBoxColor = hexToRGB("#333333", 1.0);
|
|
22
|
+
const toolTipMetricItemBoxFillColor = hexToRGB("#f7f7f7", 1.0);
|
|
23
|
+
|
|
24
|
+
const activeSelectionColor = '#FF0000'
|
|
25
|
+
const directoryNodeColor = '#3b8cff'
|
|
26
|
+
const fileNodeColor = '#d1e3ff'
|
|
27
|
+
const defaultNodeColor = '#1f77b4'
|
|
28
|
+
const semanticHeaderYellow = '#f5bc42'
|
|
29
|
+
const contributorsPurple = '#ff00ff'
|
|
30
|
+
const changeCouplingColor = '#ff0000'
|
|
31
|
+
|
|
32
|
+
let metricNameMap = {
|
|
33
|
+
"metric_ws_complexity_in_file" : "whitespace complexity",
|
|
34
|
+
"metric_number_of_methods_in_file" : "number of methods (file)",
|
|
35
|
+
"metric_number_of_methods_in_entity" : "number of methods (entity)",
|
|
36
|
+
"metric_sloc_in_file" : "source lines of code (file)",
|
|
37
|
+
"metric_sloc_in_entity" : "source lines of code (entity)",
|
|
38
|
+
"metric_fan_in_dependency_graph" : "fan in (dependency graph)",
|
|
39
|
+
"metric_fan_out_dependency_graph" : "fan out (dependency graph)",
|
|
40
|
+
"metric_fan_in_inheritance_graph" : "fan in (inheritance graph)",
|
|
41
|
+
"metric_fan_out_inheritance_graph" : "fan out (inheritance graph)",
|
|
42
|
+
"metric_fan_in_complete_graph" : "fan in (complete graph)",
|
|
43
|
+
"metric_fan_out_complete_graph" : "fan out (complete graph)",
|
|
44
|
+
|
|
45
|
+
//"metric_file_result_dependency_graph_louvain_modularity_in_file" : null,
|
|
46
|
+
|
|
47
|
+
"metric_git_code_churn" : "code churn (git)",
|
|
48
|
+
"metric_git_ws_complexity" : "whitespace complexity (git)",
|
|
49
|
+
"metric_git_sloc" : "source lines of code (git)",
|
|
50
|
+
|
|
51
|
+
// "metric_git_contributors" : "contributors (git)",
|
|
52
|
+
|
|
53
|
+
"metric_git_number_authors": "number of authors (git)",
|
|
54
|
+
"metric_fan_in_complete_graph" : "fan in (complete graph)",
|
|
55
|
+
"metric_fan_out_complete_graph" : "fan out (complete graph)"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* * MARK: - Math constants
|
|
60
|
+
*/
|
|
61
|
+
const TWO_TIMES_PI = 2 * Math.PI
|
|
62
|
+
const ONE_THIRD_TWO_TIMES_PI = (1.0 / 3.0) * TWO_TIMES_PI
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* * MARK: - UI workarounds
|
|
66
|
+
*/
|
|
67
|
+
// workaround from https://stackoverflow.com/questions/6985507/one-time-page-refresh-after-first-page-load to fix strange safari full screen loading problems
|
|
68
|
+
let userAgent = navigator.userAgent.toLowerCase();
|
|
69
|
+
if (userAgent.includes('safari')) {
|
|
70
|
+
window.onload = function() {
|
|
71
|
+
if (!window.location.hash) {
|
|
72
|
+
window.location = window.location + '#loaded';
|
|
73
|
+
window.location.reload();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Workaround to prevent buttons that trigger modals to stay focused after dismiss (https://stackoverflow.com/questions/30322918/bootstrap-modal-restores-button-focus-on-close)
|
|
79
|
+
$('body').on('hidden.bs.modal', '.modal', function() {
|
|
80
|
+
$('#buttonShowOverallMetrics').blur();
|
|
81
|
+
$('#buttonShowOverallStatistics').blur();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// cancel the node search by pressing the escape key
|
|
85
|
+
$(document).keyup(function(e) {
|
|
86
|
+
if (e.key === "Escape") {
|
|
87
|
+
cancelNodeSearch()
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
function setDarkMode(mode) {
|
|
92
|
+
if (mode == true) {
|
|
93
|
+
darkMode = true
|
|
94
|
+
currentActiveNodeLabelColor = dmActiveNodeLabelColor
|
|
95
|
+
currentPassiveNodeLabelColor = dmPassiveNodeLabelColor
|
|
96
|
+
currentActiveEdgeColor = dmActiveEdgeColor
|
|
97
|
+
currentPassiveEdgeColor = dmPassiveEdgeColor
|
|
98
|
+
simulationUpdate()
|
|
99
|
+
} else {
|
|
100
|
+
darkMode = false
|
|
101
|
+
currentActiveNodeLabelColor = activeNodeLabelColor
|
|
102
|
+
currentPassiveNodeLabelColor = passiveNodeLabelColor
|
|
103
|
+
currentActiveEdgeColor = activeEdgeColor
|
|
104
|
+
currentPassiveEdgeColor = passiveEdgeColor
|
|
105
|
+
simulationUpdate()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
|
110
|
+
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
|
111
|
+
|
|
112
|
+
let heatmapMerged = false
|
|
113
|
+
let heatmapActive = false
|
|
114
|
+
let heatmapChurn = false
|
|
115
|
+
let heatmapHotspot = false
|
|
116
|
+
|
|
117
|
+
let selectedNodesMap = {}
|
|
118
|
+
var unselectedNodesOpacity = 0.20
|
|
119
|
+
let fadeUnselectedNodes = false
|
|
120
|
+
let currentActiveNodeLabelColor = activeNodeLabelColor
|
|
121
|
+
let currentPassiveNodeLabelColor = passiveNodeLabelColor
|
|
122
|
+
|
|
123
|
+
let currentActiveEdgeColor = activeEdgeColor
|
|
124
|
+
let currentPassiveEdgeColor = passiveEdgeColor
|
|
125
|
+
|
|
126
|
+
let dateRangePickerFrom = ""
|
|
127
|
+
let dateRangePickerTo = ""
|
|
128
|
+
let fileResultPrefix = ""
|
|
129
|
+
let fileResultPrefixFull = ""
|
|
130
|
+
|
|
131
|
+
let includeGitMetrics = false
|
|
132
|
+
let commit_dates = []
|
|
133
|
+
let commit_first_date = ""
|
|
134
|
+
let commit_last_date = ""
|
|
135
|
+
|
|
136
|
+
if (typeof commit_metrics !== 'undefined') {
|
|
137
|
+
fileResultPrefix = commit_metrics[0].file_result_prefix // TODO: pass/extract as/from extra common git metrics dict
|
|
138
|
+
fileResultPrefixFull = commit_metrics[0].file_result_prefix_full
|
|
139
|
+
commit_dates = commit_metrics.map(x => x.date);
|
|
140
|
+
commit_first_date = commit_dates[0]
|
|
141
|
+
commit_last_date = commit_dates[commit_dates.length - 1]
|
|
142
|
+
dateRangePickerFrom = commit_first_date
|
|
143
|
+
dateRangePickerTo = commit_last_date
|
|
144
|
+
includeGitMetrics = true
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let gitMetricsIndexFrom = 0
|
|
148
|
+
let gitMetricsIndexTo = 0
|
|
149
|
+
|
|
150
|
+
let nodeColorMap = {}
|
|
151
|
+
|
|
152
|
+
const nodeStrokeStyle = "#333333";
|
|
153
|
+
|
|
154
|
+
let darkMode = false
|
|
155
|
+
let isSearching = false
|
|
156
|
+
let addSemanticSearch = false
|
|
157
|
+
let addContributorSearch = false
|
|
158
|
+
|
|
159
|
+
let hoverCoupling = false
|
|
160
|
+
|
|
161
|
+
let searchString = ""
|
|
162
|
+
|
|
163
|
+
let searchTerms = []
|
|
164
|
+
let searchResults = 0
|
|
165
|
+
|
|
166
|
+
const radius = 7;
|
|
167
|
+
const height = window.innerHeight * 2;
|
|
168
|
+
const graphWidth = window.innerWidth * 2;
|
|
169
|
+
|
|
170
|
+
const maxClusterHulls = 20
|
|
171
|
+
let selectedClusterHullIds = []
|
|
172
|
+
let hoveredClusterHullId = undefined
|
|
173
|
+
|
|
174
|
+
let nodeLabelsEnabled = false
|
|
175
|
+
|
|
176
|
+
let currentTranslation = {
|
|
177
|
+
horizontal: 0,
|
|
178
|
+
vertical: 0,
|
|
179
|
+
lastDirection: ""
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let activeMetrics
|
|
183
|
+
let currentMetricKeys
|
|
184
|
+
|
|
185
|
+
let currentGraphType
|
|
186
|
+
let closeNode;
|
|
187
|
+
|
|
188
|
+
function setDirection(direction) {
|
|
189
|
+
currentTranslation.lastDirection = direction
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function toggleNodeLabels() {
|
|
193
|
+
nodeLabelsEnabled = !nodeLabelsEnabled
|
|
194
|
+
simulationUpdate()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let zoom_handler = d3.zoom()
|
|
198
|
+
.on("zoom", zoomed);
|
|
199
|
+
|
|
200
|
+
let graphCanvas = d3.select('#graphDiv').append('canvas')
|
|
201
|
+
.attr('width', graphWidth + 'px')
|
|
202
|
+
.attr('height', height + 'px')
|
|
203
|
+
.attr('id', 'mainCanvas')
|
|
204
|
+
.node();
|
|
205
|
+
|
|
206
|
+
let graphData = {}
|
|
207
|
+
let currentGraph = ''
|
|
208
|
+
|
|
209
|
+
let clusterMap = {}
|
|
210
|
+
let clusterMetricsMap = {}
|
|
211
|
+
|
|
212
|
+
let statistics
|
|
213
|
+
let overall_metric_results
|
|
214
|
+
|
|
215
|
+
initAppConfig()
|
|
216
|
+
initHeatmapSwitches()
|
|
217
|
+
|
|
218
|
+
initHoverCouplingSwitch()
|
|
219
|
+
|
|
220
|
+
initSemanticSearchSwitch()
|
|
221
|
+
initContributorSearchSwitch()
|
|
222
|
+
|
|
223
|
+
setInitialDarkMode()
|
|
224
|
+
prepareGraphStructures()
|
|
225
|
+
initAppUI()
|
|
226
|
+
|
|
227
|
+
let context = graphCanvas.getContext('2d');
|
|
228
|
+
|
|
229
|
+
// nice fix from https://stackoverflow.com/questions/8696631/canvas-drawings-like-lines-are-blurry to remove blurry drawing
|
|
230
|
+
graphCanvas.style.height = (height / 2) + "px";
|
|
231
|
+
graphCanvas.style.width = (graphWidth / 2) + "px";
|
|
232
|
+
graphCanvas.getContext('2d').scale(2, 2);
|
|
233
|
+
|
|
234
|
+
let div = d3.select("body").append("div")
|
|
235
|
+
.attr("class", "tooltip")
|
|
236
|
+
.style("opacity", 0);
|
|
237
|
+
|
|
238
|
+
let currentChargeForce = -500
|
|
239
|
+
let currentLinkDistance = 20
|
|
240
|
+
let simulation
|
|
241
|
+
|
|
242
|
+
let transform = d3.zoomIdentity;
|
|
243
|
+
|
|
244
|
+
// heatmap
|
|
245
|
+
var heat = simpleheat('mainCanvas');
|
|
246
|
+
|
|
247
|
+
// bring back d3 schemeCategory20 to live
|
|
248
|
+
const schemeCategory20 = "1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"
|
|
249
|
+
|
|
250
|
+
function d3ColorExport(specifier) {
|
|
251
|
+
let n = specifier.length / 6 | 0,
|
|
252
|
+
colors = new Array(n),
|
|
253
|
+
i = 0;
|
|
254
|
+
while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
|
|
255
|
+
return colors;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// important to define the domain for the ordinal scale
|
|
259
|
+
const color = d3.scaleOrdinal(d3ColorExport(schemeCategory20))
|
|
260
|
+
.domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
|
|
261
|
+
|
|
262
|
+
function initModals() {
|
|
263
|
+
const modal = document.getElementById('timeSeriesModal')
|
|
264
|
+
modal.addEventListener('hidden.bs.modal', event => {
|
|
265
|
+
d3.select("#timeSeriesComplexityChart").remove();
|
|
266
|
+
d3.select("#timeSeriesChurnChart").remove();
|
|
267
|
+
d3.select("#timeSeriesSlocChart").remove();
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const chordModal = document.getElementById('changeCouplingModal')
|
|
271
|
+
chordModal.addEventListener('hidden.bs.modal', event => {
|
|
272
|
+
d3.select("#svg_change_coupling_chord_diagram").remove();
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function setInitialDarkMode() {
|
|
277
|
+
if (document.body.getAttribute("data-theme") == "dark") {
|
|
278
|
+
currentActiveNodeLabelColor = dmActiveNodeLabelColor
|
|
279
|
+
currentPassiveNodeLabelColor = dmPassiveNodeLabelColor
|
|
280
|
+
currentActiveEdgeColor = dmActiveEdgeColor
|
|
281
|
+
currentPassiveEdgeColor = dmPassiveEdgeColor
|
|
282
|
+
darkMode = true
|
|
283
|
+
} else {
|
|
284
|
+
currentActiveNodeLabelColor = activeNodeLabelColor
|
|
285
|
+
currentPassiveNodeLabelColor = passiveNodeLabelColor
|
|
286
|
+
currentActiveEdgeColor = activeEdgeColor
|
|
287
|
+
currentPassiveEdgeColor = passiveEdgeColor
|
|
288
|
+
darkMode = false
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* * MARK: - Translations on canvas
|
|
294
|
+
*/
|
|
295
|
+
function translateCanvas(direction) {
|
|
296
|
+
switch (direction) {
|
|
297
|
+
case 'left':
|
|
298
|
+
transform.x += 100
|
|
299
|
+
simulationUpdate()
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
case 'down':
|
|
303
|
+
transform.y -= 100
|
|
304
|
+
simulationUpdate()
|
|
305
|
+
break;
|
|
306
|
+
|
|
307
|
+
case 'right':
|
|
308
|
+
transform.x -= 100
|
|
309
|
+
simulationUpdate()
|
|
310
|
+
break;
|
|
311
|
+
|
|
312
|
+
case 'up':
|
|
313
|
+
transform.y += 100
|
|
314
|
+
simulationUpdate()
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* * MARK: - Adjusting style
|
|
321
|
+
*/
|
|
322
|
+
|
|
323
|
+
function unselectedOpacityChange(val) {
|
|
324
|
+
unselectedNodesOpacity = val / 100.0
|
|
325
|
+
simulationUpdate()
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* * MARK: - Zooming and scaling on canvas
|
|
330
|
+
*/
|
|
331
|
+
function zoomed(event) {
|
|
332
|
+
transform = event.transform;
|
|
333
|
+
simulationUpdate();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function zoomIn() {
|
|
337
|
+
d3.select(graphCanvas)
|
|
338
|
+
.call(zoom_handler.scaleBy, 2)
|
|
339
|
+
.call(d3.zoom().scaleExtent([1 / 10, 8]).on("zoom", zoomed))
|
|
340
|
+
simulationUpdate()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function zoomOut() {
|
|
344
|
+
d3.select(graphCanvas)
|
|
345
|
+
.call(zoom_handler.scaleBy, 0.5)
|
|
346
|
+
.call(d3.zoom().scaleExtent([1 / 10, 8]).on("zoom", zoomed))
|
|
347
|
+
simulationUpdate()
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
startWithGraph('file_result_dependency_graph')
|
|
351
|
+
|
|
352
|
+
zoomOut() // initialliy zoom out a bit
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* * MARK: - Called on startup an d every time you change a graph
|
|
356
|
+
*/
|
|
357
|
+
function startWithGraph(graphType, chargeForce = currentChargeForce, linkDistance = currentLinkDistance) {
|
|
358
|
+
|
|
359
|
+
activeMetrics = []
|
|
360
|
+
currentMetricKeys = []
|
|
361
|
+
|
|
362
|
+
currentGraphType = graphType
|
|
363
|
+
|
|
364
|
+
currentLinkDistance = linkDistance
|
|
365
|
+
currentChargeForce = chargeForce
|
|
366
|
+
currentLinkDistance = linkDistance
|
|
367
|
+
|
|
368
|
+
resetSimulationData()
|
|
369
|
+
|
|
370
|
+
currentGraph = JSON.parse(JSON.stringify(graphData[graphType]['graph']))
|
|
371
|
+
statistics = graphData[graphType]['statistics']
|
|
372
|
+
overall_metric_results = graphData[graphType]['overall_metric_results']
|
|
373
|
+
clusterMetricsMap = graphData[graphType]['cluster_metrics_map']
|
|
374
|
+
|
|
375
|
+
addGitMetricToFileNodes()
|
|
376
|
+
|
|
377
|
+
createStatistics();
|
|
378
|
+
createOverallMetricResults();
|
|
379
|
+
|
|
380
|
+
currentGraph.nodes.forEach(function(d, i) {
|
|
381
|
+
d.radius = radius
|
|
382
|
+
|
|
383
|
+
if (!d.hasOwnProperty('metrics')) {
|
|
384
|
+
d.metrics = {}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
for (let key in d) {
|
|
388
|
+
|
|
389
|
+
if (key.includes('metric_')) {
|
|
390
|
+
d.metrics[key] = d[key]
|
|
391
|
+
if (!currentMetricKeys.includes(key)) {
|
|
392
|
+
// do not include tag/tfidf metrics in the 'apply metrics' dropDown menu
|
|
393
|
+
if (!key.includes('metric_tag')) {
|
|
394
|
+
currentMetricKeys.push(key)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
setupGraphClustersById();
|
|
402
|
+
|
|
403
|
+
createClusterHullMenu();
|
|
404
|
+
createMetricsMenuEntries();
|
|
405
|
+
|
|
406
|
+
updateAppUI()
|
|
407
|
+
|
|
408
|
+
initNodeColorMap();
|
|
409
|
+
|
|
410
|
+
enableSearchInput();
|
|
411
|
+
enableNodeSelection();
|
|
412
|
+
|
|
413
|
+
addToolTipsToMetricEntries();
|
|
414
|
+
addTooltipToHoverCoupling();
|
|
415
|
+
addTooltipToContributorSearch();
|
|
416
|
+
addToolTipsToHeatMap();
|
|
417
|
+
addToolTipToShortcuts();
|
|
418
|
+
addTooltipUnselectedOpacity();
|
|
419
|
+
addTooltipSemanticSearch();
|
|
420
|
+
addTooltipClusterHulls();
|
|
421
|
+
|
|
422
|
+
currentGraph.links.forEach((d) => {
|
|
423
|
+
linkedByIndex[`${d.source},${d.target}`] = true;
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
simulation = d3.forceSimulation()
|
|
427
|
+
.force("center", d3.forceCenter(graphWidth / 4, height / 4))
|
|
428
|
+
.force("x", d3.forceX(graphWidth / 2).strength(0.1))
|
|
429
|
+
.force("y", d3.forceY(height / 2).strength(0.1))
|
|
430
|
+
.force("charge", d3.forceManyBody().strength(currentChargeForce))
|
|
431
|
+
.force("link", d3.forceLink()
|
|
432
|
+
.strength(1)
|
|
433
|
+
.distance(currentLinkDistance)
|
|
434
|
+
.id(function(d) {
|
|
435
|
+
return d.id;
|
|
436
|
+
}))
|
|
437
|
+
.alphaTarget(0)
|
|
438
|
+
.alphaDecay(0.05)
|
|
439
|
+
|
|
440
|
+
addGraphTypeSelectionToMenu()
|
|
441
|
+
|
|
442
|
+
addTooltipProjectInfo();
|
|
443
|
+
addTooltipGraphInfo();
|
|
444
|
+
|
|
445
|
+
d3.select(graphCanvas)
|
|
446
|
+
.call(d3.drag().subject(dragsubject).on("start", dragstarted).on("drag", dragged).on("end", dragended))
|
|
447
|
+
.call(d3.zoom().scaleExtent([1 / 10, 8]).on("zoom", zoomed))
|
|
448
|
+
|
|
449
|
+
function dragsubject(event) {
|
|
450
|
+
|
|
451
|
+
let i,
|
|
452
|
+
x = transform.invertX(event.x),
|
|
453
|
+
y = transform.invertY(event.y),
|
|
454
|
+
dx,
|
|
455
|
+
dy;
|
|
456
|
+
|
|
457
|
+
for (i = currentGraph.nodes.length - 1; i >= 0; --i) {
|
|
458
|
+
node = currentGraph.nodes[i];
|
|
459
|
+
dx = x - node.x;
|
|
460
|
+
dy = y - node.y;
|
|
461
|
+
|
|
462
|
+
if (dx * dx + dy * dy < radius * radius) {
|
|
463
|
+
|
|
464
|
+
node.x = transform.applyX(node.x);
|
|
465
|
+
node.y = transform.applyY(node.y);
|
|
466
|
+
|
|
467
|
+
return node;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function dragstarted(event) {
|
|
473
|
+
if (!event.active) simulation.alphaTarget(0.3).restart();
|
|
474
|
+
event.subject.fx = transform.invertX(event.x);
|
|
475
|
+
event.subject.fy = transform.invertY(event.y);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function dragged(event) {
|
|
479
|
+
event.subject.fx = transform.invertX(event.x);
|
|
480
|
+
event.subject.fy = transform.invertY(event.y);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function dragended(event) {
|
|
484
|
+
if (!event.active) simulation.alphaTarget(0);
|
|
485
|
+
event.subject.fx = null;
|
|
486
|
+
event.subject.fy = null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
d3.select("canvas").on("mousemove", (event) => {
|
|
490
|
+
let p = d3.pointer(event);
|
|
491
|
+
let invX = transform.invertX(p[0])
|
|
492
|
+
let invY = transform.invertY(p[1])
|
|
493
|
+
|
|
494
|
+
let foundNode = simulation.find(transform.invertX(p[0]), transform.invertY(p[1]));
|
|
495
|
+
|
|
496
|
+
// check within a close area if hovered point is nearby of foundNode
|
|
497
|
+
if ((Math.abs(foundNode.x - invX) < radius) && (Math.abs(foundNode.y - invY) < radius)) {
|
|
498
|
+
closeNode = simulation.find(transform.invertX(p[0]), transform.invertY(p[1]));
|
|
499
|
+
} else {
|
|
500
|
+
closeNode = null
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
simulationUpdate();
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
simulation.nodes(currentGraph.nodes)
|
|
507
|
+
.on("tick", simulationUpdate);
|
|
508
|
+
|
|
509
|
+
simulation.force("link")
|
|
510
|
+
.links(currentGraph.links);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Based on https://bl.ocks.org/jodyphelan/5dc989637045a0f48418101423378fbd
|
|
514
|
+
function simulationUpdate() {
|
|
515
|
+
|
|
516
|
+
context.save();
|
|
517
|
+
|
|
518
|
+
context.clearRect(0, 0, graphWidth, height);
|
|
519
|
+
context.translate(transform.x, transform.y);
|
|
520
|
+
context.scale(transform.k, transform.k);
|
|
521
|
+
|
|
522
|
+
// draw heatmap
|
|
523
|
+
if (mergedHeatmapIsActive() || normalHeatmapIsActive() || churnHeatmapIsActive() || hotspotHeatmapIsActive()) {
|
|
524
|
+
drawHeatMap(context)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// this is pretty cpu hungry
|
|
528
|
+
drawHulls(context)
|
|
529
|
+
|
|
530
|
+
// draw edges
|
|
531
|
+
drawEdges(context)
|
|
532
|
+
|
|
533
|
+
// Draw nodes
|
|
534
|
+
drawNodes(context)
|
|
535
|
+
|
|
536
|
+
context.restore();
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* * MARK: - Handling of data structures
|
|
541
|
+
*/
|
|
542
|
+
function prepareGraphStructures() {
|
|
543
|
+
if (typeof file_result_dependency_graph !== 'undefined') {
|
|
544
|
+
graphData['file_result_dependency_graph'] = {}
|
|
545
|
+
graphData['file_result_dependency_graph']['graph'] = file_result_dependency_graph
|
|
546
|
+
graphData['file_result_dependency_graph']['statistics'] = file_result_dependency_graph_statistics
|
|
547
|
+
graphData['file_result_dependency_graph']['overall_metric_results'] =
|
|
548
|
+
file_result_dependency_graph_overall_metric_results
|
|
549
|
+
graphData['file_result_dependency_graph']['cluster_metrics_map'] = file_result_dependency_graph_cluster_metrics_map
|
|
550
|
+
currentGraphType = 'file_result_dependency_graph'
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (typeof entity_result_dependency_graph !== 'undefined') {
|
|
554
|
+
graphData['entity_result_dependency_graph'] = {}
|
|
555
|
+
graphData['entity_result_dependency_graph']['graph'] = entity_result_dependency_graph
|
|
556
|
+
graphData['entity_result_dependency_graph']['statistics'] = entity_result_dependency_graph_statistics
|
|
557
|
+
graphData['entity_result_dependency_graph']['overall_metric_results'] =
|
|
558
|
+
entity_result_dependency_graph_overall_metric_results
|
|
559
|
+
graphData['entity_result_dependency_graph']['cluster_metrics_map'] = entity_result_dependency_graph_cluster_metrics_map
|
|
560
|
+
currentGraphType = 'entity_result_dependency_graph'
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (typeof entity_result_inheritance_graph !== 'undefined') {
|
|
564
|
+
graphData['entity_result_inheritance_graph'] = {}
|
|
565
|
+
graphData['entity_result_inheritance_graph']['graph'] = entity_result_inheritance_graph
|
|
566
|
+
graphData['entity_result_inheritance_graph']['statistics'] = entity_result_inheritance_graph_statistics
|
|
567
|
+
graphData['entity_result_inheritance_graph']['overall_metric_results'] =
|
|
568
|
+
entity_result_inheritance_graph_overall_metric_results
|
|
569
|
+
graphData['entity_result_inheritance_graph']['cluster_metrics_map'] = entity_result_inheritance_graph_cluster_metrics_map
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (typeof entity_result_complete_graph !== 'undefined') {
|
|
573
|
+
graphData['entity_result_complete_graph'] = {}
|
|
574
|
+
graphData['entity_result_complete_graph']['graph'] = entity_result_complete_graph
|
|
575
|
+
graphData['entity_result_complete_graph']['statistics'] = entity_result_complete_graph_statistics
|
|
576
|
+
graphData['entity_result_complete_graph']['overall_metric_results'] =
|
|
577
|
+
entity_result_complete_graph_overall_metric_results
|
|
578
|
+
graphData['entity_result_complete_graph']['cluster_metrics_map'] = entity_result_complete_graph_cluster_metrics_map
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (typeof filesystem_graph !== 'undefined') {
|
|
582
|
+
graphData['filesystem_graph'] = {}
|
|
583
|
+
graphData['filesystem_graph']['graph'] = filesystem_graph
|
|
584
|
+
graphData['filesystem_graph']['statistics'] = filesystem_graph_statistics
|
|
585
|
+
graphData['filesystem_graph']['overall_metric_results'] = filesystem_graph_overall_metric_results
|
|
586
|
+
graphData['filesystem_graph']['cluster_metrics_map'] = filesystem_graph_cluster_metrics_map
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function resetSimulationData() {
|
|
591
|
+
if (simulation !== undefined) {
|
|
592
|
+
simulation.stop()
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
metricKeys = nodesData = linksData = []
|
|
596
|
+
simulation = undefined
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// based on https://bocoup.com/blog/smoothly-animate-thousands-of-points-with-html5-canvas-and-d3 / Peter Beshai
|
|
600
|
+
// basically animates the increase/decrease of the node radius based on chosen metrics
|
|
601
|
+
function animateRadiusWithMetric(metricName) {
|
|
602
|
+
|
|
603
|
+
let addedMetric = true
|
|
604
|
+
if (activeMetrics.includes(metricName)) {
|
|
605
|
+
removeItemAll(activeMetrics, metricName)
|
|
606
|
+
addedMetric = false
|
|
607
|
+
} else {
|
|
608
|
+
activeMetrics.push(metricName)
|
|
609
|
+
addedMetric = true
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// console.log(metricName)
|
|
613
|
+
const duration = 250;
|
|
614
|
+
const ease = d3.easeCubic;
|
|
615
|
+
|
|
616
|
+
timer = d3.timer((elapsed) => {
|
|
617
|
+
// compute how far through the animation we are (0 to 1)
|
|
618
|
+
const t = Math.min(1, ease(elapsed / duration));
|
|
619
|
+
|
|
620
|
+
// update point positions (interpolate between source and target)
|
|
621
|
+
currentGraph.nodes.forEach(node => {
|
|
622
|
+
if (metricName in node.metrics) {
|
|
623
|
+
// this resets all nodes back to the default radius
|
|
624
|
+
let newRadius = 0
|
|
625
|
+
|
|
626
|
+
// now interpolate for every x between f(0) and f(1): f(x) = f(0) * (1-x) + f(1) * x
|
|
627
|
+
if (addedMetric) {
|
|
628
|
+
newRadius = node.radius * (1 - t) + (node.radius + (node.metrics[metricName] * analysis_config['metrics']['radius_multiplication'][metricName] )) * t;
|
|
629
|
+
|
|
630
|
+
if (newRadius > node.radius) {
|
|
631
|
+
node.radius = newRadius
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
} else {
|
|
635
|
+
newRadius = node.radius * (1 - t) + (node.radius - (node.metrics[metricName] * analysis_config['metrics']['radius_multiplication'][metricName] )) * t;
|
|
636
|
+
if (newRadius > radius) {
|
|
637
|
+
node.radius = newRadius
|
|
638
|
+
} else {
|
|
639
|
+
node.radius = radius
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// if this animation is over
|
|
646
|
+
if (t === 1) {
|
|
647
|
+
// always make sure that node sizes return to default if no metric is active
|
|
648
|
+
if (activeMetrics.length == 0) {
|
|
649
|
+
currentGraph.nodes.forEach(node => {
|
|
650
|
+
node.radius = radius
|
|
651
|
+
})
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// stop this timer since we are done animating.
|
|
655
|
+
timer.stop();
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// update what is drawn on screen
|
|
659
|
+
simulationUpdate();
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* * MARK: - Create/update the HTML/Bootstrap UI
|
|
665
|
+
*/
|
|
666
|
+
|
|
667
|
+
function cancelNodeSearch() {
|
|
668
|
+
$('#inputNodeSearch').val('')
|
|
669
|
+
$('#inputNodeSearchLabel').text('Search inactive')
|
|
670
|
+
searchString = ""
|
|
671
|
+
|
|
672
|
+
searchTerms = []
|
|
673
|
+
|
|
674
|
+
isSearching = false
|
|
675
|
+
simulationUpdate()
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// setup keyboard shortcut keys for node selection
|
|
679
|
+
// shift + 's' key: select/unselect node
|
|
680
|
+
// shift + 'e' key: expand selected nodes level deeper
|
|
681
|
+
// shift + 'h' key: expand hovered nodes level deeper
|
|
682
|
+
// shift + 'r' key: reset current selection
|
|
683
|
+
// shift + 'f' key: fade unselected nodes
|
|
684
|
+
function enableNodeSelection() {
|
|
685
|
+
let keySelectUnselect = 'S'
|
|
686
|
+
let keyExpandSelection = 'E'
|
|
687
|
+
let keyExpandHoveredNode = 'H'
|
|
688
|
+
let keyResetCurrentSelection = 'R'
|
|
689
|
+
let keyFadeUnselectedNodes = 'F'
|
|
690
|
+
|
|
691
|
+
d3.select('body')
|
|
692
|
+
.on("keydown", function(event) {
|
|
693
|
+
|
|
694
|
+
if (event.key == keySelectUnselect) {
|
|
695
|
+
if (closeNode != null) {
|
|
696
|
+
if (closeNode.id.toLowerCase() in selectedNodesMap) {
|
|
697
|
+
delete selectedNodesMap[closeNode.id.toLowerCase()]
|
|
698
|
+
} else {
|
|
699
|
+
selectedNodesMap[closeNode.id.toLowerCase()] = true
|
|
700
|
+
}
|
|
701
|
+
simulationUpdate()
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (event.key == keyExpandSelection) {
|
|
706
|
+
if (selectedNodesMap.length != 0) {
|
|
707
|
+
let newSelectedNodesMap = {...selectedNodesMap}
|
|
708
|
+
currentGraph.links.forEach(function(d) {
|
|
709
|
+
if (d.source.id.toLowerCase() in selectedNodesMap || d.target.id.toLowerCase() in selectedNodesMap) {
|
|
710
|
+
newSelectedNodesMap[d.source.id.toLowerCase()] = true
|
|
711
|
+
newSelectedNodesMap[d.target.id.toLowerCase()] = true
|
|
712
|
+
}
|
|
713
|
+
})
|
|
714
|
+
selectedNodesMap = newSelectedNodesMap
|
|
715
|
+
simulationUpdate()
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (event.key == keyExpandHoveredNode) {
|
|
720
|
+
if (closeNode != null) {
|
|
721
|
+
selectedNodesMap[closeNode.id.toLowerCase()] = true
|
|
722
|
+
let newSelectedNodesMap = {...selectedNodesMap}
|
|
723
|
+
currentGraph.links.forEach(function(d) {
|
|
724
|
+
if (d.source.id == closeNode.id || d.target.id == closeNode.id) {
|
|
725
|
+
newSelectedNodesMap[d.source.id.toLowerCase()] = true
|
|
726
|
+
newSelectedNodesMap[d.target.id.toLowerCase()] = true
|
|
727
|
+
}
|
|
728
|
+
})
|
|
729
|
+
selectedNodesMap = newSelectedNodesMap
|
|
730
|
+
simulationUpdate()
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (event.key == keyResetCurrentSelection) {
|
|
735
|
+
selectedNodesMap = {}
|
|
736
|
+
simulationUpdate()
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (event.key == keyFadeUnselectedNodes) {
|
|
740
|
+
fadeUnselectedNodes = !fadeUnselectedNodes
|
|
741
|
+
if (fadeUnselectedNodes == true) {
|
|
742
|
+
$("#li-unselected-opacity").removeClass('d-none');
|
|
743
|
+
$("#unselected-opacity").removeClass('d-none');
|
|
744
|
+
$('#fadeUnselectedNodesLabelText').html('<b>f fading unselected nodes</b>')
|
|
745
|
+
|
|
746
|
+
} else {
|
|
747
|
+
$("#li-unselected-opacity").addClass('d-none');
|
|
748
|
+
$("#unselected-opacity").addClass('d-none');
|
|
749
|
+
$('#fadeUnselectedNodesLabelText').html('<b>f</b> fade unselected nodes')
|
|
750
|
+
}
|
|
751
|
+
simulationUpdate()
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function enableSearchInput() {
|
|
757
|
+
$('#inputNodeSearchLabel').text('Search inactive')
|
|
758
|
+
$('#inputNodeSearch').on('keyup change', function() {
|
|
759
|
+
searchString = $(this).val().toLowerCase()
|
|
760
|
+
|
|
761
|
+
searchTerms = searchString.split(" ")
|
|
762
|
+
searchTerms = searchTerms.filter(Boolean);
|
|
763
|
+
// console.log(searchTerms)
|
|
764
|
+
|
|
765
|
+
searchResults = 0
|
|
766
|
+
if (searchString.length > 0 && searchTerms.length > 0) {
|
|
767
|
+
isSearching = true
|
|
768
|
+
simulationUpdate()
|
|
769
|
+
$('#inputNodeSearchLabel').text(searchResults + ' nodes found')
|
|
770
|
+
|
|
771
|
+
} else {
|
|
772
|
+
isSearching = false
|
|
773
|
+
simulationUpdate()
|
|
774
|
+
$('#inputNodeSearchLabel').text('Search inactive')
|
|
775
|
+
}
|
|
776
|
+
})
|
|
777
|
+
|
|
778
|
+
$('#inputNodeSearchCancel').on('click', function() {
|
|
779
|
+
cancelNodeSearch()
|
|
780
|
+
})
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function createStatistics() {
|
|
784
|
+
// create statistics table
|
|
785
|
+
statistics_html = ""
|
|
786
|
+
for (let key in statistics) {
|
|
787
|
+
if (statistics.hasOwnProperty(key)) {
|
|
788
|
+
statistics_html += "<tr>"
|
|
789
|
+
|
|
790
|
+
statistics_html += "<td>"
|
|
791
|
+
statistics_html += key
|
|
792
|
+
statistics_html += "</td>"
|
|
793
|
+
|
|
794
|
+
statistics_html += "<td>"
|
|
795
|
+
statistics_html += statistics[key]
|
|
796
|
+
statistics_html += "</td>"
|
|
797
|
+
|
|
798
|
+
statistics_html += "</tr>"
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
d3.select("#tbody-statistics").html(statistics_html)
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function createOverallMetricResults() {
|
|
805
|
+
// create metrics table
|
|
806
|
+
metrics_html = ""
|
|
807
|
+
for (let key in overall_metric_results) {
|
|
808
|
+
if (overall_metric_results.hasOwnProperty(key)) {
|
|
809
|
+
metrics_html += "<tr>"
|
|
810
|
+
|
|
811
|
+
metrics_html += "<td>"
|
|
812
|
+
metrics_html += key
|
|
813
|
+
metrics_html += "</td>"
|
|
814
|
+
|
|
815
|
+
metrics_html += "<td>"
|
|
816
|
+
|
|
817
|
+
let valueString = String(overall_metric_results[key])
|
|
818
|
+
if (valueString.length > 30) {
|
|
819
|
+
valueString = valueString.substring(0, 32) + '...';
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
metrics_html += String(valueString)
|
|
823
|
+
metrics_html += "</td>"
|
|
824
|
+
|
|
825
|
+
metrics_html += "</tr>"
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
d3.select("#tbody-metrics").html(metrics_html)
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function createMetricsMenuEntries() {
|
|
832
|
+
// create apply metrics menu entries
|
|
833
|
+
applyMetricHtml = ""
|
|
834
|
+
|
|
835
|
+
console.log(currentMetricKeys)
|
|
836
|
+
|
|
837
|
+
for (let key in currentMetricKeys) {
|
|
838
|
+
|
|
839
|
+
if ( Object.keys(metricNameMap).includes(currentMetricKeys[key]) ) {
|
|
840
|
+
|
|
841
|
+
applyMetricHtml += '<li> <input data-value="'
|
|
842
|
+
applyMetricHtml += currentMetricKeys[key]
|
|
843
|
+
applyMetricHtml += '" type="checkbox" onclick="animateRadiusWithMetric(\''
|
|
844
|
+
applyMetricHtml += currentMetricKeys[key]
|
|
845
|
+
applyMetricHtml += '\');"/> <span id="'
|
|
846
|
+
applyMetricHtml += '" style="font-size:10px;">'
|
|
847
|
+
|
|
848
|
+
let visibleMetricName = metricNameMap[currentMetricKeys[key]]
|
|
849
|
+
|
|
850
|
+
applyMetricHtml += visibleMetricName
|
|
851
|
+
applyMetricHtml += '</span> <small><span id="'
|
|
852
|
+
applyMetricHtml += 'badge_' + currentMetricKeys[key]
|
|
853
|
+
applyMetricHtml += '" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" class="badge badge-primary badge-pill text-bg-primary"> ?</span> </small> </li>'
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
d3.select("#dropdown-apply-metric").html(applyMetricHtml)
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function getAnalysisName() {
|
|
861
|
+
let analysisName = analysis_config['analysis_name']
|
|
862
|
+
if (analysisName.length > 24) {
|
|
863
|
+
analysisName = analysisName.slice(0, 24) + '...'
|
|
864
|
+
}
|
|
865
|
+
return analysisName
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function addGraphTypeSelectionToMenu() {
|
|
869
|
+
graphSelectHtml = ""
|
|
870
|
+
for (let key in graphData) {
|
|
871
|
+
graphSelectHtml += '<button class="dropdown-item btn-sm" style="font-size: 10px;" type="button" onclick="startWithGraph(\''
|
|
872
|
+
graphSelectHtml += key
|
|
873
|
+
graphSelectHtml += '\');">'
|
|
874
|
+
graphSelectHtml += key.replace(/_/gi, " ")
|
|
875
|
+
graphSelectHtml += "</button>"
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
d3.select("#dropdown-graph").html(graphSelectHtml)
|
|
879
|
+
d3.select("#selectedGraph").text(currentGraphType.replace(/_/gi, " ").slice(0, 20) + '...')
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function increaseCurrentChargeForce() {
|
|
883
|
+
if (currentChargeForce < -50) {
|
|
884
|
+
currentChargeForce += 50
|
|
885
|
+
d3.select("#chargeForce").text(currentChargeForce)
|
|
886
|
+
simulation.force("charge", d3.forceManyBody().strength(currentChargeForce))
|
|
887
|
+
simulation.alpha(1).restart();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function decreaseCurrentChargeForce() {
|
|
892
|
+
currentChargeForce -= 50
|
|
893
|
+
d3.select("#chargeForce").text(currentChargeForce)
|
|
894
|
+
simulation.force("charge", d3.forceManyBody().strength(currentChargeForce))
|
|
895
|
+
simulation.alpha(1).restart();
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* * MARK: - heatmap
|
|
900
|
+
*/
|
|
901
|
+
|
|
902
|
+
function normalHeatmapIsActive() { return heatmapActive }
|
|
903
|
+
function mergedHeatmapIsActive() { return heatmapMerged }
|
|
904
|
+
function churnHeatmapIsActive() { return heatmapChurn }
|
|
905
|
+
function hotspotHeatmapIsActive() { return heatmapHotspot }
|
|
906
|
+
|
|
907
|
+
function initHoverCouplingSwitch() {
|
|
908
|
+
$("#switchHoverCoupling").on('change', function() {
|
|
909
|
+
hoverCoupling = $(this).is(':checked');
|
|
910
|
+
simulationUpdate();
|
|
911
|
+
})
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function initSemanticSearchSwitch() {
|
|
915
|
+
$("#switchAddSemanticSearch").on('change', function() {
|
|
916
|
+
addSemanticSearch = $(this).is(':checked');
|
|
917
|
+
searchResults = 0
|
|
918
|
+
simulationUpdate();
|
|
919
|
+
$('#inputNodeSearchLabel').text(searchResults + ' nodes found')
|
|
920
|
+
})
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function initContributorSearchSwitch() {
|
|
924
|
+
$("#switchAddContributorSearch").on('change', function() {
|
|
925
|
+
addContributorSearch = $(this).is(':checked');
|
|
926
|
+
searchResults = 0
|
|
927
|
+
simulationUpdate();
|
|
928
|
+
$('#inputNodeSearchLabel').text(searchResults + ' nodes found')
|
|
929
|
+
})
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function initAppConfig() {
|
|
933
|
+
$('#configEmergeVersion').text('Emerge ' + analysis_config['emerge_version'])
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function updateAppUI() {
|
|
937
|
+
if (fadeUnselectedNodes == true) {
|
|
938
|
+
$("#li-unselected-opacity").removeClass('d-none');
|
|
939
|
+
$("#unselected-opacity").removeClass('d-none');
|
|
940
|
+
} else {
|
|
941
|
+
$("#li-unselected-opacity").addClass('d-none');
|
|
942
|
+
$("#unselected-opacity").addClass('d-none');
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// currently only show git functionality for file dependency graphs
|
|
946
|
+
if (currentGraphType.includes('file_result_dependency_graph') && includeGitMetrics) {
|
|
947
|
+
$("#formSwitchChurnHeatmap").removeClass('d-none');
|
|
948
|
+
$("#formSwitchHotspotHeatmap").removeClass('d-none');
|
|
949
|
+
$("#button_complexity_churn").removeClass('d-none');
|
|
950
|
+
$("#button_change_coupling").removeClass('d-none');
|
|
951
|
+
$("#container_git_settings").removeClass('d-none');
|
|
952
|
+
$("#formSwitchHoverCoupling").removeClass('d-none');
|
|
953
|
+
$("#formSwitchAddContributorSearch").removeClass('d-none')
|
|
954
|
+
} else {
|
|
955
|
+
$("#formSwitchChurnHeatmap").addClass('d-none');
|
|
956
|
+
$("#formSwitchHotspotHeatmap").addClass('d-none');
|
|
957
|
+
$("#button_complexity_churn").addClass('d-none');
|
|
958
|
+
$("#button_change_coupling").addClass('d-none');
|
|
959
|
+
$("#container_git_settings").addClass('d-none');
|
|
960
|
+
$("#formSwitchHoverCoupling").addClass('d-none');
|
|
961
|
+
$("#formSwitchAddContributorSearch").addClass('d-none');
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function initAppUI() {
|
|
966
|
+
updateAppUI()
|
|
967
|
+
if (includeGitMetrics) {
|
|
968
|
+
initGitMetricsForDateRange()
|
|
969
|
+
initDateRangeUI()
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
initModals()
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* * MARK: - Helper functions
|
|
977
|
+
*/
|
|
978
|
+
|
|
979
|
+
// borrowed from https://stackoverflow.com/questions/5767325/how-can-i-remove-a-specific-item-from-an-array
|
|
980
|
+
function removeItemAll(arr, value) {
|
|
981
|
+
let i = 0;
|
|
982
|
+
while (i < arr.length) {
|
|
983
|
+
if (arr[i] === value) {
|
|
984
|
+
arr.splice(i, 1);
|
|
985
|
+
} else {
|
|
986
|
+
++i;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return arr;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
//https://stackoverflow.com/questions/21646738/convert-hex-to-rgba
|
|
993
|
+
function hexToRGB(hex, alpha) {
|
|
994
|
+
let r = parseInt(hex.slice(1, 3), 16),
|
|
995
|
+
g = parseInt(hex.slice(3, 5), 16),
|
|
996
|
+
b = parseInt(hex.slice(5, 7), 16);
|
|
997
|
+
|
|
998
|
+
if (alpha) {
|
|
999
|
+
return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
|
|
1000
|
+
} else {
|
|
1001
|
+
return "rgb(" + r + ", " + g + ", " + b + ")";
|
|
1002
|
+
}
|
|
1003
|
+
}
|