1(function (root, factory) { 2 if (typeof define === 'function' && define.amd) { 3 define([], factory); 4 } else if (typeof module === 'object' && module.exports) { 5 module.exports = factory(); 6 } else { 7 root.insight = factory(); 8 } 9} (this, function () { 10 'use strict'; 11 12 let document; 13 let strsData, mods, tagIds; 14 let domPathInput, domFuzzyMatch; 15 let domTBody; 16 17 //-------------------------------------------------------------------------- 18 // DOM Helper Functions 19 //-------------------------------------------------------------------------- 20 function domNewText(text) { 21 return document.createTextNode(text); 22 } 23 24 function domNewElem(type) { 25 let dom = document.createElement(type); 26 for (let i = 1; i < arguments.length; ++i) { 27 let arg = arguments[i]; 28 if (typeof(arg) == 'string' || typeof(arg) == 'number') { 29 arg = domNewText(arg) 30 } 31 dom.appendChild(arg); 32 } 33 return dom; 34 } 35 36 function domNewLink(text, onClick) { 37 let dom = domNewElem('a', text); 38 dom.setAttribute('href', '#'); 39 dom.addEventListener('click', onClick); 40 return dom; 41 } 42 43 //-------------------------------------------------------------------------- 44 // Module Row 45 //-------------------------------------------------------------------------- 46 function countDeps(deps) { 47 let direct = 0; 48 let indirect = 0; 49 if (deps.length > 0) { 50 direct = deps[0].length; 51 for (let i = 1; i < deps.length; ++i) { 52 indirect += deps[i].length; 53 } 54 } 55 return [direct, indirect]; 56 } 57 58 function Module(id, modData) { 59 this.id = id; 60 this.path = strsData[modData[0]]; 61 this.cls = modData[1]; 62 this.tagIds = new Set(modData[2]); 63 this.deps = modData[3]; 64 this.users = modData[4]; 65 this.srcDirs = modData[5].map(function (x) { return strsData[x]; }); 66 67 [this.numDirectDeps, this.numIndirectDeps] = countDeps(this.deps); 68 this.numUsers = this.users.length; 69 70 this.dom = null; 71 this.visible = false; 72 73 this.linkDoms = Object.create(null); 74 } 75 76 Module.prototype.isTagged = function (tagId) { 77 return this.tagIds.has(tagId); 78 } 79 80 Module.prototype.createModuleLinkDom = function (mod) { 81 let dom = domNewElem('a', mod.path); 82 dom.setAttribute('href', '#mod_' + mod.id); 83 dom.setAttribute('data-mod-id', mod.id); 84 dom.setAttribute('data-owner-id', this.id); 85 dom.addEventListener('click', onModuleLinkClicked); 86 dom.addEventListener('mouseover', onModuleLinkMouseOver); 87 dom.addEventListener('mouseout', onModuleLinkMouseOut); 88 89 this.linkDoms[mod.id] = dom; 90 91 return dom; 92 } 93 94 Module.prototype.createModuleRelationsDom = function (parent, label, 95 modIds) { 96 parent.appendChild(domNewElem('h2', label)); 97 98 let domOl = domNewElem('ol'); 99 parent.appendChild(domOl); 100 for (let modId of modIds) { 101 domOl.appendChild( 102 domNewElem('li', this.createModuleLinkDom(mods[modId]))); 103 } 104 } 105 106 Module.prototype.createModulePathTdDom = function (parent) { 107 let domTd = domNewElem('td'); 108 domTd.appendChild(domNewElem('p', this.createModuleLinkDom(this))); 109 for (let dir of this.srcDirs) { 110 let domP = domNewElem('p', 'source: ' + dir); 111 domP.setAttribute('class', 'module_src_dir'); 112 domTd.appendChild(domP); 113 } 114 parent.appendChild(domTd); 115 } 116 117 Module.prototype.createTagsTdDom = function (parent) { 118 let domTd = domNewElem('td'); 119 for (let tag of this.tagIds) { 120 domTd.appendChild(domNewElem('p', strsData[tag])); 121 } 122 parent.appendChild(domTd); 123 } 124 125 Module.prototype.createDepsTdDom = function (parent) { 126 let domTd = domNewElem( 127 'td', this.numDirectDeps + ' + ' + this.numIndirectDeps); 128 129 let deps = this.deps; 130 if (deps.length > 0) { 131 this.createModuleRelationsDom(domTd, 'Direct', deps[0]); 132 133 for (let i = 1; i < deps.length; ++i) { 134 this.createModuleRelationsDom(domTd, 'Indirect #' + i, deps[i]); 135 } 136 } 137 138 parent.appendChild(domTd); 139 } 140 141 Module.prototype.createUsersTdDom = function (parent) { 142 let domTd = domNewElem('td', this.numUsers); 143 144 let users = this.users; 145 if (users.length > 0) { 146 this.createModuleRelationsDom(domTd, 'Direct', users); 147 } 148 149 parent.appendChild(domTd); 150 } 151 152 Module.prototype.createDom = function () { 153 let dom = this.dom = domNewElem('tr'); 154 dom.setAttribute('id', 'mod_' + this.id); 155 156 this.createModulePathTdDom(dom); 157 this.createTagsTdDom(dom); 158 this.createDepsTdDom(dom); 159 this.createUsersTdDom(dom) 160 } 161 162 Module.prototype.showDom = function () { 163 if (this.visible) { 164 return; 165 } 166 domTBody.appendChild(this.dom); 167 this.visible = true; 168 } 169 170 Module.prototype.hideDom = function () { 171 if (!this.visible) { 172 return; 173 } 174 this.dom.parentNode.removeChild(this.dom); 175 this.visible = false; 176 } 177 178 function createModulesFromData(stringsData, modulesData) { 179 return modulesData.map(function (modData, id) { 180 return new Module(id, modData); 181 }); 182 } 183 184 function createTagIdsFromData(stringsData, mods) { 185 let tagIds = new Set(); 186 for (let mod of mods) { 187 for (let tag of mod.tagIds) { 188 tagIds.add(tag); 189 } 190 } 191 192 tagIds = Array.from(tagIds); 193 tagIds.sort(function (a, b) { 194 return strsData[a].localeCompare(strsData[b]); 195 }); 196 197 return tagIds; 198 } 199 200 //-------------------------------------------------------------------------- 201 // Data 202 //-------------------------------------------------------------------------- 203 function init(doc, stringsData, modulesData) { 204 document = doc; 205 strsData = stringsData; 206 207 mods = createModulesFromData(stringsData, modulesData); 208 tagIds = createTagIdsFromData(stringsData, mods); 209 210 document.addEventListener('DOMContentLoaded', function (evt) { 211 createControlDom(document.body); 212 createTableDom(document.body); 213 }); 214 } 215 216 //-------------------------------------------------------------------------- 217 // Control 218 //-------------------------------------------------------------------------- 219 function createControlDom(parent) { 220 let domTBody = domNewElem('tbody'); 221 222 createSelectionTrDom(domTBody); 223 createAddByTagsTrDom(domTBody); 224 createAddByPathTrDom(domTBody); 225 226 let domTable = domNewElem('table', domTBody); 227 domTable.id = 'control'; 228 229 let domFixedLink = domNewElem('a', 'Menu'); 230 domFixedLink.href = '#control'; 231 domFixedLink.id = 'control_menu'; 232 233 parent.appendChild(domFixedLink); 234 parent.appendChild(domTable); 235 } 236 237 function createControlMenuTr(parent, label, items) { 238 let domUl = domNewElem('ul'); 239 domUl.className = 'menu'; 240 for (let [txt, callback] of items) { 241 domUl.appendChild(domNewElem('li', domNewLink(txt, callback))); 242 } 243 244 let domTr = domNewElem('tr', 245 createControlLabelTdDom(label), 246 domNewElem('td', domUl)); 247 248 parent.appendChild(domTr); 249 } 250 251 function createSelectionTrDom(parent) { 252 const items = [ 253 ['All', onAddAll], 254 ['32-bit', onAddAll32], 255 ['64-bit', onAddAll64], 256 ['Clear', onClear], 257 ]; 258 259 createControlMenuTr(parent, 'Selection:', items); 260 } 261 262 function createAddByTagsTrDom(parent) { 263 if (tagIds.length == 0) { 264 return; 265 } 266 267 const items = tagIds.map(function (tagId) { 268 return [strsData[tagId], function (evt) { 269 evt.preventDefault(true); 270 showModulesByTagId(tagId); 271 }]; 272 }); 273 274 createControlMenuTr(parent, 'Add by Tags:', items); 275 } 276 277 function createAddByPathTrDom(parent) { 278 let domForm = domNewElem('form'); 279 domForm.addEventListener('submit', onAddModuleByPath); 280 281 domPathInput = domNewElem('input'); 282 domPathInput.type = 'text'; 283 domForm.appendChild(domPathInput); 284 285 let domBtn = domNewElem('input'); 286 domBtn.type = 'submit'; 287 domBtn.value = 'Add'; 288 domForm.appendChild(domBtn); 289 290 domFuzzyMatch = domNewElem('input'); 291 domFuzzyMatch.setAttribute('id', 'fuzzy_match'); 292 domFuzzyMatch.setAttribute('type', 'checkbox'); 293 domFuzzyMatch.setAttribute('checked', 'checked'); 294 domForm.appendChild(domFuzzyMatch); 295 296 let domFuzzyMatchLabel = domNewElem('label', 'Fuzzy Match'); 297 domFuzzyMatchLabel.setAttribute('for', 'fuzzy_match'); 298 domForm.appendChild(domFuzzyMatchLabel); 299 300 let domTr = domNewElem('tr', 301 createControlLabelTdDom('Add by Path:'), 302 domNewElem('td', domForm)); 303 304 parent.appendChild(domTr); 305 } 306 307 function createControlLabelTdDom(text) { 308 return domNewElem('td', domNewElem('strong', text)); 309 } 310 311 312 //-------------------------------------------------------------------------- 313 // Table 314 //-------------------------------------------------------------------------- 315 function createTableDom(parent) { 316 domTBody = domNewElem('tbody'); 317 domTBody.id = 'module_tbody'; 318 319 createTableHeaderDom(domTBody); 320 createAllModulesDom(); 321 showAllModules(); 322 323 let domTable = domNewElem('table', domTBody); 324 domTable.id = 'module_table'; 325 326 parent.appendChild(domTable); 327 } 328 329 function createTableHeaderDom(parent) { 330 const labels = [ 331 'Name', 332 'Tags', 333 'Dependencies (Direct + Indirect)', 334 'Users', 335 ]; 336 337 let domTr = domNewElem('tr'); 338 for (let label of labels) { 339 domTr.appendChild(domNewElem('th', label)); 340 } 341 342 parent.appendChild(domTr); 343 } 344 345 function createAllModulesDom() { 346 for (let mod of mods) { 347 mod.createDom(); 348 } 349 } 350 351 function hideAllModules() { 352 for (let mod of mods) { 353 mod.hideDom(); 354 } 355 } 356 357 function showAllModules() { 358 for (let mod of mods) { 359 mod.showDom(); 360 } 361 } 362 363 function showModulesByFilter(pred) { 364 let numMatched = 0; 365 for (let mod of mods) { 366 if (pred(mod)) { 367 mod.showDom(); 368 ++numMatched; 369 } 370 } 371 return numMatched; 372 } 373 374 function showModulesByTagId(tagId) { 375 showModulesByFilter(function (mod) { 376 return mod.isTagged(tagId); 377 }); 378 } 379 380 381 //-------------------------------------------------------------------------- 382 // Events 383 //-------------------------------------------------------------------------- 384 385 function onAddModuleByPath(evt) { 386 evt.preventDefault(); 387 388 let path = domPathInput.value; 389 domPathInput.value = ''; 390 391 function escapeRegExp(pattern) { 392 return pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); 393 } 394 395 function createFuzzyMatcher() { 396 let parts = path.split(/\/+/g); 397 let pattern = ''; 398 for (let part of parts) { 399 pattern += escapeRegExp(part) + '(?:/[^\/]*)*'; 400 } 401 pattern = RegExp(pattern); 402 403 return function (mod) { 404 return pattern.test(mod.path); 405 }; 406 } 407 408 function exactMatcher(mod) { 409 return mod.path == path; 410 } 411 412 let numMatched = showModulesByFilter( 413 domFuzzyMatch.checked ? createFuzzyMatcher() : exactMatcher); 414 415 if (numMatched == 0) { 416 alert('No matching modules: ' + path); 417 } 418 } 419 420 function onAddAll(evt) { 421 evt.preventDefault(true); 422 hideAllModules(); 423 showAllModules(); 424 } 425 426 function onAddAllClass(evt, cls) { 427 evt.preventDefault(true); 428 hideAllModules(); 429 showModulesByFilter(function (mod) { 430 return mod.cls == cls; 431 }); 432 } 433 434 function onAddAll32(evt) { 435 onAddAllClass(evt, 32); 436 } 437 438 function onAddAll64(evt) { 439 onAddAllClass(evt, 64); 440 } 441 442 function onClear(evt) { 443 evt.preventDefault(true); 444 hideAllModules(); 445 } 446 447 function onModuleLinkClicked(evt) { 448 let modId = parseInt(evt.target.getAttribute('data-mod-id'), 10); 449 mods[modId].showDom(); 450 } 451 452 function setDirectDepBackgroundColor(modId, ownerId, color) { 453 let mod = mods[modId]; 454 let owner = mods[ownerId]; 455 let ownerLinkDoms = owner.linkDoms; 456 if (mod.deps.length > 0) { 457 for (let depId of mod.deps[0]) { 458 if (depId in ownerLinkDoms) { 459 ownerLinkDoms[depId].style.backgroundColor = color; 460 } 461 } 462 } 463 } 464 465 function onModuleLinkMouseOver(evt) { 466 let modId = parseInt(evt.target.getAttribute('data-mod-id'), 10); 467 let ownerId = parseInt(evt.target.getAttribute('data-owner-id'), 10); 468 setDirectDepBackgroundColor(modId, ownerId, '#ffff00'); 469 } 470 471 function onModuleLinkMouseOut(evt) { 472 let modId = parseInt(evt.target.getAttribute('data-mod-id'), 10); 473 let ownerId = parseInt(evt.target.getAttribute('data-owner-id'), 10); 474 setDirectDepBackgroundColor(modId, ownerId, 'transparent'); 475 } 476 477 return { 478 'init': init, 479 }; 480})); 481