diff --git a/README.md b/README.md index eba8429..2ec51ff 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ An UML Class explorer for InterSystems Caché. + Edit diagrams after build; + Switch between strict UML notation and designed view; + Export diagrams as an image; -+ See Class methods, properties, parameters, SQL queries and more; ++ See Class methods, properties, parameters, SQL queries, xDatas and more; + See any keywords and related information by hovering over everything with pointer; + Check which fields are connected by hovering over link; -+ View class methods code with syntax highlighting; ++ View methods code, sql queries and xDatas with syntax highlighting; + Zoom in and out; + Search on diagram or in class tree; + Explore! diff --git a/cache/projectTemplate.xml b/cache/projectTemplate.xml index 7e7e9c8..f3f2fd7 100644 --- a/cache/projectTemplate.xml +++ b/cache/projectTemplate.xml @@ -4,7 +4,7 @@ Cache Class Explorer vX.X.X/*build.replace:pkg.version*/ Class contains methods that return structured classes/packages data. -63844,1495 +63919,67431.456639 63653,67019.989197 @@ -97,6 +97,7 @@ Return structured data about class. set oProperties = ##class(%ZEN.proxyObject).%New() set oQueries = ##class(%ZEN.proxyObject).%New() set oIndices = ##class(%ZEN.proxyObject).%New() + set oXDatas = ##class(%ZEN.proxyObject).%New() set oClass.isDataType = classDefinition.ClientDataTypeIsDefined() set oClass.isOdbcType = classDefinition.OdbcTypeIsDefined() @@ -184,6 +185,19 @@ Return structured data about class. do oQueries.%DispatchSetProperty(q.Name, oProp) } + #dim xd as %Dictionary.XDataDefinition + set oClass.xdatas = oXDatas + set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.XDataDefinition") + for i=1:1:classDefinition.XDatas.Count() { + set oProp = ##class(%ZEN.proxyObject).%New() + set xd = classDefinition.XDatas.GetAt(i) + for j=1:1:props.Properties.Count() { + set pname = props.Properties.GetAt(j).Name + set:(pname '= "parent") $PROPERTY(oProp, pname) = $PROPERTY(xd, pname) + } + do oXDatas.%DispatchSetProperty(xd.Name, oProp) + } + #dim ind as %Dictionary.IndexDefinition set oClass.indices = oIndices set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.IndexDefinition") diff --git a/package.json b/package.json index 9fb0310..3a3c65c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CacheClassExplorer", - "version": "1.11.0", + "version": "1.12.0", "description": "Class Explorer for InterSystems Caché", "directories": { "test": "test" diff --git a/web/css/classView.css b/web/css/classView.css index ee47b8e..ef76575 100644 --- a/web/css/classView.css +++ b/web/css/classView.css @@ -19,7 +19,7 @@ svg { /*fill: lightgray;*/ } -.uml-class-attrs-rect, .uml-class-methods-rect, .uml-class-queries-rect { +.uml-class-attrs-rect, .uml-class-methods-rect, .uml-class-queries-rect, .uml-class-xdatas-rect { fill: white; } @@ -78,6 +78,12 @@ text { fill: magenta; } +.uml-class-xdatas-label { + font-family: monospace; + font-weight: 900; + fill: #0ab; +} + .line-clickable { cursor: pointer; -webkit-transition: all .2s ease; diff --git a/web/index.html b/web/index.html index 7a375b0..1629bdb 100644 --- a/web/index.html +++ b/web/index.html @@ -132,6 +132,11 @@

Display block with class queries + + + + Display block with class xDatas +

diff --git a/web/js/CacheClassExplorer.js b/web/js/CacheClassExplorer.js index 4d07582..da4ad5a 100644 --- a/web/js/CacheClassExplorer.js +++ b/web/js/CacheClassExplorer.js @@ -47,7 +47,8 @@ var CacheClassExplorer = function (treeViewContainer, classViewContainer) { showParameters: id("setting.showParameters"), showProperties: id("setting.showProperties"), showMethods: id("setting.showMethods"), - showQueries: id("setting.showQueries") + showQueries: id("setting.showQueries"), + showXDatas: id("setting.showXDatas") } }; @@ -64,7 +65,8 @@ var CacheClassExplorer = function (treeViewContainer, classViewContainer) { showParameters: settingsValue("showParameters", true), showProperties: settingsValue("showProperties", true), showMethods: settingsValue("showMethods", true), - showQueries: settingsValue("showQueries", true) + showQueries: settingsValue("showQueries", true), + showXDatas: settingsValue("showXDatas", true) }; this.UI = new UI(this); diff --git a/web/js/ClassView.js b/web/js/ClassView.js index 7bc0689..16f03db 100644 --- a/web/js/ClassView.js +++ b/web/js/ClassView.js @@ -341,6 +341,10 @@ ClassView.prototype.getPropertyHoverText = function (prop, type) { "Encoded": 1, // -- queries "SqlView": 1, + // -- xDatas + "MimeType": function (data) { + return "MimeType = " + data; + }, // -- class "ClientDataType": function (data, p) { return !p["isDataType"] ? "" @@ -450,6 +454,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { classProps = classMetaData["properties"], classMethods = classMetaData["methods"], classQueries = classMetaData["queries"], + classXDatas = classMetaData["xdatas"], keyWordsArray = [name], self = this; @@ -525,6 +530,22 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { } return arr; })(classQueries), + xdatas: (function (xds) { + var arr = [], n; + for (n in xds) { + keyWordsArray.push(n); + arr.push({ + name: n, + text: n + (xds[n]["MimeType"] ? ": " + xds[n]["MimeType"] : ""), + hover: self.getPropertyHoverText(xds[n], "xdata"), + icons: self.getPropertyIcons(xds[n]), + clickHandler: (function (d, className) { + return function () { self.showXData(className, d); } + })(xds[n], name) + }); + } + return arr; + })(classXDatas), classSigns: this.getClassSigns(classMetaData), classType: classMetaData.ClassType || "registered", SYMBOL_12_WIDTH: self.SYMBOL_12_WIDTH @@ -571,6 +592,22 @@ ClassView.prototype.showQuery = function (className, queryData) { }; +/** + * @param {string} className + * @param {string} xData + */ +ClassView.prototype.showXData = function (className, xData) { + + xData = xData || ""; + + this.showPanel({ + header: "##class(" + className + ")." + xData["Name"] + (xData["MimeType"] ? " (" + xData["MimeType"] + ")" : xData["MimeType"]), + comment: xData["Description"], + body: lib.highlightXML(xData["Data"] || "") + }); + +}; + /** * Show panel filled with given HTML contents. * @param {string} data.header @@ -949,7 +986,7 @@ ClassView.prototype.bindLinkHighlight = function () { highlighted = !!fields.length; }); - this.paper.on("cell:mouseout", function (e) { + this.paper.on("cell:mouseout", function () { highlighted = false; freeFields(); }); diff --git a/web/js/Lib.js b/web/js/Lib.js index 67c723c..952e6f6 100644 --- a/web/js/Lib.js +++ b/web/js/Lib.js @@ -178,9 +178,7 @@ Lib.prototype.sqlKeyWords = { */ Lib.prototype.highlightCOS = function (code) { var self = this; - return code.replace(/[<>&]/g, function (r) { - return r === "<" ? "<" : r === ">" ? ">" : "&" - }).replace(/(&[lgtamp]{2,3};)|(\/\/[^\n]*)\n|("[^"]*")|([\$#]{1,3}[a-zA-Z][a-zA-Z0-9]*)|\((%?[a-zA-Z0-9\.]+)\)\.|(%?[a-zA-Z][a-zA-Z0-9]*)\(|([a-zA-Z]+)|(\/\*[^]*?\*\/)|(\^%?[a-zA-Z][a-zA-Z0-9]*)/g, function (part) { + return this.replaceSpecial(code).replace(/(&[lgtamp]{2,3};)|(\/\/[^\n]*)\n|("[^"]*")|([\$#]{1,3}[a-zA-Z][a-zA-Z0-9]*)|\((%?[a-zA-Z0-9\.]+)\)\.|(%?[a-zA-Z][a-zA-Z0-9]*)\(|([a-zA-Z]+)|(\/\*[^]*?\*\/)|(\^%?[a-zA-Z][a-zA-Z0-9]*)/g, function (part) { var i = -1, c; [].slice.call(arguments, 1, arguments.length - 2).every(function (e) { i++; @@ -201,15 +199,19 @@ Lib.prototype.highlightCOS = function (code) { }); }; +Lib.prototype.replaceSpecial = function (str) { + return str.replace(/[<>&]/g, function (r) { + return r === "<" ? "<" : r === ">" ? ">" : "&"; + }); +}; + /** * Highlight SQL code. * @param {string} code */ Lib.prototype.highlightSQL = function (code) { var self = this; - return code.replace(/[<>&]/g, function (r) { - return r === "<" ? "<" : r === ">" ? ">" : "&" - }).replace(/(&[lgtamp]{2,3};)|([a-zA-Z]+)/gi, function (part, a, kw) { + return this.replaceSpecial(code).replace(/(&[lgtamp]{2,3};)|([a-zA-Z]+)/gi, function (part, a, kw) { var i = -1, c; [].slice.call(arguments, 1, arguments.length - 2).every(function (e) { i++; @@ -224,6 +226,47 @@ Lib.prototype.highlightSQL = function (code) { }); }; +/** + * Highlight XML code. + * @param {string} code + */ +Lib.prototype.highlightXML = function (code) { + + var replaceSpecial = this.replaceSpecial, + level = 0, + regex = new RegExp("|<(\\/?)[\\w](?:.*(?=\\/>)|.*(?=>))(\\/?)>|()" + + "|(<\\?[^]*?\\?>)|(<[^]*?>)", "g"); + + function stringFill (n) { + return new Array(n + 1).join(" ") + } + + return code.replace(regex, function (part, cData, tagS, tagG, comment, special, etc) { + return typeof tagS !== "undefined" ? part.replace(/<\/?([^\s]+)(.*(?=\/>)|.*(?=>))\/?>/ig, function (p, tagName, attrs) { + if (tagS) level--; + var s = stringFill(level) + " <" + tagS + "" + + tagName + "" + + attrs.replace(/(\s*[^=]+)=(\s*(?:'[^']*'|"[^"]*"))/g, function (part, a, b) { + return "" + a + + "=" + b + ""; + }) + + tagG + ">"; + if (!tagS && !tagG) level++; + return s; + }) : comment ? "" + replaceSpecial(comment) + "" + : special ? replaceSpecial(special) + : etc ? replaceSpecial(etc) + : "<![CDATA[" + + replaceSpecial(cData) + "]]>"; + }); + +}; + Lib.prototype.getSelection = function () { var html = ""; if (typeof window.getSelection != "undefined") { diff --git a/web/js/Logic.js b/web/js/Logic.js index 6577ccc..12a3704 100644 --- a/web/js/Logic.js +++ b/web/js/Logic.js @@ -39,6 +39,7 @@ Logic.prototype.process = function (data) { if (cls.properties && !this.umlExplorer.settings.showProperties) delete cls.properties; if (cls.methods && !this.umlExplorer.settings.showMethods) delete cls.methods; if (cls.queries && !this.umlExplorer.settings.showQueries) delete cls.queries; + if (cls.xdatas && !this.umlExplorer.settings.showXDatas) delete cls.xdatas; } if (!this.umlExplorer.settings.showDataTypesOnDiagram) { diff --git a/web/jsLib/joint.shapes.uml.js b/web/jsLib/joint.shapes.uml.js index 8abbba1..c5ae191 100644 --- a/web/jsLib/joint.shapes.uml.js +++ b/web/jsLib/joint.shapes.uml.js @@ -14,22 +14,25 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ markup: [ '', - '', - '', - '', - 'Parameters', - '', - 'Properties', - '', - 'Methods', - '', - 'Queries', - '', - '', - '', - '', - '', - '', + '', + '', + '', + 'Parameters', + '', + 'Properties', + '', + 'Methods', + '', + 'Queries', + '', + 'xDatas', + '', + '', + '', + '', + '', + '', + '', '' ].join(''), @@ -50,6 +53,7 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ '.uml-class-attrs-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' }, '.uml-class-methods-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' }, '.uml-class-queries-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' }, + '.uml-class-xdatas-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' }, '.uml-class-name-text': { 'ref': '.uml-class-name-rect', 'ref-y': .5, 'ref-x': .5, 'text-anchor': 'middle', 'y-alignment': 'middle', 'font-weight': 'bold', @@ -71,6 +75,10 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ 'ref': '.uml-class-queries-rect', 'ref-y': 5, 'ref-x': 5, 'fill': 'black', 'font-size': 12 }, + '.uml-class-xdatas-text': { + 'ref': '.uml-class-xdatas-rect', 'ref-y': 5, 'ref-x': 5, + 'fill': 'black', 'font-size': 12 + }, '.uml-class-attrs-label': { ref: '.uml-class-attrs-label', fill: "black", 'font-size': 10, xPos: -56 @@ -83,6 +91,9 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ }, '.uml-class-params-label': { ref: '.uml-class-methods-label', fill: "black", 'font-size': 10 + }, + '.uml-class-xdatas-label': { + ref: '.uml-class-xdatas-label', fill: "black", 'font-size': 10 } }, @@ -91,6 +102,7 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ attributes: [], methods: [], queries: [], + xdatas: [], classSigns: [] }, joint.shapes.basic.Generic.prototype.defaults), @@ -100,10 +112,11 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ var o, rects = [ { type: 'name', text: this.getClassName() }, - { type: 'params', text: (o = this.get('params')) , o: (o.forEach(function(e){e._BLOCK="parameters"}) && o) }, - { type: 'attrs', text: (o = this.get('attributes')), o: (o.forEach(function(e){e._BLOCK="properties"}) && o) }, - { type: 'methods', text: (o = this.get('methods')) , o: (o.forEach(function(e){e._BLOCK="methods"}) && o) }, - { type: 'queries', text: (o = this.get('queries')) , o: (o.forEach(function(e){e._BLOCK="queries"}) && o) } + { type: 'params', text: (o = this.get('params')||[]) , o: (o.forEach(function(e){e._BLOCK="parameters"}) && o) }, + { type: 'attrs', text: (o = this.get('attributes')||[]), o: (o.forEach(function(e){e._BLOCK="properties"}) && o) }, + { type: 'methods', text: (o = this.get('methods')||[]) , o: (o.forEach(function(e){e._BLOCK="methods"}) && o) }, + { type: 'queries', text: (o = this.get('queries')||[]) , o: (o.forEach(function(e){e._BLOCK="queries"}) && o) }, + { type: 'xdatas', text: (o = this.get('xdatas')||[]) , o: (o.forEach(function(e){e._BLOCK="xdatas"}) && o) } ], self = this, classSigns = this.get('classSigns'), @@ -199,7 +212,8 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ { type: 'params', text: this.get('params') }, { type: 'attrs', text: this.get('attributes') }, { type: 'methods', text: this.get('methods') }, - { type: 'queries', text: this.get('queries') } + { type: 'queries', text: this.get('queries') }, + { type: 'xdatas', text: this.get('xdatas') } ]; var offsetY = 0;