detecti-cli 2.0.0__py3-none-any.whl
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.
- detecti/__init__.py +0 -0
- detecti/cli.py +649 -0
- detecti/config.py +188 -0
- detecti/core/__init__.py +1 -0
- detecti/core/database/__init__.py +5 -0
- detecti/core/database/config_db.py +73 -0
- detecti/core/database/schema.py +136 -0
- detecti/core/database/storage.py +1388 -0
- detecti/core/engine.py +1032 -0
- detecti/core/models.py +278 -0
- detecti/data/config.sqlite +0 -0
- detecti/data/dbs/.gitkeep +2 -0
- detecti/data/dbs/example.com.sqlite +0 -0
- detecti/modules/__init__.py +29 -0
- detecti/modules/base.py +57 -0
- detecti/modules/censys.py +813 -0
- detecti/modules/crtsh.py +98 -0
- detecti/modules/exploitdb.py +138 -0
- detecti/modules/masscan.py +561 -0
- detecti/modules/nuclei.py +449 -0
- detecti/modules/nvd.py +300 -0
- detecti/modules/reverse_whois.py +225 -0
- detecti/modules/shodan.py +412 -0
- detecti/reporters/__init__.py +7 -0
- detecti/reporters/csv_reporter.py +74 -0
- detecti/reporters/html_reporter.py +356 -0
- detecti/reporters/json_reporter.py +26 -0
- detecti/reporters/markdown_reporter.py +203 -0
- detecti/utils/__init__.py +1 -0
- detecti/utils/http.py +294 -0
- detecti/utils/logger.py +378 -0
- detecti/utils/setup.py +453 -0
- detecti/web/__init__.py +6 -0
- detecti/web/api/__init__.py +1 -0
- detecti/web/api/auth.py +109 -0
- detecti/web/api/graph_builder.py +901 -0
- detecti/web/api/routes.py +1602 -0
- detecti/web/process_manager.py +283 -0
- detecti/web/server.py +183 -0
- detecti/web/static/android-chrome-192x192.png +0 -0
- detecti/web/static/android-chrome-512x512.png +0 -0
- detecti/web/static/apple-touch-icon.png +0 -0
- detecti/web/static/css/__init__.py +1 -0
- detecti/web/static/css/dashboard.css +3802 -0
- detecti/web/static/favicon-16x16.png +0 -0
- detecti/web/static/favicon-32x32.png +0 -0
- detecti/web/static/favicon.ico +0 -0
- detecti/web/static/img/DetecTI_Security_Logo.png +0 -0
- detecti/web/static/img/detecti-ico.png +0 -0
- detecti/web/static/index.html +677 -0
- detecti/web/static/js/__init__.py +1 -0
- detecti/web/static/js/api.js +177 -0
- detecti/web/static/js/cytoscape-cose-bilkent.js +458 -0
- detecti/web/static/js/cytoscape-dagre.js +397 -0
- detecti/web/static/js/cytoscape.min.js +31 -0
- detecti/web/static/js/dagre.min.js +3809 -0
- detecti/web/static/js/graph.js +7439 -0
- detecti/web/static/js/lucide.min.js +12 -0
- detecti/web/static/login.html +290 -0
- detecti/web/static/site.webmanifest +1 -0
- detecti_cli-2.0.0.dist-info/METADATA +554 -0
- detecti_cli-2.0.0.dist-info/RECORD +64 -0
- detecti_cli-2.0.0.dist-info/WHEEL +4 -0
- detecti_cli-2.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Static JavaScript files for DetecTI-CLI dashboard
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client for DetecTI-CLI EASM Dashboard
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
class APIClient {
|
|
6
|
+
constructor(baseURL = '/api/v1') {
|
|
7
|
+
this.baseURL = baseURL;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async request(endpoint, options = {}) {
|
|
11
|
+
const url = `${this.baseURL}${endpoint}`;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log(`Making API request to: ${url}`);
|
|
15
|
+
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
headers: {
|
|
18
|
+
'Accept': 'application/json',
|
|
19
|
+
...options.headers
|
|
20
|
+
},
|
|
21
|
+
...options
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log(`API response status: ${response.status}`);
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const errorText = await response.text();
|
|
28
|
+
console.error(`API error response: ${errorText}`);
|
|
29
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
console.log(`API response data:`, data);
|
|
34
|
+
return data;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(`API request failed: ${endpoint}`, error);
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getSummary() {
|
|
42
|
+
return this.request('/summary');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getGraphData() {
|
|
46
|
+
return this.request('/graph');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Removed getLeads() - Lead Selector is now 100% frontend-based using graph data
|
|
50
|
+
|
|
51
|
+
async getAssets() {
|
|
52
|
+
return this.request('/assets');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getDatabases() {
|
|
56
|
+
return this.request('/databases');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async selectDatabase(dbName) {
|
|
60
|
+
return this.request('/databases/select', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json'
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({ name: dbName })
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async deleteDatabase(dbName) {
|
|
70
|
+
return this.request('/databases/delete', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json'
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify({ name: dbName })
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getExportUrl(format = 'json') {
|
|
80
|
+
return `${this.baseURL}/export?format=${format}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Target Management & Active Scan APIs
|
|
84
|
+
async getTargets() {
|
|
85
|
+
return this.request('/targets');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async setTarget(target) {
|
|
89
|
+
return this.request('/targets/set', {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
headers: {
|
|
92
|
+
'Content-Type': 'application/json'
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify({ target: target, ip: target })
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async removeTarget(target) {
|
|
99
|
+
return this.request('/targets/remove', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: {
|
|
102
|
+
'Content-Type': 'application/json'
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({ target: target, ip: target })
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async clearTargets() {
|
|
109
|
+
return this.request('/targets/clear', {
|
|
110
|
+
method: 'POST'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async checkScanPermissions() {
|
|
115
|
+
return this.request('/scan/check-permissions');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async startActiveScan(config = {}) {
|
|
119
|
+
return this.request('/scan/active', {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json'
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify(config)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async startNucleiScan(config = {}) {
|
|
129
|
+
return this.request('/scan/nuclei', {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'application/json'
|
|
133
|
+
},
|
|
134
|
+
body: JSON.stringify(config)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async cancelActiveScan(target = null, all = false, scanType = 'all') {
|
|
139
|
+
return this.request('/scan/cancel', {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: {
|
|
142
|
+
'Content-Type': 'application/json'
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify({ target: target, all: all, scan_type: scanType })
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async unverifyServices(serviceIds = [], ipAddresses = []) {
|
|
149
|
+
return this.request('/services/unverify', {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
headers: {
|
|
152
|
+
'Content-Type': 'application/json'
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
service_ids: serviceIds,
|
|
156
|
+
ip_addresses: ipAddresses
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async getScanStatus() {
|
|
162
|
+
return this.request('/scan/status');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getScanLogs(limit = 100, target = null) {
|
|
166
|
+
let endpoint = `/scan/logs?limit=${encodeURIComponent(limit)}`;
|
|
167
|
+
if (target) {
|
|
168
|
+
endpoint += `&target=${encodeURIComponent(target)}`;
|
|
169
|
+
}
|
|
170
|
+
return this.request(endpoint);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Global API client instance
|
|
175
|
+
window.api = new APIClient();
|
|
176
|
+
|
|
177
|
+
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
(function webpackUniversalModuleDefinition(root, factory) {
|
|
2
|
+
if(typeof exports === 'object' && typeof module === 'object')
|
|
3
|
+
module.exports = factory(require("cose-base"));
|
|
4
|
+
else if(typeof define === 'function' && define.amd)
|
|
5
|
+
define(["cose-base"], factory);
|
|
6
|
+
else if(typeof exports === 'object')
|
|
7
|
+
exports["cytoscapeCoseBilkent"] = factory(require("cose-base"));
|
|
8
|
+
else
|
|
9
|
+
root["cytoscapeCoseBilkent"] = factory(root["coseBase"]);
|
|
10
|
+
})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {
|
|
11
|
+
return /******/ (function(modules) { // webpackBootstrap
|
|
12
|
+
/******/ // The module cache
|
|
13
|
+
/******/ var installedModules = {};
|
|
14
|
+
/******/
|
|
15
|
+
/******/ // The require function
|
|
16
|
+
/******/ function __webpack_require__(moduleId) {
|
|
17
|
+
/******/
|
|
18
|
+
/******/ // Check if module is in cache
|
|
19
|
+
/******/ if(installedModules[moduleId]) {
|
|
20
|
+
/******/ return installedModules[moduleId].exports;
|
|
21
|
+
/******/ }
|
|
22
|
+
/******/ // Create a new module (and put it into the cache)
|
|
23
|
+
/******/ var module = installedModules[moduleId] = {
|
|
24
|
+
/******/ i: moduleId,
|
|
25
|
+
/******/ l: false,
|
|
26
|
+
/******/ exports: {}
|
|
27
|
+
/******/ };
|
|
28
|
+
/******/
|
|
29
|
+
/******/ // Execute the module function
|
|
30
|
+
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
31
|
+
/******/
|
|
32
|
+
/******/ // Flag the module as loaded
|
|
33
|
+
/******/ module.l = true;
|
|
34
|
+
/******/
|
|
35
|
+
/******/ // Return the exports of the module
|
|
36
|
+
/******/ return module.exports;
|
|
37
|
+
/******/ }
|
|
38
|
+
/******/
|
|
39
|
+
/******/
|
|
40
|
+
/******/ // expose the modules object (__webpack_modules__)
|
|
41
|
+
/******/ __webpack_require__.m = modules;
|
|
42
|
+
/******/
|
|
43
|
+
/******/ // expose the module cache
|
|
44
|
+
/******/ __webpack_require__.c = installedModules;
|
|
45
|
+
/******/
|
|
46
|
+
/******/ // identity function for calling harmony imports with the correct context
|
|
47
|
+
/******/ __webpack_require__.i = function(value) { return value; };
|
|
48
|
+
/******/
|
|
49
|
+
/******/ // define getter function for harmony exports
|
|
50
|
+
/******/ __webpack_require__.d = function(exports, name, getter) {
|
|
51
|
+
/******/ if(!__webpack_require__.o(exports, name)) {
|
|
52
|
+
/******/ Object.defineProperty(exports, name, {
|
|
53
|
+
/******/ configurable: false,
|
|
54
|
+
/******/ enumerable: true,
|
|
55
|
+
/******/ get: getter
|
|
56
|
+
/******/ });
|
|
57
|
+
/******/ }
|
|
58
|
+
/******/ };
|
|
59
|
+
/******/
|
|
60
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
61
|
+
/******/ __webpack_require__.n = function(module) {
|
|
62
|
+
/******/ var getter = module && module.__esModule ?
|
|
63
|
+
/******/ function getDefault() { return module['default']; } :
|
|
64
|
+
/******/ function getModuleExports() { return module; };
|
|
65
|
+
/******/ __webpack_require__.d(getter, 'a', getter);
|
|
66
|
+
/******/ return getter;
|
|
67
|
+
/******/ };
|
|
68
|
+
/******/
|
|
69
|
+
/******/ // Object.prototype.hasOwnProperty.call
|
|
70
|
+
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
|
71
|
+
/******/
|
|
72
|
+
/******/ // __webpack_public_path__
|
|
73
|
+
/******/ __webpack_require__.p = "";
|
|
74
|
+
/******/
|
|
75
|
+
/******/ // Load entry module and return exports
|
|
76
|
+
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
|
77
|
+
/******/ })
|
|
78
|
+
/************************************************************************/
|
|
79
|
+
/******/ ([
|
|
80
|
+
/* 0 */
|
|
81
|
+
/***/ (function(module, exports) {
|
|
82
|
+
|
|
83
|
+
module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
|
|
84
|
+
|
|
85
|
+
/***/ }),
|
|
86
|
+
/* 1 */
|
|
87
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
88
|
+
|
|
89
|
+
"use strict";
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
var LayoutConstants = __webpack_require__(0).layoutBase.LayoutConstants;
|
|
93
|
+
var FDLayoutConstants = __webpack_require__(0).layoutBase.FDLayoutConstants;
|
|
94
|
+
var CoSEConstants = __webpack_require__(0).CoSEConstants;
|
|
95
|
+
var CoSELayout = __webpack_require__(0).CoSELayout;
|
|
96
|
+
var CoSENode = __webpack_require__(0).CoSENode;
|
|
97
|
+
var PointD = __webpack_require__(0).layoutBase.PointD;
|
|
98
|
+
var DimensionD = __webpack_require__(0).layoutBase.DimensionD;
|
|
99
|
+
|
|
100
|
+
var defaults = {
|
|
101
|
+
// Called on `layoutready`
|
|
102
|
+
ready: function ready() {},
|
|
103
|
+
// Called on `layoutstop`
|
|
104
|
+
stop: function stop() {},
|
|
105
|
+
// 'draft', 'default' or 'proof"
|
|
106
|
+
// - 'draft' fast cooling rate
|
|
107
|
+
// - 'default' moderate cooling rate
|
|
108
|
+
// - "proof" slow cooling rate
|
|
109
|
+
quality: 'default',
|
|
110
|
+
// include labels in node dimensions
|
|
111
|
+
nodeDimensionsIncludeLabels: false,
|
|
112
|
+
// number of ticks per frame; higher is faster but more jerky
|
|
113
|
+
refresh: 30,
|
|
114
|
+
// Whether to fit the network view after when done
|
|
115
|
+
fit: true,
|
|
116
|
+
// Padding on fit
|
|
117
|
+
padding: 10,
|
|
118
|
+
// Whether to enable incremental mode
|
|
119
|
+
randomize: true,
|
|
120
|
+
// Node repulsion (non overlapping) multiplier
|
|
121
|
+
nodeRepulsion: 4500,
|
|
122
|
+
// Ideal edge (non nested) length
|
|
123
|
+
idealEdgeLength: 50,
|
|
124
|
+
// Divisor to compute edge forces
|
|
125
|
+
edgeElasticity: 0.45,
|
|
126
|
+
// Nesting factor (multiplier) to compute ideal edge length for nested edges
|
|
127
|
+
nestingFactor: 0.1,
|
|
128
|
+
// Gravity force (constant)
|
|
129
|
+
gravity: 0.25,
|
|
130
|
+
// Maximum number of iterations to perform
|
|
131
|
+
numIter: 2500,
|
|
132
|
+
// For enabling tiling
|
|
133
|
+
tile: true,
|
|
134
|
+
// Type of layout animation. The option set is {'during', 'end', false}
|
|
135
|
+
animate: 'end',
|
|
136
|
+
// Duration for animate:end
|
|
137
|
+
animationDuration: 500,
|
|
138
|
+
// Represents the amount of the vertical space to put between the zero degree members during the tiling operation(can also be a function)
|
|
139
|
+
tilingPaddingVertical: 10,
|
|
140
|
+
// Represents the amount of the horizontal space to put between the zero degree members during the tiling operation(can also be a function)
|
|
141
|
+
tilingPaddingHorizontal: 10,
|
|
142
|
+
// Gravity range (constant) for compounds
|
|
143
|
+
gravityRangeCompound: 1.5,
|
|
144
|
+
// Gravity force (constant) for compounds
|
|
145
|
+
gravityCompound: 1.0,
|
|
146
|
+
// Gravity range (constant)
|
|
147
|
+
gravityRange: 3.8,
|
|
148
|
+
// Initial cooling factor for incremental layout
|
|
149
|
+
initialEnergyOnIncremental: 0.5
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
function extend(defaults, options) {
|
|
153
|
+
var obj = {};
|
|
154
|
+
|
|
155
|
+
for (var i in defaults) {
|
|
156
|
+
obj[i] = defaults[i];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (var i in options) {
|
|
160
|
+
obj[i] = options[i];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return obj;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
function _CoSELayout(_options) {
|
|
167
|
+
this.options = extend(defaults, _options);
|
|
168
|
+
getUserOptions(this.options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
var getUserOptions = function getUserOptions(options) {
|
|
172
|
+
if (options.nodeRepulsion != null) CoSEConstants.DEFAULT_REPULSION_STRENGTH = FDLayoutConstants.DEFAULT_REPULSION_STRENGTH = options.nodeRepulsion;
|
|
173
|
+
if (options.idealEdgeLength != null) CoSEConstants.DEFAULT_EDGE_LENGTH = FDLayoutConstants.DEFAULT_EDGE_LENGTH = options.idealEdgeLength;
|
|
174
|
+
if (options.edgeElasticity != null) CoSEConstants.DEFAULT_SPRING_STRENGTH = FDLayoutConstants.DEFAULT_SPRING_STRENGTH = options.edgeElasticity;
|
|
175
|
+
if (options.nestingFactor != null) CoSEConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = FDLayoutConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = options.nestingFactor;
|
|
176
|
+
if (options.gravity != null) CoSEConstants.DEFAULT_GRAVITY_STRENGTH = FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH = options.gravity;
|
|
177
|
+
if (options.numIter != null) CoSEConstants.MAX_ITERATIONS = FDLayoutConstants.MAX_ITERATIONS = options.numIter;
|
|
178
|
+
if (options.gravityRange != null) CoSEConstants.DEFAULT_GRAVITY_RANGE_FACTOR = FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR = options.gravityRange;
|
|
179
|
+
if (options.gravityCompound != null) CoSEConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = options.gravityCompound;
|
|
180
|
+
if (options.gravityRangeCompound != null) CoSEConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = options.gravityRangeCompound;
|
|
181
|
+
if (options.initialEnergyOnIncremental != null) CoSEConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = options.initialEnergyOnIncremental;
|
|
182
|
+
|
|
183
|
+
if (options.quality == 'draft') LayoutConstants.QUALITY = 0;else if (options.quality == 'proof') LayoutConstants.QUALITY = 2;else LayoutConstants.QUALITY = 1;
|
|
184
|
+
|
|
185
|
+
CoSEConstants.NODE_DIMENSIONS_INCLUDE_LABELS = FDLayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = LayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = options.nodeDimensionsIncludeLabels;
|
|
186
|
+
CoSEConstants.DEFAULT_INCREMENTAL = FDLayoutConstants.DEFAULT_INCREMENTAL = LayoutConstants.DEFAULT_INCREMENTAL = !options.randomize;
|
|
187
|
+
CoSEConstants.ANIMATE = FDLayoutConstants.ANIMATE = LayoutConstants.ANIMATE = options.animate;
|
|
188
|
+
CoSEConstants.TILE = options.tile;
|
|
189
|
+
CoSEConstants.TILING_PADDING_VERTICAL = typeof options.tilingPaddingVertical === 'function' ? options.tilingPaddingVertical.call() : options.tilingPaddingVertical;
|
|
190
|
+
CoSEConstants.TILING_PADDING_HORIZONTAL = typeof options.tilingPaddingHorizontal === 'function' ? options.tilingPaddingHorizontal.call() : options.tilingPaddingHorizontal;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
_CoSELayout.prototype.run = function () {
|
|
194
|
+
var ready;
|
|
195
|
+
var frameId;
|
|
196
|
+
var options = this.options;
|
|
197
|
+
var idToLNode = this.idToLNode = {};
|
|
198
|
+
var layout = this.layout = new CoSELayout();
|
|
199
|
+
var self = this;
|
|
200
|
+
|
|
201
|
+
self.stopped = false;
|
|
202
|
+
|
|
203
|
+
this.cy = this.options.cy;
|
|
204
|
+
|
|
205
|
+
this.cy.trigger({ type: 'layoutstart', layout: this });
|
|
206
|
+
|
|
207
|
+
var gm = layout.newGraphManager();
|
|
208
|
+
this.gm = gm;
|
|
209
|
+
|
|
210
|
+
var nodes = this.options.eles.nodes();
|
|
211
|
+
var edges = this.options.eles.edges();
|
|
212
|
+
|
|
213
|
+
this.root = gm.addRoot();
|
|
214
|
+
this.processChildrenList(this.root, this.getTopMostNodes(nodes), layout);
|
|
215
|
+
|
|
216
|
+
for (var i = 0; i < edges.length; i++) {
|
|
217
|
+
var edge = edges[i];
|
|
218
|
+
var sourceNode = this.idToLNode[edge.data("source")];
|
|
219
|
+
var targetNode = this.idToLNode[edge.data("target")];
|
|
220
|
+
if (sourceNode !== targetNode && sourceNode.getEdgesBetween(targetNode).length == 0) {
|
|
221
|
+
var e1 = gm.add(layout.newEdge(), sourceNode, targetNode);
|
|
222
|
+
e1.id = edge.id();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
var getPositions = function getPositions(ele, i) {
|
|
227
|
+
if (typeof ele === "number") {
|
|
228
|
+
ele = i;
|
|
229
|
+
}
|
|
230
|
+
var theId = ele.data('id');
|
|
231
|
+
var lNode = self.idToLNode[theId];
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
x: lNode.getRect().getCenterX(),
|
|
235
|
+
y: lNode.getRect().getCenterY()
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/*
|
|
240
|
+
* Reposition nodes in iterations animatedly
|
|
241
|
+
*/
|
|
242
|
+
var iterateAnimated = function iterateAnimated() {
|
|
243
|
+
// Thigs to perform after nodes are repositioned on screen
|
|
244
|
+
var afterReposition = function afterReposition() {
|
|
245
|
+
if (options.fit) {
|
|
246
|
+
options.cy.fit(options.eles, options.padding);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!ready) {
|
|
250
|
+
ready = true;
|
|
251
|
+
self.cy.one('layoutready', options.ready);
|
|
252
|
+
self.cy.trigger({ type: 'layoutready', layout: self });
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
var ticksPerFrame = self.options.refresh;
|
|
257
|
+
var isDone;
|
|
258
|
+
|
|
259
|
+
for (var i = 0; i < ticksPerFrame && !isDone; i++) {
|
|
260
|
+
isDone = self.stopped || self.layout.tick();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// If layout is done
|
|
264
|
+
if (isDone) {
|
|
265
|
+
// If the layout is not a sublayout and it is successful perform post layout.
|
|
266
|
+
if (layout.checkLayoutSuccess() && !layout.isSubLayout) {
|
|
267
|
+
layout.doPostLayout();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// If layout has a tilingPostLayout function property call it.
|
|
271
|
+
if (layout.tilingPostLayout) {
|
|
272
|
+
layout.tilingPostLayout();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
layout.isLayoutFinished = true;
|
|
276
|
+
|
|
277
|
+
self.options.eles.nodes().positions(getPositions);
|
|
278
|
+
|
|
279
|
+
afterReposition();
|
|
280
|
+
|
|
281
|
+
// trigger layoutstop when the layout stops (e.g. finishes)
|
|
282
|
+
self.cy.one('layoutstop', self.options.stop);
|
|
283
|
+
self.cy.trigger({ type: 'layoutstop', layout: self });
|
|
284
|
+
|
|
285
|
+
if (frameId) {
|
|
286
|
+
cancelAnimationFrame(frameId);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
ready = false;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
var animationData = self.layout.getPositionsData(); // Get positions of layout nodes note that all nodes may not be layout nodes because of tiling
|
|
294
|
+
|
|
295
|
+
// Position nodes, for the nodes whose id does not included in data (because they are removed from their parents and included in dummy compounds)
|
|
296
|
+
// use position of their ancestors or dummy ancestors
|
|
297
|
+
options.eles.nodes().positions(function (ele, i) {
|
|
298
|
+
if (typeof ele === "number") {
|
|
299
|
+
ele = i;
|
|
300
|
+
}
|
|
301
|
+
// If ele is a compound node, then its position will be defined by its children
|
|
302
|
+
if (!ele.isParent()) {
|
|
303
|
+
var theId = ele.id();
|
|
304
|
+
var pNode = animationData[theId];
|
|
305
|
+
var temp = ele;
|
|
306
|
+
// If pNode is undefined search until finding position data of its first ancestor (It may be dummy as well)
|
|
307
|
+
while (pNode == null) {
|
|
308
|
+
pNode = animationData[temp.data('parent')] || animationData['DummyCompound_' + temp.data('parent')];
|
|
309
|
+
animationData[theId] = pNode;
|
|
310
|
+
temp = temp.parent()[0];
|
|
311
|
+
if (temp == undefined) {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (pNode != null) {
|
|
316
|
+
return {
|
|
317
|
+
x: pNode.x,
|
|
318
|
+
y: pNode.y
|
|
319
|
+
};
|
|
320
|
+
} else {
|
|
321
|
+
return {
|
|
322
|
+
x: ele.position('x'),
|
|
323
|
+
y: ele.position('y')
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
afterReposition();
|
|
330
|
+
|
|
331
|
+
frameId = requestAnimationFrame(iterateAnimated);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/*
|
|
335
|
+
* Listen 'layoutstarted' event and start animated iteration if animate option is 'during'
|
|
336
|
+
*/
|
|
337
|
+
layout.addListener('layoutstarted', function () {
|
|
338
|
+
if (self.options.animate === 'during') {
|
|
339
|
+
frameId = requestAnimationFrame(iterateAnimated);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
layout.runLayout(); // Run cose layout
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
* If animate option is not 'during' ('end' or false) perform these here (If it is 'during' similar things are already performed)
|
|
347
|
+
*/
|
|
348
|
+
if (this.options.animate !== "during") {
|
|
349
|
+
self.options.eles.nodes().not(":parent").layoutPositions(self, self.options, getPositions); // Use layout positions to reposition the nodes it considers the options parameter
|
|
350
|
+
ready = false;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return this; // chaining
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
//Get the top most ones of a list of nodes
|
|
357
|
+
_CoSELayout.prototype.getTopMostNodes = function (nodes) {
|
|
358
|
+
var nodesMap = {};
|
|
359
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
360
|
+
nodesMap[nodes[i].id()] = true;
|
|
361
|
+
}
|
|
362
|
+
var roots = nodes.filter(function (ele, i) {
|
|
363
|
+
if (typeof ele === "number") {
|
|
364
|
+
ele = i;
|
|
365
|
+
}
|
|
366
|
+
var parent = ele.parent()[0];
|
|
367
|
+
while (parent != null) {
|
|
368
|
+
if (nodesMap[parent.id()]) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
parent = parent.parent()[0];
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return roots;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
_CoSELayout.prototype.processChildrenList = function (parent, children, layout) {
|
|
380
|
+
var size = children.length;
|
|
381
|
+
for (var i = 0; i < size; i++) {
|
|
382
|
+
var theChild = children[i];
|
|
383
|
+
var children_of_children = theChild.children();
|
|
384
|
+
var theNode;
|
|
385
|
+
|
|
386
|
+
var dimensions = theChild.layoutDimensions({
|
|
387
|
+
nodeDimensionsIncludeLabels: this.options.nodeDimensionsIncludeLabels
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (theChild.outerWidth() != null && theChild.outerHeight() != null) {
|
|
391
|
+
theNode = parent.add(new CoSENode(layout.graphManager, new PointD(theChild.position('x') - dimensions.w / 2, theChild.position('y') - dimensions.h / 2), new DimensionD(parseFloat(dimensions.w), parseFloat(dimensions.h))));
|
|
392
|
+
} else {
|
|
393
|
+
theNode = parent.add(new CoSENode(this.graphManager));
|
|
394
|
+
}
|
|
395
|
+
// Attach id to the layout node
|
|
396
|
+
theNode.id = theChild.data("id");
|
|
397
|
+
// Attach the paddings of cy node to layout node
|
|
398
|
+
theNode.paddingLeft = parseInt(theChild.css('padding'));
|
|
399
|
+
theNode.paddingTop = parseInt(theChild.css('padding'));
|
|
400
|
+
theNode.paddingRight = parseInt(theChild.css('padding'));
|
|
401
|
+
theNode.paddingBottom = parseInt(theChild.css('padding'));
|
|
402
|
+
|
|
403
|
+
//Attach the label properties to compound if labels will be included in node dimensions
|
|
404
|
+
if (this.options.nodeDimensionsIncludeLabels) {
|
|
405
|
+
if (theChild.isParent()) {
|
|
406
|
+
var labelWidth = theChild.boundingBox({ includeLabels: true, includeNodes: false }).w;
|
|
407
|
+
var labelHeight = theChild.boundingBox({ includeLabels: true, includeNodes: false }).h;
|
|
408
|
+
var labelPos = theChild.css("text-halign");
|
|
409
|
+
theNode.labelWidth = labelWidth;
|
|
410
|
+
theNode.labelHeight = labelHeight;
|
|
411
|
+
theNode.labelPos = labelPos;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Map the layout node
|
|
416
|
+
this.idToLNode[theChild.data("id")] = theNode;
|
|
417
|
+
|
|
418
|
+
if (isNaN(theNode.rect.x)) {
|
|
419
|
+
theNode.rect.x = 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (isNaN(theNode.rect.y)) {
|
|
423
|
+
theNode.rect.y = 0;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (children_of_children != null && children_of_children.length > 0) {
|
|
427
|
+
var theNewGraph;
|
|
428
|
+
theNewGraph = layout.getGraphManager().add(layout.newGraph(), theNode);
|
|
429
|
+
this.processChildrenList(theNewGraph, children_of_children, layout);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @brief : called on continuous layouts to stop them before they finish
|
|
436
|
+
*/
|
|
437
|
+
_CoSELayout.prototype.stop = function () {
|
|
438
|
+
this.stopped = true;
|
|
439
|
+
|
|
440
|
+
return this; // chaining
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
var register = function register(cytoscape) {
|
|
444
|
+
// var Layout = getLayout( cytoscape );
|
|
445
|
+
|
|
446
|
+
cytoscape('layout', 'cose-bilkent', _CoSELayout);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// auto reg for globals
|
|
450
|
+
if (typeof cytoscape !== 'undefined') {
|
|
451
|
+
register(cytoscape);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
module.exports = register;
|
|
455
|
+
|
|
456
|
+
/***/ })
|
|
457
|
+
/******/ ]);
|
|
458
|
+
});
|