diff --git a/README.md b/README.md index 07a7f13..3da6e7e 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,13 @@ An UML Class explorer for InterSystems Caché. + Edit diagrams after build; + Export diagrams as an image; + View class methods code with syntax highlighting; -+ Zoom in and out, explore big packages and more. ++ Zoom in and out; ++ Search on diagram or in class tree; ++ Explore! ## Screenshots -![Demo](https://cloud.githubusercontent.com/assets/4989256/7898598/7d367720-070d-11e5-9177-dded6abf93e1.png) +![Demo](https://cloud.githubusercontent.com/assets/4989256/7902384/e7882a48-07c0-11e5-9c3f-7203a40263d2.png) ## Installation diff --git a/package.json b/package.json index e61105d..23220db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CacheUMLExplorer", - "version": "0.12.1", + "version": "0.13.1", "description": "An UML Class explorer for InterSystems Caché", "directories": { "test": "test" diff --git a/web/css/classView.css b/web/css/classView.css index 700c6d7..ecf12e5 100644 --- a/web/css/classView.css +++ b/web/css/classView.css @@ -82,4 +82,23 @@ text { .line-clickable:hover { fill: red; +} + +.inlineSearchBlock { + display: inline-block; + vertical-align: bottom; +} + +#diagramSearch { + display: block; + box-sizing: border-box; + border: 1px solid gray; + border-radius: 5px; + width: 200px; + margin: 4px 0 4px 4px; + height: 22px; +} + +.element.highlighted { + outline: 4px solid rgba(255, 0, 0, 0.6); } \ No newline at end of file diff --git a/web/css/extras.css b/web/css/extras.css index edf9e7a..109667c 100644 --- a/web/css/extras.css +++ b/web/css/extras.css @@ -84,6 +84,35 @@ box-shadow: 0 0 5px 2px #ffcc1b; } +.icon.search:after { + content: ""; + position: absolute; + display: block; + width: 6px; + height: 6px; + left: 4px; + top: 4px; + border: 3px solid #fff; + background: transparent; + border-radius: 6px; +} + +.icon.search:before { + content: ""; + position: absolute; + display: block; + left: 14px; + top: 12px; + width: 3px; + height: 7px; + background: #fff; + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); +} + .icon.select { width: auto; color: white; diff --git a/web/index.html b/web/index.html index 1418cab..e7aa9af 100644 --- a/web/index.html +++ b/web/index.html @@ -24,12 +24,13 @@ +
- +
@@ -50,6 +51,10 @@
+
+ +
+
diff --git a/web/js/CacheUMLExplorer.js b/web/js/CacheUMLExplorer.js index df4f160..0ab9c86 100644 --- a/web/js/CacheUMLExplorer.js +++ b/web/js/CacheUMLExplorer.js @@ -11,6 +11,7 @@ var CacheUMLExplorer = function (treeViewContainer, classViewContainer) { var id = function (e) { return document.getElementById(e); }; this.elements = { + favicon: id("favicon"), uiBody: id("ui-body"), className: id("className"), treeViewContainer: treeViewContainer, @@ -30,7 +31,9 @@ var CacheUMLExplorer = function (treeViewContainer, classViewContainer) { methodViewBounds: id("methodViewBounds"), namespaces: id("namespaces"), classTreeSearch: id("classTreeSearch"), - searchBlock: id("searchBlock") + searchBlock: id("searchBlock"), + diagramSearch: id("diagramSearch"), + diagramSearchButton: id("button.diagramSearch") }; this.UI = new UI(this); @@ -119,6 +122,8 @@ CacheUMLExplorer.prototype.init = function () { var self = this, restored; + this.elements.favicon.href = lib.image.binoculars; + restored = this.restoreFromURL(); this.classTree.showLoader(); this.source.getClassTree(function (err, data) { diff --git a/web/js/ClassView.js b/web/js/ClassView.js index 275c21c..b118d87 100644 --- a/web/js/ClassView.js +++ b/web/js/ClassView.js @@ -21,10 +21,29 @@ var ClassView = function (parent, container) { this.CLASS_DOC_PATH = "/csp/documatic/%25CSP.Documatic.cls"; this.SYMBOL_12_WIDTH = 6.6; + this.HIGHLIGHTED_VIEW = null; + this.SEARCH_INDEX = 0; + this.init(); }; +ClassView.prototype.highlightElement = function (jointElement) { + + if (this.HIGHLIGHTED_VIEW || (!jointElement && this.HIGHLIGHTED_VIEW)) { + this.HIGHLIGHTED_VIEW.unhighlight(); + this.HIGHLIGHTED_VIEW = null; + } + + if (!jointElement) return; + var view = this.paper.findViewByModel(jointElement); + if (!view) return; + + view.highlight(); + this.HIGHLIGHTED_VIEW = view; + +}; + ClassView.prototype.showLoader = function (html) { var d2; @@ -60,6 +79,9 @@ ClassView.prototype.resetView = function () { this.objects = []; this.paper.setOrigin(0, 0); this.graph.clear(); + this.HIGHLIGHTED_VIEW = null; + this.SEARCH_INDEX = 0; + this.cacheUMLExplorer.elements.diagramSearch.value = ""; }; @@ -268,6 +290,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { var classParams = classMetaData["parameters"], classProps = classMetaData["properties"], classMethods = classMetaData["methods"], + keyWordsArray = [name], self = this; var classInstance = new joint.shapes.uml.Class({ @@ -283,6 +306,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { params: (function (params) { var arr = [], n; for (n in params) { + keyWordsArray.push(n); arr.push({ text: n + (params[n]["type"] ? ": " + params[n]["type"] : "") }); @@ -292,6 +316,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { attributes: (function (ps) { var arr = [], n; for (n in ps) { + keyWordsArray.push(n); arr.push({ text: n + (ps[n]["type"] ? ": " + ps[n]["type"] : ""), icons: self.getMethodIcons(ps[n]) @@ -302,6 +327,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { methods: (function (met) { var arr = [], n; for (n in met) { + keyWordsArray.push(n); arr.push({ text: n + (met[n]["returns"] ? ": " + met[n]["returns"] : ""), styles: (function (t) { @@ -319,6 +345,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { SYMBOL_12_WIDTH: self.SYMBOL_12_WIDTH }); + classInstance.SEARCH_KEYWORDS = keyWordsArray.join(",").toLowerCase(); this.objects.push(classInstance); this.graph.addCell(classInstance); @@ -562,6 +589,69 @@ ClassView.prototype.zoom = function (delta) { }; +/** + * Focus on joint instance. + * @param jointInstance + */ +ClassView.prototype.focusOnInstance = function (jointInstance) { + + var bb = jointInstance.getBBox(); + + this.focusOnXY(bb.x + bb.width/2, bb.y + bb.height/2); + +}; + +/** + * Focus on x and y coordinates considering scale. + * @param {number} x + * @param {number} y + */ +ClassView.prototype.focusOnXY = function (x, y) { + + var sw = this.cacheUMLExplorer.elements.classViewContainer.offsetWidth, + sh = this.cacheUMLExplorer.elements.classViewContainer.offsetHeight, + scale = this.PAPER_SCALE; + + this.paper.setOrigin( + -(x * scale) + sw/2, + -(y * scale) + sh/2 + ); + +}; + +/** + * Find text on diagram and focus on element. + * + * @param {string} text + */ +ClassView.prototype.searchOnDiagram = function (text) { + + var p, found = [], o; + + if (!text) { + this.highlightElement(null); + return; + } + + text = text.toLowerCase(); + + for (p in this.objects) { + if (this.objects[p].SEARCH_KEYWORDS.indexOf(text) !== -1) { + found.push(this.objects[p]); + } + } + + if (found.length) { + o = found[this.SEARCH_INDEX % found.length]; + this.focusOnInstance(o); + this.highlightElement(o); + return; + } + + this.highlightElement(null); + +}; + ClassView.prototype.init = function () { var p, self = this, @@ -635,6 +725,19 @@ ClassView.prototype.init = function () { this.cacheUMLExplorer.elements.helpButton.addEventListener("click", function () { self.renderInfoGraphic(); }); + this.cacheUMLExplorer.elements.diagramSearch.addEventListener("input", function (e) { + self.searchOnDiagram((e.target || e.srcElement).value); + }); + this.cacheUMLExplorer.elements.diagramSearch.addEventListener("keydown", function (e) { + if (e.keyCode === 13) { + self.SEARCH_INDEX++; + self.searchOnDiagram((e.target || e.srcElement).value); + } + }); + this.cacheUMLExplorer.elements.diagramSearchButton.addEventListener("click", function () { + self.SEARCH_INDEX++; + self.searchOnDiagram(self.cacheUMLExplorer.elements.diagramSearch.value); + }); this.SYMBOL_12_WIDTH = (function () { var e = document.createElementNS("http://www.w3.org/2000/svg", "text"), diff --git a/web/js/Lib.js b/web/js/Lib.js index f61916a..ea0204d 100644 --- a/web/js/Lib.js +++ b/web/js/Lib.js @@ -165,5 +165,6 @@ Lib.prototype.image = { table: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADC0lEQVR42p2TS2wbRRyHv5ldr2Nnk9ixHbe0QC2BWoGQONGQFAgPicIBBBQkhBAHHleoQNw4cCwPcUOoQryCUtkkbSJLEVCVtiBRDkUUFQmaCrUQKCJOG7vrR7Pr3eHvdaB3ZrWa3dndb/6/b2bVgem5x7bvKL0WhcoYei1ERR5GD8u15mpryZn6b2wgYamfz5x/V3339Q/GLx3F+Hb8QAnAxD3SXwUo+vh/xxKJiM6p24gBh7c+zfLqGknLkjcd0omXaAdvYozamNEn6dyPH3wvYzW5TxIOtNjz66E+oJx7jp9qNVK2IqEHyVovshrs25hNyREwrHfT5iTr0Qra9AAd9v5ViQHezl23un4dHElRW4cTDXhoDLpRv3xbONUVGM9AIQlBKBFG4KvqiaY6duSYN7lrwq3XG3EFNV/xbV3x8FiEvwFw5JyvaSYyhoJj6HQNuVyG6sIXTXX8yyPexJ3jrtfwGBDAhY7hm1WLx7eErJvYmsSCuT80k3nD5pShFRhGs8NUD34eA8xbzRvQgU8maaG15lJgcVOuJ9AmJV7TjsWyb3G9rOKQROi5HcpmcY4fRC3OL3rjd427l+uXxb7mz7biqFTw1LVdrkT9CpKWYWbZYqoQco1AWuIpl8+wMFttqrnKgnfHPZPu2lodtwfoKBb/tnl+m087VLGDtADeP+/wQLHLFonQlAiF/Ciz5fmmOlwue/c9OOU2LtUZSsBZT1OW2V7ZHnBFbEdSRVI4+85Y7BEvJTeiIXKKYzk++agSA8z04E7CdpvRZIIuFr91NLcX5Utti0DNoMg97dncnDWMDMheFWo2n2e1/B7q4w9mvIm7JcLFNYYdzbm2pnLB5tUbfdYlglGKlDa8cTbBE1tDtqWjOEI+n+OzmVnUgU8rzXt3Tw1elK08IoBfmrD/nM3btwR9B1JIOmF4+bTNC6WQHS7UexE25flw/3QMMPlNBeo9iXEFFodWUuy9rtlfhd6fJ/vgnd9dHhnrUEqHtHzIjI5w6uSPqGeefPbR4ubiHP+jLS0tvf4PKUZIoAyrwMEAAAAASUVORK5CYII=", earth: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADUklEQVR42l1TW2hcVRRd5z7mcedOXtMmmmknpg+VZEIFrfjT0lZqQiemoWXyVxpRKNUKiiD4WUHBHxE/IvVnIuhHDLYiHUhKBEmxUEgKLbEW0kkyebSTmWle87iPmXNOd6a0VA9cOHeffdZae+19GP63XhvJRPv3+o7uCWrfpwq8Fmv1K9nLc9Z52q6Mv9t88/l89nTTl3xoRuu8o2c6zZ5XG3XMbEmoUiBTYQiqQJ0qIewqfpwpjV1btuP/nA0XnwHsHpo3hmPNd49FjLYncBILRYG/1wUadAZbAJ1eib1eQFckvr1dTA/dLnakzreVawBfT+bHvjgU6oYQ4IJhyRJYcoANV0JTUAOI+gQaCditAi8YDBcm18Z/iL3Yw966dP/E5GAkqasqbC5xqyCRLgMGyfbQ5W0XHIq/bW5flnhUAXboQNaqYuDKgxiL/7KQGBnYPVhwBR4Qa5GSc5yhIiSYBAr0f8CgMjU6q5AUijsViRZi+Pyv7DA7+/vKavxIa7NacvB6QAEn1iVL4l8bZJ7EJjEerWeIEECOSCy6XKlyAtDx5Y18ll2YyFj7XtnhS226eCeooHcnQ4kUTG8J3CmJWhkGYwiRebvI0CZNoEqlSIXh3J+PbPbRtYzVvj/kW1x30ElS39/lwQaxBFUFc6UKvlpwoZKsnM1xsEHBh2EdnHMk8hI/3czZrC8xu3roWHvzUq6MAnXg47CKiF9BPQEkcy4SaRemyqgjHFFTxaftHpRdIA0F312dz7I3Lk4ljg90Da5tOMg7EsdDCs616ZgvS3w2U4ZDbdMgYJMXglxNvmlgalPgt1WOGxOzwyz8wcSJw/3RpNkSxPKahfciHsRbPTgzVUKqUEUd1epKCU51bzoC3a0apksKpu+tIXAvHasN0v5PJse6Th7ozhPNNy97cT3HcSllIexntZZVxZMZEFR7lvxgNOKBW3Pj80NHemoALfE/jEC48W7T4Y622EseLOcsXM848NGpS3NQ3f6oEy7TwGkU1TtzaW2r2LH4c2/52WMK9Y+aeoM56u3a06O31INrKoRbqTFzejKc5CO/Dn12cUx17PjKyOnif17j09XU92uv2micQtCMSb8/IEguLKukbFlJpVi+nL0Sv/p8/mODDJQs66BSJwAAAABJRU5ErkJggg==", zed: "data:image/png;base64,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", - eye: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7UlEQVR42qWTS0iUURTH//eb8ROdZ5o46qRktHBSFCzJlfRCKNqULXoZtOgdZSY9rIULIYLIHliQRBmIUBCREkSlUSBZmjROqTHj6ExOJqYzozPf896uBhLhqg5c7uHA73A4//8h+M8gfxc0z+FiaGotFKmEKbLOJAmQNQNkqZvNxuoT97zrWbSB+rXaTqh2jcOVoCoEXYMuxyFIcbDZOMAf5c1YTGqmCjuRdLx/eqGB6q3JIWBug4FaWDwG6veBBoMgPGdUBwQRMJkhGBJAZAWKokd5ucB00jNCOCwYjEKQGGnGdFcPIq86QFkyaGY+qCUdRjEJiIyBTAzAODMBU2Y2rGl2yCoN6Tp1EjZ2/iRi0as9Nx/wUZMhl5+FVlAG1bYKUDgbnUWK0wrDzyHonz/A3HkZxvHPWOlygoqWKqK69wbeNT5zOjacQeb20+ic4RMzIF/lydQ4Ghtv4FsM2FzXADERcGkM0Te3IbfXoWAZCRLv/R1S6rpLibZluRjmzGBUw2RgFNG3D9Hd14+WR48gxyWUVd9C3q6DWKMFsHm5HSP+ISQ9PSqTKb9Hsue4EueW2T8ex70gw3DvJ7w4UIrIH3IVHrkJ+9b92CIGsNMRw5gtD/G+JzLxNRYFzGVNzjRXMSb4wiueBJGdkoLnx8rxw/N2HjZbUrG6yQNfRMadUhFOsxGh94/heHMhSFjP9lPu1o4reu5uFFUcQk3IhdYvDOlWgh9tzYiHw0jeuA+jmhVltnHczeqFt70BjlAnslc4q4j+eptBIFrA/7IrIxySgdz1uDebB68lH35bITRK4JjqQ2G4C3tENxK+f0SyKGBpdlZImZNxbkS5bVOOyHS3PDllkXwBcOWhQ+A2I7+dRhgSqIYZaoRuWgIiGKOyohVkXBwcWbCy0rLWLkjqNaaqlZRLxSgF4zvhHzcWm/9VyuuMNXMXnkivHZhe9Jik665iSkktZ0o0ynTOzHUwaBTdnK5POzew+DH9a/wCg6d3xpUf4V0AAAAASUVORK5CYII=" + eye: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7UlEQVR42qWTS0iUURTH//eb8ROdZ5o46qRktHBSFCzJlfRCKNqULXoZtOgdZSY9rIULIYLIHliQRBmIUBCREkSlUSBZmjROqTHj6ExOJqYzozPf896uBhLhqg5c7uHA73A4//8h+M8gfxc0z+FiaGotFKmEKbLOJAmQNQNkqZvNxuoT97zrWbSB+rXaTqh2jcOVoCoEXYMuxyFIcbDZOMAf5c1YTGqmCjuRdLx/eqGB6q3JIWBug4FaWDwG6veBBoMgPGdUBwQRMJkhGBJAZAWKokd5ucB00jNCOCwYjEKQGGnGdFcPIq86QFkyaGY+qCUdRjEJiIyBTAzAODMBU2Y2rGl2yCoN6Tp1EjZ2/iRi0as9Nx/wUZMhl5+FVlAG1bYKUDgbnUWK0wrDzyHonz/A3HkZxvHPWOlygoqWKqK69wbeNT5zOjacQeb20+ic4RMzIF/lydQ4Ghtv4FsM2FzXADERcGkM0Te3IbfXoWAZCRLv/R1S6rpLibZluRjmzGBUw2RgFNG3D9Hd14+WR48gxyWUVd9C3q6DWKMFsHm5HSP+ISQ9PSqTKb9Hsue4EueW2T8ex70gw3DvJ7w4UIrIH3IVHrkJ+9b92CIGsNMRw5gtD/G+JzLxNRYFzGVNzjRXMSb4wiueBJGdkoLnx8rxw/N2HjZbUrG6yQNfRMadUhFOsxGh94/heHMhSFjP9lPu1o4reu5uFFUcQk3IhdYvDOlWgh9tzYiHw0jeuA+jmhVltnHczeqFt70BjlAnslc4q4j+eptBIFrA/7IrIxySgdz1uDebB68lH35bITRK4JjqQ2G4C3tENxK+f0SyKGBpdlZImZNxbkS5bVOOyHS3PDllkXwBcOWhQ+A2I7+dRhgSqIYZaoRuWgIiGKOyohVkXBwcWbCy0rLWLkjqNaaqlZRLxSgF4zvhHzcWm/9VyuuMNXMXnkivHZhe9Jik665iSkktZ0o0ynTOzHUwaBTdnK5POzew+DH9a/wCg6d3xpUf4V0AAAAASUVORK5CYII=", + binoculars: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADrElEQVR42m2Sa0zTVxiHfwfaSgthCxTEuZlMdkn2wW26utlEwtQB2rUUicSJ4gRS7cBNXIlmSGCbcYuGbMsYirawoWSJyEAm09VOII6tYKmOS8cYtEBrL7b0Tiv08t9/zZa4ZOfDSc553/Pk5Pe8BI+s3cUljrzc3FSj6T41NjZOLn/bRv6+31tSSvH5m6hJ3e9kfFLnuaW88fi/b8ijgLJy6ZL8QhPL4fSjq7sLkrKSWL29/TK1Z88uWBxeHJO9//DiN3L2/wK2C8XTIoEgs3/gNn4bvYfJidFYXVxQRG3kb0RPZzfC4WWHRjOc9h/Aidr6D9LTMo72DfQl7ywsZI6MaKH+ZRDqX2/H6ufOK6jC4t0ofWsfDPqZaAo3dSHgXxzUaIYKYg3Ha+rChw5K42fn/kRvrwpTf+jAZDJgsVjg9/pwov4kNBo1nlv7DGwOG9gJiejvU+FqdychlYer+C63b1Amq8KaJ7jwBJfQ/FUzrA/siI9jYqD/Jta9vAEejxs/3rgGr9sJl28RPVev04COHFJT++E+p9fbVnmoHHHReDooOzo7rsDpciIaAbTaIWRlb8UDmwVnzzbCarbCanPAYrPjYptCSmSy4/XJKal1ktK3kchmwWRzoUUhh8NqRSgKDA8PIntLDuz0+bz8HDhJHExN6ZH4WAqq35UqyIFSycj42Nj6CEXhYTCIYCCAlasy8NKL67Gw4MDo6F28nv0Gpg163NOqEfAHQOjkCL2tfnK1lVRUvncyLX1VzbHqKlz7QYVbdDgUHazZOB/gcrnapOSku0ajsexNkZhTVCDEqTOfwevxwDCjh9fl+IJUHDl6msfbVF1cJIKitR3jEzpEIhGY5ueWv+/5bsU/E7okEOazdgrz8PGpM/DQZrxuN53H/CekXPLOhChf/MIrG9bRvltgNpsRCYVx32iEUtkb01xSVk5t2ZqLLD4PDQ2N8C76EA2FMDc7rSPSiiOB/Qf2sp99OhNNF1rh93ngcroxO2+AsrcnBpAcrqKyN2dBKMjBp6c/h9vpjFmaM+iDJE8gpj6qrYPda0dzkxwhmhwOLSMajeIn1fUYYP/BCqp4VyFMJgs6Oq8gshxChIrARpshmZnPUw2NX0KlvIlLrS10exQJHDaeWrMWd9Q/xwDbtuVSX7ddwo7tefS39WCtYCF9ZQZYDAbIq/zNEcGO/LiZmUlqaEgNGg0Gkwk2J4HcGVbHADzea5RAVEB1d3XASauNoxXS40ziGXHRvwAVvrukyLa34QAAAABJRU5ErkJggg==" }; \ No newline at end of file