Skip to content

Commit b621ec5

Browse files
Arbitrary list of classes display feature, dependency level fixes, style fixes
1 parent 97bfb49 commit b621ec5

File tree

10 files changed

+306
-54
lines changed

10 files changed

+306
-54
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
An UML Class explorer for InterSystems Caché.
33

44
##### Key features
5-
+ Build class diagrams;
6-
+ Build diagrams for any package or subpackage;
5+
+ Build class diagrams for arbitrary list of classes;
6+
+ Build diagrams for whole package or subpackage;
77
+ Edit diagrams after build;
88
+ Switch between strict UML notation and designed view;
99
+ Export diagrams as an image;

cache/projectTemplate.xml

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Description>
55
Cache Class Explorer vX.X.X/*build.replace:pkg.version*/
66
Class contains methods that return structured classes/packages data.</Description>
7-
<TimeChanged>63934,52160.80625</TimeChanged>
7+
<TimeChanged>64010,55351.57242</TimeChanged>
88
<TimeCreated>63653,67019.989197</TimeCreated>
99

1010
<Method name="getAllNamespacesList">
@@ -87,8 +87,8 @@ Return structured data about class.</Description>
8787
<Private>1</Private>
8888
<ReturnType>%ZEN.proxyObject</ReturnType>
8989
<Implementation><![CDATA[
90-
if ((level'="")&&(+currLevel>+level)) quit ""
91-
set currLevel=$increment(currLevel)
90+
if ((level'="") && (+currLevel>+level)) { quit "" }
91+
set currLevel = $increment(currLevel)
9292
set classDefinition = ##class(%Dictionary.ClassDefinition).%OpenId(className)
9393
set compiledClassDefinition = ##class(%Dictionary.CompiledClass).%OpenId(className)
9494
if (classDefinition = "") || (oData.classes.%DispatchGetProperty(classDefinition.Name) '= "") quit ""
@@ -248,10 +248,20 @@ Return method data.</Description>
248248
<Description><![CDATA[
249249
Returns if <var>packageName</var> is in <var>basePackageName</var>.]]></Description>
250250
<ClassMethod>1</ClassMethod>
251-
<FormalSpec>basePackageName:%String,packageName:%String</FormalSpec>
251+
<FormalSpec>basePackageNames:%String,packageName:%String</FormalSpec>
252252
<Private>1</Private>
253253
<ReturnType>%Boolean</ReturnType>
254-
<Implementation><![CDATA[ quit $FIND(packageName, basePackageName) - $LENGTH(basePackageName) = 1
254+
<Implementation><![CDATA[
255+
set pack = $PIECE(packageName, ".", 1)
256+
set list = $LISTFROMSTRING(basePackageNames, ",")
257+
set OK = 0
258+
for i=1:1:$LISTLENGTH(list) {
259+
if ($LISTGET(list, i) = pack) {
260+
set OK = 1
261+
quit
262+
}
263+
}
264+
quit OK
255265
]]></Implementation>
256266
</Method>
257267

@@ -392,6 +402,34 @@ Returns structured package data</Description>
392402
quit oData
393403
]]></Implementation>
394404
</Method>
405+
406+
<Method name="getArbitraryView">
407+
<ClassMethod>1</ClassMethod>
408+
<FormalSpec>classList:%String,namespace:%String,level:%String=""</FormalSpec>
409+
<Implementation><![CDATA[
410+
set baseNamespace = $namespace
411+
zn:$GET(namespace)'="" namespace
412+
set list = $LISTFROMSTRING(classList, ",")
413+
414+
set basePackages = $LB()
415+
set count = 0
416+
for i=1:1:$LISTLENGTH(list) {
417+
set packName = $PIECE($LISTGET(list, i), ".", 1)
418+
if ($LISTFIND(basePackages, packName) = 0) {
419+
set $LIST(basePackages, count + 1) = packName
420+
set count = count + 1
421+
}
422+
}
423+
424+
set oData = ..getBaseOData($LISTTOSTRING(basePackages, ",", 1), baseNamespace, "ARBITRARY:"_classList)
425+
426+
for i=1:1:$LISTLENGTH(list) {
427+
do ..fillClassData(oData, $LISTGET(list, i), level)
428+
}
429+
430+
quit oData
431+
]]></Implementation>
432+
</Method>
395433
</Class>
396434

397435

@@ -409,7 +447,7 @@ Returns structured package data</Description>
409447
<Description>
410448
REST interface for ClassExplorer</Description>
411449
<Super>%CSP.REST</Super>
412-
<TimeChanged>63928,63486.89174</TimeChanged>
450+
<TimeChanged>64010,52108.241141</TimeChanged>
413451
<TimeCreated>63648,30450.187229</TimeCreated>
414452

415453
<XData name="UrlMap">
@@ -424,6 +462,7 @@ REST interface for ClassExplorer</Description>
424462
<Route Url="/GetClassView" Method="GET" Call="GetClassView"/>
425463
<Route Url="/GetAllNamespacesList" Method="GET" Call="GetAllNamespacesList"/>
426464
<Route Url="/GetPackageView" Method="GET" Call="GetPackageView"/>
465+
<Route Url="/GetArbitraryView" Method="GET" Call="GetArbitraryView"/>
427466
<Route Url="/GetMethod" Method="GET" Call="GetMethod"/>
428467
<Route Url="/SaveView" Method="POST" Call="SaveView"/>
429468
<Route Url="/ResetView" Method="GET" Call="ResetView"/>
@@ -455,6 +494,19 @@ Returns classTree by given class name</Description>
455494
]]></Implementation>
456495
</Method>
457496

497+
<Method name="GetArbitraryView">
498+
<Description>
499+
Returns classTree by given class name</Description>
500+
<ClassMethod>1</ClassMethod>
501+
<ReturnType>%Status</ReturnType>
502+
<Implementation><![CDATA[
503+
set classList = %request.Get("list")
504+
set classData = ##class(ClassView).getArbitraryView(classList, %request.Get("namespace"), %request.Get("level"))
505+
do classData.%ToJSON(, "o")
506+
return $$$OK
507+
]]></Implementation>
508+
</Method>
509+
458510
<Method name="SaveView">
459511
<Description>
460512
Saves the view preferences</Description>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "CacheClassExplorer",
3-
"version": "1.15.0",
3+
"version": "1.16.0",
44
"description": "Class Explorer for InterSystems Caché",
55
"directories": {
66
"test": "test"

web/css/interface.css

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,17 @@ html, body {
3030
position: absolute;
3131
top: 0;
3232
left: 0;
33-
padding: .5em;
33+
padding: .5em 200px .5em .5em;
3434
font-weight: 600;
35-
font-size: 18pt;
35+
font-size: 14pt;
3636
z-index: 1;
37+
box-sizing: border-box;
38+
width: 100%;
39+
}
40+
41+
.ui-wrapTextOverflow {
42+
overflow: hidden;
43+
text-overflow: ellipsis;
3744
}
3845

3946
.ui-rightBottomToolBar {

web/css/treeView.css

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,32 @@
4848
user-select: none;
4949
}
5050

51-
.tv-class-name:hover, .tv-package-name:hover {
51+
.tv-package-name:hover {
5252
background: #ffcc1b;
5353
font-weight: 900;
5454
padding-left: 30px;
5555
}
5656

57+
.tv-class-name:hover {
58+
background: #ffcc1b;
59+
font-weight: 900;
60+
}
61+
62+
.tv-class-name > input[type=checkbox] {
63+
vertical-align: top;
64+
}
65+
66+
.tv-class-name > span {
67+
-webkit-transition: all .2s ease;
68+
-moz-transition: all .2s ease;
69+
-o-transition: all .2s ease;
70+
transition: all .2s ease;
71+
}
72+
73+
.tv-class-name:hover > span {
74+
padding-left: 10px;
75+
}
76+
5777
.tv-package-name:hover {
5878
background: #7dcdeb;
5979
}
@@ -110,10 +130,10 @@
110130

111131
.tv-class-name {
112132
position: relative;
113-
padding-left: 20px;
133+
white-space: pre;
114134
}
115135

116-
.tv-class-name:after {
136+
/*.tv-class-name:after {
117137
content: "";
118138
box-sizing: border-box;
119139
position: absolute;
@@ -125,7 +145,7 @@
125145
background: black;
126146
border: 1px solid gray;
127147
border-radius: 5px 5px 5px 5px;
128-
}
148+
}*/
129149

130150
.tv-package .tv-package-content {
131151
padding-left: 20px;

web/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
</div>
4747
<div class="ui-mainBlock">
4848
<div class="ui-ClassInfo">
49-
<span id="className">Welcome!</span>
49+
<div class="ui-wrapTextOverflow">
50+
<span id="className">Welcome!</span>
51+
</div>
5052
</div>
5153
<div class="ui-topRightToolBar">
5254
<div id="button.showInfo" class="icon info"></div>

web/js/CacheClassExplorer.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,21 @@ CacheClassExplorer.prototype.restoreFromURL = function () {
182182

183183
try { obj = JSON.parse(hash); } catch (e) { obj = {}; }
184184

185+
if (obj.level) {
186+
this.classTree.SELECTED_LEVEL = obj.level;
187+
}
188+
185189
if (obj.namespace) this.NAMESPACE = obj.namespace;
186190
if (obj.type === "class") {
187-
this.classView.loadClass(obj.name);
191+
this.classView.loadClasses([obj.name], true);
188192
} else if (obj.type === "package") {
189-
this.classView.loadPackage(obj.name);
193+
this.classView.loadPackage(obj.name, true);
194+
} else if (obj.type === "arbitrary") {
195+
try {
196+
this.classView.loadClasses(obj.name.split(","), true);
197+
} catch (e) {
198+
console.error("Unable to parse class list " + obj.name);
199+
}
190200
} else {
191201
this.classView.renderInfoGraphic();
192202
}

web/js/ClassTree.js

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ var ClassTree = function (parent, treeViewContainer) {
1616
this.SELECTED_ELEMENT = null;
1717
this.SELECTED_LEVEL = null;
1818
this.treeObject = null;
19+
/**
20+
* @private
21+
* @type {string[]}
22+
*/
23+
this.selectedClassList = [];
1924

2025
this.cacheClassExplorer.elements.classTreeSearch.addEventListener("input", function (e) {
2126
self.searchChanged.call(self, (e.target || e.srcElement).value);
@@ -29,6 +34,13 @@ var ClassTree = function (parent, treeViewContainer) {
2934

3035
};
3136

37+
ClassTree.prototype.setSelectedClassList = function (list) {
38+
39+
// this enables saved view to be consistent for any class names order
40+
this.selectedClassList = list.sort();
41+
42+
};
43+
3244
ClassTree.prototype.updateSizes = function () {
3345

3446
var dh = this.cacheClassExplorer.elements.searchBlock.clientHeight,
@@ -63,6 +75,11 @@ ClassTree.prototype.removeLoader = function () {
6375

6476
};
6577

78+
/**
79+
* @deprecated
80+
* @param element
81+
* @param className
82+
*/
6683
ClassTree.prototype.classSelected = function (element, className) {
6784

6885
if (element !== this.SELECTED_ELEMENT) {
@@ -91,6 +108,18 @@ ClassTree.prototype.packageSelected = function (element, packageName) {
91108

92109
};
93110

111+
/**
112+
* @param {string[]} classList
113+
*/
114+
ClassTree.prototype.classesSelected = function (classList) {
115+
116+
if (this.SELECTED_ELEMENT) this.SELECTED_ELEMENT.classList.remove("selected");
117+
this.SELECTED_ELEMENT = null;
118+
119+
this.cacheClassExplorer.classView.loadClasses(classList);
120+
121+
};
122+
94123
ClassTree.prototype.searchChanged = function (query) {
95124

96125
var o = this.treeObject;
@@ -116,6 +145,14 @@ ClassTree.prototype.searchChanged = function (query) {
116145

117146
};
118147

148+
ClassTree.prototype.uncheckAll = function () {
149+
this.setSelectedClassList([]);
150+
[].slice.call(document.querySelectorAll("#treeView input[type=checkbox]"))
151+
.forEach(function (cb) {
152+
cb.checked = false;
153+
});
154+
};
155+
119156
/**
120157
* @param treeObject
121158
* @param {boolean} [doNotChangeRoot] - Determines to restrict assign of this.treeObject.
@@ -142,24 +179,46 @@ ClassTree.prototype.updateTree = function (treeObject, doNotChangeRoot) {
142179

143180
};
144181

145-
var classClick = function (e) {
182+
function checkboxClick (e, checked, className) {
146183

147-
var el = e.target || e.srcElement;
184+
e.cancelBubble = true;
185+
186+
self.setSelectedClassList(
187+
self.selectedClassList.filter(function (n) { return n !== className; })
188+
);
189+
if (checked) {
190+
self.setSelectedClassList(self.selectedClassList.concat(className));
191+
}
148192

149-
self.classSelected(el, el.CLASS_NAME);
193+
self.classesSelected(self.selectedClassList);
150194

151-
};
195+
}
196+
197+
function inPath (path, classList) {
198+
var inside = false,
199+
s = path + ".";
200+
console.log(s);
201+
classList.forEach(function (e) {
202+
if (e.indexOf(s) === 0)
203+
inside = true;
204+
});
205+
return inside;
206+
}
152207

153208
var append = function (rootElement, elementName, isPackage, path, level) {
154209

155210
var sel = selectedClassElement.length
156211
&& sce === level && selectedClassElement[sce] === elementName ? ++sce : null,
157212
el1 = div(),
158-
el2, el3, el4;
213+
el2, el3, el4, checkbox, span,
214+
selectedNames = self.SELECTED_NAME ? self.SELECTED_NAME.split(",") : [];
159215

160216
if (isPackage) {
161217
el1.className = "tv-package";
162-
(el2 = div()).className = "tv-package-name" + (sel ? "" : " minimized");
218+
(el2 = div()).className = "tv-package-name" + (
219+
sel || inPath((path ? path + "." : path) + elementName, self.selectedClassList)
220+
? ""
221+
: " minimized");
163222
el2.textContent = elementName;
164223
if (sel && sce === selectedClassElement.length) {
165224
el2.className += " selected";
@@ -176,9 +235,27 @@ ClassTree.prototype.updateTree = function (treeObject, doNotChangeRoot) {
176235
} else {
177236
if (sel) self.SELECTED_ELEMENT = el1;
178237
el1.className = "tv-class-name" + (sel ? " selected" : "");
179-
el1.textContent = elementName;
180-
el1.addEventListener("click", classClick);
181238
el1.CLASS_NAME = path + (path ? "." : "") + elementName;
239+
checkbox = document.createElement("input");
240+
checkbox.setAttribute("type", "checkbox");
241+
if (selectedNames.indexOf(el1.CLASS_NAME) !== -1)
242+
checkbox.setAttribute("checked", "true");
243+
span = document.createElement("span");
244+
span.textContent = elementName;
245+
span.setAttribute("title", elementName);
246+
el1.appendChild(checkbox);
247+
el1.appendChild(span);
248+
el1.addEventListener("click", (function (el, cbx, clsName) {
249+
return function (e) {
250+
cbx["checked"] = !cbx["checked"];
251+
checkboxClick(e, cbx["checked"], clsName);
252+
};
253+
})(el1, checkbox, el1.CLASS_NAME));
254+
checkbox.addEventListener("click", (function (cb, name) {
255+
return function (e) {
256+
checkboxClick(e, cb["checked"], name);
257+
};
258+
})(checkbox, el1.CLASS_NAME));
182259
}
183260

184261
rootElement.appendChild(el1);

0 commit comments

Comments
 (0)