diff --git a/README.md b/README.md index 18a5103..4a48a05 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ An UML Class explorer for InterSystems Caché. ## Screenshots -![Demo](https://cloud.githubusercontent.com/assets/4989256/7622499/9cb98048-f9d8-11e4-9c27-4257e53ec70d.png) +![Demo](https://cloud.githubusercontent.com/assets/4989256/7642972/f2c3e33c-fa9d-11e4-90b4-d35c809c9e60.png) ## Installation diff --git a/cache/projectTemplate.xml b/cache/projectTemplate.xml index 3ac3620..1875c87 100644 --- a/cache/projectTemplate.xml +++ b/cache/projectTemplate.xml @@ -3,7 +3,7 @@ Class contains methods that return structured classes/packages data. -63686,4398.893381 +63686,85630.818189 63653,67019.989197 @@ -115,8 +115,8 @@ Return structured data about class. for i=1:1:count { set oPar = ##class(%ZEN.proxyObject).%New() set p = classDefinition.Parameters.GetAt(i) + set oPar.type = p.Type do oParameters.%DispatchSetProperty(p.Name, oPar) - do oPar.%DispatchSetProperty("type", p.Type) } do ..collectInheritance(oData, classDefinition, package) @@ -302,7 +302,7 @@ Returns structured package data - + diff --git a/package.json b/package.json index 426eb4e..4d688ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CacheUMLExplorer", - "version": "0.8.0", + "version": "0.9.2", "description": "An UML Class explorer for InterSystems Caché", "directories": { "test": "test" diff --git a/web/css/extras.css b/web/css/extras.css index fc04ddf..6894c81 100644 --- a/web/css/extras.css +++ b/web/css/extras.css @@ -84,6 +84,18 @@ box-shadow: 0 0 5px 2px #ffcc1b; } +.icon.help:after { + content: "?"; + position: absolute; + color: white; + display: block; + font-size: 21px; + font-weight: 900; + height: 100%; + width: 100%; + text-align: center; +} + .icon.plus:before { content: ""; background-color: #fff; diff --git a/web/css/interface.css b/web/css/interface.css index 77ec99c..2b5a746 100644 --- a/web/css/interface.css +++ b/web/css/interface.css @@ -32,6 +32,7 @@ html, body { padding: .5em; font-weight: 600; font-size: 18pt; + z-index: 1; } .ui-rightBottomToolBar { @@ -39,6 +40,7 @@ html, body { bottom: 0; right: 0; padding: .5em; + z-index: 1; } .ui-leftBottomToolBar { @@ -46,6 +48,7 @@ html, body { bottom: 0; left: 0; padding: .5em; + z-index: 1; } .ui-topRightToolBar { @@ -53,6 +56,7 @@ html, body { top: 0; right: 0; padding: .5em; + z-index: 1; } #className { diff --git a/web/index.html b/web/index.html index 73d877e..be5fee1 100644 --- a/web/index.html +++ b/web/index.html @@ -37,6 +37,7 @@
+
diff --git a/web/js/CacheUMLExplorer.js b/web/js/CacheUMLExplorer.js index 0a101ab..8e10ce7 100644 --- a/web/js/CacheUMLExplorer.js +++ b/web/js/CacheUMLExplorer.js @@ -18,6 +18,7 @@ var CacheUMLExplorer = function (treeViewContainer, classViewContainer) { zoomInButton: id("button.zoomIn"), zoomOutButton: id("button.zoomOut"), zoomNormalButton: id("button.zoomNormal"), + helpButton: id("button.showHelp"), infoButton: id("button.showInfo"), methodCodeView: id("methodCodeView"), closeMethodCodeView: id("closeMethodCodeView"), @@ -53,7 +54,11 @@ CacheUMLExplorer.prototype.init = function () { this.classView.loadClass(hash.substr(7)); } else if (hash.indexOf("package:") === 1) { this.classView.loadPackage(hash.substr(9)); + } else { + this.classView.renderInfoGraphic(); } + } else { + this.classView.renderInfoGraphic(); } this.elements.infoButton.addEventListener("click", function () { diff --git a/web/js/ClassView.js b/web/js/ClassView.js index 8227fb5..07d745f 100644 --- a/web/js/ClassView.js +++ b/web/js/ClassView.js @@ -73,10 +73,130 @@ ClassView.prototype.openClassDoc = function (className, nameSpace) { }; +/** + * Render help info + */ +ClassView.prototype.renderInfoGraphic = function () { + + this.cacheUMLExplorer.classTree.SELECTED_CLASS_NAME = + this.cacheUMLExplorer.elements.className.innerHTML = + "Welcome to Caché UML explorer!"; + + location.hash = "help"; + + this.showLoader(); + this.render({ + basePackageName: "Welcome to Cach? UML explorer!", + classes: { + "Shared object": { + super: "Super object", + parameters: { + "Also inherit Super object": {} + }, + methods: {}, + properties: {} + }, + "Class name": { + super: "Super object", + ABSTRACT: 1, + FINAL: 1, + HIDDEN: 1, + NAMESPACE: "SAMPLES", + PROCEDUREBLOCK: 0, + SYSTEM: 4, + methods: { + "Abstract public method": { + abstract: 1 + }, + "Class method": { + classMethod: 1 + }, + "Client method": { + clientMethod: 1 + }, + "Final method": { + final: 1 + }, + "Not inheritable method": { + notInheritable: 1 + }, + "Private method": { + private: 1 + }, + "Sql procedure": { + sqlProc: 1 + }, + "Web method": { + webMethod: 1 + }, + "ZEN method": { + zenMethod: 1 + }, + "Method": { + returns: "%Return type" + } + }, + parameters: { + "PARAMETER WITHOUT TYPE": {}, + "PARAMETER": { + type: "Type" + } + }, + properties: { + "Public property name": { + private: 0 + }, + "Private property name": { + private: 1 + }, + "Public read-only property": { + private: 0, + readOnly: 1 + }, + "Property": { + type: "Type of property" + }, + "Other object": { + private: 0, + type: "Shared object" + }, + "Another object": { + private: 1, + type: "Not shared object" + } + } + }, + "Super object": { + methods: {}, + properties: {}, + parameters: {} + }, + "HELP": { + parameters: { + "See the basics here!": {} + } + } + }, + composition: {}, + aggregation: { + "Class name": { + "Shared object": "1..1" + } + }, + inheritance: { + "Class name": { "Super object": 1 }, + "Shared object": { "Super object": 1 } + }, + restrictPackage: 1 + }); + + this.removeLoader(); + +}; + /** * Returns array of signs to render or empry array. * - * @private * @param classMetaData */ ClassView.prototype.getClassSigns = function (classMetaData) { @@ -85,11 +205,11 @@ ClassView.prototype.getClassSigns = function (classMetaData) { if (classMetaData["classType"]) signs.push({ icon: lib.image.greenPill, - text: classMetaData["classType"], + text: lib.capitalize(classMetaData["classType"]), textStyle: "fill:rgb(130,0,255)" }); if (classMetaData["ABSTRACT"]) signs.push({ - icon: lib.image.iceCube, + icon: lib.image.crystalBall, text: "Abstract", textStyle: "fill:rgb(130,0,255)" }); @@ -115,6 +235,29 @@ ClassView.prototype.getClassSigns = function (classMetaData) { }; +/** + * Returns array of icons according to method metadata. + * + * @param method + */ +ClassView.prototype.getMethodIcons = function (method) { + + var icons = []; + + icons.push({ src: lib.image[method["private"] ? "minus" : "plus"] }); + if (method["abstract"]) icons.push({ src: lib.image.crystalBall }); + if (method["clientMethod"]) icons.push({ src: lib.image.user }); + if (method["final"]) icons.push({ src: lib.image.blueFlag }); + if (method["notInheritable"]) icons.push({ src: lib.image.redFlag }); + if (method["sqlProc"]) icons.push({ src: lib.image.table }); + if (method["webMethod"]) icons.push({ src: lib.image.earth }); + if (method["zenMethod"]) icons.push({ src: lib.image.zed }); + if (method["readOnly"]) icons.push({ src: lib.image.eye }); + + return icons; + +}; + /** * @param {string} name * @param classMetaData @@ -127,65 +270,53 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) { classMethods = classMetaData["methods"], self = this; - var insertString = function (array, string, extraString) { - array.push({ text: string + (extraString ? extraString : "")}); - }; - var classInstance = new joint.shapes.uml.Class({ - name: name, + name: [{ + text: name, + clickHandler: function () { + self.openClassDoc(name, classMetaData["NAMESPACE"]); + }, + styles: { + cursor: "help" + } + }], params: (function (params) { var arr = [], n; for (n in params) { - insertString(arr, n + (params[n]["type"] ? ": " + params[n]["type"] : "")); + arr.push({ + text: n + (params[n]["type"] ? ": " + params[n]["type"] : "") + }); } return arr; })(classParams), attributes: (function (ps) { var arr = [], n; for (n in ps) { - insertString( - arr, - (ps[n]["private"] ? "- " : "+ ") + n - + (ps[n]["type"] ? ": " + ps[n]["type"] : "") - ); + arr.push({ + text: n + (ps[n]["type"] ? ": " + ps[n]["type"] : ""), + icons: self.getMethodIcons(ps[n]) + }); } return arr; })(classProps), methods: (function (met) { var arr = [], n; for (n in met) { - insertString( - arr, - (met[n]["private"] ? "- " : "+ ") + n - + (met[n]["returns"] ? ": " + met[n]["returns"] : ""), - (met[n]["classMethod"] ? - "\x1b" + JSON.stringify({STYLES:{ - textDecoration: "underline" - }}) : "") - ); + arr.push({ + text: n + (met[n]["returns"] ? ": " + met[n]["returns"] : ""), + styles: (function (t) { + return t ? { textDecoration: "underline" } : {} + })(met[n]["classMethod"]), + clickHandler: (function (n) { + return function () { self.showMethodCode(name, n); } + })(n), + icons: self.getMethodIcons(met[n]) + }); } return arr; })(classMethods), - directProps: { - nameClickHandler: function () { - self.openClassDoc(name, classMetaData["NAMESPACE"]); - } - }, classSigns: this.getClassSigns(classMetaData), - SYMBOL_12_WIDTH: self.SYMBOL_12_WIDTH, - attrs: { - ".uml-class-methods-text": { - lineClickHandlers: (function (ps) { - var arr = [], p; - for (p in ps) { - arr.push((function (p) { return function () { - self.showMethodCode(name, p) - }})(p)); - } - return arr; - })(classMethods) - } - } + SYMBOL_12_WIDTH: self.SYMBOL_12_WIDTH }); this.objects.push(classInstance); @@ -358,7 +489,7 @@ ClassView.prototype.loadClass = function (className) { self.showLoader("Unable to get " + self.cacheUMLExplorer.classTree.SELECTED_CLASS_NAME); console.error.call(console, err); } else { - self.cacheUMLExplorer.classView.render(data); + self.render(data); } }); @@ -380,7 +511,7 @@ ClassView.prototype.loadPackage = function (packageName) { self.showLoader("Unable to get package " + packageName); console.error.call(console, err); } else { - self.cacheUMLExplorer.classView.render(data); + self.render(data); } }); @@ -492,6 +623,9 @@ ClassView.prototype.init = function () { this.cacheUMLExplorer.elements.closeMethodCodeView.addEventListener("click", function () { self.hideMethodCode(); }); + this.cacheUMLExplorer.elements.helpButton.addEventListener("click", function () { + self.renderInfoGraphic(); + }); 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 e23a372..6df7342 100644 --- a/web/js/Lib.js +++ b/web/js/Lib.js @@ -41,6 +41,14 @@ Lib.prototype.countProperties = function (object) { }; +/** + * Make first letter of string uppercase. + * @param {string} string + */ +Lib.prototype.capitalize = function (string) { + return string[0].toUpperCase() + string.substr(1); +}; + /** * Contains graphic base64s for the application. */ @@ -51,5 +59,14 @@ Lib.prototype.image = { ghost: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAERvIDYgTXJ6IDIwMDMgMjM6NTk6MTcgKzAxMDCMVVlHAAAAB3RJTUUH0wMGFxkwngXNbAAAAAlwSFlzAAAK8AAACvABQqw0mAAAAARnQU1BAACxjwv8YQUAAAJkSURBVHjapVNNTxNRFL3TTvno9EuqmNZpymihWKhNkBrTLtQ0Gt1IYsqiEohrkiYuYOEPkISlEFmQuGqicclC2RAjLthha2hjMBSaJii0NLRTnDLTmY53pqGpKaKJL3l5d97cc+45790H8J+DOG1zdnbtXqm0P03T5us8L1RTqewnvd7wfG7uceKvjAsLX5+urqZrctOQJFmen//AR6Nvw2eCY7Hs1ZWV7SrHSQ1wPJ5W1xpSzsy8/zk5+cbVjNE0f+zt7T9hGDPp9wdgfT0BiUQWxscjsLj4GqpVgHD4vp5lD5/9kcBi6QhkMt/A5wtALleGjY0kDA/fht3dQ5BlAJOJAJq2PEKjmlMJurp0vTpdGzCMFxMZBBjB4ehTp6JAkpQiVsvEhNzTQjA29rlNFNnzPl8vJtHg9dLg93uAoi7A4OA1qFTqBAaDCeMKfYIjT4Lu7i1be/tlrVJ1auquume3WyESCYHRSAHLAoiiQmAEnuftLQpEUTtgs1kadvJ5Qa1qMFDqWqlICASwWs/B8XGVaVHAsqXQ0JBTjRWpxaIIJNmmgpQpCDKeg4xKL+J/0dtCYLebH5IkCRwHOCWsKKuVBaFOoCjAXgCdjsRcW/A3C6OjLz14aFeUq9JqASVqMbGjcfIEUfev0RBIUsMbsjmCwehAQ4Ek/bhkwkuOxw9gaekjZLM5cLl6YGTkARQKZWyqL5DJbIPb7YZA4Cb09/cRy8vv7iA0pT4mj+eVjqY3XxQKRyGK6tSixy1JktbQUid2cblUKt5yOpkbOztplqKo7wRBbHLc0XQyGcv/05M9a/wC59cUbcvinwEAAAAASUVORK5CYII=", moleculeCubeCross: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAE1pIDUgTXJ6IDIwMDMgMjM6MzY6MDEgKzAxMDC/PwNMAAAAB3RJTUUH0wMFFx0vZdSqcwAAAAlwSFlzAAAK8AAACvABQqw0mAAAAARnQU1BAACxjwv8YQUAAALsSURBVHjarVNrSJNhGH2+75vfvuY25/IuKs50UxNBrbwMFbwWqWnQxQwvf0qICBHth4JJZaWE6C+pDAuxKLFBzcRQ0ywrLbw3W1hqbl72remmm5t7c9YEu/zz/Hg5z+Fw4Dy8D8BOAAFgwwIQqQHIbXooFKwksul+DM5v0/nAtXLM8mhjyUaWHz9zrEE59KC88i4TxwJmlvUBY2ODYRJTK/M4kbIcJBQ8WcNs3tT2lqWaHfhJ75/TVeFaKNoMoCMoWuXJs6/kZgBmg+tp2UhzoaIrmVwC7rgCzFI7p2t0jDguUBQQxVFPYKWGNuiWaH7E0GBPWAJS5kyraxPa2I+f5VJ9eNTUBdVg2v6TjmwXWzOhnMUYi6WXo50pUiP7NtUY2NSE3L4YPORqaHgI8GyrV0l1TUt8atoxC190h3so3RahcEAdBCy7BwW7WH25Z/MT8zKzuq0zw0oo7dIMj7Rxt/AFBZT3S3VhBiO4tyWlGb+3SpRW324uh6MBpLLOuJUIhUI4kp4uRgid9jeDvKOiumaprg6PFHuz53A4Y/WxKIpDEoTmrwC34VEvqrQ0boM6TfX1ZRyoulHhqV9jnxK5MK8nJpcczc4OtPiMFJtjZLG3ArDf/wDvdmQZo08E460jJK1WKO0yExiE9KnW7COOllwcl/XwwsLyfHHjZOrSwJ53SsBcX3yIOQQwv7mDaVfYtahZxYBHgnBdzhdEcKFHMmU0XKrViXJzMiyefRSz1p/UP3YIWffP+zoINQCHN+T6zQBPBegGi4s6uhrr4mLjPUA3rYYFQYhMx3Pwma+uEhdOznr7OvKLZ0xgenv7psx1AdadANq2KrwCyMEEdrciEzwYsGIAMBuhs2sevK5cBVuChc4NyMZUo0Nlne3tzb8a/4FmAh9GBXvRSKiD+aWIr0apzghlOaLXfq6IbqxfbwWw/98dbVZwwkHecueTr1Zjyp8EuK+agF6KgfkxmQgbb5E+Ogig3pGr/Rd+Ai1UJxH01MR4AAAAAElFTkSuQmCC", greenPill: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAK3RFWHRDcmVhdGlvbiBUaW1lAE1pIDE3IFNlcCAyMDAzIDIzOjI5OjEwICswMTAwQo74agAAAAd0SU1FB9MJERUdI/ot6l8AAAAJcEhZcwAACvAAAArwAUKsNJgAAAAEZ0FNQQAAsY8L/GEFAAACa0lEQVR42qWTT0hUQRzHv+/Ne+7uc3dphVSMjYoWsbA/h6g8iNWtKCjqoEmXugoh0aFDHkICLx2ELh0khAjKCIsKPGSY7FL+QYvIlCKSfM99m2/dt7O7789Ms4fCUknoC78fM8x8P/zmNzPAf0r6NejpGW4Nh8m1aLQybhjWhCSFbnR2Hn65IUpX1/P2oaE55vucU8p5Nsv54OBHv7d3/OqGAN3dz97rOucLus/TJue6ISAW5+Mpi9++NXnnSscTdT2vXE7BoBpPmzlYVgk0z6CIg9kFhtfBPqQSNy99PdT34uzEsdi6AF3PTGcyJuycDbfEMW1+wIm3ccwGBrDjgI14k3V00ZxLNifrd64J8H3SPT72iS0t/YCeNtA6eRAtiW2IqVVw86JCSUVzw+56vuykGodrW1YCSDklkw/m9u87J0jekae5fpDIDKJyCAWfig0EnIsl5iFRW6MZeq5NviB9s/oLU78BZY2O3n/V2HAmMZwf2BMN5eA7HMxjKHmOADARHI7noi4UIxmTnqSnvWTxoftZWVmOprkX82Zhu15Bm5woR6DSRSisoBhxEKxUQQgRRQK7gtXknbFwXViGpL+bgstaTSDnvamvjW1VNVkYFWgRBRUBAlmRQSRJhIIRY7642JENrQaU1Ya9GgIjW6rDETUooSJIoKjlEAAi5jLBxPJiOttFq5U1AfcwRU95rd+p/XhTTFWIMKtlsypBJTKYuLssLT36o4mrNMNn/TpuOjY7zhmTPMrgFrl4aD7mLXtWvPvzmAJdHyDEv/AxvllKFbN+vFRgVdT20suF0l3msnYMILOhf/Iv/QS2OyBbr28yxQAAAABJRU5ErkJggg==", - iceCube: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAK3RFWHRDcmVhdGlvbiBUaW1lAE1pIDE5IEZlYiAyMDAzIDAwOjU2OjEzICswMTAwhV8lEQAAAAd0SU1FB9MCEwAKIKGTp3IAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAACoUlEQVR42qWTz08TQRTHvzOz3W63lLbQWixIBRU08QdcTAwxil5M0IPRuwfiP6DGq3f9B4gXPegBLx5M8KAnY6JEQtB4QIOGIhhagVbLtrvbnRnfQqwYj0wyO28y8z7v+97bAXY52B/jflHnN1cwmmlTp3tTOKYYhp+t+7FAonIiLsp5Q5RW6qpc9VQpcLDka/Xkzmhs0Qidn6/qiZEOXE/0hkCOL75kjUDjYlwgE2Xp/ZZIL3vBoPA1phZcJHMMqXU2Tq6DW4DFqjduWIxlOUefLdgBUwAmHdjb6r65Cg+Xm/hck+i2GYpNhbhgA+HZFsBpKuNVRUNoYI/JMUSR10lBv8UxYJNNDnsthqH2CGZ+BogEgFQqaAF8qd2UgCU0A91DmRzmHYVKU2ODQO8dCUnwGtkWZ6BM4PjaDX15+FFSNxlZFbogaA2nRVkE0ARRsGmfpD35ImcyXMpGoQLdaAGSgjlRxhAnPZzoaYPjYIzjsG0gFxXYR1NTfbvMcAUelzw0KGgLUHVlszdmoDvKsY/yXqGiFT1FTVYhHHWp4ChKx1eULnCh0wQ14q8CLbUf5jhdlTiTNuFpjdlfElNrATJU1KKrcZzkHU0IMLo3U6PgSnmtIkakdgiDBO0mSR51EgUqgmRhXRTaKP+iJ0kBwxtqZZ3OI9D1FmDVDZwRkl+mqrsU7VrBwpXstrMkNcNEThoM7TQ3qde9dgR331amW4D5jdqLl5/MU1cLMRzp5PhI0fIRvtW6bIT+xgjw9AcpI1XLJY5Hc8Hk7Dtxe8db0Ozkg9pYLpoYM7k639FeP9TfI5BKM1zusTCzGaDkSizMBY3JD/aNrzf5xH+PaefI3FrqFqo2moqZ5zq7omfzKaNv9bs7+3pNjONeYW63L/if8Rs4lyRrD9xpXAAAAABJRU5ErkJggg==" + iceCube: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAK3RFWHRDcmVhdGlvbiBUaW1lAE1pIDE5IEZlYiAyMDAzIDAwOjU2OjEzICswMTAwhV8lEQAAAAd0SU1FB9MCEwAKIKGTp3IAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAACoUlEQVR42qWTz08TQRTHvzOz3W63lLbQWixIBRU08QdcTAwxil5M0IPRuwfiP6DGq3f9B4gXPegBLx5M8KAnY6JEQtB4QIOGIhhagVbLtrvbnRnfQqwYj0wyO28y8z7v+97bAXY52B/jflHnN1cwmmlTp3tTOKYYhp+t+7FAonIiLsp5Q5RW6qpc9VQpcLDka/Xkzmhs0Qidn6/qiZEOXE/0hkCOL75kjUDjYlwgE2Xp/ZZIL3vBoPA1phZcJHMMqXU2Tq6DW4DFqjduWIxlOUefLdgBUwAmHdjb6r65Cg+Xm/hck+i2GYpNhbhgA+HZFsBpKuNVRUNoYI/JMUSR10lBv8UxYJNNDnsthqH2CGZ+BogEgFQqaAF8qd2UgCU0A91DmRzmHYVKU2ODQO8dCUnwGtkWZ6BM4PjaDX15+FFSNxlZFbogaA2nRVkE0ARRsGmfpD35ImcyXMpGoQLdaAGSgjlRxhAnPZzoaYPjYIzjsG0gFxXYR1NTfbvMcAUelzw0KGgLUHVlszdmoDvKsY/yXqGiFT1FTVYhHHWp4ChKx1eULnCh0wQ14q8CLbUf5jhdlTiTNuFpjdlfElNrATJU1KKrcZzkHU0IMLo3U6PgSnmtIkakdgiDBO0mSR51EgUqgmRhXRTaKP+iJ0kBwxtqZZ3OI9D1FmDVDZwRkl+mqrsU7VrBwpXstrMkNcNEThoM7TQ3qde9dgR331amW4D5jdqLl5/MU1cLMRzp5PhI0fIRvtW6bIT+xgjw9AcpI1XLJY5Hc8Hk7Dtxe8db0Ozkg9pYLpoYM7k639FeP9TfI5BKM1zusTCzGaDkSizMBY3JD/aNrzf5xH+PaefI3FrqFqo2moqZ5zq7omfzKaNv9bs7+3pNjONeYW63L/if8Rs4lyRrD9xpXAAAAABJRU5ErkJggg==", + minus: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABNElEQVR42u2SsUoDQRCG/73EEBMPLCwkhahgI1j6BHYiFvoUFhZB8OmsBMEHCAGxEImEgImBvYu5y+3u+O/tRQtLCy08mJu5ufn+mZs9hR9e6l8ASk7O+tjttDEvvsuJoy37uOCsvzHZqAOD0UzJdVdwegiMXoGVGusiLxuKvBnPWnoKOFpBs0y2WHf7AGUvL3R0tBdjMCRI9VkKTMdAohlngE4IETD2C84Zk3dPOlHzq27SPFhfw/0d8ObBNHQrbAC9N0ur8pkrm+kXnSp9fqzjHROj9wg0V1kgFVxB3lsCWTVBXn2GEYwn74nqbW/K1v4GzHBCVe7AFOVLERvE2FWMfEI+FiuIONzzIoe6aTT6nVa9nWcLTqUIlmdT+mBV7BfqFCwfHGqIuNgpt/QHfqRfF/gAIz+4k570HlcAAAAASUVORK5CYII=", + plus: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABh0lEQVQ4y52SPUtcURCGn3Pu2V3ZpAhaLAQtQiwDVrGSoK1dKlvxP1j6D2wFK6ukCf6DkCJoE6IogWCzoiFgsHDDsnv3494zMykuCYl774KZ7hzeeZh35nWU1MrBorUWZgj1GgAxy7n9PuJ4q+3ua0MZYJBlbLxc52vnDQDd1HN12SyTlgMAUEfNNwBIrFJVDYjqEAVRSMf+4QBUyMVjQJQpE6wcLJYOmOWOYe5QgywXuv0hz3dnJ7ShRoOt188w54laNGgeOL97BxpQPM3HjlerCS9ay6gVtlTg8P0JIRspX24/46ghAgpE9ajWEQNVEPOYwfnNKfwGAN1enyBixOjwDnIFU080MEDEIUCMxVsNogIGikcihJgZ7euk+AWg2NhSa41veoQa/LiJADyq37vUWAnpYMzZx39383PQYWmTP5YAPn24K7/Cxc71RDznt5/ak+Yc444HBHGFJN0bTWgrE5KLYSaIJZhSWZVBEgPRBKWw8mDA3+fS/5lg/+0hvaxbAKZk+ReMaMs7XA7SqAAAAABJRU5ErkJggg==", + crystalBall: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAK3RFWHRDcmVhdGlvbiBUaW1lAEZyIDE5IERleiAyMDAzIDIwOjIzOjM3ICswMTAwgTNEtwAAAAd0SU1FB9MCGRcHLoVRoakAAAAJcEhZcwAACvAAAArwAUKsNJgAAAAEZ0FNQQAAsY8L/GEFAAACeUlEQVR42qWTy08TURTGv3m3nSm2lVcLKYkkGIVA3MhSNsQtK6uJG1m4UBJZ+l8YdhhYYKJ148ZHWBlcGYm60EIGfPAoFZBiQYZ2Ou/xzERMGogbTnJzMnfu+d3vPC5wSmNO2nw4Odmpadqdqq4PeUCSBTYlQXiVTqenbo2O1v4LeJrP51ZKpenW9nalv7cXqWQSW9vbWFBV7O/srJ9NJkfujo19PhEwPTU1XDk8nO0fGOA702lIkQgYhoFt2zAMAyvFItRCoZyQ5Uv3xse3ghjuKPjRzAz7s1KZzXZ3t6QSCZB0OK4L23FgWBbqBBF4Hg7DyHvlcurN3NzzII49ApRKpUFelnsYloXj++FyAwh5n1T45C3PAycIYCXp2vVcTgzi+COAUa/3tTU3Q6ebtioVRGIxSJIU5uhRYJCCYZpgCRCJx2We58/Rr+V/gFqt5kVkGTwdEChQEEXQIQSKfAIwf/ddSknb3w8u9BoUkBVMXUc8kwkhUjQKjiAsAQIFLtXBrNdBuaCmaYeqqq411ODBxMTHzfXiokJtC+UTQInHoTQ1hV4idVFFQYy+vy8uPlaXl+2GLgTmJ1oKQmv6ZldvHxcAAlA0JofyQWq4RAr5J/nS23fzN3Y3ivqxCfSz6CtexOqH+7f9+aUl/xOV/gutAq15VfWfjQz7L1i8XgWyxwbJvwwRHdk1dGUzKK7D+foD1V+AGXSoAliUrEGN26UIxcG3wTp6Gov4Hr511dwTLSeDM0nw5xkk0gdAVYPVBpTpBehUQ5M852L3xFF2OxFd85Dz4uKQyHMX4LodjmmLVcuvHujY+G1hoWZj7oqFlx00Y6d9yaH9AfjUFDAtnmCxAAAAAElFTkSuQmCC", + user: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACyUlEQVR42nWTa0hTYRjH/+dsZze10i5j81JhLqOijBSVqcSIjKLAvGSB0Lf1QcxSJCFDoRsUZQT5IVJKIx2VlWIfSsxrWSrzEpmXWro5LzHNy25n5/Ruo0Jx5/By4Ln8nuf98z8UVnmURW0lieFrsxUbZLLZBTv/YXSubO6386LpWoJ1ZS21MhBR0v62NGuXJiYsADMOQEAqBqdtKNb1D09NLqkM15N4n4CNeU3qolRVS0q0Am0GG2iaB0XKQ9dLoTdYUFzZlz1+I+meT0Dwhcb7rwvjteMLFFjeBZpk3eNEZA2BiEbJI31re15Mgk/AzsvNt17kq89/NFvhJwAYigZPKliew6ZACa5U9JgbcvYrfAMuNd2uzVef65y0w8HxsDg42Fw82YKDzF+Mmmd6c0dBnG9AiLb+bmlBYvanXxzsDue/tFgkxKLDhYbnPfXDNzVHfQJCtXXyrSq5OUq9DbyN9STdIooDGHzRm9DdNnTY+OD4G58A/9SaCKXC71t6WhSmeAa80wWKKEkREd+/0mPWbDlrrkovWxUQeFp35mDc5ofHkrdjYJrFiMUGxt1MXgFNg5EIMdo3jqHPI9Vj5SknlwGCMnTRB+LDOjNT96KyawZLZLKMob1XgNc3AqEA0gApxgaMGGweuGN6kpHrAQSeepq8Ria6WpiniarutQAcBzEt8DRx1P8VvV8e0nV+GHzXh9mxSY2xKrORitS+rIuN3XKEDZGj3zAPf5G7i1p2RwpeQ7kVFTIi2GcXYWzvbzVUpCVQe3LqepMO7djdOs/AOW8FjZVT/1Joj6BuPWg/Ccx1HROG8hNKskFtl1Il3/dTEghifkAmIYM4z0yKmMkTcx8n6z02B7gFK6xfv5uMj9ODKXlqZRBrc6pEUnEuJWUiIRGFw8WSXcXEAESLJfIH0wIhHOwEWNcwZ3d0k1gLLRMPmHRZP/4A67cYINx3gGMAAAAASUVORK5CYII=", + redFlag: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAERpIDIgU2VwIDIwMDMgMTY6MDk6MzEgKzAxMDAbRI2sAAAAB3RJTUUH0wkCDgsfmwWX7wAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAAHnSURBVHjapZK/axRBFMe/s79md0nOhEQtvErwBwhik0JBTCGW1hZXpdFWFCz9G4QUV9gasBBFEJU0R2wMKFhYCBIPlLvb3MU79/b2bn/c7Ph2wy4LZ+AkDx4MM/P9zHvfecAxg+WLer3+iKnq3bj90z71/tmbW60fWx7DKI5gCRW31TOVm4lIFgKB4bDtf7zmyHupTssB1Wr1/o319dOv3m5jde/1hn39/IYSAPK3D1syaPYUuLgCrF3Ap4c7q3AOMp2SA3zf70zGY3T3HfTCBPq5BSxeOYHKSQXaXgeYSiCiDKbw++Ew1xUVeJ7naJoG2+QYC9qO6bJMgKTc6OHaG8UFQCkdOUIImJxjIg0CCLpcVuYKhskgcGcAjLFOEAQwdAMTZiBJSy4/m7LUQ4UbYrYFRVGcFGBSC5HKIQUBNFKplDrDn0GMwRcXy1YfEY4AhGGYAWLJ4X53oQcSSS9Erz2Vn3f6T8mOF9FW6+wSsDsDoMha4IaONl/B5kv/+dWDXw1iCF3FtzvAh38NUgGgH8gq4LoOVlnEu0uXvz5uNOrZoTh6EgsTpZQpQBqGAZOSA0vzjHIBqNVqHlUxMk0TnL6SPPk/QBrkwb5lWRmAYPY8gLKJ6Ha7m81m84Hrui2CPJkHcOz4C2fdx/hnBd3YAAAAAElFTkSuQmCC", + 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=" }; \ No newline at end of file diff --git a/web/jsLib/joint.js b/web/jsLib/joint.js index 41a8aad..b119fe2 100644 --- a/web/jsLib/joint.js +++ b/web/jsLib/joint.js @@ -17138,7 +17138,7 @@ if ( typeof window === "object" && typeof window.document === "object" ) { text: function(content, opt) { opt = opt || {}; - var lines = content.split('\n'); + var lines = content/*.split('\n')*/; var i = 0; var tspan; @@ -17162,6 +17162,8 @@ if ( typeof window === "object" && typeof window.document === "object" ) { this.node.textContent = ''; var textNode = this.node; + var image; + //console.log(textNode.parentNode); if (opt.textPath) { @@ -17201,45 +17203,58 @@ if ( typeof window === "object" && typeof window.document === "object" ) { textNode = textPath.node; } - //if (lines.length === 1) { - // textNode.textContent = content; - // return this; - //} + // trash - elements collected outside tspan element, variable "to save algorithm" + if (textNode.TRASH instanceof Array) { + _.each(textNode.TRASH, function (e) { e.parentNode.removeChild(e); }); + } + textNode.TRASH = []; for (; i < lines.length; i++) { - var jj, setup; + var jj, setup, iconLeft, xOrigin = this.attr('x') || 0, + iconXOrigin = (opt["ref-x"] || 0) + xOrigin; // Shift all the but first by one line (`1em`) - tspan = V('tspan', { dy: (i == 0 ? '0em' : opt.lineHeight || '1em'), x: this.attr('x') || 0 }); + tspan = V('tspan', { + dy: (i == 0 ? '0em' : opt.lineHeight || '1em'), + x: xOrigin + (lines[i].icons ? lines[i].icons.length*10 + 2 : 0) + }); tspan.addClass('line'); - if (!lines[i]) { + if (!lines[i].text) { tspan.addClass('empty-line'); } - if (lines[i].indexOf("\x1b") !== -1) { - jj = lines[i].split("\x1b"); - lines[i] = jj[0]; - setup = JSON.parse(jj[1]); - if (setup["STYLES"]) { - for (var j in setup["STYLES"]) { - tspan.node.style[j] = setup["STYLES"][j]; - } + if (lines[i]["styles"]) { + for (var j in lines[i]["styles"]) { + tspan.node.style[j] = lines[i]["styles"][j]; } } - if (opt.clickHandler) { - tspan.node.onclick = opt.clickHandler; + if (typeof lines[i]["clickHandler"] === "function") { + tspan.node.addEventListener("click", lines[i]["clickHandler"]); + tspan.addClass('line-clickable'); } - if (opt.lineClickHandlers && opt.lineClickHandlers[i]) { - tspan.node.addEventListener("click", opt.lineClickHandlers[i]); - tspan.node.setAttribute("class", tspan.node.getAttribute("class") + " line-clickable"); - } // Make sure the textContent is never empty. If it is, add an additional // space (an invisible character) so that following lines are correctly // relatively positioned. `dy=1em` won't work with empty lines otherwise. - tspan.node.textContent = lines[i] || ' '; + tspan.node.textContent = lines[i].text || ' '; V(textNode).append(tspan); + + if (lines[i].icons instanceof Array) { + iconLeft = iconXOrigin; + _.each(lines[i].icons, function (ic) { + image = V("image"); + image.attr("xlink:href", ic.src); + image.attr("width", 10); + image.attr("height", 10); + image.attr("y", textNode.getBoundingClientRect().top + i*(opt["font-size"] || 14) + 2); + image.attr("x", iconLeft); + iconLeft += 10; + V(textNode.parentNode).append(image); + textNode.TRASH.push(image.node); + }); + } + } return this; }, @@ -20952,7 +20967,7 @@ joint.dia.ElementView = joint.dia.CellView.extend({ // to rewrite them, if needed. (i.e display: 'none') if (!_.isUndefined(attrs.text)) { $selected.each(function() { - V(this).text(attrs.text + '', attrs); + V(this).text(attrs.text, attrs); }); specialAttributes.push('lineHeight','textPath'); } diff --git a/web/jsLib/joint.shapes.uml.js b/web/jsLib/joint.shapes.uml.js index 51d3b6f..cb07998 100644 --- a/web/jsLib/joint.shapes.uml.js +++ b/web/jsLib/joint.shapes.uml.js @@ -88,9 +88,9 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ var o, rects = [ { type: 'name', text: this.getClassName() }, - { type: 'params', text: (o = this.get('params')) .map(function (e) { return e.text; }), o: o }, - { type: 'attrs', text: (o = this.get('attributes')).map(function (e) { return e.text; }), o: o }, - { type: 'methods', text: (o = this.get('methods')) .map(function (e) { return e.text; }), o: o } + { type: 'params', text: (o = this.get('params')) , o: o }, + { type: 'attrs', text: (o = this.get('attributes')), o: o }, + { type: 'methods', text: (o = this.get('methods')) , o: o } ], self = this, classSigns = this.get('classSigns'), @@ -109,8 +109,8 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ this.defaults.size.width = Math.max(this.defaults.MIN_WIDTH, Math.min(w, 250)); _.each(rects, function (rect) { - (rect.text instanceof Array ? rect.text : [rect.text]).forEach(function (s) { - var t = s.split("\x1b")[0].length*SYMBOL_12_WIDTH + 8; + rect.text.forEach(function (s) { + var t = s.text.length*SYMBOL_12_WIDTH + 8 + (s.icons ? s.icons.length*10 + 2 : 0); if (t > self.defaults.size.width) { self.defaults.size.width = t; } @@ -157,7 +157,8 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ }, getClassName: function () { - return this.get('name'); + var n = this.get('name'); + return n instanceof Array ? n : [{ text: n }]; }, updateRectangles: function () { @@ -168,9 +169,9 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ var rects = [ { type: 'name', text: this.getClassName() }, - { type: 'params', text: this.get('params').map(function (e) { return e.text; }) }, - { type: 'attrs', text: this.get('attributes').map(function (e) { return e.text; }) }, - { type: 'methods', text: this.get('methods').map(function (e) { return e.text; }) } + { type: 'params', text: this.get('params') }, + { type: 'attrs', text: this.get('attributes') }, + { type: 'methods', text: this.get('methods') } ]; var offsetY = 0; @@ -180,11 +181,10 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ _.each(rects, function(rect) { - var lines = _.isArray(rect.text) ? rect.text : [rect.text]; - + var lines = _.isArray(rect.text) ? rect.text : [{ text: rect.text }]; if (rect.type === "name") { if (self.HEAD_EMPTY_LINES) lines.unshift(""); - for (var i = 0; i < self.HEAD_EMPTY_LINES; i++) lines.unshift(""); + for (var i = 0; i < self.HEAD_EMPTY_LINES; i++) lines.unshift({ text: "" }); } var rectHeight = lines.length * 12 + (lines.length ? 10 : 0), @@ -192,7 +192,8 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({ rectRect = attrs['.uml-class-' + rect.type + '-rect'], rectLabel = attrs['.uml-class-' + rect.type + '-label']; - rectText.text = lines.join('\n'); + rectText.text = lines; + if (nameClickHandler) { if (rect.type === "name") { rectText.clickHandler = nameClickHandler;