dependency-cruiser
Version:
Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.
252 lines (219 loc) • 6.09 kB
JavaScript
var gMode = new Mode();
var title2ElementMap = (function makeElementMap() {
/** @type {NodeListOf<SVGGElement>} */
var nodes = document.querySelectorAll(".node");
/** @type {NodeListOf<SVGGElement>} */
var edges = document.querySelectorAll(".edge");
return new Title2ElementMap(edges, nodes);
})();
function getHoverHandler(pTitle2ElementMap) {
/** @type {string} */
var currentHighlightedTitle;
/** @param {MouseEvent} pMouseEvent */
return function hoverHighlightHandler(pMouseEvent) {
var closestNodeOrEdge = pMouseEvent.target.closest(".edge, .node");
var closestTitleText = getTitleText(closestNodeOrEdge);
if (
!(currentHighlightedTitle === closestTitleText) &&
gMode.get() === gMode.HOVER
) {
resetNodesAndEdges();
addHighlight(closestNodeOrEdge);
pTitle2ElementMap.get(closestTitleText).forEach(addHighlight);
currentHighlightedTitle = closestTitleText;
}
};
}
function getSelectHandler(pTitle2ElementMap) {
/** @type {string} */
var currentHighlightedTitle;
/** @param {MouseEvent} pMouseEvent */
return function selectHighlightHandler(pMouseEvent) {
pMouseEvent.preventDefault();
var closestNodeOrEdge = pMouseEvent.target.closest(".edge, .node");
var closestTitleText = getTitleText(closestNodeOrEdge);
if (!!closestNodeOrEdge) {
gMode.setToSelect();
} else {
gMode.setToHover();
}
if (!(currentHighlightedTitle === closestTitleText)) {
resetNodesAndEdges();
addHighlight(closestNodeOrEdge);
pTitle2ElementMap.get(closestTitleText).forEach(addHighlight);
currentHighlightedTitle = closestTitleText;
}
};
}
function Mode() {
var HOVER = 1;
var SELECT = 2;
function setToHover() {
this._mode = HOVER;
}
function setToSelect() {
this._mode = SELECT;
}
function get() {
return this._mode || HOVER;
}
return {
HOVER: HOVER,
SELECT: SELECT,
setToHover: setToHover,
setToSelect: setToSelect,
get: get,
};
}
/**
*
* @param {SVGGelement[]} pEdges
* @param {SVGGElement[]} pNodes
* @return {{get: (pTitleText:string) => SVGGElement[]}}
*/
function Title2ElementMap(pEdges, pNodes) {
/* {{[key: string]: SVGGElement[]}} */
var elementMap = buildMap(pEdges, pNodes);
/**
* @param {NodeListOf<SVGGElement>} pEdges
* @param {NodeListOf<SVGGElement>} pNodes
* @return {{[key: string]: SVGGElement[]}}
*/
function buildMap(pEdges, pNodes) {
var title2NodeMap = buildTitle2NodeMap(pNodes);
return nodeListToArray(pEdges).reduce(addEdgeToMap(title2NodeMap), {});
}
/**
* @param {NodeListOf<SVGGElement>} pNodes
* @return {{[key: string]: SVGGElement}}
*/
function buildTitle2NodeMap(pNodes) {
return nodeListToArray(pNodes).reduce(addNodeToMap, {});
}
function addNodeToMap(pMap, pNode) {
var titleText = getTitleText(pNode);
if (titleText) {
pMap[titleText] = pNode;
}
return pMap;
}
function addEdgeToMap(pNodeMap) {
return function (pEdgeMap, pEdge) {
/** @type {string} */
var titleText = getTitleText(pEdge);
if (titleText) {
var edge = pryEdgeFromTitle(titleText);
pEdgeMap[titleText] = [pNodeMap[edge.from], pNodeMap[edge.to]];
(pEdgeMap[edge.from] || (pEdgeMap[edge.from] = [])).push(pEdge);
(pEdgeMap[edge.to] || (pEdgeMap[edge.to] = [])).push(pEdge);
}
return pEdgeMap;
};
}
/**
*
* @param {string} pString
* @return {{from?: string; to?:string;}}
*/
function pryEdgeFromTitle(pString) {
var nodeNames = pString.split(/\s*->\s*/);
return {
from: nodeNames.shift(),
to: nodeNames.shift(),
};
}
/**
*
* @param {string} pTitleText
* @return {SVGGElement[]}
*/
function get(pTitleText) {
return (pTitleText && elementMap[pTitleText]) || [];
}
return {
get: get,
};
}
/**
* @param {SVGGElement} pGElement
* @return {string?}
*/
function getTitleText(pGElement) {
/** @type {SVGTitleElement} */
var title = pGElement && pGElement.querySelector("title");
/** @type {string} */
var titleText = title && title.textContent;
if (titleText) {
titleText = titleText.trim();
}
return titleText;
}
/**
* @param {NodeListOf<Element>} pNodeList
* @return {Element[]}
*/
function nodeListToArray(pNodeList) {
var lReturnValue = [];
pNodeList.forEach(function (pElement) {
lReturnValue.push(pElement);
});
return lReturnValue;
}
function resetNodesAndEdges() {
nodeListToArray(document.querySelectorAll(".current")).forEach(
removeHighlight
);
}
/**
* @param {SVGGElement} pGElement
*/
function removeHighlight(pGElement) {
if (pGElement && pGElement.classList) {
pGElement.classList.remove("current");
}
}
/**
* @param {SVGGElement} pGroup
*/
function addHighlight(pGroup) {
if (pGroup && pGroup.classList) {
pGroup.classList.add("current");
}
}
var hints = {
HIDDEN: 1,
SHOWN: 2,
state: this.HIDDEN,
show: function () {
document.getElementById("hints").removeAttribute("style");
hints.state = hints.SHOWN;
},
hide: function () {
document.getElementById("hints").style = "display:none";
hints.state = hints.HIDDEN;
},
toggle: function () {
if ((hints.state || hints.HIDDEN) === hints.HIDDEN) {
hints.show();
} else {
hints.hide();
}
},
};
/** @param {KeyboardEvent} pKeyboardEvent */
function keyboardEventHandler(pKeyboardEvent) {
if (pKeyboardEvent.key === "Escape") {
resetNodesAndEdges();
gMode.setToHover();
hints.hide();
}
if (pKeyboardEvent.key === "F1") {
pKeyboardEvent.preventDefault();
hints.toggle();
}
}
document.addEventListener("contextmenu", getSelectHandler(title2ElementMap));
document.addEventListener("mouseover", getHoverHandler(title2ElementMap));
document.addEventListener("keydown", keyboardEventHandler);
document.getElementById("close-hints").addEventListener("click", hints.hide);
document.getElementById("button_help").addEventListener("click", hints.toggle);