Browse Source

Merge branch 'update' of bentley/AToMPM into master

simon 6 years ago
parent
commit
5994199c9a
100 changed files with 24210 additions and 3746 deletions
  1. 3 0
      .eslintignore
  2. 35 0
      .eslintrc.json
  3. BIN
      .etc/tcorepaper.pdf
  4. 9 1
      .gitignore
  5. BIN
      .manual/figures/atompm.pdf
  6. BIN
      .manual/figures/forestCSMM1.pdf
  7. BIN
      .manual/figures/forestCSMM2.pdf
  8. BIN
      .manual/figures/forestMM.pdf
  9. BIN
      .manual/figures/geomctrl_ok.pdf
  10. BIN
      .manual/figures/geomctrl_resize.pdf
  11. BIN
      .manual/figures/geomctrl_resizeH.pdf
  12. BIN
      .manual/figures/geomctrl_resizeW.pdf
  13. BIN
      .manual/figures/geomctrl_rotate.pdf
  14. BIN
      .manual/figures/icon_cloudmgmt.pdf
  15. 0 148
      .manual/figures/icon_compileToASMM.pdf
  16. 0 150
      .manual/figures/icon_compileToCSMM.pdf
  17. BIN
      .manual/figures/icon_compileToPatternMM.pdf
  18. BIN
      .manual/figures/icon_copy.pdf
  19. BIN
      .manual/figures/icon_download.pdf
  20. BIN
      .manual/figures/icon_dropToolbar.pdf
  21. BIN
      .manual/figures/icon_editprefs.pdf
  22. BIN
      .manual/figures/icon_exportSVG.pdf
  23. BIN
      .manual/figures/icon_insertModel.pdf
  24. BIN
      .manual/figures/icon_load.pdf
  25. 0 150
      .manual/figures/icon_loadModel.pdf
  26. BIN
      .manual/figures/icon_loadToolbar.pdf
  27. BIN
      .manual/figures/icon_newTab.pdf
  28. BIN
      .manual/figures/icon_paste.pdf
  29. BIN
      .manual/figures/icon_pause.pdf
  30. BIN
      .manual/figures/icon_play.pdf
  31. 0 149
      .manual/figures/icon_redo.pdf
  32. BIN
      .manual/figures/icon_saveModel.pdf
  33. 0 152
      .manual/figures/icon_saveModelAs.pdf
  34. BIN
      .manual/figures/icon_step.pdf
  35. BIN
      .manual/figures/icon_stop.pdf
  36. BIN
      .manual/figures/icon_toggleVisibleContainmentLinks.pdf
  37. BIN
      .manual/figures/icon_toggledebug.pdf
  38. BIN
      .manual/figures/icon_togglemm.pdf
  39. BIN
      .manual/figures/icon_undo.pdf
  40. BIN
      .manual/figures/icon_validateM.pdf
  41. BIN
      .manual/figures/mainmenum.pdf
  42. BIN
      .manual/manual.pdf
  43. 0 938
      .manual/manual.tex
  44. 20 0
      .travis.yml
  45. 49 27
      README.md
  46. 0 0
      REFS
  47. 4 19
      ___dataurize.js
  48. 39 51
      ___do.js
  49. 9 21
      ___fs++.js
  50. 59 37
      __worker.js
  51. 43 31
      asworker.js
  52. 1309 0
      client/3rd_party_libs/d3/API.md
  53. 1407 0
      client/3rd_party_libs/d3/CHANGES.md
  54. 27 0
      client/3rd_party_libs/d3/LICENSE
  55. 57 0
      client/3rd_party_libs/d3/README.md
  56. 17787 0
      client/3rd_party_libs/d3/d3.js
  57. 2 0
      client/3rd_party_libs/d3/d3.min.js
  58. 29 48
      client/atompm.html
  59. 4 20
      client/behaviourmanager.js
  60. 20 35
      client/behavioursc_canvas.js
  61. 5 20
      client/behavioursc_dialog.js
  62. 4 19
      client/behavioursc_inputbar.js
  63. 28 37
      client/client.js
  64. 4 20
      client/collaboration.js
  65. 57 42
      client/compile_utils.js
  66. 66 32
      client/connection_utils.js
  67. 9 22
      client/constants.js
  68. 82 74
      client/data_utils.js
  69. 4 20
      client/edit_utils.js
  70. 436 0
      client/file_browser.js
  71. 156 73
      client/geometry_utils.js
  72. 5 19
      client/globalVariables.js
  73. 119 251
      client/gui_utils.js
  74. 25 28
      client/http_utils.js
  75. 4 20
      client/init.js
  76. 6 1
      client/input_bar_utils.js
  77. 153 0
      client/layout.js
  78. 10 28
      client/mmm_utils.js
  79. 1074 0
      client/modelverse_connector.js
  80. 333 0
      client/modelverse_connector_rendering.js
  81. 9 4
      client/query_response.js
  82. 16 26
      client/selection_utils.js
  83. 251 248
      client/styles.css
  84. 5 0
      client/svg_utils.js
  85. 4 20
      client/user_management.js
  86. 19 22
      client/window_event.js
  87. 95 268
      client/window_management.js
  88. 309 260
      csworker.js
  89. 10 215
      doc/Makefile
  90. BIN
      doc/_build/doctrees/environment.pickle
  91. BIN
      doc/_build/doctrees/executing_transformation.doctree
  92. BIN
      doc/_build/doctrees/index.doctree
  93. BIN
      doc/_build/doctrees/installation.doctree
  94. BIN
      doc/_build/doctrees/modelling_transformation.doctree
  95. BIN
      doc/_build/doctrees/new_language.doctree
  96. BIN
      doc/_build/doctrees/overview.doctree
  97. BIN
      doc/_build/doctrees/troubleshooting.doctree
  98. BIN
      doc/_build/doctrees/using_language.doctree
  99. BIN
      doc/_build/doctrees/workflows.doctree
  100. 0 0
      doc/_build/html/.buildinfo

+ 3 - 0
.eslintignore

@@ -0,0 +1,3 @@
+client/3rd_party_libs/*
+doc/*
+coverage/*

+ 35 - 0
.eslintrc.json

@@ -0,0 +1,35 @@
+{
+    "env": {
+        "browser": true,
+        "es6": true,
+        "node": true,
+        "jquery": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "sourceType": "module"
+    },
+    "rules": {
+        
+        "linebreak-style": [
+            "warn",
+            "unix"
+        ],
+        "quotes": [
+            "off",
+            "single"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "no-console": ["off"],
+        
+        //ERRORS TO WARN AND FIX LATER
+        "no-mixed-spaces-and-tabs": ["off"],
+        "no-unused-vars": ["off"],
+        "no-redeclare": ["off"],
+         "no-undef": ["off"],
+         "no-useless-escape": ["off"]
+    }
+}

BIN
.etc/tcorepaper.pdf


+ 9 - 1
.gitignore

@@ -1,3 +1,10 @@
+#ignore autosave files
+.autosave.*
+
+#Jetbrains project files
+.idea/
+AToMPM.iml
+
 # ---> Python
 # Byte-compiled / optimized / DLL files
 __pycache__/
@@ -54,9 +61,10 @@ coverage.xml
 
 # Sphinx documentation
 docs/_build/
+doc/_build/
 
 # PyBuilder
 target/
 
 # Node Modules
-node_modules/
+node_modules/

BIN
.manual/figures/atompm.pdf


BIN
.manual/figures/forestCSMM1.pdf


BIN
.manual/figures/forestCSMM2.pdf


BIN
.manual/figures/forestMM.pdf


BIN
.manual/figures/geomctrl_ok.pdf


BIN
.manual/figures/geomctrl_resize.pdf


BIN
.manual/figures/geomctrl_resizeH.pdf


BIN
.manual/figures/geomctrl_resizeW.pdf


BIN
.manual/figures/geomctrl_rotate.pdf


BIN
.manual/figures/icon_cloudmgmt.pdf


+ 0 - 148
.manual/figures/icon_compileToASMM.pdf

@@ -1,148 +0,0 @@
-%PDF-1.4 
-1 0 obj
-<<
-/Pages 2 0 R
-/Type /Catalog
->>
-endobj
-2 0 obj
-<<
-/Type /Pages
-/Kids [ 3 0 R ]
-/Count 1
->>
-endobj
-3 0 obj
-<<
-/Type /Page
-/Parent 2 0 R
-/Resources <<
-/XObject << /Im0 8 0 R >>
-/ProcSet 6 0 R >>
-/MediaBox [0 0 32 32]
-/CropBox [0 0 32 32]
-/Contents 4 0 R
-/Thumb 11 0 R
->>
-endobj
-4 0 obj
-<<
-/Length 5 0 R
->>
-stream
-q
-32 0 0 32 0 0 cm
-/Im0 Do
-Q
-endstream
-endobj
-5 0 obj
-29
-endobj
-6 0 obj
-[ /PDF /Text /ImageC ]
-endobj
-7 0 obj
-<<
->>
-endobj
-8 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Im0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/SMask 15 0 R
-/Length 9 0 R
->>
-stream
-xÚ…Ï‹‚0ЊF|‹ŠDå55ÆWÄÿÿ7×n#q]Ø	in¹d@Ó|;4ÍÇãíñòx¸<Q¼»P
oªÊWÝ拃é1×̹ªu�[ÅœªJÕ´-€CÈVÚKD�Di+�~_QäyaÉ$ó½�fL*™úpH™½Ôö{n'µçï¸é:Ù&LŒ¨¥´�-›kÀ©Ž£+ÈK¡5²-—‘îi]psó;¸ÌL�yÊML�ËØEÕGaÈ�yè1ðè{½NÁÖ–W
-endstream
-endobj
-9 0 obj
-206
-endobj
-10 0 obj
-/DeviceGray
-endobj
-11 0 obj
-<<
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/Length 12 0 R
->>
-stream
-xÚ…Ï‹‚0ЊF|‹ŠDå55ÆWÄÿÿ7×n#q]Ø	in¹d@Ó|;4ÍÇãíñòx¸<Q¼»P
oªÊWÝ拃é1×̹ªu�[ÅœªJÕ´-€CÈVÚKD�Di+�~_QäyaÉ$ó½�fL*™úpH™½Ôö{n'µçï¸é:Ù&LŒ¨¥´�-›kÀ©Ž£+ÈK¡5²-—‘îi]psó;¸ÌL�yÊML�ËØEÕGaÈ�yè1ðè{½NÁÖ–W
-endstream
-endobj
-12 0 obj
-206
-endobj
-13 0 obj
-endobj
-14 0 obj
-206
-endobj
-15 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Ma0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace /DeviceGray
-/BitsPerComponent 8
-/Length 16 0 R
->>
-stream
-xÚµ“QÄ0D‘€„H@B$TB¥œƒ“€„J‰„HˆJÚ´w7ä£sû—<†,³„ªªb-�?hCèb�§Cvp91 ÃyppFJ!/˜SÀ7\%ûþÞÚ¢`æÏåaWÏ+ZC�~P,Ö›—_ ßœš<â^ÁÌß÷|¶Ã[¯žúˆí^Ü ÊŸq‰ó/ç¯bÞ&\(ÞOÍ?ýäÿDu‚ã\iX(¡
-endstream
-endobj
-16 0 obj
-167
-endobj
-17 0 obj
-<<
-/Title (compileToASMM.icon.png.pdf)
-/CreationDate (D:20120323152053)
-/ModDate (D:20120323152053)
-/Producer (ImageMagick 6.6.0-4 2011-06-15 Q16 http://www.imagemagick.org)
->>
-endobj
-xref
-0 18
-0000000000 65535 f 
-0000000010 00000 n 
-0000000059 00000 n 
-0000000118 00000 n 
-0000000296 00000 n 
-0000000377 00000 n 
-0000000395 00000 n 
-0000000433 00000 n 
-0000000454 00000 n 
-0000000854 00000 n 
-0000000873 00000 n 
-0000000901 00000 n 
-0000001247 00000 n 
-0000001267 00000 n 
-0000001283 00000 n 
-0000001303 00000 n 
-0000001657 00000 n 
-0000001677 00000 n 
-trailer
-<<
-/Size 18
-/Info 17 0 R
-/Root 1 0 R
->>
-startxref
-1870
-%%EOF

+ 0 - 150
.manual/figures/icon_compileToCSMM.pdf

@@ -1,150 +0,0 @@
-%PDF-1.4 
-1 0 obj
-<<
-/Pages 2 0 R
-/Type /Catalog
->>
-endobj
-2 0 obj
-<<
-/Type /Pages
-/Kids [ 3 0 R ]
-/Count 1
->>
-endobj
-3 0 obj
-<<
-/Type /Page
-/Parent 2 0 R
-/Resources <<
-/XObject << /Im0 8 0 R >>
-/ProcSet 6 0 R >>
-/MediaBox [0 0 32 32]
-/CropBox [0 0 32 32]
-/Contents 4 0 R
-/Thumb 11 0 R
->>
-endobj
-4 0 obj
-<<
-/Length 5 0 R
->>
-stream
-q
-32 0 0 32 0 0 cm
-/Im0 Do
-Q
-endstream
-endobj
-5 0 obj
-29
-endobj
-6 0 obj
-[ /PDF /Text /ImageC ]
-endobj
-7 0 obj
-<<
->>
-endobj
-8 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Im0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/SMask 15 0 R
-/Length 9 0 R
->>
-stream
-xÚmÒÛ‚0EÑ#ŠÑhŒä&¢hŒF#þÿ¿	b;-œõ2�ìÇNó!hoJú‹’þ¤¤?(éwJú�’~¥¤_É8Ûê¶“
UUu³}?šPš[Ùõƒ(Íå§í…@^ä@¦å�TC’Ž$Ø+ò²Ä
-¢˜ˆB!((_�OyÊΣä'¶”ô
%}MI_QÆ),	óT„uKó‘Á±¹3‹‹¡©„3ùsúý+ÛNÞ
-endstream
-endobj
-9 0 obj
-173
-endobj
-10 0 obj
-/DeviceGray
-endobj
-11 0 obj
-<<
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/Length 12 0 R
->>
-stream
-xÚmÒÛ‚0EÑ#ŠÑhŒä&¢hŒF#þÿ¿	b;-œõ2�ìÇNó!hoJú‹’þ¤¤?(éwJú�’~¥¤_É8Ûê¶“
UUu³}?šPš[Ùõƒ(Íå§í…@^ä@¦å�TC’Ž$Ø+ò²Ä
-¢˜ˆB!((_�OyÊΣä'¶”ô
%}MI_QÆ),	óT„uKó‘Á±¹3‹‹¡©„3ùsúý+ÛNÞ
-endstream
-endobj
-12 0 obj
-173
-endobj
-13 0 obj
-endobj
-14 0 obj
-173
-endobj
-15 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Ma0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace /DeviceGray
-/BitsPerComponent 8
-/Length 16 0 R
->>
-stream
-xÚÕ’A
D!DG�€$ )8©¤ ¡øÉÏ(Ù½î»ÑGHi§tBÁ SþÜëÝ«äqó-;XŒ‡ í½(áô¾ÎSõ›ÏHjZIÆÒÞlZ|s^w¯ËëµÔá�MjÂü(f4æG1^6õÍ—O{Îsÿ{xÓóÿMÏç³D�Îw�îg…í×^8óa9óµ³çóv~Éô
-endstream
-endobj
-16 0 obj
-157
-endobj
-17 0 obj
-<<
-/Title (compileToCSMM.icon.png.pdf)
-/CreationDate (D:20120323152053)
-/ModDate (D:20120323152053)
-/Producer (ImageMagick 6.6.0-4 2011-06-15 Q16 http://www.imagemagick.org)
->>
-endobj
-xref
-0 18
-0000000000 65535 f 
-0000000010 00000 n 
-0000000059 00000 n 
-0000000118 00000 n 
-0000000296 00000 n 
-0000000377 00000 n 
-0000000395 00000 n 
-0000000433 00000 n 
-0000000454 00000 n 
-0000000821 00000 n 
-0000000840 00000 n 
-0000000868 00000 n 
-0000001181 00000 n 
-0000001201 00000 n 
-0000001217 00000 n 
-0000001237 00000 n 
-0000001581 00000 n 
-0000001601 00000 n 
-trailer
-<<
-/Size 18
-/Info 17 0 R
-/Root 1 0 R
->>
-startxref
-1794
-%%EOF

BIN
.manual/figures/icon_compileToPatternMM.pdf


BIN
.manual/figures/icon_copy.pdf


BIN
.manual/figures/icon_download.pdf


BIN
.manual/figures/icon_dropToolbar.pdf


BIN
.manual/figures/icon_editprefs.pdf


BIN
.manual/figures/icon_exportSVG.pdf


BIN
.manual/figures/icon_insertModel.pdf


BIN
.manual/figures/icon_load.pdf


+ 0 - 150
.manual/figures/icon_loadModel.pdf

@@ -1,150 +0,0 @@
-%PDF-1.4 
-1 0 obj
-<<
-/Pages 2 0 R
-/Type /Catalog
->>
-endobj
-2 0 obj
-<<
-/Type /Pages
-/Kids [ 3 0 R ]
-/Count 1
->>
-endobj
-3 0 obj
-<<
-/Type /Page
-/Parent 2 0 R
-/Resources <<
-/XObject << /Im0 8 0 R >>
-/ProcSet 6 0 R >>
-/MediaBox [0 0 32 32]
-/CropBox [0 0 32 32]
-/Contents 4 0 R
-/Thumb 11 0 R
->>
-endobj
-4 0 obj
-<<
-/Length 5 0 R
->>
-stream
-q
-32 0 0 32 0 0 cm
-/Im0 Do
-Q
-endstream
-endobj
-5 0 obj
-29
-endobj
-6 0 obj
-[ /PDF /Text /ImageC ]
-endobj
-7 0 obj
-<<
->>
-endobj
-8 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Im0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/SMask 15 0 R
-/Length 9 0 R
->>
-stream
-xÚ­ÏÍ
-‚`…á‰~Ȥ¢Å4-Š>-³û¿·<cA¡gV=‹Ù¼ÌÀˆ4Ï·FÕ��z(ß¿Üú¹úÕë®dœC¿Ð/ô³ýÔCQ­,ynfÉ2»§i7™äÐæ„‹±¾�©=âBô�Cè;NÜrÚ7òjÍ¡/9=ïsÈÞ‚CŸsz~ÆiŸL)ícNûˆ’?xÝc;0
-endstream
-endobj
-9 0 obj
-151
-endobj
-10 0 obj
-/DeviceGray
-endobj
-11 0 obj
-<<
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/Length 12 0 R
->>
-stream
-xÚ­ÏÍ
-‚`…á‰~Ȥ¢Å4-Š>-³û¿·<cA¡gV=‹Ù¼ÌÀˆ4Ï·FÕ��z(ß¿Üú¹úÕë®dœC¿Ð/ô³ýÔCQ­,ynfÉ2»§i7™äÐæ„‹±¾�©=âBô�Cè;NÜrÚ7òjÍ¡/9=ïsÈÞ‚CŸsz~ÆiŸL)ícNûˆ’?xÝc;0
-endstream
-endobj
-12 0 obj
-151
-endobj
-13 0 obj
-endobj
-14 0 obj
-151
-endobj
-15 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Ma0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace /DeviceGray
-/BitsPerComponent 8
-/Length 16 0 R
->>
-stream
-xÚ­ÓA„0Ð/¡�P	H¨”JÀA%T�P	•€„0awg™ÒŸràzyC“f䛌nêÏ%öx‘ü�Ý~ñênž…eŸÔ…gyÇó#"&C*lÏzÖc™ç�(úy¤\“úJ={H'ÏÜ]»�î°QOxP>p?Ë'ÊÌá~6ÀñëçAùÁ¯ÍÿÓ&^ïíö^È!¨{3
-endstream
-endobj
-16 0 obj
-159
-endobj
-17 0 obj
-<<
-/Title (loadModel.icon.png.pdf)
-/CreationDate (D:20120322115414)
-/ModDate (D:20120322115414)
-/Producer (ImageMagick 6.6.0-4 2011-06-15 Q16 http://www.imagemagick.org)
->>
-endobj
-xref
-0 18
-0000000000 65535 f 
-0000000010 00000 n 
-0000000059 00000 n 
-0000000118 00000 n 
-0000000296 00000 n 
-0000000377 00000 n 
-0000000395 00000 n 
-0000000433 00000 n 
-0000000454 00000 n 
-0000000799 00000 n 
-0000000818 00000 n 
-0000000846 00000 n 
-0000001137 00000 n 
-0000001157 00000 n 
-0000001173 00000 n 
-0000001193 00000 n 
-0000001539 00000 n 
-0000001559 00000 n 
-trailer
-<<
-/Size 18
-/Info 17 0 R
-/Root 1 0 R
->>
-startxref
-1748
-%%EOF

BIN
.manual/figures/icon_loadToolbar.pdf


BIN
.manual/figures/icon_newTab.pdf


BIN
.manual/figures/icon_paste.pdf


BIN
.manual/figures/icon_pause.pdf


BIN
.manual/figures/icon_play.pdf


+ 0 - 149
.manual/figures/icon_redo.pdf

@@ -1,149 +0,0 @@
-%PDF-1.4 
-1 0 obj
-<<
-/Pages 2 0 R
-/Type /Catalog
->>
-endobj
-2 0 obj
-<<
-/Type /Pages
-/Kids [ 3 0 R ]
-/Count 1
->>
-endobj
-3 0 obj
-<<
-/Type /Page
-/Parent 2 0 R
-/Resources <<
-/XObject << /Im0 8 0 R >>
-/ProcSet 6 0 R >>
-/MediaBox [0 0 32 32]
-/CropBox [0 0 32 32]
-/Contents 4 0 R
-/Thumb 11 0 R
->>
-endobj
-4 0 obj
-<<
-/Length 5 0 R
->>
-stream
-q
-32 0 0 32 0 0 cm
-/Im0 Do
-Q
-endstream
-endobj
-5 0 obj
-29
-endobj
-6 0 obj
-[ /PDF /Text /ImageC ]
-endobj
-7 0 obj
-<<
->>
-endobj
-8 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Im0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/SMask 15 0 R
-/Length 9 0 R
->>
-stream
-xÚÝÒQ‚0Àñ“$R33L͈[¶¾ÿ‡ëvÓÙFÛkÐïepÿ=mð/8Mø—:>?�r6ªÌ:ÆhƦ<ÜMÂCæÞŠòÍŽúÕNäîbÕ‰Þ*çÖ„¹ž4�¸ÜÔ:€J:ÍOQi <Jê©Êy"”P�ƒÊûB9Qy—ë –Ê)YzjˆQb~v"Äd‹"ËÆDhƒ\[µCçÖAà쾿rvσŸzи14
-endstream
-endobj
-9 0 obj
-173
-endobj
-10 0 obj
-/DeviceGray
-endobj
-11 0 obj
-<<
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/Length 12 0 R
->>
-stream
-xÚÝÒQ‚0Àñ“$R33L͈[¶¾ÿ‡ëvÓÙFÛkÐïepÿ=mð/8Mø—:>?�r6ªÌ:ÆhƦ<ÜMÂCæÞŠòÍŽúÕNäîbÕ‰Þ*çÖ„¹ž4�¸ÜÔ:€J:ÍOQi <Jê©Êy"”P�ƒÊûB9Qy—ë –Ê)YzjˆQb~v"Äd‹"ËÆDhƒ\[µCçÖAà쾿rvσŸzи14
-endstream
-endobj
-12 0 obj
-173
-endobj
-13 0 obj
-endobj
-14 0 obj
-173
-endobj
-15 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Ma0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace /DeviceGray
-/BitsPerComponent 8
-/Length 16 0 R
->>
-stream
-xÚÝ’QÄ D‘€$ 	•P	HˆƒJ@B% 	H@B.4@¡wðÛ™ÛOÞ&d3â_¤¬Ç¬è¶T:¼w~Ó
›„ƒ¼"P¼ãSIFÃxÃo%Â<YÂ‰Ž‹ûF?ë^ƒdë�\xºr�WÐñ3
-xr±©«èEU‰{o²Oc…-¾†‡€ÛÃSÀ“uŽóS¦¬Ò‡�½ÁÝ«É@µQš('C¶|3ûêªïxª¼‚%OrÉ=ˆWõ‘µdÞ
-endstream
-endobj
-16 0 obj
-194
-endobj
-17 0 obj
-<<
-/Title (redo.icon.png.pdf)
-/CreationDate (D:20120322115414)
-/ModDate (D:20120322115414)
-/Producer (ImageMagick 6.6.0-4 2011-06-15 Q16 http://www.imagemagick.org)
->>
-endobj
-xref
-0 18
-0000000000 65535 f 
-0000000010 00000 n 
-0000000059 00000 n 
-0000000118 00000 n 
-0000000296 00000 n 
-0000000377 00000 n 
-0000000395 00000 n 
-0000000433 00000 n 
-0000000454 00000 n 
-0000000821 00000 n 
-0000000840 00000 n 
-0000000868 00000 n 
-0000001181 00000 n 
-0000001201 00000 n 
-0000001217 00000 n 
-0000001237 00000 n 
-0000001618 00000 n 
-0000001638 00000 n 
-trailer
-<<
-/Size 18
-/Info 17 0 R
-/Root 1 0 R
->>
-startxref
-1822
-%%EOF

BIN
.manual/figures/icon_saveModel.pdf


+ 0 - 152
.manual/figures/icon_saveModelAs.pdf

@@ -1,152 +0,0 @@
-%PDF-1.4 
-1 0 obj
-<<
-/Pages 2 0 R
-/Type /Catalog
->>
-endobj
-2 0 obj
-<<
-/Type /Pages
-/Kids [ 3 0 R ]
-/Count 1
->>
-endobj
-3 0 obj
-<<
-/Type /Page
-/Parent 2 0 R
-/Resources <<
-/XObject << /Im0 8 0 R >>
-/ProcSet 6 0 R >>
-/MediaBox [0 0 31.996 31.996]
-/CropBox [0 0 31.996 31.996]
-/Contents 4 0 R
-/Thumb 11 0 R
->>
-endobj
-4 0 obj
-<<
-/Length 5 0 R
->>
-stream
-q
-31.996 0 0 31.996 0 0 cm
-/Im0 Do
-Q
-endstream
-endobj
-5 0 obj
-37
-endobj
-6 0 obj
-[ /PDF /Text /ImageC ]
-endobj
-7 0 obj
-<<
->>
-endobj
-8 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Im0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/SMask 15 0 R
-/Length 9 0 R
->>
-stream
-xÚ…Ñ‹r‚0„áíÝ­T[´Vð"¢8¥ïÿnÝ%N&´|c<˜P�ú÷¶º~þ‡s‡ªÃ‘N8Z'à¤yÇ’€Ò̧Ë(
-KÝ�9¹¥}wîØYÚwgJ@jiß�[¶–öݹ&`m­€•Ù3»K–-êš	I‹ºæ‚pƒÚ7µKݼ«}Q+WçJCmF^+@ó|¥ZD^/öû|߬Bíƒü/ϲ4Õ+ƒÚ„ü¾¹üS¨½“OqÌ»O’8^¨…ääyssð†oäžÏKžòŠ£h:›©äÿþxbŒ¡6$¿�Bcµ>yy¯A³šÖ£·÷\:g Ô{¾êÉ=^uo�:ÜuøMN³
-endstream
-endobj
-9 0 obj
-271
-endobj
-10 0 obj
-/DeviceGray
-endobj
-11 0 obj
-<<
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace 10 0 R
-/BitsPerComponent 8
-/Length 12 0 R
->>
-stream
-xÚ…Ñ‹r‚0„áíÝ­T[´Vð"¢8¥ïÿnÝ%N&´|c<˜P�ú÷¶º~þ‡s‡ªÃ‘N8Z'à¤yÇ’€Ò̧Ë(
-KÝ�9¹¥}wîØYÚwgJ@jiß�[¶–öݹ&`m­€•Ù3»K–-êš	I‹ºæ‚pƒÚ7µKݼ«}Q+WçJCmF^+@ó|¥ZD^/öû|߬Bíƒü/ϲ4Õ+ƒÚ„ü¾¹üS¨½“OqÌ»O’8^¨…ääyssð†oäžÏKžòŠ£h:›©äÿþxbŒ¡6$¿�Bcµ>yy¯A³šÖ£·÷\:g Ô{¾êÉ=^uo�:ÜuøMN³
-endstream
-endobj
-12 0 obj
-271
-endobj
-13 0 obj
-endobj
-14 0 obj
-271
-endobj
-15 0 obj
-<<
-/Type /XObject
-/Subtype /Image
-/Name /Ma0
-/Filter [ /FlateDecode ]
-/Width 32
-/Height 32
-/ColorSpace /DeviceGray
-/BitsPerComponent 8
-/Length 16 0 R
->>
-stream
-xÚÍ‘Û
à E=‚GÉ�<Š7@ê"ŒÀŒÐá–EIZÚŸ*	ƒ�ð‹Š±êB„Ké„Cñ…Â!JK¹¬–(<7ß±
-¢~k†'œîÈ÷þ|þ§ŸëÇל¹¯1·kÎà!W!¬›º\�Ø`œ ~ü„Ò ?ä¶
-»œÅbVÒÍ‹oÝ‘Oýáý4ÿ´~§ÿàÏï�?¨Nð‚á�ó
-endstream
-endobj
-16 0 obj
-159
-endobj
-17 0 obj
-<<
-/Title (saveModelAs.icon.png.pdf)
-/CreationDate (D:20120322115414)
-/ModDate (D:20120322115414)
-/Producer (ImageMagick 6.6.0-4 2011-06-15 Q16 http://www.imagemagick.org)
->>
-endobj
-xref
-0 18
-0000000000 65535 f 
-0000000010 00000 n 
-0000000059 00000 n 
-0000000118 00000 n 
-0000000312 00000 n 
-0000000401 00000 n 
-0000000419 00000 n 
-0000000457 00000 n 
-0000000478 00000 n 
-0000000943 00000 n 
-0000000962 00000 n 
-0000000990 00000 n 
-0000001401 00000 n 
-0000001421 00000 n 
-0000001437 00000 n 
-0000001457 00000 n 
-0000001803 00000 n 
-0000001823 00000 n 
-trailer
-<<
-/Size 18
-/Info 17 0 R
-/Root 1 0 R
->>
-startxref
-2014
-%%EOF

BIN
.manual/figures/icon_step.pdf


BIN
.manual/figures/icon_stop.pdf


BIN
.manual/figures/icon_toggleVisibleContainmentLinks.pdf


BIN
.manual/figures/icon_toggledebug.pdf


BIN
.manual/figures/icon_togglemm.pdf


BIN
.manual/figures/icon_undo.pdf


BIN
.manual/figures/icon_validateM.pdf


BIN
.manual/figures/mainmenum.pdf


BIN
.manual/manual.pdf


File diff suppressed because it is too large
+ 0 - 938
.manual/manual.tex


+ 20 - 0
.travis.yml

@@ -0,0 +1,20 @@
+language: node_js
+node_js:
+ - "node"
+
+cache:
+  directories:
+    - "node_modules"
+    - "$HOME/.cache/pip"
+
+sudo: required
+addons:
+  chrome: stable
+ 
+before_script:
+ - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
+ - pip install --user python-igraph
+    
+script:
+ - xvfb-run --server-args="-screen 0 2880x1800x24" ./run_tests.sh
+    

File diff suppressed because it is too large
+ 49 - 27
README.md


.etc/REFS → REFS


+ 4 - 19
___dataurize.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 
 /* return a datauri encoding of the resource at the given url */

+ 39 - 51
___do.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-REF:: https://github.com/creationix/do (+ .setup/do.js.patch)
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 // Takes an array of actions and runs them all in parallel.
 // You can either pass in an array of actions, or several actions
@@ -45,7 +29,7 @@ exports.parallel = function parallel(actions) {
         }
       }, errback);
     });
-  }
+  };
 };
 
 // Chains together several actions feeding the output of the first to the
@@ -66,12 +50,13 @@ exports.chain = function chain(actions) {
       }
     }
     actions[pos](loop, errback);
-  }
-}
+  };
+};
 
 // Takes an array and does an array map over it using the async callback `fn`
 // The signature of `fn` is `function fn(item, callback, errback)`
-exports.map = function map(array, fn) { return function (callback, errback) {
+exports.map = function map(array, fn) {
+  return function (callback, errback) {
   var counter = array.length;
   var new_array = [];
   array.forEach(function (item, index) {
@@ -79,7 +64,7 @@ exports.map = function map(array, fn) { return function (callback, errback) {
       new_array[index] = result;
       counter--;
       if (counter <= 0) {
-        new_array.length = array.length
+        new_array.length = array.length;
         callback(new_array);
       }
     };
@@ -88,7 +73,8 @@ exports.map = function map(array, fn) { return function (callback, errback) {
       cont(local_callback, errback);
     }
   });
-}}
+};
+};
 
 // Takes an array and does an array filter over it using the async callback `fn`
 // The signature of `fn` is `function fn(item, callback, errback)`
@@ -114,7 +100,8 @@ exports.filter = function filter(array, fn) { return function (callback, errback
       cont(local_callback, errback);
     }
   });
-}}
+};
+};
 
 // Takes an array and does a combined filter and map over it.  If the result
 // of an item is undefined, then it's filtered out, otherwise it's mapped in.
@@ -138,28 +125,29 @@ exports.filterMap = function filterMap(array, fn) { return function (callback, e
       cont(local_callback, errback);
     }
   });
-}};
+};
+};
 
 // Allows to group several callbacks.
-exports.combo = function combo(callback) {
-  var items = 0,
-      results = [];
-  function add() {
-    var id = items;
-    items++;
-    return function () {
-      check(id, arguments);
-    };
-  }
-  function check(id, arguments) {
-    results[id] = Array.prototype.slice.call(arguments);
-    items--;
-    if (items == 0) {
-      callback.apply(this, results);
-    }
-  }
-  return { add: add, check: check };
-}
+// exports.combo = function combo(callback) {
+//   var items = 0,
+//       results = [];
+//   function add() {
+//     var id = items;
+//     items++;
+//     return function () {
+//       check(id, arguments);
+//     };
+//   }
+//   function check(id, arguments) {
+//     results[id] = Array.prototype.slice.call(arguments);
+//     items--;
+//     if (items == 0) {
+//       callback.apply(this, results);
+//     }
+//   }
+//   return { add: add, check: check };
+// }
 
 
 // Takes any async lib that uses callback based signatures and converts
@@ -180,10 +168,10 @@ exports.convert = function (lib, names) {
             callback(val);
           }
         });
-        lib[key].apply(lib, args)
-      }
-    }
+        lib[key].apply(lib, args);
+      };
+    };
   });
   return newlib;
-}
+};
 

+ 9 - 21
___fs++.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 
 /* this file contains platform-oblivious wrappers for various windows and unix 
@@ -88,7 +73,10 @@ exports.findfiles =
                                             if (_fs.lstatSync(path).isDirectory()) {
                                                 newpath = newpath + '/';
                                             }
-                                        } catch (e) {}                                        
+                                        } catch (e) {
+                                            console.log("Error!");
+                                            console.log(e);
+										}
                                         return newpath;
 									});
 							paths.pop();
@@ -110,7 +98,7 @@ exports.findfiles =
                                     if (_fs.lstatSync(path).isDirectory()) {
                                         return path + "/";
                                     } else return path;
-                                })
+                                });
 							callback(err,newpaths.join('\n'),stderr);
                         }
 					});

+ 59 - 37
__worker.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /* NOTES: 
 	because of the *asynchronous* nature of numerous operations in our system, 
@@ -121,21 +106,19 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 
 /**************************** LIBRARIES and GLOBALS ****************************/
 var  _util 	= require('util'),
-	 _path 	= require('path'),
 	 _http 	= require('http'),
 	 _do  	= require('./___do'),
 	 _fs 	 	= _do.convert(require('fs'), ['readFile', 'writeFile', 'readdir']),
-	 _fspp	= _do.convert(require('./___fs++'), ['mkdirs']),	 
-	 _siocl	= require('socket.io-client'),
 	 _utils	= require('./utils'),
-	 _styleinfo = require('./styleinfo'),
-	 _svg		= require('./libsvg').SVG,
 	 _wlib,
 	 _mmmk,
 	 _mt,
 	 _plugins,
-	 __wid,
 	 __wtype;
+
+//have worker id global so that workers can detect it when loaded
+global.__wid = null;
+
 var keepaliveAgent = new _http.Agent({keepAlive: true, maxSockets: 10, maxFreeSockets: 5}); // proposed by yentl to improve performance
 
 
@@ -144,13 +127,13 @@ var keepaliveAgent = new _http.Agent({keepAlive: true, maxSockets: 10, maxFreeSo
 /* return a failure continuable */
 function __errorContinuable(err)	
 {
-	return function(callback,errback) {errback(err);}
+	return function(callback,errback) {errback(err);};
 }
 
 /* return a success continuable */
 function __successContinuable(arg)	
 {
-	return function(callback,errback) {callback(arg);}
+	return function(callback,errback) {callback(arg);};
 }
 
 
@@ -168,7 +151,8 @@ function __httpReq(method,url,data,port)
 				 if( data != undefined )
 				 {
 					 data = _utils.jsons(data);
-					 options['headers'] = {'Content-Length':unescape(encodeURIComponent(data)).length};
+					 options['headers'] = {'Content-Length':unescape(encodeURIComponent(data)).length,
+					 'Access-Control-Allow-Origin': '*'};
 				 }
 
 				 var request = 
@@ -323,6 +307,11 @@ function __postMessage(msg)
 				  _utils.jsons(msg.data) : 
 				  msg.data)));
 
+	//make sure that reason is a string
+	if (typeof msg.reason == 'object'){
+		msg.reason = _utils.jsons(msg.reason);
+	}
+
 	if( 'respIndex' in msg )
 		__onRequestResponse(msg.respIndex);
 
@@ -507,9 +496,17 @@ process.on('message',
 
 			__wtype = msg['workerType'];
 			__wid   = msg['workerId'];
-			_wlib   = eval('('+_fs.readFileSync('.'+__wtype+'.js', 'utf8')+')');
-			_mmmk   = eval('('+_fs.readFileSync('./mmmk.js', 'utf8')+')');
-			_mt  	  = eval('('+_fs.readFileSync('./libmt.js', 'utf8')+')');
+
+            if (__wtype == "/asworker") {
+                _wlib = require("./asworker");
+            } else if (__wtype == "/csworker") {
+                _wlib = require("./csworker");
+            } else {
+                throw "Error! Unknown worker type: " + __wtype;
+            }
+			_mmmk   = require('./mmmk');
+
+            _mt  	  = require('./libmt');
 
 			_plugins = {};
 			_fs.readdirSync('./plugins').forEach(
@@ -518,11 +515,11 @@ process.on('message',
 					try 
 					{
 						if( ! p.match(/.*\.js$/) )
-							throw 'invalid plugin filename, see user\'s manual';
+							return;
+							//throw 'invalid plugin filename, see user\'s manual';
 
 						p = p.match(/(.*)\.js$/)[1];
-						_plugins[p] = eval(
-							'('+_fs.readFileSync('./plugins/'+p+'.js','utf8')+')');
+						_plugins[p] = require('./plugins/' + p);
 						if( ! ('interfaces' in _plugins[p]) ||
 							 ! ('csworker' in _plugins[p])  ||
 							 ! ('asworker' in _plugins[p]) )
@@ -638,7 +635,7 @@ function __handleClientRequest(uri,method,reqData,respIndex)
 							 method,
  							 uri,
 							 reqData,
- 							 _wlib)
+ 							 _wlib);
  						 return true;
 	 				 }
 	 			 }) )
@@ -762,7 +759,7 @@ function POST_batchedit(resp,reqData)
 			 							  '/batchCheckpoint?wid='+__wid+
 	  										  '&backstagePass='+__backstagePass,
 				 						  {'name':name});
-						  }
+						  };
 	 		 },
 		 actions = [__successContinuable(), setbchkpt(startchkpt)];
 
@@ -823,7 +820,7 @@ function POST_batchedit(resp,reqData)
 					function()	
 					{
 						__backstagePass = undefined;
-						__postInternalErrorMsg(resp,err)
+						__postInternalErrorMsg(resp,err);
 					},
 					function(undoErr)	
 					{	
@@ -836,3 +833,28 @@ function POST_batchedit(resp,reqData)
 			}
 	);
 }
+
+module.exports = {
+	__errorContinuable,
+	__successContinuable,
+	__httpReq,
+	__wHttpReq,
+
+	__postInternalErrorMsg,
+	__postForbiddenErrorMsg,
+	__postBadReqErrorMsg,
+	__sequenceNumber,
+
+	__postMessage,
+	__uri_to_id,
+	__id_to_uri,
+	__batchCheckpoint,
+
+	GET__current_state,
+
+	//GLOBAL VARS
+	__ids2uris,
+	__nextSequenceNumber,
+	__wtype,
+
+};

+ 43 - 31
asworker.js

@@ -1,24 +1,24 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
-
-{
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
+const {
+    __errorContinuable,
+    __httpReq,
+	__wHttpReq,
+    __postInternalErrorMsg, __postMessage,
+    __sequenceNumber,
+    __successContinuable,
+	__uri_to_id
+} = require("./__worker");
+
+const _do = require("./___do");
+const _utils = require('./utils');
+const _mmmk = require("./mmmk");
+const _fs = _do.convert(require('fs'), ['readFile', 'writeFile', 'readdir']);
+
+
+module.exports = {
 	/************************** REST REQUEST HANDLING **************************/
 	/* INTENT :
 			ask our mtworker to do something (e.g., change transformation execution
@@ -139,14 +139,20 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 			}
 
 
-
-			_do.chain(actions)(
-					function() 
-					{
-						__postMessage({'statusCode':200, 'respIndex':resp});
-					},
-					function(err) 	{__postInternalErrorMsg(resp,err);}
-			);
+            _do.chain(actions)(
+                function () {
+                    __postMessage({'statusCode': 200, 'respIndex': resp});
+                },
+                function (err) {
+                    if (err.includes("ECONNREFUSED")) {
+                        let msg = "could not connect to model transformation worker!\n" +
+                            "please ensure the worker is running!";
+                        __postInternalErrorMsg(resp, msg);
+                    } else {
+                        __postInternalErrorMsg(resp, err);
+                    }
+                }
+            );
 		},
 
 		
@@ -207,7 +213,13 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 							 'hitchhiker':reqData['hitchhiker'],
 							 'respIndex':resp});
 					},
-					function(err) 	{__postInternalErrorMsg(resp,err);}
+					function (err) {
+
+						if (err['code'].includes("ENOENT")) {
+							err = "Error! File not found: " + err['path'];
+						}
+						__postInternalErrorMsg(resp, err);
+					}
 			);
 		},
 
@@ -508,4 +520,4 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 					 'sequence#':__sequenceNumber(),
 					 'respIndex':resp});
 		}	
-}
+};

File diff suppressed because it is too large
+ 1309 - 0
client/3rd_party_libs/d3/API.md


File diff suppressed because it is too large
+ 1407 - 0
client/3rd_party_libs/d3/CHANGES.md


+ 27 - 0
client/3rd_party_libs/d3/LICENSE

@@ -0,0 +1,27 @@
+Copyright 2010-2017 Mike Bostock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of contributors may be used to
+  endorse or promote products derived from this software without specific prior
+  written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 57 - 0
client/3rd_party_libs/d3/README.md

@@ -0,0 +1,57 @@
+# D3: Data-Driven Documents
+
+<a href="https://d3js.org"><img src="https://d3js.org/logo.svg" align="left" hspace="10" vspace="6"></a>
+
+**D3** (or **D3.js**) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.
+
+## Resources
+
+* [API Reference](https://github.com/d3/d3/blob/master/API.md)
+* [Release Notes](https://github.com/d3/d3/releases)
+* [Gallery](https://github.com/d3/d3/wiki/Gallery)
+* [Examples](https://bl.ocks.org/mbostock)
+* [Wiki](https://github.com/d3/d3/wiki)
+
+## Installing
+
+If you use npm, `npm install d3`. Otherwise, download the [latest release](https://github.com/d3/d3/releases/latest). The released bundle supports anonymous AMD, CommonJS, and vanilla environments. You can load directly from [d3js.org](https://d3js.org), [CDNJS](https://cdnjs.com/libraries/d3), or [unpkg](https://unpkg.com/d3/). For example:
+
+```html
+<script src="https://d3js.org/d3.v5.js"></script>
+```
+
+For the minified version:
+
+```html
+<script src="https://d3js.org/d3.v5.min.js"></script>
+```
+
+You can also use the standalone D3 microlibraries. For example, [d3-selection](https://github.com/d3/d3-selection):
+
+```html
+<script src="https://d3js.org/d3-selection.v1.js"></script>
+```
+
+D3 is written using [ES2015 modules](http://www.2ality.com/2014/09/es6-modules-final.html). Create a [custom bundle using Rollup](https://bl.ocks.org/mbostock/bb09af4c39c79cffcde4), Webpack, or your preferred bundler. To import D3 into an ES2015 application, either import specific symbols from specific D3 modules:
+
+```js
+import {scaleLinear} from "d3-scale";
+```
+
+Or import everything into a namespace (here, `d3`):
+
+```js
+import * as d3 from "d3";
+```
+
+In Node:
+
+```js
+var d3 = require("d3");
+```
+
+You can also require individual modules and combine them into a `d3` object using [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign):
+
+```js
+var d3 = Object.assign({}, require("d3-format"), require("d3-geo"), require("d3-geo-projection"));
+```

File diff suppressed because it is too large
+ 17787 - 0
client/3rd_party_libs/d3/d3.js


File diff suppressed because it is too large
+ 2 - 0
client/3rd_party_libs/d3/d3.min.js


+ 29 - 48
client/atompm.html

@@ -1,25 +1,12 @@
 <!--****************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
+*  This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
 *****************************************************************************-->
 
 <html>
 	<head>
+        <title>AToMPM</title>
 		<meta charset="utf-8">
 		<link rel="stylesheet" type="text/css" href="client/styles.css"/>
 		<script text="text/javascript" src="client/globalVariables.js"></script>
@@ -29,7 +16,8 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 		<script text="text/javascript" src="client/3rd_party_libs/raphael/plugins/point.js"></script>				
 		<script text="text/javascript" src="client/3rd_party_libs/raphael/plugins/group.js"></script>		
 		<script text="text/javascript" src="client/3rd_party_libs/raphael/plugins/raphael.primitives.js"></script>
-		<script text="text/javascript" src="client/3rd_party_libs/jquery-1.8.2.min.js"></script>	
+		<script text="text/javascript" src="client/3rd_party_libs/jquery-1.8.2.min.js"></script>
+		<script text="text/javascript" src="client/3rd_party_libs/d3/d3.min.js"></script>
         <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
 		<script text="text/javascript" src="client/constants.js"></script>		
 		<script text="text/javascript" src="types.js"></script>		
@@ -53,37 +41,28 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 		<script text="text/javascript" src="client/geometry_utils.js"></script>
 		<script text="text/javascript" src="client/connection_utils.js"></script>
 		<script text="text/javascript" src="client/window_management.js"></script>
+		<script text="text/javascript" src="client/file_browser.js"></script>
+        <script text="text/javascript" src="client/modelverse_connector.js"></script>
 		<script text="text/javascript" src="client/query_response.js"></script>
 		<script text="text/javascript" src="client/collaboration.js"></script>
+		<script text="text/javascript" src="client/layout.js"></script>
 		<script text="text/javascript" src="client/client.js"></script>
 		<script text="text/javascript" src="styleinfo.js"></script>
         <link rel="icon" href="favicon.png">
-		<script>
-			$(document).ready(function(){
-				/**
-				 * Initiate the default Window Event actions
-				 */
-				//WindowEventHelper.initDefault();
-			});
-		</script>
 	</head>
 	<body class="default_style">
-		<div id="rootDiv" class="rootDiv">
-			<div id="commandHistory" class="commandHistory">
-			</div>
-			<!--div id="inputDiv" class="inputDiv">
-				<input placeholder="type 'help' to get information about querying" name="mainInput" type="text" class="mainInput" id="mainInput" onkeyup="InputBarUtils.processKey( event )"/>
-			</div-->
-			<div id="contentDiv" class="contentDiv">
-				<div id="div_container" class="container">
-					<div id="div_canvas" class="canvas"></div>
-				</div>
-				<div class="header" align="right">
-					<a id="a_logout" class="enabled_link unselectable" onclick="UserManagement.logout(); return false;"><b>logout</b></a>
-				</div>
-				<div id="div_dock" class="dock"></div>
-			</div>
-		</div>
+        <div id="rootDiv" class="rootDiv">
+            <div id="contentDiv" class="contentDiv">
+                <div id="div_dock" class="dock"></div>
+
+                <div id="div_container" class="container">
+                    <div id="div_canvas" class="canvas"></div>
+                </div>
+                <div class="header" align="right">
+                    <a id="a_logout" class="enabled_link unselectable" onclick="UserManagement.logout(); return false;"><b>logout</b></a>
+                </div>
+            </div>
+        </div>
 
 		<div class="footer" align="right">
 			<!--a class="enabled_link unselectable" id="showChat" href=
@@ -120,8 +99,8 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 				<h3>Log in to use AToMPM</h3>
 					<form onsubmit="return false;">
 						<table class="default_style">
-							<tr><td>username</td><td><input id="input_username" class="default_style" type="text"/></td></tr>
-							<tr><td>password</td><td><input id="input_password" class="default_style" type="password"/></td></tr>
+							<tr><td>username</td><td><input id="input_username" class="default_style" type="text" autocomplete="username"/></td></tr>
+							<tr><td>password</td><td><input id="input_password" class="default_style" type="password" autocomplete="current-password"/></td></tr>
 						</table></br>
 						<div id="div_login_error" class="error"></div>
 						<a href="#" class="enabled_link" 
@@ -136,9 +115,9 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 
 					<form id="form_signup" onsubmit="return false;" style="display:none">
 						<table class="default_style">
-							<tr><td>choose username</td><td><input id="new_username" class="default_style" type="text"/></td></tr>
-							<tr><td>choose password</td><td><input id="new_password" class="default_style" type="password"/></td></tr>
-							<tr><td>re-type password</td><td><input id="new_password2" class="default_style" type="password"/></td></tr>
+							<tr><td>choose username</td><td><input id="new_username" class="default_style" type="text" autocomplete="off"/></td></tr>
+							<tr><td>choose password</td><td><input id="new_password" class="default_style" type="password" autocomplete="new-password"/></td></tr>
+							<tr><td>re-type password</td><td><input id="new_password2" class="default_style" type="password" autocomplete="new-password"/></td></tr>
 						</table></br>
 						<div id="div_signup_error" class="error"></div>
 						<a href="#" class="enabled_link" 
@@ -168,7 +147,9 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 		
 
 		<div id="div_dim_bg" class="dim_bg" style="display:none"></div>
-		<div id="div_dialog" class="dialog" style="display:none;text-align:center"></div>		
+		<div id="centerpoint" class="center_point">
+		    <div id="div_dialog" class="dialog" style="display:none;text-align:center"></div>
+        </div>
 		<div id="div_geom_ctrls" class="geometry_ctrls" style="display:none"></div>
 	</body>
 </html>

+ 4 - 20
client/behaviourmanager.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 BehaviorManager = new function(){
 	var activeBehaviourStatechart = undefined;

+ 20 - 35
client/behavioursc_canvas.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 __canvasBehaviourStatechart = {
 	'__STATE_IDLE'	 				 		 				: 0,
@@ -33,19 +18,19 @@ __canvasBehaviourStatechart = {
 			function(event)
 			{
 				GUIUtils.disableDock();
-				__initCanvasSelectionOverlay(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
+				__initCanvasSelectionOverlay(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event));
 			},
 		3:
 			function(event)	
 			{
 				GUIUtils.disableDock();
-				GeometryUtils.initSelectionTransformationPreviewOverlay(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
+				GeometryUtils.initSelectionTransformationPreviewOverlay(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event));
 			},
 		4:
 			function(event)	
 			{
 				GUIUtils.disableDock();
-				ConnectionUtils.initConnectionPath(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY),event.target);
+				ConnectionUtils.initConnectionPath(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event),event.target);
 			},
 		6:
 			function(event)	
@@ -107,7 +92,7 @@ __canvasBehaviourStatechart = {
 			if( this.__currentState == this.__STATE_IDLE )
 			{
 				if( name == __EVENT_RIGHT_RELEASE_CANVAS )
-					DataUtils.create(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
+					DataUtils.create(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event));
 			
 				else if( name == __EVENT_LEFT_PRESS_CANVAS )
 					this.__T(this.__STATE_CANVAS_SELECTING,event);
@@ -166,7 +151,7 @@ __canvasBehaviourStatechart = {
 			else if( this.__currentState == this.__STATE_CANVAS_SELECTING )
 			{
 				if( name == __EVENT_MOUSE_MOVE ){
-					__updateCanvasSelectionOverlay(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
+					__updateCanvasSelectionOverlay(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event));
 				}
 				
 				else if( name == __EVENT_LEFT_RELEASE_CANVAS ||
@@ -217,7 +202,7 @@ __canvasBehaviourStatechart = {
 					if( ! GeometryUtils.areTransformationsAllowed() )
 						console.warn('the selection dragging mode can only be activated if '+
 										 'all of the ends of selected edges are also selected, '+
-										 'and if the geometry controls are inactive')
+										 'and if the geometry controls are inactive');
 					else			
 						this.__T(this.__STATE_DRAGGING_SELECTION,event);
 				}
@@ -227,7 +212,7 @@ __canvasBehaviourStatechart = {
 					if( ! GeometryUtils.areTransformationsAllowed() )
 						console.warn('the geometry controls can only be activated if all '+
 										 'of the ends of selected edges are also selected, and'+
-										 'if the geometry controls aren\'t already active')
+										 'if the geometry controls aren\'t already active');
 					else			
 						GeometryUtils.showGeometryControlsOverlay();
 				}
@@ -281,7 +266,7 @@ __canvasBehaviourStatechart = {
 			else if( this.__currentState == this.__STATE_DRAGGING_SELECTION )
 			{
 				if( name == __EVENT_MOUSE_MOVE )
-					GeometryUtils.previewSelectionTranslation(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
+					GeometryUtils.previewSelectionTranslation(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event));
 				
 				else if( name == __EVENT_KEYUP_ESC )
 				{
@@ -330,7 +315,7 @@ __canvasBehaviourStatechart = {
 			else if( this.__currentState == this.__STATE_DRAWING_EDGE )
 			{
 				if( name == __EVENT_MOUSE_MOVE ){
-					ConnectionUtils.updateConnectionSegment(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
+					ConnectionUtils.updateConnectionSegment(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event));
 				}
 				else if( name == __EVENT_KEYUP_ESC ||
 							name == __EVENT_RIGHT_RELEASE_CANVAS )
@@ -393,9 +378,9 @@ __canvasBehaviourStatechart = {
 				else if( name == __EVENT_MIDDLE_RELEASE_CTRL_POINT )
 					ConnectionUtils.deleteControlPoint(event.target);
 		
-				else if( name == __EVENT_RIGHT_RELEASE_CTRL_POINT )
-					ConnectionUtils.addControlPoint(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY),event.target);
-						
+				else if( name == __EVENT_RIGHT_RELEASE_CTRL_POINT ) {
+                    ConnectionUtils.addControlPoint(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event), event.target);
+				}
 				else if( name == __EVENT_KEYUP_TAB )
 					ConnectionUtils.snapControlPoint();
 		
@@ -419,9 +404,9 @@ __canvasBehaviourStatechart = {
 
 			else if( this.__currentState == this.__STATE_DRAGGING_CONNECTION_PATH_CTRL_POINT )
 			{
-				if( name == __EVENT_MOUSE_MOVE )
-					ConnectionUtils.previewControlPointTranslation(GUIUtils.convertToCanvasX(event.pageX), GUIUtils.convertToCanvasY(event.pageY));
-		
+				if( name == __EVENT_MOUSE_MOVE ) {
+                    ConnectionUtils.previewControlPointTranslation(GUIUtils.convertToCanvasX(event), GUIUtils.convertToCanvasY(event), event.ctrlKey);
+				}
 				else if( name == __EVENT_LEFT_RELEASE_CTRL_POINT )
 				{
 					ConnectionUtils.updateConnectionPath();
@@ -438,5 +423,5 @@ __canvasBehaviourStatechart = {
 				}
 			}
 		}
-	}
+	};
 

+ 5 - 20
client/behavioursc_dialog.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 __dialogBehaviourStatechart = {
 	'__STATE_OPEN'	 : 0,
@@ -106,4 +91,4 @@ __dialogBehaviourStatechart = {
 				}
 			}
 		}	
-}
+};

+ 4 - 19
client/behavioursc_inputbar.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2012 Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /**
  * This object defines my DummyStateChart formalism. It has

+ 28 - 37
client/client.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /*TODO: look into reworking naming convention to be more intuitive
 *ie: 
@@ -280,7 +264,7 @@ function _loadModel(fname)
 		else
 			__saveas = fname;
 		DataUtils.loadm(fname);
-		if (__msg != '')
+		if (__msg != '' && __msg != null)
 			WindowManagement.openDialog(_CUSTOM,{'widgets':[{'id':'1','type':'text','label':'text message','default':''}],"title":__msg});
 	}
 }
@@ -295,7 +279,7 @@ function _loadToolbar(fname)
 		DataUtils.loadbm(fname);
 	else if( __isIconMetamodel(fname) )
 		DataUtils.loadmm(fname);
-	if (__msg != '')
+	if (__msg != '' && __msg != null)
 		WindowManagement.openDialog(_CUSTOM,{'widgets':[{'id':'1','type':'text','label':'text message','default':''}],"title":__msg});
 }
 
@@ -318,7 +302,7 @@ function _saveModel(fname,backup,autosave)
 			var options = {'extensions':['\\.model'],
 						   'multipleChoice':false,
 						   'manualInput':true,
-						   'title':'specify target model',
+						   'title':'specify target model\nextension: .model',
 						   'startDir':'model'},
 				callback =
 					function(fnames)
@@ -333,7 +317,7 @@ function _saveModel(fname,backup,autosave)
 	} else if( ! __isModel(fname) )	{
 		WindowManagement.openDialog(
 			_ERROR,
-			'invalid extension... models must be saved as "*.model" files');
+			'invalid extension on \'' + fname +'\' - models must be saved as "*.model" files');
 		return;
 	}
 
@@ -352,7 +336,7 @@ function _saveModel(fname,backup,autosave)
 	  collaborator) inherit them */
 function _setInvisibleMetamodels(mms)
 {
-	mms = mms.map( function(mm) {return mm.match(/(.*)\.metamodel/)[1]} );
+	mms = mms.map( function(mm) {return mm.match(/(.*)\.metamodel/)[1];} );
 
 	function hideOrShow(uri,icon)
 	{
@@ -412,8 +396,8 @@ function _newFormalism(formalism_name) {
 			if( ! utils.isHttpSuccessCode(statusCode) ) {
 				WindowManagement.openDialog(_ERROR, 'failed to create new formalism :: '+resp);
             } else {
-                WindowManagement.spawnClient("/Formalisms/" + formalism_name + "/" + formalism_name + ".model")
-                WindowManagement.spawnClient("/Formalisms/" + formalism_name + "/" + formalism_name + ".defaultIcons.model")
+                WindowManagement.spawnClient("/Formalisms/" + formalism_name + "/" + formalism_name + ".model");
+                WindowManagement.spawnClient("/Formalisms/" + formalism_name + "/" + formalism_name + ".defaultIcons.model");
             }
 		});
 }
@@ -434,7 +418,7 @@ function _newTransformation(transformation_loc) {
                 if( ! utils.isHttpSuccessCode(statusCode) ) {
                     WindowManagement.openDialog(_ERROR, 'failed to create new transformation :: '+resp);
                 } else {
-                    WindowManagement.spawnClient(transformation_loc)
+                    WindowManagement.spawnClient(transformation_loc);
                 }
             });
     } else {
@@ -458,7 +442,7 @@ function _newRule(rule_loc) {
                 if( ! utils.isHttpSuccessCode(statusCode) ) {
                     WindowManagement.openDialog(_ERROR, 'failed to create new rule :: '+resp);
                 } else {
-                    WindowManagement.spawnClient(rule_loc)
+                    WindowManagement.spawnClient(rule_loc);
                 }
             });
     } else {
@@ -578,7 +562,7 @@ function _loadModelInWindow(args/*fname[]*/)
 	else if(tf > 0 )
 		option = 'TF';
 	else if (vas == 'VAS')
-		option = 'VAS';;
+		option = 'VAS';
 	WindowManagement.spawnClientOption(loc[aid],'',loc[path],option,loc[msg]);
 }
 
@@ -649,7 +633,7 @@ function _openNewDialog(args)
 				}
 				data += ','+msg;				
 				data = '{'+data;
-				data += '}'
+				data += '}';
 				_updateAttr({"asid":pid,"attr":"parameterList","val":data});
 				play = function()
                {
@@ -700,7 +684,7 @@ function _loadToolbarInWindow(args/*fname[]*/)
 	else if(tr > 0 )
 		option = 'TR';
 	else if(tf > 0 )
-		option = 'TF';;	
+		option = 'TF';	
 	WindowManagement.spawnClientOption(loc[path],loc[aid],option,'',loc[msg]);
 }
 
@@ -824,6 +808,12 @@ function __iconToFront(tgt)
 	__icons[__vobj2uri(tgt)]['icon'].toFront();
 }
 
+/*---------------------------- LAYOUT -----------------------------*/
+
+function _autolayout()
+{
+    Layout.autolayout();
+}
 
 /*---------------------------- SELECTION OVERLAY -----------------------------*/
 
@@ -1032,11 +1022,12 @@ function __relativizeURL(url)
 
 
 /* returns the csuri of the icon that contains the specified VisualObject */
-function __vobj2uri(vobj)
-{
-	if( vobj != document.body )
-		return vobj.parentNode.getAttribute('__csuri') ||
-				 __vobj2uri(vobj.parentNode);
+function __vobj2uri(vobj) {
+    if (vobj != document.body) {
+        return vobj.parentNode.getAttribute('__csuri') ||
+            vobj.parentNode.getAttribute('__linkuri') ||
+            __vobj2uri(vobj.parentNode);
+    }
 }
 
 function __getRecentDir(name) {

+ 4 - 20
client/collaboration.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 Collaboration = function(){
 	//css/html based on http://net.tutsplus.com/tutorials/javascript-ajax/how-to-create-a-simple-web-based-chat-application/ 

+ 57 - 42
client/compile_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 ///////////////////////////////////////////////////////////////////////////////
 // DEPRECATED FUNCTIONS
@@ -217,7 +201,7 @@ CompileUtils = function(){
 				continue;
 	
 			var r  = __getVobjGeomAttrVal(vobj['orientation']['value']),
-				 sx = __getVobjGeomAttrVal(vobj['scale']['value'][0])
+				 sx = __getVobjGeomAttrVal(vobj['scale']['value'][0]);
 				 sy = __getVobjGeomAttrVal(vobj['scale']['value'][1]);
 			vobjects[vid].attr(vobj['style']['value']);
 			vobjects[vid].transform(
@@ -285,6 +269,7 @@ CompileUtils = function(){
 				icon.setAttr('__r',0);			
 				icon.setAttr('__sx',1);
 				icon.setAttr('__sy',1);
+				icon.setAttr('id', id);
 			}
 	
 			if( 'attrs' in options )
@@ -325,7 +310,8 @@ CompileUtils = function(){
 								BehaviorManager.handleUserEvent(__EVENT_MIDDLE_RELEASE_ICON,event);
 						}
 					};
-				icon.node.onmousewheel = 
+
+				let shiftWheelFunction =
 					function(event)
 					{
 						if( event.shiftKey )
@@ -334,6 +320,10 @@ CompileUtils = function(){
 							return false;
 						}
 					};
+
+				icon.node.onmousewheel = shiftWheelFunction;
+				icon.node.onwheel = shiftWheelFunction;
+
 				/*icon.node.onmouseover = 
 					function(event)
 					{
@@ -353,26 +343,51 @@ CompileUtils = function(){
 	/**
 	 * Compile the current model to an Abstract Syntax Metamodel
 	 */
-	this.compileToASMM = function(fname){
-		if( ! __isAbstractSyntaxMetamodel(fname) )
-			WindowManagement.openDialog(
-				_ERROR,
-				'invalid extension... abstract syntax metamodels are "*.metamodel" files');
-		else
-			HttpUtils.httpReq('PUT', HttpUtils.url(fname,__FORCE_GET));
-	};
-	
-	/**
-	 * Compile the current model to a Concrete Syntax Metamodel
-	 */
-	this.compileToCSMM = function(fname){
-		if( ! __isIconMetamodel(fname) )
-			WindowManagement.openDialog(
-				_ERROR,
-				'invalid extension... icon definition metamodels are "*Icons.metamodel" files');
-		else
-			HttpUtils.httpReq('PUT', HttpUtils.url(fname,__FORCE_GET));
-	};
+    this.compileToASMM = function (fname) {
+        if (!__isAbstractSyntaxMetamodel(fname))
+            WindowManagement.openDialog(
+                _ERROR,
+                'invalid extension... abstract syntax metamodels are "*.metamodel" files');
+        else
+            HttpUtils.httpReq('PUT', HttpUtils.url(fname, __FORCE_GET), null,
+                function (status, text) {
+                    //there was a problem
+                    if (!utils.isHttpSuccessCode(status)) {
+                        //let resp = JSON.parse(text);
+                        WindowManagement.openDialog(_ERROR, JSON.stringify(text));
+                    }
+
+                });
+    };
+
+    /**
+     * Compile the current model to a Concrete Syntax Metamodel
+     */
+    this.compileToCSMM = function (fname) {
+        if (!__isIconMetamodel(fname))
+            WindowManagement.openDialog(
+                _ERROR,
+                'invalid extension... icon definition metamodels are "*Icons.metamodel" files');
+        else
+            HttpUtils.httpReq('PUT', HttpUtils.url(fname, __FORCE_GET), null,
+                function (status, text) {
+                    //there was a problem
+                    if (!utils.isHttpSuccessCode(status)) {
+                        let resp = text;
+                        if (!(resp.startsWith("500"))) {
+                            resp = JSON.parse(text);
+                        }
+
+                        if (resp["code"] && resp["code"].includes("ENOENT")) {
+                            let msg = "ERROR: Corresponding metamodel could not be found in same directory!";
+                            WindowManagement.openDialog(_ERROR, msg);
+                        } else {
+                            WindowManagement.openDialog(_ERROR, JSON.stringify(text));
+                        }
+                    }
+
+                });
+    };
 	
 	/**
 	 * Compiles the current model to an Icon Pattern Metamodel

+ 66 - 32
client/connection_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 ConnectionUtils = function(){
 	var connectionPathEditingOverlay = {};
@@ -216,18 +200,68 @@ ConnectionUtils = function(){
 		connectionPathEditingOverlay = {};
 		currentControlPoint = undefined;
 	};
-	
-	/**
-	 * Moves the control point and its overlay to the specified coordinates
-	 */
-	this.previewControlPointTranslation = function(x,y){
-		var _x = parseInt( currentControlPoint.node.getAttribute('_x') ),
-			 _y = parseInt( currentControlPoint.node.getAttribute('_y') );
-		currentControlPoint.translate(x-_x,y-_y);
-		currentControlPoint.node.setAttribute('_x',x);
-		currentControlPoint.node.setAttribute('_y',y);
-		ConnectionUtils.updateConnectionPath(true);
-	};
+
+    /**
+     * Moves the control point and its overlay to the specified coordinates
+     */
+    this.previewControlPointTranslation = function (x, y, ctrl_key_down) {
+
+        // if the control key is not down,
+        // restrict control point to within bounding box
+        if (!ctrl_key_down) {
+            let new_points = this.restrictControlPoint(x, y);
+            x = new_points[0];
+            y = new_points[1];
+        }
+
+        let _x = parseInt(currentControlPoint.node.getAttribute('_x')),
+            _y = parseInt(currentControlPoint.node.getAttribute('_y'));
+
+        currentControlPoint.translate(x - _x, y - _y);
+
+        currentControlPoint.node.setAttribute('_x', x);
+        currentControlPoint.node.setAttribute('_y', y);
+        ConnectionUtils.updateConnectionPath(true);
+    };
+
+    /**
+     * Restricts the control point to within an icon's bounding box
+     */
+    this.restrictControlPoint = function (x, y) {
+        let start = currentControlPoint.node.getAttribute("__start");
+        let end = currentControlPoint.node.getAttribute("__end");
+
+        // something went wrong, or we're not an
+		// outside edge, so return the points
+        if (start == undefined && end == undefined) {
+            return [x, y];
+        }
+
+        //get the bounding box rectangle
+        let icon = __getIcon(start || end);
+        let bbox = icon.getBBox();
+
+        //get the dimensions
+        let iconX = bbox.x;
+        let iconY = bbox.y;
+
+        let width = bbox.width;
+        let height = bbox.height;
+
+        //restrict x and y to within the bounding box
+        if (x < iconX) {
+            x = iconX;
+        } else if (x > iconX + width) {
+            x = iconX + width;
+        }
+
+        if (y < iconY) {
+            y = iconY;
+        } else if (y > iconY + height) {
+            y = iconY + height;
+        }
+        return [Math.round(x), Math.round(y)];
+    };
 	
 	/**
 	 * Show the connection path editing overlay. This shows draggable circles

+ 9 - 22
client/constants.js

@@ -1,26 +1,13 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /****************************** GLOBAL CONSTANTS ******************************/
-var __WEBPAGE__ = 'http://www-ens.iro.umontreal.ca/~syriani/atompm/atompm.htm',
-    __VERSION__ = '0.5.4',
+var __WEBPAGE__ = 'https://atompm.github.io/',
+	__RELEASE_LOC__ = "https://api.github.com/repos/AToMPM/atompm/releases/latest",
+	__DOC_WEBPAGE__ = "https://msdl.uantwerpen.be/documentation/AToMPM/index.html",
+    __VERSION__ = '0.8.1',
     __DEFAULT_SAVEAS		 	= '.autosave.model',
 	 __TITLE						= 'AToMPM',
 	 __EXITWARNING				= 'There are unsaved changes. Proceeding will cause'+
@@ -71,7 +58,7 @@ var __WEBPAGE__ = 'http://www-ens.iro.umontreal.ca/~syriani/atompm/atompm.htm',
 	 __CONTAINMENT_LINK	 	= 'containment',
 
 	 __EVENT_RIGHT_RELEASE_CANVAS			= 0,
- 	 __EVENT_RIGHT_RELEASE_ICON			= 1
+ 	 __EVENT_RIGHT_RELEASE_ICON			= 1,
  	 __EVENT_RIGHT_PRESS_ICON				= 2,
  	 __EVENT_LEFT_RELEASE_CANVAS			= 3,
 	 __EVENT_LEFT_RELEASE_ICON				= 4,

+ 82 - 74
client/data_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /* delete specified file/folder */
 
@@ -66,16 +50,19 @@ DataUtils = function(){
 	 * Request creation of an instance of __typeToCreate at the specified
 	 * x and y coordinates
 	 */
-	this.create = function(x,y){
-		if( __typeToCreate != undefined )
-			HttpUtils.httpReq(
-					'POST',
-					HttpUtils.url(__typeToCreate+'.type',__NO_USERNAME),
-					{'pos':[x,y]});
-		else
-			WindowManagement.openDialog(_ERROR,'you must select a type to create');
-	};
-	
+
+    this.create = function (x, y, callback) {
+        if (__typeToCreate == undefined) {
+            WindowManagement.openDialog(_ERROR, 'you must select a type to create');
+        } else {
+            HttpUtils.httpReq(
+                'POST',
+                HttpUtils.url(__typeToCreate + '.type', __NO_USERNAME),
+                {'pos': [x, y]},
+                callback);
+        }
+    };
+
 	/**
 	 * Deletes the current selection entities
 	 */
@@ -286,19 +273,29 @@ DataUtils = function(){
 	 * 
 	 * @param bm - the button model to load
 	 */
-	this.loadbm = function(bm){
-		HttpUtils.httpReq(
-				'GET',
-				HttpUtils.url(bm,true),
-				undefined,
-				function(statusCode,resp)
-				{
-					GUIUtils.setupAndShowToolbar(
-						bm,
-						eval('('+resp+')'),
-						__BUTTON_TOOLBAR);					
-				});	
-	};
+    this.loadbm = function (bm) {
+        HttpUtils.httpReq(
+            'GET',
+            HttpUtils.url(bm, true),
+            undefined,
+            function (statusCode, resp) {
+                if (!utils.isHttpSuccessCode(statusCode)) {
+
+                    if (resp.includes("ENOENT")) {
+                        let err_msg = "Error! File not found: " + bm;
+                        WindowManagement.openDialog(_ERROR, err_msg);
+                    } else {
+                        WindowManagement.openDialog(_ERROR, resp);
+                    }
+                    return;
+                }
+
+                GUIUtils.setupAndShowToolbar(
+                    bm,
+                    eval('(' + resp + ')'),
+                    __BUTTON_TOOLBAR);
+            });
+    };
 	
 	/* 
 		1. does the deed
@@ -309,37 +306,48 @@ DataUtils = function(){
 	/**
 	 * Request that the specified model be loaded
 	 */
-	this.loadm = function(fname,insert) {
-		HttpUtils.httpReq(
-				'PUT',
-				HttpUtils.url('/current.model', __NO_USERNAME),
-				{'m':HttpUtils.url(fname,__NO_WID),
-				 'insert':insert},
-				function(statusCode,resp)
-				{
-					if( ! utils.isHttpSuccessCode(statusCode) )
-					{
-						if( (matches = resp.match(/metamodel not loaded :: (.*)/)) )
-						{
-							var missing = matches[1]+'.metamodel';
-							console.warn('auto-loading missing metamodel :: '+missing);
-							DataUtils.loadmm(
-									missing,
-									function(_statusCode,_resp)
-									{
-										if( ! utils.isHttpSuccessCode(_statusCode) )
-											WindowManagement.openDialog(_ERROR,_resp);
-										else
-											DataUtils.loadm(fname,insert);
-									});
-						}
-						else
-							WindowManagement.openDialog(_ERROR,resp);
-					}
-					else
-						WindowManagement.setWindowTitle();
-				});				
-	};
+    this.loadm = function (fname, insert) {
+        HttpUtils.httpReq(
+            'PUT',
+            HttpUtils.url('/current.model', __NO_USERNAME),
+            {
+                'm': HttpUtils.url(fname, __NO_WID),
+                'insert': insert
+            },
+            function (statusCode, resp) {
+                if (utils.isHttpSuccessCode(statusCode)) {
+                    WindowManagement.setWindowTitle();
+                    return;
+                }
+
+                if ((matches = resp.match(/metamodel not loaded :: (.*)/))) {
+                    var missing = matches[1] + '.metamodel';
+                    console.warn('auto-loading missing metamodel :: ' + missing);
+                    DataUtils.loadmm(missing,
+                        function (_statusCode, _resp) {
+                            if (!utils.isHttpSuccessCode(_statusCode)) {
+
+                                if (_resp.includes("ENOENT")) {
+                                    _resp = utils.jsonp(_resp);
+                                    _resp = "Error! File not found: " + _resp['path'];
+                                }
+                                WindowManagement.openDialog(_ERROR, _resp);
+                            }
+                            else {
+                                DataUtils.loadm(fname, insert);
+                            }
+                        });
+                } else {
+                    if (resp.includes("cannot read")) {
+                        let err_msg = "Error! File cannot be read: " + fname;
+                        WindowManagement.openDialog(_ERROR, err_msg);
+                    } else {
+                        WindowManagement.openDialog(_ERROR, resp);
+                    }
+                }
+
+            });
+    };
 	
 	/*
 	CASE 1: asmm is already loaded but with a different csmm

+ 4 - 20
client/edit_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 // DEPRECATED FUNCTIONS
 ///////////////////////////////////////////////////////////////////////////////

+ 436 - 0
client/file_browser.js

@@ -0,0 +1,436 @@
+class FileBrowser{
+
+    static buildFileBrowser(extensions, manualInput, title, startDir, callback) {
+        HttpUtils.httpReq(
+            'GET',
+            HttpUtils.url('/filelist', __NO_WID),
+            undefined,
+            function (statusCode, resp) {
+
+                if (statusCode == 404){
+                    let err_msg = "Error! Cannot load file list!";
+                    WindowManagement.openDialog(_ERROR, err_msg);
+                    return;
+                }
+
+                extensions.push('/');
+                var fnames = __localizeFilenames(
+                    __filterFilenamesByExtension(
+                        resp.split('\n'),
+                        extensions || ['.*'])
+                    ).sort(),
+                    folder_buttons = $('<div>'),
+                    new_folder_b = $('<button>'),
+                    rename_folder_b = $('<button>'),
+                    delete_folder_b = $('<button>'),
+                    move_folder_b = $('<button>'),
+                    file_buttons = $('<div>'),
+                    rename_file_b = $('<button>'),
+                    delete_file_b = $('<button>'),
+                    move_file_b = $('<button>'),
+                    feedbackarea = $('<div>'),
+                    feedback = GUIUtils.getTextSpan('', "feedback"),
+                    fileb =
+                        FileBrowser.getFileBrowser(fnames, false, manualInput, __getRecentDir(startDir));
+
+                new_folder_b.attr('id', 'new_folder')
+                    .html('new folder')
+                    .click(function (ev) {
+                        var folder_name = prompt("please fill in a name for the folder");
+                        if (folder_name != null) {
+                            folder_name = folder_name.replace(/^\s+|\s+$/g, ''); // trim
+                            if (!folder_name.match(/^[a-zA-Z0-9_\s]+$/i)) {
+                                feedback.html("invalid folder name: " + folder_name);
+                            } else {
+                                console.log("/" + window.localStorage.getItem('user') + fileb['getcurrfolder']() + folder_name + '.folder');
+                                DataUtils.createFolder("/" + window.localStorage.getItem('user') + fileb['getcurrfolder']() + folder_name + '.folder', function (statusCode, resp) {
+                                    if (!utils.isHttpSuccessCode(statusCode)) {
+                                        feedback.html(resp);
+                                    } else {
+                                        feedback.html('created ' + folder_name);
+                                        fnames.push(fileb['getcurrfolder']() + folder_name + "/");
+                                        fileb['refresh'](fnames);
+                                    }
+                                });
+                            }
+                        }
+                    });
+                folder_buttons.append(new_folder_b);
+
+                rename_folder_b.html('rename folder')
+                    .click(function (ev) {
+                        var value = fileb['getcurrfolder']();
+                        var folder_name = prompt("please fill in a new name for folder " + value);
+                        if (folder_name != null) {
+                            folder_name = folder_name.replace(/^\s+|\s+$/g, ''); // trim
+                            if (!folder_name.match(/^[a-zA-Z0-9_\s]+$/i)) {
+                                feedback.html("invalid folder name: " + folder_name);
+                            } else {
+                                DataUtils.renameInCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", folder_name, function (statusCode, resp) {
+                                    if (!utils.isHttpSuccessCode(statusCode)) {
+                                        feedback.html(resp);
+                                    } else {
+                                        var matches = value.match(/^\/(.*\/)?(.*)\/$/),
+                                            newvalue = "/" + (matches[1] || "") + folder_name + "/";
+                                        for (var idx in fnames) {
+                                            fnames[idx] = fnames[idx].replace(new RegExp("^(" + value + ")(.*)"), newvalue + "$2");
+                                        }
+                                        fileb['refresh'](fnames, newvalue);
+                                        fileb['clearselection']();
+                                        feedback.html('renamed ' + value + ' to ' + newvalue);
+                                    }
+                                });
+                            }
+                        }
+                    });
+                folder_buttons.append(rename_folder_b);
+
+                delete_folder_b.html('delete folder')
+                    .click(function (ev) {
+                        var value = fileb['getcurrfolder']();
+                        if (confirm("are you sure you want to delete " + value + "?")) {
+                            DataUtils.deleteFromCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", function (statusCode, resp) {
+                                if (!utils.isHttpSuccessCode(statusCode)) {
+                                    feedback.html(resp);
+                                } else {
+                                    var matches = value.match(/^\/(.*\/)?(.*)\/$/),
+                                        newvalue = "/_Trash_" + value;
+                                    for (var idx in fnames) {
+                                        fnames[idx] = fnames[idx].replace(new RegExp("^(" + value + ")(.*)"), newvalue + "$2");
+                                    }
+                                    fileb['refresh'](fnames);
+                                    fileb['clearselection']();
+                                    feedback.html('deleted ' + value);
+                                }
+                            });
+                        }
+                    });
+                folder_buttons.append(delete_folder_b);
+
+                move_folder_b.html('move folder')
+                    .click(function (ev) {
+                        var value = fileb['getcurrfolder']();
+                        var folder_loc = prompt("please fill in a new parent folder for folder " + value);
+                        if (folder_loc != null) {
+                            folder_loc = folder_loc.replace(/^\s+|\s+$/g, ''); // trim
+                            if (!folder_loc.match(/^\/([a-zA-Z0-9_\s]+\/)*$/i)) {
+                                feedback.html("invalid parent location: " + folder_loc);
+                            } else {
+                                DataUtils.moveInCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", folder_loc, function (statusCode, resp) {
+                                    if (!utils.isHttpSuccessCode(statusCode)) {
+                                        feedback.html(resp);
+                                    } else {
+                                        var matches = value.match(/^\/(.*\/)?(.*)\/$/),
+                                            newvalue = folder_loc + matches[2] + "/";
+                                        for (var idx in fnames) {
+                                            fnames[idx] = fnames[idx].replace(new RegExp("^(" + value + ")(.*)"), newvalue + "$2");
+                                        }
+                                        fileb['refresh'](fnames, newvalue);
+                                        fileb['clearselection']();
+                                        feedback.html('moved ' + value + ' to ' + folder_loc);
+                                    }
+                                });
+                            }
+                        }
+                    });
+                folder_buttons.append(move_folder_b);
+
+                rename_file_b.html('rename file')
+                    .click(function (ev) {
+                        var value = fileb['getselection']();
+                        var file_name = prompt("please fill in a new name for file " + value);
+                        if (file_name != null) {
+                            file_name = file_name.replace(/^\s+|\s+$/g, ''); // trim
+                            if (!file_name.match(/^[a-zA-Z0-9_\s\.]+$/i)) {
+                                feedback.html("invalid file name: " + file_name);
+                            } else {
+                                DataUtils.renameInCloud("/" + window.localStorage.getItem('user') + value + ".file", file_name, function (statusCode, resp) {
+                                    if (!utils.isHttpSuccessCode(statusCode)) {
+                                        feedback.html(resp);
+                                    } else {
+                                        var matches = value.match(/^\/(.*\/)?(.*)$/),
+                                            newvalue = "/" + (matches[1] || "") + file_name;
+                                        var idx = fnames.indexOf(value);
+                                        if (idx >= 0) {
+                                            fnames[idx] = newvalue;
+                                        }
+                                        fileb['refresh'](fnames);
+                                        fileb['clearselection']();
+                                        feedback.html('renamed ' + value + ' to ' + newvalue);
+                                    }
+                                });
+                            }
+                        }
+                    });
+                file_buttons.append(rename_file_b);
+
+                delete_file_b.html('delete file')
+                    .click(function (ev) {
+                        var value = fileb['getselection']();
+                        if (confirm("are you sure you want to delete " + value + "?")) {
+                            DataUtils.deleteFromCloud("/" + window.localStorage.getItem('user') + value + ".file", function (statusCode, resp) {
+                                if (!utils.isHttpSuccessCode(statusCode)) {
+                                    feedback.html(resp);
+                                } else {
+                                    feedback.html('deleted ' + value);
+                                    var idx = fnames.indexOf(value);
+                                    if (idx >= 0) {
+                                        fnames.splice(idx, 1);
+                                    }
+                                    fileb['refresh'](fnames);
+                                    fileb['clearselection']();
+                                }
+                            });
+                        }
+                    });
+                file_buttons.append(delete_file_b);
+
+                move_file_b.html('move file')
+                    .click(function (ev) {
+                        var value = fileb['getselection']();
+                        var folder_loc = prompt("please fill in a new parent folder for file " + value);
+                        if (folder_loc != null) {
+                            folder_loc = folder_loc.replace(/^\s+|\s+$/g, ''); // trim
+                            if (!folder_loc.match(/^\/([a-zA-Z0-9_\s]+\/)*$/i)) {
+                                feedback.html("invalid parent location: " + folder_loc);
+                            } else {
+                                DataUtils.moveInCloud("/" + window.localStorage.getItem('user') + value + ".file", folder_loc, function (statusCode, resp) {
+                                    if (!utils.isHttpSuccessCode(statusCode)) {
+                                        feedback.html(resp);
+                                    } else {
+                                        var matches = value.match(/^\/(.*\/)?(.*)$/),
+                                            newvalue = folder_loc + matches[2];
+                                        feedback.html('moved ' + value + ' to ' + folder_loc);
+                                        var idx = fnames.indexOf(value);
+                                        if (idx >= 0) {
+                                            fnames[idx] = newvalue;
+                                        }
+                                        fileb['refresh'](fnames);
+                                        fileb['clearselection']();
+                                    }
+                                });
+                            }
+                        }
+                    });
+                file_buttons.append(move_file_b);
+
+                GUIUtils.setupAndShowDialog(
+                    [fileb['filebrowser'], folder_buttons, file_buttons, feedback],
+                    function () {
+                        var value = [fileb['getselection']()];
+                        if (value.length > 0 && value[0] != "" && startDir) {
+                            __setRecentDir(startDir, value[0].substring(0, value[0].lastIndexOf('/') + 1));
+                        }
+                        return value;
+                    },
+                    __TWO_BUTTONS,
+                    title,
+                    callback);
+            });
+    }
+
+
+    /**
+     * Returns a <div> with an interactive file browser within it
+     *
+     * @param fnames - a complete list of all files in the directory tree
+     * structure
+     * @param draggable - when true, files and folders can be meaningfully
+     * dragged
+     * @param newfile when true, an editable new file icon is present
+     * @param startfolder if set, starts navigation at the specified folder
+     */
+    static getFileBrowser(fnames, draggable, newfile, startfolder) {
+        var fileb = $("<div>"),
+            navdiv = $("<div>"),
+            input = $("<input>"),
+            selection = undefined,
+            currfolder = '/',
+            clearSelection =
+                function () {
+                    if (selection) {
+                        selection.attr("class", 'fileb_icon');
+                        selection = undefined;
+                        input.val('');
+                    }
+                },
+            navbuttononclick =
+                /* 1 construct the full path associated to the clicked button
+                    2 remove 'deeper' buttons
+                    3 chdir to selected folder */
+                function (ev) {
+                    var path = '';
+                    for (var i = 0; i < navdiv.children("button").length; i++) {
+                        path += $(navdiv.children()[i]).html() + '/';
+                        if ($(navdiv.children()[i]).html() == $(ev.target).html())
+                            break;
+                    }
+
+                    setCurrentFileBrowserFolder(path.substring(1), fnames);
+                },
+            setCurrentFileBrowserFolder =
+                /* 1 determine files and folders from the given folder
+                    2 produce icons for them and add them to to-be content div
+                    3 create navigation toolbar for complete directory hierarchy
+                    4 replace previous content div, if any, with new one
+                      5 clear past selection, if any, and remember current folder */
+                async function (folder, fnames) {
+                    var div = $('#div_fileb-contents'),
+                        folders = [],
+                        files = [],
+                        exists = false;
+
+                    // If it already exists, remove everything!
+                    if (div.length > 0) {
+                        $('#div_fileb-contents').remove();
+                        //					 exists = true;
+                    }
+
+                    div = $("<div>");
+
+                    div.attr("class", 'fileb_pane')
+                        .attr("id", 'div_fileb-contents');
+
+
+                    //fnames might be a function that returns the files in
+                    //the folder
+                    //bentley: the ModelVerse only examines one folder at a time
+                    let file_list = fnames;
+                    if (!(Array.isArray(fnames))){
+                        file_list = await fnames(folder);
+                    }
+
+                    let _folder = utils.regexpe(folder);
+                    file_list.forEach(function (fname) {
+
+                        let matches = fname.match('^' + _folder + '(.+?/)');
+                        if (matches) {
+                            if (!utils.contains(folders, matches[1]))
+                                folders.push(matches[1]);
+                        }
+                        else if ((matches = fname.match('^' + _folder + '(.*)'))) {
+                            if (matches[1].length > 0) {
+                                files.push(matches[1]);
+                            }
+                        }
+                    });
+
+                    let all_entries = folders.concat(files);
+
+                    //get the maximum filename length
+                    let maxFnameLength = utils.max(all_entries, function (_) {
+                        return _.length;
+                    });
+
+                    //				 var tmpDiv = $("<div>");
+                    all_entries.forEach(function (fname) {
+                        let icon = HttpUtils.getFileIcon(fname);
+                        if (icon) {
+                            icon.css("width", 8 + maxFnameLength + 'ex');
+                            icon.click(function (ev) {
+                                clearSelection();
+
+                                if (fname.match(/\/$/))
+                                    setCurrentFileBrowserFolder(folder + fname, fnames);
+                                else {
+                                    input.val(folder + fname);
+                                    selection = icon;
+                                    selection.attr("class", 'fileb_icon_selected');
+                                }
+                            });
+
+                            if (draggable) {
+                                //icon.setAttribute('draggable',true);
+                                icon.attr("draggable", true);
+                                icon.get(0).ondragstart =
+                                    function (event) {
+                                        var uri = HttpUtils.url(folder + fname + '.file', __NO_WID);
+                                        event.dataTransfer.effectAllowed = 'copyMove';
+                                        event.dataTransfer.setData(
+                                            'DownloadURL',
+                                            'application/zip:' + fname + '.zip:' +
+                                            window.location.origin + uri);
+                                        event.dataTransfer.setData('uri', uri);
+                                    };
+                            }
+
+                            div.append(icon);
+                        }
+                    });
+
+                    if (newfile) {
+                        var icon = HttpUtils.getNewFileIcon(function (ev) {
+                            input.val(folder + ev.target.textContent);
+                        });
+                        icon.css("width", 8 + maxFnameLength + 'ex');
+                        div.append(icon);
+                    }
+
+                    navdiv.empty();
+
+                    //				 while (navdiv.children().length > 0) {
+                    //					navdiv.find(navdiv.lastChild).remove();
+                    //				 }
+                    //let tmpDiv = $("<div>");
+                    var subfolders = folder.split('/').filter(function (subf) {
+                        return subf != '';
+                    });
+                    subfolders.unshift('/');
+                    subfolders.forEach(function (subfolder) {
+                        var navbutton = $('<button>');
+                        navbutton.html(subfolder);
+                        navbutton.attr('id', 'navbar_' + subfolder);
+                        navbutton.click(navbuttononclick);
+                        navdiv.append(navbutton);
+                    });
+
+                    //navdiv.html( tmpDiv.html() );
+
+
+                    if (exists) {
+                        //					 $('#div_fileb-contents').html( div.html() );
+                        //					 fileb.append(div);
+                        $('#div_fileb-contents').remove();
+                        fileb.append(div);
+                    } else
+                        fileb.append(div);
+
+                    clearSelection();
+                    currfolder = folder;
+                };
+
+        let file_browser_width = 120;
+        fileb.css("width", file_browser_width + 'ex')
+            .css("maxWidth", '100%');
+
+        navdiv.css("align", 'left');
+
+        input.attr("type", 'hidden');
+
+        fileb.append(navdiv);
+        fileb.append(input);
+
+        setCurrentFileBrowserFolder(startfolder ? startfolder : '/', fnames);
+
+        return {
+            'filebrowser': fileb,
+            'filepane': function () {
+                return $('#div_fileb-contents');
+            },
+            'getcurrfolder': function () {
+                return currfolder;
+            },
+            'getselection': function () {
+                return input.val().trim();
+            },
+            'clearselection': function () {
+                clearSelection();
+            },
+            'refresh': function (fnames, the_folder) {
+                setCurrentFileBrowserFolder(the_folder || currfolder, fnames);
+            }
+        };
+    }
+
+
+}

+ 156 - 73
client/geometry_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 GeometryUtils = function(){
 	
@@ -123,7 +107,7 @@ GeometryUtils = function(){
 	 */
 	this.previewSelectionTransformation = function(op,dir) {
         if (transformationPreviewOverlay == undefined)
-            return
+            return;
 		var bbox  = __selection['bbox'],
 			 scale = (dir > 0 ? 1.05 : 0.95),
 			 angle = (dir > 0 ? 3 : -3);
@@ -142,7 +126,7 @@ GeometryUtils = function(){
 	 */
 	this.previewSelectionTranslation = function(x,y) {
         if (transformationPreviewOverlay == undefined)
-            return
+            return;
 		var _x = parseInt(transformationPreviewOverlay.node.getAttribute('_x')),
 			 _y = parseInt(transformationPreviewOverlay.node.getAttribute('_y'));
 		transformationPreviewOverlay.translate(x-_x,y-_y);
@@ -292,7 +276,7 @@ GeometryUtils = function(){
                                                          true,
                                                          false,
                                                          requests)
-                                         )
+                                         );
             requests = requests.concat(to_concat);
         }
 	
@@ -321,18 +305,30 @@ GeometryUtils = function(){
 					var img = $('<img>');
 					img.attr('class', 'geometry_ctrl');
 					img.attr('src', 'client/media/'+x+'.png');
-					img.get(0).onmousewheel = 
-						function(event)	
-						{
-							var dir = event.wheelDelta;
-							GeometryUtils.previewSelectionTransformation(x,dir);
-							return false;
-						};
+					img.attr('id', x + "_btn");
+
+					let wheelFunc = function(event)
+					{
+						let dir = null;
+						if (event.wheelDelta){
+							dir = event.wheelDelta;
+						}else if (event.deltaY){
+							dir = event.deltaY;
+						}
+						GeometryUtils.previewSelectionTransformation(x,dir);
+						return false;
+					};
+
+					//detect mouse wheel on all browsers
+					img.get(0).onmousewheel = wheelFunc;
+					img.get(0).onwheel = wheelFunc;
+
 					geometryControlsOverlay.append(img);
 				});
 			var img = $('<img>');
 			img.attr('class', 'geometry_ctrl');
 			img.attr('src', 'client/media/ok.png');
+			img.attr('id', "ok_btn");
 			img.click(function(event) {GeometryUtils.transformSelection(__GEOM_TRANSF);});
 			geometryControlsOverlay.append(img);
 		}
@@ -456,7 +452,7 @@ GeometryUtils = function(){
 				 latter... the results of this form the emitted batchEdit */
 	this.transformSelection = function(callingContext,insertInfo) {
         if (transformationPreviewOverlay == undefined)
-            return
+            return;
 		var T = transformationPreviewOverlay.node.getAttribute('transform');
 		if( T == null || T == 'matrix(1,0,0,1,0,0)' )
 		{
@@ -513,48 +509,61 @@ GeometryUtils = function(){
 		  						 offsetTy + parseFloat(icon.getAttr('__y'))];
 					}
 	 				uris2changes[it] = changes;
-	
-	
-					if( ! __isConnectionType(it) )
-					{
-						/* have edge ends out follow */
-						__icons[it]['edgesOut'].forEach(
-							function(edgeId)
-							{
-								var linkuri = __edgeId2linkuri(edgeId);
-								if( __isSelected(linkuri) )
-									return;
-					
-								var segments = __edges[edgeId]['segments'],
-									 points	 = segments.match(/([\d\.]*,[\d\.]*)/g),
-									 xy 		 = utils.head(points).split(','),
-									 newXY 	 = GeometryUtils.transformPoint(xy[0],xy[1],T);
-										 
-								connectedEdgesChanges[linkuri] = 
-									(connectedEdgesChanges[linkuri] || {});
-								points.splice(0,1,newXY.join(','));
-								connectedEdgesChanges[linkuri][edgeId] = 
-									'M'+points.join('L');
-							});
-	
-						/* have edge ends in follow */
-						__icons[it]['edgesIn'].forEach(
-							function(edgeId)
-							{
-								var linkuri = __edgeId2linkuri(edgeId);
-								if( __isSelected(linkuri) )
-									return;
-				
-								var segments = __edges[edgeId]['segments'],
-									 points	 = segments.match(/([\d\.]*,[\d\.]*)/g),
-									 xy 		 = utils.tail(points).split(','),
-									 newXY 	 = GeometryUtils.transformPoint(xy[0],xy[1],T);
-								connectedEdgesChanges[linkuri] = 
-									(connectedEdgesChanges[linkuri] || {});
-								points.splice(points.length-1,1,newXY.join(','));
-								connectedEdgesChanges[linkuri][edgeId] = 
-									'M'+points.join('L');
-							});
+
+
+                    if (!__isConnectionType(it)) {
+                        let inLinkUris = __icons[it]['edgesIn'].map(__edgeId2linkuri);
+                        let outLinkUris = __icons[it]['edgesOut'].map(__edgeId2linkuri);
+                        /* have edge ends out follow */
+                        __icons[it]['edgesOut'].forEach(
+                            function (edgeId) {
+                                let linkuri = __edgeId2linkuri(edgeId);
+                                if (__isSelected(linkuri))
+                                    return;
+
+                                let isLooping = inLinkUris.includes(linkuri);
+                                let changes = moveEdges(edgeId, T, true, isLooping);
+                                let newEdgeChanges = changes[0];
+                                let centrePoint = changes[1];
+
+                                connectedEdgesChanges[linkuri] =
+                                    (connectedEdgesChanges[linkuri] || {});
+                                connectedEdgesChanges[linkuri] = utils.mergeDicts([connectedEdgesChanges[linkuri], newEdgeChanges]);
+
+                                //move the assoc text if the central point changed
+                                if (centrePoint != null) {
+                                    if (uris2changes[__edgeId2linkuri(edgeId)] == null) {
+                                        uris2changes[__edgeId2linkuri(edgeId)] = {};
+                                    }
+                                    uris2changes[__edgeId2linkuri(edgeId)]['position'] = centrePoint;
+                                }
+
+                            });
+
+                        /* have edge ends in follow */
+                        __icons[it]['edgesIn'].forEach(
+                            function (edgeId) {
+                                let linkuri = __edgeId2linkuri(edgeId);
+                                if (__isSelected(linkuri))
+                                    return;
+
+                                let isLooping = outLinkUris.includes(linkuri);
+                                let changes = moveEdges(edgeId, T, false, isLooping);
+                                let newEdgeChanges = changes[0];
+                                let centrePoint = changes[1];
+
+                                connectedEdgesChanges[linkuri] =
+                                    (connectedEdgesChanges[linkuri] || {});
+                                connectedEdgesChanges[linkuri] = utils.mergeDicts([connectedEdgesChanges[linkuri], newEdgeChanges]);
+
+                                //move the assoc text if the central point changed
+                                if (centrePoint != null) {
+                                    if (uris2changes[__edgeId2linkuri(edgeId)] == null) {
+                                        uris2changes[__edgeId2linkuri(edgeId)] = {};
+                                    }
+                                    uris2changes[__edgeId2linkuri(edgeId)]['position'] = centrePoint;
+                                }
+                            });
 					}
 					else
 					{
@@ -636,6 +645,80 @@ GeometryUtils = function(){
 					insertRequests));
 		}
 	};
+
+    /**
+     * Moves the points for this edge using the transformation T
+     * If the edge is only comprised of three points
+     * (the point on the icon, the central point, and the connected node's point)
+     * then move the central point
+	 * Returns the changes to be made to the edges, and the central point if calculated
+     */
+    this.moveEdges = function (edgeId, T, isOutDir, isLooping) {
+
+        let segments = __edges[edgeId]['segments'];
+        let points = segments.match(/([\d\.]*,[\d\.]*)/g);
+
+        let xy = null;
+        let newXY = null;
+
+        //update the point connected to the icon
+        if (isOutDir) {
+            xy = utils.head(points).split(',');
+            newXY = GeometryUtils.transformPoint(xy[0], xy[1], T);
+            points.splice(0, 1, newXY.join(','));
+        } else {
+            xy = utils.tail(points).split(',');
+            newXY = GeometryUtils.transformPoint(xy[0], xy[1], T);
+            points.splice(points.length - 1, 1, newXY.join(','));
+        }
+
+        //dict to hold edge updates
+        let edgeDict = {};
+
+        // the centre point if the association text should be moved
+        let centrePoint = null;
+
+        // if there are exactly two points in this edge,
+        // move the middle control point as well
+        // by updating the other edge in the association
+        //
+        //don't do this if it's a looping edge
+        //as it will overwrite the changes
+        if (points.length == 2 && !isLooping) {
+            let connectionPartici = __getConnectionParticipants(edgeId);
+            let otherEdge = isOutDir ? connectionPartici[2] : connectionPartici[1];
+
+            let otherSegments = __edges[otherEdge]['segments'];
+            let otherPoints = otherSegments.match(/([\d\.]*,[\d\.]*)/g);
+
+            //get the other edge's point which is not the center point
+            let otherxy = isOutDir ? otherPoints[1] : otherPoints[otherPoints.length - 2];
+            otherxy = otherxy.split(",");
+
+            let xCentrePoint = (parseFloat(newXY[0]) + parseFloat(otherxy[0])) / 2;
+            let yCentrePoint = (parseFloat(newXY[1]) + parseFloat(otherxy[1])) / 2;
+
+            let centrePointStr = xCentrePoint + "," + yCentrePoint;
+
+            if (isOutDir) {
+                points.splice(points.length - 1, 1, centrePointStr);
+                otherPoints.splice(0, 1, centrePointStr);
+            } else {
+                points.splice(0, 1, centrePointStr);
+                otherPoints.splice(otherPoints.length - 1, 1, centrePointStr);
+            }
+
+            let newOtherEdge = 'M' + otherPoints.join('L');
+            edgeDict[otherEdge] = newOtherEdge;
+
+            centrePoint = [xCentrePoint, yCentrePoint];
+        }
+
+        let newEdge = 'M' + points.join('L');
+        edgeDict[edgeId] = newEdge;
+
+        return [edgeDict, centrePoint];
+    };
 	
 	/**
 	 * Apply the specified transformation to the given point and return

+ 5 - 19
client/globalVariables.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2012 Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 
 /**
@@ -41,6 +26,7 @@ var DataUtils; /* The Data Utilities object. Handles the loaded objects, edges,
 var EditUtils; /* Handles the Copy, Paste, Undo, and Redo options. Consider merging this with GUIUtils */
 var GeometryUtils; /* Handles transformations and translations. */
 var MMMUtils;
+var Layout; /*For auto-layout capabilities. */
 
 var currentKeys = [];
 

+ 119 - 251
client/gui_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 GUIUtils = function(){
 	
@@ -31,16 +15,16 @@ GUIUtils = function(){
 	/**
 	 * Converts from page centric X coordinates to canvas centric X coordinates
 	 */
-	this.convertToCanvasX = function(pageX){
-		return pageX + $('#div_container').scrollLeft() - $('#contentDiv').offset().left;
-	};
+    this.convertToCanvasX = function (event) {
+        return event.layerX;
+    };
 
 	/**
 	 * Converts from page centric Y coordinates to canvas centric Y coordinates
-	 */	
-	this.convertToCanvasY = function(pageY){
-		return pageY + $('#div_container').scrollTop() - $('#contentDiv').offset().top;
-	};
+	 */
+    this.convertToCanvasY = function (event) {
+        return event.layerY;
+    };
 	
 	/**
 	 * Disables the dock bar
@@ -65,198 +49,7 @@ GUIUtils = function(){
 		cb.prop("checked", checked);
 		return cb;
 	};
-	
-	/**
-	 * Returns a <div> with an interactive file browser within it
-	 * 
-	 * @param fnames - a complete list of all files in the directory tree
-	 * structure
-	 * @param draggable - when true, files and folders can be meaningfully
-	 * dragged
-	 * @param newfile when true, an editable new file icon is present
-	 * @param startfolder if set, starts navigation at the specified folder
-	 */
-	this.getFileBrowser = function(fnames, draggable, newfile, startfolder){
-		var maxFnameLength = utils.max(fnames,function(_) {return _.length;}),
-			 fileb = $("<div>"),
-			 navdiv = $("<div>"),
-			 input = $("<input>"),
-			 selection = undefined,
-			 currfolder = '/',
-			 clearSelection = 
-				 function()
-				 {
-					 if( selection )
-					 {
-						 selection.attr("class", 'fileb_icon');
-						 selection = undefined;
-						 input.val('');
-					 }
-				 },
-			 navbuttononclick	=
-				 /* 1 construct the full path associated to the clicked button
-					 2 remove 'deeper' buttons
-					 3 chdir to selected folder */
-				 function(ev)
-				 {
-					 var path = '';
-					 for( var i=0; i<navdiv.children("button").length; i++ )
-					 {
-						 path += $(navdiv.children()[i]).html() +'/';
-						 if( $(navdiv.children()[i]).html() == $(ev.target).html() )
-							 break;
-					 }
-					 
-					 setCurrentFileBrowserFolder(path.substring(1), fnames);
-				 },
-			 setCurrentFileBrowserFolder = 
-				 /* 1 determine files and folders from the given folder
-					 2 produce icons for them and add them to to-be content div
-					 3 create navigation toolbar for complete directory hierarchy
-					 4 replace previous content div, if any, with new one 
-				  	 5 clear past selection, if any, and remember current folder */
-				 function(folder,fnames) 
-				 {
-					 var div = $('#div_fileb-contents'),
-						  folders		  = [],
-						  files			  = [],
-						  maxFnameLength = 0,
-						  exists = false;
-					 
-					 // If it already exists, remove everything!
-					 if( div.length > 0 ) {
-						 $('#div_fileb-contents').remove();
-	//					 exists = true;
-					 }
-					 
-					 div = $("<div>");
-						 
-					 div.attr("class", 'fileb_pane')
-					 	.attr("id", 'div_fileb-contents');
-					 
-					 fnames.forEach( function(fname) {
-						 var _folder = utils.regexpe(folder);
-						 if( (matches=fname.match('^'+_folder+'(.+?/)')) )
-						 {
-							 if( ! utils.contains(folders,matches[1]) )
-								 folders.push(matches[1]);
-							 else
-								 return;
-						 }
-						 else if( (matches=fname.match('^'+_folder+'(.*)')) ) {
-                             if (matches[1].length > 0) {
-                                 files.push(matches[1]);
-                             } else {
-                                 return;
-                             }
-                         }
-						 else
-							 return;
-							 
-						 maxFnameLength = 
-							 Math.max(maxFnameLength,matches[1].length);
-					 });
-							 
-	//				 var tmpDiv = $("<div>");
-					 folders.concat(files).forEach( function(fname) {
-						 var icon = HttpUtils.getFileIcon(fname);
-						 if( icon )
-						 {
-							 icon.css("width", 8+maxFnameLength+'ex');
-							 icon.click( function(ev) {
-							 	 clearSelection();								 	
-	
-								 if( fname.match(/\/$/) )
-									 setCurrentFileBrowserFolder(folder+fname,fnames);
-								 else
-								 {
-									 input.val( folder+fname );
-									 selection = icon;
-									 selection.attr("class", 'fileb_icon_selected');
-								 }
-							 });
-				 			 
-				 			 if( draggable )
-							 {
-								 //icon.setAttribute('draggable',true);
-				 				 icon.attr("draggable", true);
-								 icon.get(0).ondragstart = 
-									 function(event) 
-									 {
-										 var uri = HttpUtils.url(folder+fname+'.file',__NO_WID);
-			  							 event.dataTransfer.effectAllowed = 'copyMove';
-			  							 event.dataTransfer.setData(
-											 'DownloadURL', 
-											 'application/zip:'+fname+'.zip:'+
-												 window.location.origin+uri);
-										 event.dataTransfer.setData('uri',uri);
-									 };
-							 }
-	
-							 div.append(icon);
-						 }
-					 });
-					 
-					 if( newfile )
-					 {
-						 var icon = HttpUtils.getNewFileIcon( function(ev) {
-							 input.val( folder+ev.target.textContent );
-						 });
-						 icon.css("width", 8+maxFnameLength+'ex');
-						 div.append(icon);
-					 }
-					 
-					 navdiv.empty();
-					 
-	//				 while (navdiv.children().length > 0) {
-	//					navdiv.find(navdiv.lastChild).remove();
-	//				 }
-					 tmpDiv = $("<div>");
-					 var subfolders = folder.split('/').filter(function(subf) {return subf != '';});
-					 subfolders.unshift('/');
-					 subfolders.forEach(function(subfolder) {
-						var navbutton = $('<button>');
-						navbutton.html( subfolder );
-						navbutton.click(navbuttononclick);
-						navdiv.append(navbutton);
-					 });
-					 
-					 //navdiv.html( tmpDiv.html() );
-					 
-					 
-					 if( exists ) {
-	//					 $('#div_fileb-contents').html( div.html() );
-	//					 fileb.append(div);
-						 $('#div_fileb-contents').remove();
-						 fileb.append(div);
-					 } else
-						 fileb.append(div);
-	
-					 clearSelection();				 
-					 currfolder = folder;
-				 };
-	
-		fileb.css("width", maxFnameLength+'ex')
-			.css("maxWidth", '100%');
-		
-		navdiv.css("align",  'left');
-		
-		input.attr("type", 'hidden');
-		
-		fileb.append(navdiv);
-		fileb.append(input);
-		
-		setCurrentFileBrowserFolder(startfolder ? startfolder : '/',fnames);
-	
-		return {'filebrowser':	 fileb,
-				  'filepane':	 	 function() {return $('#div_fileb-contents');},
-				  'getcurrfolder': function() {return currfolder;},	
-				  'getselection':	 function()	{return input.val();},
-                  'clearselection': function() {clearSelection()},
-                  'refresh': function(fnames, the_folder) {
-                      setCurrentFileBrowserFolder(the_folder || currfolder, fnames);
-                  }};
-	};
+
 	
 	// TODO: replace the bundled function with an actual object generation. The
 	// current method is sloppy.
@@ -381,7 +174,7 @@ GUIUtils = function(){
 			var vals 	 = type.match(/^ENUM\((.*)\)$/)[1],
 				 input 	 = GUIUtils.getSelector(vals.split(','),false,[value]),
 				 getinput = 
-					 function(_){return HttpUtils.getSelectorSelection(_)[0]};
+					 function(_){return HttpUtils.getSelectorSelection(_)[0];};
 		}
 	
 		else if( type.match(/^boolean$/) )
@@ -394,9 +187,9 @@ GUIUtils = function(){
 		else if( type.match(/^\$/) )
 			return GUIUtils.getInputField(__specialTypes[type],value);
 	
-		else if (matches = type.match("^file<(.*)>")) {
+		else if ((matches = type.match("^file<(.*)>"))) {
 			var input 	 = GUIUtils.getFileInput(value,matches[1],"code_style string_input",1),
-				 getinput = function(_){return _.val();}
+				 getinput = function(_){return _.val();};
         }
 	
 		else
@@ -415,7 +208,7 @@ GUIUtils = function(){
 	 * 
 	 * @param choices - the choices for the <select> element
 	 * @param multipleChoice - if true, allows for multiple options to be selected
-	 * @param defaultSelect - sets the default selection for the list
+	 * @param defaultSelection - sets the default selection for the list
 	 * @param numVisibleOptions - sets the number of visible options
 	 */
 	this.getSelector = function(choices,multipleChoice,defaultSelection,numVisibleOptions){
@@ -430,6 +223,7 @@ GUIUtils = function(){
 					var option = $('<option>');
 					option.val( choice ); 
 					option.html( choice );
+					option.attr('id', "choice_" + choice);
 					select.append(option);
 					if( defaultSelection != undefined && 
 						 utils.contains(defaultSelection,choice) )
@@ -480,7 +274,7 @@ GUIUtils = function(){
         });
         string_input.extra_el = extra_el;
         return string_input;
-    }
+    };
 	
 	/**
 	 * Constructs a <textarea> element. In this element, Alt + Right Arrow
@@ -515,27 +309,21 @@ GUIUtils = function(){
 				
 				return true;
 			}
-            // https://media.giphy.com/media/12XMGIWtrHBl5e/giphy.gif
-			else if( event.keyCode == KEY_ENTER )
-			{
-                if (rows > 1) {
-                    // only for multi-line input fields
-                    var cursorPos = event.target.selectionStart;
-                    input.val( 
-                            input.val().substring(0,cursorPos)+'\r\n'+
-                            input.val().substring(cursorPos));
-                    input.get(0).setSelectionRange(cursorPos+1,cursorPos+1);
+
+            else if (event.keyCode == KEY_ENTER) {
+                //for single row fields, don't create a new line
+                if (rows == 1) {
+                    event.preventDefault();
                 }
-				event.stopPropagation();
-                event.preventDefault();
-				return true;
-			}
+            }
 		});
-        input.keyup( function (event) {
-			if( event.keyCode == KEY_ENTER )
-			{
-				event.stopPropagation();
-                event.preventDefault();
+        input.keyup(function (event) {
+            if (event.keyCode == KEY_ENTER) {
+                //don't send the enter key for multi-line fields
+                //this closes the window
+                if (rows > 1) {
+                    event.stopPropagation();
+                }
             }
         });
 		return input;
@@ -653,6 +441,7 @@ GUIUtils = function(){
 					callback(input); 
 				}
 			});
+			ok.attr("id", "dialog_btn");
 			ok.html('ok');
 			dialog.append(ok);
 		}
@@ -667,8 +456,48 @@ GUIUtils = function(){
 			dialog.append(cancel);
 		}
 
-		BehaviorManager.setActiveBehaviourStatechart(__SC_DIALOG);
-		BehaviorManager.handleUserEvent(__EVENT_SHOW_DIALOG);
+        dialog.keydown(function (event) {
+
+            //tab through the fields
+            if (event.key == "Tab") {
+
+                try {
+                    if (title.startsWith("edit")) {
+                        let table_row = event.target.parentElement.parentElement;
+                        let nextEle = table_row.nextElementSibling;
+
+                        // at end, so select first element
+                        if (nextEle == null) {
+                            nextEle = table_row.parentElement.firstElementChild;
+                        }
+
+                        //get the actual text field
+                        let nextField = nextEle.children[1].children[0];
+                        nextField.focus();
+                    } else if (title.startsWith("Parameters")) { //try to tab through workflow parameters
+                        let element = event.target;
+
+                        //get the next element
+                        //skips the <br>s and labels
+                        let nextEle = element.nextElementSibling.nextElementSibling
+                            .nextElementSibling.nextElementSibling;
+
+                        //cycle back around to the top
+                        if (nextEle.nodeName == "BUTTON") {
+                            nextEle = nextEle.parentElement.children[3];
+                        }
+                        nextEle.focus();
+                    }
+
+                } catch (err) { //catch errors if something was unexpected
+                    console.debug("Tab event failed: " + err);
+                }
+
+            }
+        });
+
+        BehaviorManager.setActiveBehaviourStatechart(__SC_DIALOG);
+        BehaviorManager.handleUserEvent(__EVENT_SHOW_DIALOG);
 	};
 	
 	/* 
@@ -769,10 +598,17 @@ GUIUtils = function(){
 									 _ERROR,
 									 'error in button code ::\n '+res['$err']);
 			 		 });
-//			 		 var url = HttpUtils.url(imgSrc(name),__NO_WID);
-			 		 img.attr("src", HttpUtils.url(imgSrc(name),__NO_WID));
-			 		 div.append(img);
-			 		 return div;
+                     var url = HttpUtils.url(imgSrc(name),__NO_WID);
+                     img.attr("src", url);
+
+                     //handle missing icon
+                     let defaultUrl = HttpUtils.url("/Formalisms/default.icon.png");
+                     let missingMsg = "Warning: The icon \"" + url + "\" is missing! The default icon has been used.";
+                     let onerrorStr = "this.onerror = ''; this.src = '" + defaultUrl + "'; console.log('" + missingMsg + "');";
+                     img.attr('onerror', onerrorStr);
+
+                     div.append(img);
+                     return div;
 			 	 };
 
 		GUIUtils.removeToolbar(tb);
@@ -814,9 +650,41 @@ GUIUtils = function(){
 		if( tb_div.children().length == 0 )
 			tb_div.append( GUIUtils.getTextSpan(tb,'toolbar_alt') );
 
-		$('#div_dock').append(tb_div);
+        //get the toolbar
+        let dock = $('#div_dock');
+
+        //create an array and add the new toolbar
+        let items = Array.from(dock[0].childNodes);
+        items.push(tb_div[0]);
+
+        //sort the dock
+        items.sort(function(a, b) {
+
+            //main menu comes first
+            if (a.id.includes("MainMenu")){
+                return -1;
+            }
+
+            //toolbars come first
+            if (a.id.includes("Toolbars") && !(b.id.includes("Toolbars"))){
+                return -1;
+            }
+
+            if (b.id.includes("Toolbars") && !(a.id.includes("Toolbars"))){
+                return 1;
+            }
+
+            //otherwise, sort by name
+            return a.id == b.id? 0 : (a.id > b.id ? 1 : -1);
+        });
+
+        //add the elements back into the dock
+        for (let i = 0; i < items.length; ++i) {
+            dock.append(items[i]);
+        }
+
 
-		__loadedToolbars[tb] = data;
+        __loadedToolbars[tb] = data;
 	};
 	
 	return this;

+ 25 - 28
client/http_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 //Todo: replace this with JQuery
 //This remains outside for easier replacement later down the line
@@ -44,13 +28,17 @@ HttpUtils = function(){
 		var onreadystatechange = function(ev) {
 			 if( req.readyState == 4 )
 			 {
-	 			 console.debug(method+' '+url+' >> '+req.status);
-				 if( req.status == 0 )
-	 				 WindowManagement.openDialog(__FATAL_ERROR,'lost connection to back-end');
-				 else if( onresponse )
-					 onresponse(req.status,req.responseText);
-				 else if( ! utils.isHttpSuccessCode(req.status) )
-	 				 WindowManagement.openDialog(_ERROR,req.responseText);
+                 console.debug(method + ' ' + url + ' >> ' + req.status);
+                 //ignore calls made to other addresses
+                 if (url.startsWith("http://") || url.startsWith("https://")) {
+                     onresponse(req.status, req.responseText);
+                 }
+                 else if (req.status == 0)
+                     WindowManagement.openDialog(__FATAL_ERROR, 'lost connection to back-end');
+                 else if (onresponse)
+                     onresponse(req.status, req.responseText);
+                 else if (!utils.isHttpSuccessCode(req.status))
+                     WindowManagement.openDialog(_ERROR, req.responseText);
 			 }
  		 };
 	
@@ -75,7 +63,12 @@ HttpUtils = function(){
 //			console.debug(utils.jsons(params));
 			req.open(method, url, !sync); 
 			req.onreadystatechange = onreadystatechange;
-			req.send(utils.jsons(params));
+
+			if (typeof params != 'string'){
+				params = JSON.stringify(params);
+			}
+			req.send(params);
+
 		}
 	};
 	
@@ -125,6 +118,8 @@ HttpUtils = function(){
 		span.attr("class", 'fileb_icon');
 //		img.attr("class", 'clickable');
 		txt.css("padding", '5px');
+
+		txt.attr('id', fname.replace("/", ""));
 		
 		span.append(img);
 		span.append(txt);
@@ -151,6 +146,7 @@ HttpUtils = function(){
 		txt.attr("contentEditable", true);
 		// JQuery does not support HTML5 oninput
 		txt.keyup( oninput );
+		txt.attr('id', 'new_file');
 		span.append(img);
 		span.append(txt);
 		return span;
@@ -227,6 +223,7 @@ HttpUtils = function(){
 		var _context = {
 				'username':__user,
 				'wid':__wid,
+				'aswid' :__aswid,
 				'mms':utils.keys(__loadedToolbars).filter(__isIconMetamodel)};
 		try			{eval(code); return {};}
 		catch(err)	

+ 4 - 20
client/init.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 //TODO: shred initClient()
 /* initialize client 

+ 6 - 1
client/input_bar_utils.js

@@ -1,3 +1,8 @@
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
+
 InputBarUtils = function(){
 	this.processKey = function( event ){
 		if( event.keyCode == KEY_ENTER ){
@@ -27,7 +32,7 @@ InputBarUtils = function(){
 			}
 			
 		}
-	}
+	};
 	
 	return this;
 }();

+ 153 - 0
client/layout.js

@@ -0,0 +1,153 @@
+Layout = function(){
+
+
+    this.autolayout = function()
+    {
+
+        //sets for nodes and edges
+        var nodes = [];
+        var links = [];
+
+        //mapping between uris and nodes
+        var nodes2uris = {};
+        var uris2nodes = {};
+
+        //radius between nodes
+        var radius = 0;
+
+        //for computing the centre of the nodes
+        var centreX = 0;
+        var centreY = 0;
+
+        //keep track of the sources and targets for edges
+        //there are edges between nodes and associations
+        var edgeSource = {};
+        var edgeTarget = {};
+
+        var i = 0;
+        for( var uri in __icons ){
+
+            //ignore link iconsmoveEdgeHead
+            var is_link = false;
+            for (var edgeId in __edges){
+                var edgeuri = __edgeId2linkuri(edgeId);
+                if (edgeuri == uri){
+                    is_link = true;
+                    break;
+                }
+            }
+            if (is_link){
+                continue;
+            }
+
+            var icon = __icons[uri]['icon'];
+
+
+            //get x and y, and add to the centre calculation
+            var x = parseFloat(icon.getAttr('__x'));
+            var y = parseFloat(icon.getAttr('__y'));
+
+            centreX = centreX + x;
+            centreY = centreY + y;
+
+            //determine the maximum radius of a node
+            var bbox = icon.getBBox();
+            radius = Math.max(radius, bbox.width, bbox.height);
+
+            //create the node
+            var n = {index: i, x:x, y:y};
+            nodes.push(n);
+
+            //keep the mapping
+            nodes2uris[n.index] = uri;
+            uris2nodes[uri] = n.index;
+
+            i = i + 1;
+        }
+
+        //find centre of the nodes for the layout
+        centreX = centreX / nodes.length;
+        centreY = centreY / nodes.length;
+
+
+        //create the edges
+        for (var edge in __edges){
+
+        	var start = __edges[edge]['start'];
+        	var end = __edges[edge]['end'];
+
+        	var source = uris2nodes[start];
+        	var target = uris2nodes[end];
+
+            //associations are composed of two edges
+            //so record the node IDs at either end
+            //TODO: Replaceable with var linkIn = __edgeId2ends(edgeId)[0];?
+        	if (source == undefined){
+        	    edgeTarget[start] = target;
+            }
+
+        	if (target == undefined){
+        	    edgeSource[end] = source;
+            }
+        }
+
+        //for each association, create the link in the force graph
+        for (var assoc in edgeSource) {
+
+            var s = edgeSource[assoc];
+            var t = edgeTarget[assoc];
+
+            links.push({source: s, target: t});
+        }
+
+
+        //init the simulation
+        var simulation = d3.forceSimulation(nodes)
+            .force("charge", d3.forceManyBody().strength(10))
+            .force("link", d3.forceLink(links).distance(20).strength(1).iterations(10))
+        	.force("collide", d3.forceCollide(radius))
+            .force("center", d3.forceCenter(centreX, centreY))
+            ;
+
+        //stop the simulation (as we're not visualizing it)
+        simulation.stop();
+
+        //progress the simulation
+        for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
+            simulation.tick();
+        }
+
+        for (var i = 0; i < nodes.length; i++){
+
+            var n = nodes[i];
+            var uri = nodes2uris[n.index];
+
+
+            var icon = __icons[uri]['icon'];
+            var bbox = icon.getBBox();
+
+            //restrict to stay in the canvas
+            var x = (n.x > bbox.width)?n.x:bbox.width;
+            var y = (n.y > bbox.height)?n.y:bbox.height;
+
+            //move each icon
+            //TODO: should move edges as well,
+            //but moving the two nodes at each end of the edge
+            //double moves the edge ends, with one overwriting
+            //the other.
+            //would have to break apart
+            //functions in geometry_utils or query_response to be
+            //able to move ends of edges independently
+            __select([uri]);
+            GeometryUtils.initSelectionTransformationPreviewOverlay(bbox.x,bbox.y);
+		    GeometryUtils.previewSelectionTranslation(x, y);
+		    GeometryUtils.transformSelection();
+
+		    GeometryUtils.hideTransformationPreviewOverlay();
+		    __select();
+        }
+
+     };
+
+    return this;
+}();

+ 10 - 28
client/mmm_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /**
  * Gets the metamodel at the current URI
@@ -196,18 +180,17 @@ function __createEdge(segments,style,edgeId,linkuri)
     }
     function getOrderNr(id,visited) {
         var icon = __icons[id];
-        if (visited.indexOf(icon) > 0) return
+        if (visited.indexOf(icon) > 0) return;
         if (icon['ordernr']) return;
         visited.push(icon);
         if (__isConnectionType(id)) {
-            // I like my edges as I like my women: always on top
             icon['ordernr'] = 9999;
         } else if (icon['edgesIn'].length > 0) {
             for (var edgeId in icon['edgesIn']) {
-                var associationid = __edges[icon['edgesIn'][edgeId]]['start']
+                var associationid = __edges[icon['edgesIn'][edgeId]]['start'];
                 if (__isContainmentConnectionType(associationid)) {
                     getOrderNr(__edges[__icons[associationid]['edgesIn'][0]]['start'], visited);
-                    icon['ordernr'] = __icons[__edges[__icons[associationid]['edgesIn'][0]]['start']]['ordernr'] + 1
+                    icon['ordernr'] = __icons[__edges[__icons[associationid]['edgesIn'][0]]['start']]['ordernr'] + 1;
                 }
             }
             if (!icon['ordernr']) icon['ordernr'] = 0;
@@ -219,11 +202,10 @@ function __createEdge(segments,style,edgeId,linkuri)
         getOrderNr(id, []);
     }
     
-    Object.keys(__icons).concat().sort(function(a, b) {return __icons[a]['ordernr'] - __icons[b]['ordernr']}).forEach(function(el) {
+    Object.keys(__icons).concat().sort(function(a, b) {return __icons[a]['ordernr'] - __icons[b]['ordernr'];}).forEach(function(el) {
         __icons[el]['icon'].toFront();
     });
     Object.keys(__edges).forEach(function(el) {
-        // I like my edges as I like my women: always on top
         __edges[el]['icon'].toFront();
     });
     
@@ -434,7 +416,7 @@ function __getIconsInContainer(container)
 			return [];
         
         if( explored.indexOf(container) > -1 ) {
-            return []
+            return [];
         }
 	
 		var contents = 
@@ -452,7 +434,7 @@ function __getIconsInContainer(container)
 								}).concat([linkuri]);
 				}));
                 
-        explored.push(container)
+        explored.push(container);
 
         for (var ct_idx in contents) {
             var to_concat = utils.flatten(getExplicitContents(contents[ct_idx], explored));

File diff suppressed because it is too large
+ 1074 - 0
client/modelverse_connector.js


+ 333 - 0
client/modelverse_connector_rendering.js

@@ -0,0 +1,333 @@
+/* This file holds the code for rendering SimpleClassDiagrams from the ModelVerse.
+
+1) The rendering meta-model and transformation are sent to the ModelVerse.
+2) The transformation executes, creating the rendered model and returning the JSON.
+3) 'model_CS' will then contain the rendered concrete syntax
+
+This method is dependent on the meta-model being SimpleClassDiagrams.
+Therefore, this code should be put aside until a more robust rendering
+system in the ModelVerse is created.
+
+For example, it would be ideal to have a render transformation explicitly defined
+for each metamodel in the ModelVerse. Then the render_model command could just
+return the JSON for the rendered model (or just the updated portion).
+
+ */
+
+class ModelVerseRenderer {
+
+    static get_CS(model_name) {
+
+        //get CS for model
+
+        let SCD = "formalisms/SimpleClassDiagrams";
+        let MM_render = "formalisms/SCD_graphical";
+        let render_trans_model = "models/render_SCD";
+        let render_MM = this.get_render_mm();
+        let render_trans_code = this.get_render_trans();
+        let render_model_add = {
+            'data': encodeURIComponent(utils.jsons(["model_add", SCD, MM_render, render_MM]))
+        };
+
+        let transformation_between = {
+            'data': encodeURIComponent(utils.jsons(["transformation_add_AL", "rendered", MM_render, "abstract", SCD, "", "rendered", MM_render, "", render_trans_model]))
+        };
+
+        let transformation_data = {
+            'data': encodeURIComponent(utils.jsons(["transformation_add_AL", render_trans_code]))
+        };
+
+        let model_rendered = {
+            'data': encodeURIComponent(utils.jsons(["model_render", model_name, render_trans_model, "models/rendered_model"]))
+        };
+
+        //CS COMMANDS
+
+        let model_CS = null;
+
+        //TODO: only need to add models if not present
+        ModelVerseConnector.send_command(render_model_add).then(ModelVerseConnector.get_output)
+            .then(ModelVerseConnector.send_command(transformation_between)).then(ModelVerseConnector.get_output)
+            .then(ModelVerseConnector.send_command({})).then(ModelVerseConnector.get_output)
+            .then(ModelVerseConnector.send_command(transformation_data)).then(ModelVerseConnector.get_output)
+            .then(ModelVerseConnector.send_command(model_rendered)).then(ModelVerseConnector.get_output)
+
+            .then(function(data){
+                // console.log("Data before: ");
+                // console.log(data);
+                data = data.replace("Success: ", "");
+                // console.log("Data after:");
+                // console.log(data);
+                model_CS = eval(JSON.parse(data));
+            });
+
+    }
+
+    static get_render_mm() {
+        let mm = "include \"primitives.alh\"\n" +
+            "\n" +
+            "SimpleAttribute Natural {}\n" +
+            "SimpleAttribute String {}\n" +
+            "SimpleAttribute Boolean {}\n" +
+            "\n" +
+            "Class GraphicalElement {\n" +
+            "    x : Natural\n" +
+            "    y : Natural\n" +
+            "    layer : Natural\n" +
+            "}\n" +
+            "\n" +
+            "Class Group : GraphicalElement {\n" +
+            "    __asid : String\n" +
+            "    dirty : Boolean\n" +
+            "}\n" +
+            "\n" +
+            "Association ConnectingLine (Group, Group) {\n" +
+            "    offsetSourceX : Natural\n" +
+            "    offsetSourceY : Natural\n" +
+            "    offsetTargetX : Natural\n" +
+            "    offsetTargetY : Natural\n" +
+            "    lineWidth : Natural\n" +
+            "    lineColour : String\n" +
+            "    arrow : Boolean\n" +
+            "    __asid : String\n" +
+            "    dirty : Boolean\n" +
+            "    layer : Natural\n" +
+            "}\n" +
+            "\n" +
+            "Class LineElement : GraphicalElement {\n" +
+            "    lineWidth : Natural\n" +
+            "    lineColour : String\n" +
+            "}\n" +
+            "\n" +
+            "Class Text : LineElement {\n" +
+            "    text : String\n" +
+            "}\n" +
+            "\n" +
+            "Class Line : LineElement {\n" +
+            "    targetX : Natural\n" +
+            "    targetY : Natural\n" +
+            "    arrow : Boolean\n" +
+            "}\n" +
+            "\n" +
+            "Class Shape : LineElement {\n" +
+            "    fillColour : String\n" +
+            "    width : Natural\n" +
+            "    height : Natural\n" +
+            "}\n" +
+            "\n" +
+            "Class Figure : GraphicalElement {\n" +
+            "    width : Natural\n" +
+            "    height : Natural\n" +
+            "}\n" +
+            "\n" +
+            "Class SVG {\n" +
+            "    data : String\n" +
+            "}\n" +
+            "\n" +
+            "Class Rectangle : Shape {\n" +
+            "}\n" +
+            "\n" +
+            "Class Ellipse : Shape {\n" +
+            "}\n" +
+            "\n" +
+            "Association contains (Group, GraphicalElement) {}\n" +
+            "Association renders (Figure, SVG) {\n" +
+            "    source_lower_cardinality = 1\n" +
+            "    target_lower_cardinality = 1\n" +
+            "    target_upper_cardinality = 1\n" +
+            "}"
+        ;
+
+        return mm;
+    }
+
+    static get_render_trans() {
+        let trans = "include \"primitives.alh\"\n" +
+            "include \"modelling.alh\"\n" +
+            "include \"object_operations.alh\"\n" +
+            "include \"utils.alh\"\n" +
+            "\n" +
+            "Boolean function main(model : Element):\n" +
+            "\tElement elements\n" +
+            "\tString class\n" +
+            "\tElement attrs\n" +
+            "\tElement attr_keys\n" +
+            "\tString attr_key\n" +
+            "\tString group\n" +
+            "\tString elem\n" +
+            "\tInteger loc\n" +
+            "\tInteger text_loc\n" +
+            "\tElement related_groups\n" +
+            "\tloc = 10\n" +
+            "\n" +
+            "\tElement groups\n" +
+            "\tgroups = dict_create()\n" +
+            "\n" +
+            "\telements = allInstances(model, \"rendered/Group\")\n" +
+            "\twhile (set_len(elements) > 0):\n" +
+            "\t\tgroup = set_pop(elements)\n" +
+            "\t\tif (set_len(allIncomingAssociationInstances(model, group, \"TracabilityClass\")) == 0):\n" +
+            "\t\t\tElement to_remove\n" +
+            "\t\t\tString elem_to_remove\n" +
+            "\t\t\tto_remove = allAssociationDestinations(model, group, \"rendered/contains\")\n" +
+            "\t\t\twhile (set_len(to_remove) > 0):\n" +
+            "\t\t\t\telem_to_remove = set_pop(to_remove)\n" +
+            "\t\t\t\tif (read_type(model, elem_to_remove) == \"rendered/Group\"):\n" +
+            "\t\t\t\t\tset_add(to_remove, elem_to_remove)\n" +
+            "\t\t\t\telse:\n" +
+            "\t\t\t\t\tmodel_delete_element(model, elem_to_remove)\n" +
+            "\t\t\tmodel_delete_element(model, group)\n" +
+            "\n" +
+            "\telements = allInstances(model, \"abstract/Class\")\n" +
+            "\twhile (set_len(elements) > 0):\n" +
+            "\t\tclass = set_pop(elements)\n" +
+            "\t\t\n" +
+            "\t\tInteger x\n" +
+            "\t\tInteger y\n" +
+            "\t\tx = loc\n" +
+            "\t\ty = 10\n" +
+            "\n" +
+            "\t\t// Check if there is already an associated element\n" +
+            "\t\tif (set_len(allOutgoingAssociationInstances(model, class, \"TracabilityClass\")) > 0):\n" +
+            "\t\t\t// Yes, but is it still clean?\n" +
+            "\t\t\tBoolean dirty\n" +
+            "\t\t\tdirty = False\n" +
+            "\n" +
+            "\t\t\trelated_groups = allAssociationDestinations(model, class, \"TracabilityClass\")\n" +
+            "\t\t\twhile (set_len(related_groups) > 0):\n" +
+            "\t\t\t\tgroup = set_pop(related_groups)\n" +
+            "\t\t\t\tif (value_eq(read_attribute(model, group, \"dirty\"), True)):\n" +
+            "\t\t\t\t\t// No, so mark all as dirty\n" +
+            "\t\t\t\t\tdirty = True\n" +
+            "\t\t\t\t\tbreak!\n" +
+            "\t\t\t\telse:\n" +
+            "\t\t\t\t\t// Yes, so just ignore this!\n" +
+            "\t\t\t\t\tcontinue!\n" +
+            "\n" +
+            "\t\t\tif (bool_not(dirty)):\n" +
+            "\t\t\t\tdict_add(groups, class, group)\n" +
+            "\t\t\t\tcontinue!\n" +
+            "\t\t\telse:\n" +
+            "\t\t\t\trelated_groups = allAssociationDestinations(model, class, \"TracabilityClass\")\n" +
+            "\t\t\t\tElement to_remove\n" +
+            "\t\t\t\tString elem_to_remove\n" +
+            "\t\t\t\twhile (set_len(related_groups) > 0):\n" +
+            "\t\t\t\t\tgroup = set_pop(related_groups)\n" +
+            "\t\t\t\t\tto_remove = allAssociationDestinations(model, group, \"rendered/contains\")\n" +
+            "\t\t\t\t\tx = create_value(read_attribute(model, group, \"x\"))\n" +
+            "\t\t\t\t\ty = create_value(read_attribute(model, group, \"y\"))\n" +
+            "\t\t\t\t\twhile (set_len(to_remove) > 0):\n" +
+            "\t\t\t\t\t\telem_to_remove = set_pop(to_remove)\n" +
+            "\t\t\t\t\t\tif (read_type(model, elem_to_remove) == \"rendered/Group\"):\n" +
+            "\t\t\t\t\t\t\tset_add(to_remove, elem_to_remove)\n" +
+            "\t\t\t\t\t\telse:\n" +
+            "\t\t\t\t\t\t\tmodel_delete_element(model, elem_to_remove)\n" +
+            "\t\t\t\t\tmodel_delete_element(model, group)\n" +
+            "\n" +
+            "\t\tattr_keys = dict_keys(getAttributeList(model, class))\n" +
+            "\t\ttext_loc = 5\n" +
+            "\n" +
+            "\t\tgroup = instantiate_node(model, \"rendered/Group\", \"\")\n" +
+            "\t\tinstantiate_attribute(model, group, \"x\", x)\n" +
+            "\t\tinstantiate_attribute(model, group, \"y\", y)\n" +
+            "\t\tinstantiate_attribute(model, group, \"__asid\", list_read(string_split_nr(class, \"/\", 1), 1))\n" +
+            "\t\tinstantiate_attribute(model, group, \"layer\", 0)\n" +
+            "\t\tdict_add(groups, class, group)\n" +
+            "\t\tloc = loc + 200\n" +
+            "\n" +
+            "\t\telem = instantiate_node(model, \"rendered/Rectangle\", \"\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"x\", 0)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"y\", 0)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"height\", 40 + set_len(getInstantiatableAttributes(model, class, \"abstract/AttributeLink\")) * 20)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"width\", 150)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 2) \n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"fillColour\", \"white\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"layer\", 1)\n" +
+            "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
+            "\n" +
+            "\t\tString multiplicities\n" +
+            "\t\tString lower_card\n" +
+            "\t\tString upper_card\n" +
+            "\t\tif (element_eq(read_attribute(model, class, \"lower_cardinality\"), read_root())):\n" +
+            "\t\t\tlower_card = \"*\"\n" +
+            "\t\telse:\n" +
+            "\t\t\tlower_card = cast_value(read_attribute(model, class, \"lower_cardinality\"))\n" +
+            "\t\tif (element_eq(read_attribute(model, class, \"upper_cardinality\"), read_root())):\n" +
+            "\t\t\tupper_card = \"*\"\n" +
+            "\t\telse:\n" +
+            "\t\t\tupper_card = cast_value(read_attribute(model, class, \"upper_cardinality\"))\n" +
+            "\t\tmultiplicities = (((\"[\" + lower_card) + \"..\") + upper_card) + \"]\"\n" +
+            "\n" +
+            "\t\telem = instantiate_node(model, \"rendered/Text\", \"\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"x\", 5)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"y\", 3)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
+            "\t\tif (element_neq(read_attribute(model, class, \"name\"), read_root())):\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"text\", string_join(read_attribute(model, class, \"name\"), \"  \" + multiplicities))\n" +
+            "\t\telse:\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"text\", \"(unnamed) \" + multiplicities)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"layer\", 2)\n" +
+            "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
+            "\n" +
+            "\t\telem = instantiate_node(model, \"rendered/Line\", \"\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"x\", 0)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"y\", 20)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"targetX\", 150)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"targetY\", 20)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"arrow\", False)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"layer\", 2)\n" +
+            "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
+            "\n" +
+            "\t\tattrs = getInstantiatableAttributes(model, class, \"abstract/AttributeLink\")\n" +
+            "\t\tattr_keys = dict_keys(attrs)\n" +
+            "\t\twhile (dict_len(attr_keys) > 0):\n" +
+            "\t\t\tattr_key = set_pop(attr_keys)\n" +
+            "\t\t\telem = instantiate_node(model, \"rendered/Text\", \"\")\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"x\", 5)\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"y\", text_loc + 20)\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"text\", (attr_key + \" : \") + cast_string(list_read(string_split_nr(attrs[attr_key], \"/\", 1), 1)))\n" +
+            "\t\t\tinstantiate_attribute(model, elem, \"layer\", 2)\n" +
+            "\t\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
+            "\t\t\ttext_loc = text_loc + 15\n" +
+            "\n" +
+            "\t\tinstantiate_link(model, \"TracabilityClass\", \"\", class, group)\n" +
+            "\n" +
+            "\t// Flush all associations\n" +
+            "\telements = allInstances(model, \"rendered/ConnectingLine\")\n" +
+            "\twhile (set_len(elements) > 0):\n" +
+            "\t\tclass = set_pop(elements)\n" +
+            "\t\tmodel_delete_element(model, class)\n" +
+            "\n" +
+            "\t// Rerender associations\n" +
+            "\telements = allInstances(model, \"abstract/Association\")\n" +
+            "\twhile (set_len(elements) > 0):\n" +
+            "\t\tclass = set_pop(elements)\n" +
+            "\n" +
+            "\t\tattr_keys = dict_keys(getAttributeList(model, class))\n" +
+            "\n" +
+            "\t\telem = instantiate_link(model, \"rendered/ConnectingLine\", \"\", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])\n" +
+            "\t\tinstantiate_attribute(model, elem, \"offsetSourceX\", 75)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"offsetSourceY\", 30)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"offsetTargetX\", 75)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"offsetTargetY\", 30)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
+            "\t\tinstantiate_attribute(model, elem, \"arrow\", True)\n" +
+            "\t\tinstantiate_attribute(model, elem, \"__asid\", list_read(string_split_nr(class, \"/\", 1), 1))\n" +
+            "\t\tinstantiate_attribute(model, elem, \"layer\", 0)\n" +
+            "\t\tlog(\"Real ASID: \" + cast_value(class))\n" +
+            "\t\tlog(\"Found ASID \" + cast_value(list_read(string_split_nr(class, \"/\", 1), 1)))\n" +
+            "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
+            "\n" +
+            "\treturn True!";
+
+        return trans;
+    }
+
+}

+ 9 - 4
client/query_response.js

@@ -1,3 +1,8 @@
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
+
 var __nextASWSequenceNumber = '/asworker#1',
 	 __nextCSWSequenceNumber = '/csworker#1',	
 	 __pendingASWChangelogs = [],
@@ -20,7 +25,7 @@ function __clearObsoleteChangelogs(pendingChangelogs,sn)
 function __forceNextASWSequenceNumber(sn)
 {
 	__nextASWSequenceNumber = sn;
-	__clearObsoleteChangelogs(__pendingASWChangelogs,sn)
+	__clearObsoleteChangelogs(__pendingASWChangelogs,sn);
 }
 
 
@@ -28,7 +33,7 @@ function __forceNextASWSequenceNumber(sn)
 function __forceNextCSWSequenceNumber(sn)
 {
 	__nextCSWSequenceNumber = sn;
-	__clearObsoleteChangelogs(__pendingCSWChangelogs,sn)
+	__clearObsoleteChangelogs(__pendingCSWChangelogs,sn);
 }
 
 //Todo: Shred this into smaller functions
@@ -43,7 +48,7 @@ function __handleChangelog(changelog,seqNum,hitchhiker)
 {
 	console.debug(' ++ ('+seqNum+') ',changelog);
 
-	var isCSWChangelog 	 = seqNum.match(/csworker/)
+	var isCSWChangelog 	 = seqNum.match(/csworker/);
 		 nextSeqNum 	 	 = 
 			 (isCSWChangelog ? __nextCSWSequenceNumber : __nextASWSequenceNumber),
 		 pendingChangelogs = 
@@ -301,7 +306,7 @@ function __handleChangelog(changelog,seqNum,hitchhiker)
 						function(selection) {
 							__select(selection);
 						}, [__selection['items']], 5
-					)
+					);
 			}
 
 			/* react to loading of an IconDefinition (CS metamodel)

+ 16 - 26
client/selection_utils.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 var __selectionOverlay;
 var __highlighted = [];
@@ -169,7 +153,9 @@ function __flash(uri,color,timeout)
 	function turnOff()
 	{
 		try			{__icons[uri]['icon'].unhighlight();} 
-		catch(err)	{}
+		catch(err)	{
+			console.log(err);
+		}
 	}
 	window.setTimeout(turnOff,timeout || 500);
 }
@@ -215,13 +201,17 @@ function __highlight(uri,followCrossFormalismLinks,timeout,color)
 				 function() 
 				 {
 					 try			{__icons[uri]['icon'].unhighlight();}
-					 catch(err)	{}
+					 catch(err)	{
+					 	console.log(err);
+					 }
 
 					 if( followCrossFormalismLinks != undefined )
 					 	neighbors.nodes.forEach( 
 							function(n) {
 	  							try			{__icons[n]['icon'].unhighlight();}
-								catch(err)	{}
+								catch(err)	{
+	  								console.log(err);
+								}
 							} );
 					 if( timeout != undefined )
 						 window.clearTimeout(tid);
@@ -232,7 +222,7 @@ function __highlight(uri,followCrossFormalismLinks,timeout,color)
 
 function isHighlighted(uri)
 {
-	return __highlighted.length > 0 && __highlighted.filter(function(hl) {return uri == hl['uri']}).length == 1;
+	return __highlighted.length > 0 && __highlighted.filter(function(hl) {return uri == hl['uri'];}).length == 1;
 }
 
 
@@ -240,11 +230,11 @@ function __unhighlight(uri)
 {
 	if( __highlighted.length > 0 )
 	{
-		__highlighted.filter(function(hl) {return !uri || uri == hl['uri']}).forEach(function(hl) {hl.turnOff()})
+		__highlighted.filter(function(hl) {return !uri || uri == hl['uri'];}).forEach(function(hl) {hl.turnOff();});
 		if (!uri) {
 			__highlighted = [];
 		} else {
-			__highlighted = __highlighted.filter(function(hl) {return uri != hl['uri']})
+			__highlighted = __highlighted.filter(function(hl) {return uri != hl['uri'];});
 		}
 		
 	}

+ 251 - 248
client/styles.css

@@ -1,9 +1,9 @@
 
 div, span {
-	cursor:default;
+    cursor:default;
 }
 input, textarea {
-	cursor:text;
+    cursor:text;
 }
 textarea.string_input {
     resize: none;
@@ -12,440 +12,443 @@ textarea.string_input {
     resize: none;
 }
 select, option, button {
-	cursor:pointer;
+    cursor:pointer;
 }
 button {
-	font-family:"lucida grande",tahoma,verdana,arial,sans-serif;
-  	font-size:11px;
+    font-family:"lucida grande",tahoma,verdana,arial,sans-serif;
+    font-size:11px;
 }
 span.feedback {
     color: red;
 }
 
 html {
-	width: 100%;
-	height: 100%;
-	overflow: hidden;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
 }
 
 .inputDiv {
-	position: absolute;
-	left: 10px;
-	right: 20px;
-	padding: 5px 10px;
-	height: 32px;
+    position: absolute;
+    left: 10px;
+    right: 20px;
+    padding: 5px 10px;
+    height: 32px;
 }
 
 .contentDiv {
-	position: absolute;
-	left: 0;
-	right: 0;
-	top: 0;
-	bottom: 0;
-	box-shadow: inset 7px 7px 5px rgba(0, 0, 0, 0.1);
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    box-shadow: inset 7px 7px 5px rgba(0, 0, 0, 0.1);
 }
 
 .rootDiv {
-	position: absolute;
-	top: 0;
-	left: 0;
-	right: 0;
-	bottom: 0;
-	overflow: none;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: hidden;
 }
 
 .commandHistory {
-	width: 100%;
-	position: absolute;
-	display: none;
+    width: 100%;
+    position: absolute;
+    display: none;
 }
 
 .mainInput {
-	width: 100%;
-	border: 1px solid black;
+    width: 100%;
+    border: 1px solid black;
 }
 
 .mainInput:focus {
-	box-shadow: 0 0 5px rgb(100, 200, 255);
-	outline: none;
+    box-shadow: 0 0 5px rgb(100, 200, 255);
+    outline: none;
 }
 
 .mainInput.error:focus {
-	box-shadow: 0 0 5px red;
+    box-shadow: 0 0 5px red;
 }
 
 .canvas {
-	position:relative;
-	height:10000;
-	width:10000;
-	overflow:auto;
-	background: url(media/gridbg.png) top left repeat;
-	cursor:crosshair;
+    position:relative;
+    height:100%;
+    width:100%;
+    overflow:auto;
+    background: url(media/gridbg.png) top left repeat;
+    cursor:crosshair;
 }
 
 
 .canvas_selection {
-	stroke: chartreuse;
-	stroke-width:0.5;
-	stroke-dasharray:5,5;
-	fill: chartreuse;
-	fill-opacity:0.1;
-	cursor:move;
+    stroke: chartreuse;
+    stroke-width:0.5;
+    stroke-dasharray:5,5;
+    fill: chartreuse;
+    fill-opacity:0.1;
+    cursor:move;
 }
 
 
 .canvas_selection_overlay {
-	stroke-width:0.1;
-	fill:#0000ff;
-	fill-opacity:0.10;
-	cursor:crosshair;
+    stroke-width:0.1;
+    fill:#0000ff;
+    fill-opacity:0.10;
+    cursor:crosshair;
 }
 
 
 
 .clickable {
-	cursor:pointer;
+    cursor:pointer;
 }
 
 
 .code_style {
-	display:inline;
-	font-family:"andale mono",monospace;	
-  	font-size:11px;
-	border-radius: 5px;
+    display:inline;
+    font-family:"andale mono",monospace;
+    font-size:11px;
+    border-radius: 5px;
 }
 
 .container {
-	position: relative;
-	width: 100%;
-	height: 100%;
-	overflow: auto;
+    position: relative;
+    width: 100%;
+    height: 100%;
+    overflow: auto;
 }
 
 .ctrl_point_overlay {
-	stroke: purple;
-	stroke-width:1;
-	fill: coral;
-	fill-opacity:0.5;
-	cursor:move;
+    stroke: purple;
+    stroke-width:1;
+    fill: coral;
+    fill-opacity:0.5;
+    cursor:move;
 }
 
 .ctrl_point_center_overlay {
-	stroke: lime;
-	stroke-width:1;
-	fill: khaki;
-	fill-opacity:0.5;
-	cursor:move;
+    stroke: lime;
+    stroke-width:1;
+    fill: khaki;
+    fill-opacity:0.5;
+    cursor:move;
 }
 
 
 .dark_bg {
-	background-color: black;
-	opacity:0.85;
-	z-index: 100;
-	height: 100%;
-	width: 100%;
-	background-repeat:repeat;
-	position:fixed;
-	top: 0px;
-	left: 0px;
-	cursor:not-allowed;
+    opacity:0.85;
+    z-index: 100;
+    height: 100%;
+    width: 100%;
+    background: black repeat;
+    position:fixed;
+    top: 0;
+    left: 0;
+    cursor:not-allowed;
 }
 
 
 .default_style {
-	display:inline;
-	font-family:"lucida grande",tahoma,verdana,arial,sans-serif;
-  	font-size:11px;
-	border-radius: 5px;
-	padding: 0;
-	margin: 0;
+    display:inline;
+    font-family:"lucida grande",tahoma,verdana,arial,sans-serif;
+    font-size:11px;
+    border-radius: 5px;
+    padding: 0;
+    margin: 0;
 }
 
+.center_point {
+    top: 50%;
+    left: 50%;
+    position: absolute;
+}
 
 .dialog {
-	position:absolute;
-	padding:0px 20px 20px;
-	max-height:100%;
-	max-width:100%;
-	z-index: 30;			  
-	background-color: whitesmoke;
-	border:2px solid crimson;
-	border-radius: 5px;
-	overflow-x:hidden;
-	overflow-y:auto;
+    position:relative;
+    min-height: auto;
+    max-height: 80%;
+    width: 50%;
+    padding:0 20px 20px;
+    z-index: 30;
+    background-color: whitesmoke;
+    border:2px solid crimson;
+    border-radius: 5px;
+    overflow-x:hidden;
+    overflow-y:auto;
 }
 
 .dialog_title {
-	padding: 10px;
-	font-style: italic;
-	font-weight: bold;
+    padding: 10px;
+    font-style: italic;
+    font-weight: bold;
 }
 
 
 
 .dim_bg {
-	background-color: lavender;
-	opacity:0.30;
-	z-index: 20;
-	height: 100%;
-	width: 100%;
-	background-repeat:repeat;
-	position:fixed;
-	top: 0px;
-	left: 0px;
-	cursor:not-allowed;
+    opacity:0.30;
+    z-index: 20;
+    height: 100%;
+    width: 100%;
+    background: lavender repeat;
+    position:fixed;
+    top: 0;
+    left: 0;
+    cursor:not-allowed;
 }
 
 
 .disabled_link {
-	pointer-events:none;
-	color:grey;
-	cursor:not-allowed;
+    pointer-events:none;
+    color:grey;
+    cursor:not-allowed;
 }
 
 .dock {
-	position:absolute; 
-	top:5px;
-	left:5px;
-	z-index:10;
-	overflow: auto;
+    position:absolute;
+    top:5px;
+    left:5px;
+    z-index:10;
+    overflow: auto;
+    width: calc(100% - 20px);
+    background-color: rgba(245, 245, 245, 0.7)
 }
 .disabled_dock {
-	opacity:0.5;
-	z-index:-1;
+    opacity:0.5;
+    z-index:-1;
 }
 
 
 .droppable {
-	background-color: chartreuse !important ;
-	border: 1px dashed black !important;
-	opacity: 0.5 !important;
+    background-color: chartreuse !important ;
+    border: 1px dashed black !important;
+    opacity: 0.5 !important;
 }
 
 .dropzone {
-	width:200px;
-	height:50px;
-	background-color: lightblue;
-	background-repeat: no-repeat;
-	background-position: center center;
-	padding:10px;
-	border: 1px solid black;
-	border-radius:10px;
-	opacity:0.75
+    width:200px;
+    height:50px;
+    background: lightblue no-repeat center center;
+    padding:10px;
+    border: 1px solid black;
+    border-radius:10px;
+    opacity:0.75
 }
 
 
 .empty_icon {
-	opacity:0;
-	cursor:pointer;
+    opacity:0;
+    cursor:pointer;
 }
 
 
 .enabled_link {
-	color:indianred;
-	cursor:pointer;
+    color:indianred;
+    cursor:pointer;
 }
 
 
 .error{
-	color:crimson;
+    color:crimson;
 }
 
 
 .fileb_icon {
-	padding:5px;	
-	opacity:0.75;
-	display: inline-block;
-	text-align: left;
+    padding:5px;
+    opacity:0.75;
+    display: inline-block;
+    text-align: left;
 }
 .fileb_icon:hover {
-	opacity:1;
+    opacity:1;
 }
 .fileb_icon_selected {
-	padding:5px;	
-	opacity:1;
-	display: inline-block;
-	text-align: left;
-	border-radius:8px;	
-	background:lightBlue;
+    padding:5px;
+    opacity:1;
+    display: inline-block;
+    text-align: left;
+    border-radius:8px;
+    background:lightBlue;
 }
 
 
 .fileb_pane {
-	padding:3px;
-	border:1px solid black;
-	border-radius: 5px;
-	background-color: white;
-	height: 300px;
-	overflow:auto;			
+    padding:3px;
+    border:1px solid black;
+    border-radius: 5px;
+    background-color: white;
+    height: 300px;
+    overflow:auto;
 }
 
 
 .footer {
-	position:absolute; 
-	bottom:25px;
-	right:32px;
+    position:absolute;
+    bottom:25px;
+    right:32px;
 }
 
 
 .geometry_ctrls {
-	position:fixed; 
-	padding:2px;
-	background-color: whitesmoke;				
-	border:2px solid crimson;
-	border-radius: 5px;
+    position:fixed;
+    padding:2px;
+    background-color: whitesmoke;
+    border:2px solid crimson;
+    border-radius: 5px;
 }
 
 .geometry_ctrl {
-	opacity:0.75;
-	cursor:pointer;
+    opacity:0.75;
+    cursor:pointer;
 }
 .geometry_ctrl:hover {
-	opacity:1;
+    opacity:1;
 }
 
 
 .header {
-	position:absolute; 
-	top:5px;
-	right:32px;
-	z-index:20;
+    position:absolute;
+    top:5px;
+    right:32px;
+    z-index:20;
 }
 
 
 .login {
-	position:relative;
-	margin: 10% auto;
-	width: 300px;
-	opacity: 1;
-	padding: 10px;
-	z-index: 101;			  
-	background-color: snow;
-	border: 5px solid dodgerblue;
-	border-radius: 10px;
-	overflow-x:hidden;
-	overflow-y:auto;
+    position:relative;
+    margin: 10% auto;
+    width: 300px;
+    opacity: 1;
+    padding: 10px;
+    z-index: 101;
+    background-color: snow;
+    border: 5px solid dodgerblue;
+    border-radius: 10px;
+    overflow-x:hidden;
+    overflow-y:auto;
 }
 
 
 .progress_bar {
-	display: inline-block;
-	border: 1px solid black;
-	border-radius: 10px;
-	height: 10px;
-	width: 100px;
-	margin: 0px 5px;
+    display: inline-block;
+    border: 1px solid black;
+    border-radius: 10px;
+    height: 10px;
+    width: 100px;
+    margin: 0 5px;
 }
 
 .progress_fill {
-	border-radius: 10px;
-	background-color: crimson;
-	opacity: 0.75;
-	height: 10px;
-	width: 0%;
+    border-radius: 10px;
+    background-color: crimson;
+    opacity: 0.75;
+    height: 10px;
+    width: 0;
 }
 
 
 .toolbar_alt {
-	line-height:32px;	
+    line-height:32px;
 }
 
 .toolbar {
-	padding-top:4px;
-	margin:2px;
-	background-color: whitesmoke ;
-	border-radius: 5px;
-	float:left;
+    padding-top:4px;
+    margin:2px;
+    background-color: whitesmoke ;
+    border-radius: 5px;
+    float:left;
 }
 .toolbar_bm {
-	border:2px solid lightblue;
+    border:2px solid lightblue;
 }
 .toolbar_mm {
-	border:2px solid khaki;
+    border:2px solid khaki;
 }
 
 .toolbar_space {
-	padding-left:4px;
+    padding-left:4px;
 }
 
 .toolbar_button {
-	display: inline-block;
-	opacity:0.75;
-	cursor:pointer;
+    display: inline-block;
+    opacity:0.75;
+    cursor:pointer;
 }
 .toolbar_button:hover {
-	opacity:1;
+    opacity:1;
 }
 .toolbar_button:active {
-	opacity:0.5;
+    opacity:0.5;
 }
 
 
 
 
 .transformation_preview {
-	stroke: crimson;
-	stroke-width:0.5;
-	stroke-dasharray:5,5;
-	fill: lavender;
-	fill-opacity:0.1;
-	cursor:not-allowed;
+    stroke: crimson;
+    stroke-width:0.5;
+    stroke-dasharray:5,5;
+    fill: lavender;
+    fill-opacity:0.1;
+    cursor:not-allowed;
 }
 
 .unselectable {
-	-webkit-user-select:none;
-	-moz-user-select: none;
+    -webkit-user-select:none;
+    -moz-user-select: none;
 }
 
- 
+
 form, p, span {
-	margin:0 auto;
-	padding:0; }
- 
+    margin:0 auto;
+    padding:0; }
+
 input { font:12px arial; }
- 
+
 a {
-	color:#0000FF;
-	text-decoration:none; }
- 
-	a:hover { text-decoration:underline; }
- 
+    color:#0000FF;
+    text-decoration:none; }
+
+a:hover { text-decoration:underline; }
+
 #chat, #loginform {
-  display:none;
-	margin:0 auto;
-	padding-bottom:25px;
-	background:#EBF4FB;
-	width:504px;
-	border:1px solid #ACD8F0; }
- 
+    display:none;
+    margin:0 auto;
+    padding-bottom:25px;
+    background:#EBF4FB;
+    width:504px;
+    border:1px solid #ACD8F0; }
+
 #loginform { padding-top:18px; }
- 
-	#loginform p { margin: 5px; }
- 
+
+#loginform p { margin: 5px; }
+
 #chatbox {
-	text-align:left;
-	margin:0 auto;
-	margin-bottom:25px;
-	padding:10px;
-	background:#fff;
-	height:270px;
-	width:430px;
-	border:1px solid #ACD8F0;
-	overflow:auto; }
- 
+    text-align:left;
+    margin: 0 auto 25px;
+    padding:10px;
+    background:#fff;
+    height:270px;
+    width:430px;
+    border:1px solid #ACD8F0;
+    overflow:auto; }
+
 #usermsg {
-	width:395px;
-	border:1px solid #ACD8F0; }
- 
+    width:395px;
+    border:1px solid #ACD8F0; }
+
 #submit { width: 60px; }
- 
+
 .error { color: #ff0000; }
 
-#menu { padding:12.5px 25px 12.5px 25px; }
- 
+#menu { padding:13px 25px 13px 25px; }
+
 .welcome { float:left; }
- 
+
 .logout { float:right; }
- 
+
 .msgln { margin:0 0 2px 0; }

+ 5 - 0
client/svg_utils.js

@@ -1,3 +1,8 @@
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
+
 /* enables/disables scrolling the canvas */
 function __setCanvasScrolling(on)
 {

+ 4 - 20
client/user_management.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /**
  * Creates the User Management object

+ 19 - 22
client/window_event.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /**
  * The WindowEventHelper function is a helper function that holds all of 
@@ -41,8 +25,10 @@ WindowEventHelper = function(){
 				
 				// Are we actually in the table?
 				if( table != null && table.rows != undefined){
-					for(var i=0, row; row = table.rows[i]; i++){
-						
+					for(var i=0; i < table.length; i++){
+
+						let row = table.rows[i];
+
 						// Is the current row the parent row of the active element?
 						if( row == activeElement.parentNode.parentNode ){
 							if( currentKeys[ KEY_SHIFT ] == 1){
@@ -225,6 +211,16 @@ WindowEventHelper = function(){
 	this.isKeyPressed = function( keyCode ){
 		return currentKeys[ keyCode ] == 1;
 	};
+
+	/**
+	 * Called when the window gains focus
+	 * Resets all keys
+	 */
+	this.onfocus = function(){
+		for (let keyCode in currentKeys){
+			currentKeys[ keyCode ] = 0;
+		}
+	};
 	
 	/**
 	 * Initializes the default window behavior
@@ -237,6 +233,7 @@ WindowEventHelper = function(){
 		window.onmousemove = this.onMouseMove;
 		window.onkeydown = this.onKeyDown;
 		window.onkeyup = this.onKeyUp;
+		window.onfocus = this.onfocus;
 	};
 	
 	return this;

+ 95 - 268
client/window_management.js

@@ -1,23 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-Modified by Conner Hansen (chansen@crimson.ua.edu)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 ///////////////////////////////////////////////////////////////////////////////
 // DEPRECATED FUNCTIONS
@@ -74,6 +58,55 @@ WindowManagement = function(){
 		__setCanvasScrolling(false);
 	};
 	
+	this.showAboutDialog = function () {
+
+        let create_about = function (status, text) {
+
+            let version = null;
+            let time_at = null;
+
+            if (utils.isHttpSuccessCode(status)) {
+                let resp = JSON.parse(text);
+                version = resp['tag_name'];
+
+                time_at = resp['published_at'];
+                time_at = time_at.split("T")[0];
+            }
+
+            let title = "About AToMPM";
+            let elements = [];
+
+            let website = "<a href= '" + __WEBPAGE__ + "' target='_blank'>Website and Tutorials</a>";
+            elements.push(GUIUtils.getTextSpan(website));
+
+            let doc_website = "<a href= '" + __DOC_WEBPAGE__ + "' target='_blank'>Documentation</a>";
+            elements.push(GUIUtils.getTextSpan(doc_website));
+
+            let curr_version_str = "Current Version: " + __VERSION__;
+            elements.push(GUIUtils.getTextSpan(curr_version_str));
+
+            elements.push(GUIUtils.getTextSpan("\n"));
+
+            if (version != null) {
+                let new_version_str = "Newest Version: " + version;
+                elements.push(GUIUtils.getTextSpan(new_version_str));
+
+                let time_at_str = "Released on: " + time_at;
+                elements.push(GUIUtils.getTextSpan(time_at_str));
+
+            }
+
+            GUIUtils.setupAndShowDialog(
+                elements,
+                null,
+                __ONE_BUTTON,
+                title,
+                null);
+        };
+
+        HttpUtils.httpReq("GET", "https://api.github.com/repos/AToMPM/atompm/releases/latest", null, create_about);
+	};
+	
 	//Todo: Shred this function into smaller functions, as this should
 	// really just amount to a switch statement
 	//TBI: complete comments about each dialog (copy from user's manual)
@@ -115,7 +148,7 @@ WindowManagement = function(){
 										return _.length;
 									}),
 						 
-								fileb 			= GUIUtils.getFileBrowser(fnames,true),
+								fileb 			= FileBrowser.getFileBrowser(fnames,true),
 								feedbackarea	= $('<div>'),
 								feedback		 = GUIUtils.getTextSpan(''),
 								progressbar	 = $('<div>'),
@@ -408,236 +441,9 @@ WindowManagement = function(){
 			error(args,true);
 	
 		else if( type == _FILE_BROWSER )
-			/* args: extensions,multipleChoice,manualInput,title,startDir */
+			/* args: extensions,manualInput,title,startDir */
 		{
-			HttpUtils.httpReq(
-				'GET',
-				HttpUtils.url('/filelist',__NO_WID),
-				undefined,
-				function(statusCode,resp)
-				{
-                    args['extensions'].push('/');
-					var fnames = __localizeFilenames(
-							__filterFilenamesByExtension(
-								resp.split('\n'),
-								args['extensions'] || ['.*'])
-							).sort(),
-							maxFnameLength = 
-								utils.max(fnames,function(_) {return _.length;}),
-                             folder_buttons = $('<div>'),
-                             new_folder_b = $('<button>'),
-                             rename_folder_b = $('<button>'),
-                             delete_folder_b = $('<button>'),
-                             move_folder_b = $('<button>'),
-                             file_buttons = $('<div>'),
-                             rename_file_b = $('<button>'),
-                             delete_file_b = $('<button>'),
-                             move_file_b = $('<button>'),                
-                             feedbackarea = $('<div>'),
-                             feedback = GUIUtils.getTextSpan('',"feedback"),
-							 fileb = 
-								 GUIUtils.getFileBrowser(fnames,false,args['manualInput'],__getRecentDir(args['startDir']));
-                    
-                    new_folder_b.html('new folder')
-                    .click(function(ev) {
-                        var folder_name = prompt("please fill in a name for the folder");
-                        if (folder_name != null) {
-                            folder_name = folder_name.replace(/^\s+|\s+$/g, ''); // trim
-                            if (!folder_name.match(/^[a-zA-Z0-9_\s]+$/i)) {
-                                feedback.html("invalid folder name: " + folder_name);
-                            } else {
-                                console.log("/" + window.localStorage.getItem('user') + fileb['getcurrfolder']() + folder_name + '.folder');
-                                DataUtils.createFolder("/" + window.localStorage.getItem('user') + fileb['getcurrfolder']() + folder_name + '.folder', function(statusCode, resp) {
-                                    if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                        feedback.html(resp)
-                                    } else {
-                                        feedback.html('created ' + folder_name);
-                                        fnames.push(fileb['getcurrfolder']() + folder_name + "/")
-                                        fileb['refresh'](fnames);
-                                    }
-                                });
-                            }
-                        }
-                    });
-                    folder_buttons.append(new_folder_b);
-                    
-                    rename_folder_b.html('rename folder')
-                    .click(function(ev) {
-                        var value = fileb['getcurrfolder']();
-                        var folder_name = prompt("please fill in a new name for folder " + value);
-                        if (folder_name != null) {
-                            folder_name = folder_name.replace(/^\s+|\s+$/g, ''); // trim
-                            if (!folder_name.match(/^[a-zA-Z0-9_\s]+$/i)) {
-                                feedback.html("invalid folder name: " + folder_name);
-                            } else {
-                                DataUtils.renameInCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", folder_name, function(statusCode,resp)
-                                    {
-                                        if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                            feedback.html(resp);
-                                        } else {
-                                            var matches = value.match(/^\/(.*\/)?(.*)\/$/),
-                                                newvalue = "/" + (matches[1] || "") + folder_name + "/";
-                                            for (var idx in fnames) {
-                                                fnames[idx] = fnames[idx].replace(new RegExp("^("+value+")(.*)"), newvalue+"$2"); 
-                                            }
-                                            fileb['refresh'](fnames, newvalue);
-                                            fileb['clearselection']();
-                                            feedback.html('renamed ' + value + ' to ' + newvalue);
-                                        }
-                                    });
-                            }
-                        }
-                    });
-                    folder_buttons.append(rename_folder_b);
-                    
-                    delete_folder_b.html('delete folder')
-                    .click(function(ev) {
-                        var value = fileb['getcurrfolder']();
-                        if (confirm("are you sure you want to delete " + value + "?")) {
-                            DataUtils.deleteFromCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", function(statusCode,resp)
-                                {
-                                    if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                        feedback.html(resp);
-                                    } else {
-                                        var matches = value.match(/^\/(.*\/)?(.*)\/$/),
-                                            newvalue = "/_Trash_" + value;
-                                        for (var idx in fnames) {
-                                            fnames[idx] = fnames[idx].replace(new RegExp("^("+value+")(.*)"), newvalue+"$2");
-                                        }
-                                        fileb['refresh'](fnames);
-                                        fileb['clearselection']();
-                                        feedback.html('deleted ' + value);
-                                    }
-                                });
-                        }
-                    });
-                    folder_buttons.append(delete_folder_b);
-                    
-                    move_folder_b.html('move folder')
-                    .click(function(ev) {
-                        var value = fileb['getcurrfolder']();
-                        var folder_loc = prompt("please fill in a new parent folder for folder " + value);
-                        if (folder_loc != null) {
-                            folder_loc = folder_loc.replace(/^\s+|\s+$/g, ''); // trim
-                            if (!folder_loc.match(/^\/([a-zA-Z0-9_\s]+\/)*$/i)) {
-                                feedback.html("invalid parent location: " + folder_loc);
-                            } else {
-                                DataUtils.moveInCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", folder_loc, function(statusCode,resp)
-                                    {
-                                        if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                            feedback.html(resp);
-                                        } else {
-                                            var matches = value.match(/^\/(.*\/)?(.*)\/$/),
-                                                newvalue = folder_loc + matches[2] + "/";
-                                            for (var idx in fnames) {
-                                                fnames[idx] = fnames[idx].replace(new RegExp("^("+value+")(.*)"), newvalue+"$2"); 
-                                            }
-                                            fileb['refresh'](fnames, newvalue);
-                                            fileb['clearselection']();
-                                            feedback.html('moved ' + value + ' to ' + folder_loc);
-                                        }
-                                    });
-                            }
-                        }
-                    });
-                    folder_buttons.append(move_folder_b);
-                    
-                    rename_file_b.html('rename file')
-                    .click(function(ev) {
-                        var value = fileb['getselection']();
-                        var file_name = prompt("please fill in a new name for file " + value);
-                        if (file_name != null) {
-                            file_name = file_name.replace(/^\s+|\s+$/g, ''); // trim
-                            if (!file_name.match(/^[a-zA-Z0-9_\s\.]+$/i)) {
-                                feedback.html("invalid file name: " + file_name);
-                            } else {
-                                DataUtils.renameInCloud("/" + window.localStorage.getItem('user') + value + ".file", file_name, function(statusCode,resp)
-                                    {
-                                        if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                            feedback.html(resp);
-                                        } else {
-                                            var matches = value.match(/^\/(.*\/)?(.*)$/),
-                                                newvalue = "/" + (matches[1] || "") + file_name;
-                                            var idx = fnames.indexOf(value);
-                                            if (idx >= 0) {
-                                                fnames[idx] = newvalue;
-                                            }
-                                            fileb['refresh'](fnames);
-                                            fileb['clearselection']();
-                                            feedback.html('renamed ' + value + ' to ' + newvalue);
-                                        }
-                                    });
-                            }
-                        }
-                    });
-                    file_buttons.append(rename_file_b);
-                    
-                    delete_file_b.html('delete file')
-                    .click(function(ev) {
-                        var value = fileb['getselection']();
-                        if (confirm("are you sure you want to delete " + value + "?")) {
-                            DataUtils.deleteFromCloud("/" + window.localStorage.getItem('user') + value + ".file", function(statusCode,resp)
-                                {
-                                    if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                        feedback.html(resp);
-                                    } else {
-                                        feedback.html('deleted ' + value);
-                                        var idx = fnames.indexOf(value);
-                                        if (idx >= 0) {
-                                            fnames.splice(idx, 1);
-                                        }
-                                        fileb['refresh'](fnames);
-                                        fileb['clearselection']();
-                                    }
-                                });
-                        }
-                    });
-                    file_buttons.append(delete_file_b);                    
-                    
-                    move_file_b.html('move file')
-                    .click(function(ev) {
-                        var value = fileb['getselection']();
-                        var folder_loc = prompt("please fill in a new parent folder for file " + value);
-                        if (folder_loc != null) {
-                            folder_loc = folder_loc.replace(/^\s+|\s+$/g, ''); // trim
-                            if (!folder_loc.match(/^\/([a-zA-Z0-9_\s]+\/)*$/i)) {
-                                feedback.html("invalid parent location: " + folder_loc);
-                            } else {
-                                DataUtils.moveInCloud("/" + window.localStorage.getItem('user') + value + ".file", folder_loc, function(statusCode,resp)
-                                    {
-                                        if( ! utils.isHttpSuccessCode(statusCode) ) {
-                                            feedback.html(resp);
-                                        } else {
-                                            var matches = value.match(/^\/(.*\/)?(.*)$/),
-                                                newvalue = folder_loc + matches[2];
-                                            feedback.html('moved ' + value + ' to ' + folder_loc);
-                                            var idx = fnames.indexOf(value);
-                                            if (idx >= 0) {
-                                                fnames[idx] = newvalue;
-                                            }
-                                            fileb['refresh'](fnames);
-                                            fileb['clearselection']();
-                                        }
-                                    });
-                            }
-                        }
-                    });
-                    file_buttons.append(move_file_b);
-	
-					GUIUtils.setupAndShowDialog(
-						[fileb['filebrowser'],folder_buttons,file_buttons,feedback],
-						function() 
-						{
-							var value = [fileb['getselection']()];
-							if (value.length > 0 && value[0] != "" && args['startDir']) {
-								__setRecentDir(args['startDir'],value[0].substring(0, value[0].lastIndexOf('/') + 1));
-							}
-							return value;
-						},
-						__TWO_BUTTONS,
-						args['title'],
-						callback);
-				});	
+			FileBrowser.buildFileBrowser(args['extensions'], args['manualInput'], args['title'], args['startDir'], callback);
 		}
 	
 		else if( type == _LEGAL_CONNECTIONS )
@@ -703,11 +509,20 @@ WindowManagement = function(){
 				if( args['ignoreKey'] != undefined && 
 					 args['ignoreKey'](attr,args['data'][attr]['value']) )
 					continue;
-	
-				var tr = $('<tr>');
-				var ii = GUIUtils.getInputField(
-						args['data'][attr]['type'],
-						args['data'][attr]['value']);
+
+                var tr = $('<tr>');
+                tr.attr("id", "tr_" + attr);
+                var ii = null;
+                try {
+                    ii = GUIUtils.getInputField(
+                        args['data'][attr]['type'],
+                        args['data'][attr]['value']);
+                } catch (err) {
+                    console.log(args['data'][attr]);
+                    WindowManagement.openDialog(
+                        _ERROR,
+                        'unexpected error in editing mode ::\n ' + err + '\n' + utils.jsons(args['data'][attr]));
+                }
 //				var tr = table.append( $('<tr>') ),
 //					 ii = GUIUtils.getInputField(
 //							 args['data'][attr]['type'],
@@ -788,8 +603,13 @@ WindowManagement = function(){
 			 completion */
 	this.spawnClient = function (fname,callbackURL)
 	{
-		var c 		= window.open(window.location.href, '_blank'),
-		onspawn = 
+		let c 		= window.open(window.location.href, '_blank');
+
+		if (c == undefined){
+			WindowManagement.openDialog(_ERROR, "Failed to open new window. Are pop-ups blocked?");
+			return;
+		}
+			let onspawn =
 			 function()
 			 {
 				 if( (fname || callbackURL)	 &&
@@ -823,8 +643,15 @@ WindowManagement = function(){
 		the instance*/	
 	this.spawnClientOption = function (fname,tbname,option,trafo,msg)
 	{
-		var c 		= window.open(window.location.href, '_blank'),
-		onspawn = 
+        let c = window.open(window.location.href, '_blank');
+
+        if (c == undefined) {
+            WindowManagement.openDialog(_ERROR, "Failed to open new window. Are pop-ups blocked?\n" +
+                "Please reload the workflow model after allowing pop-ups.");
+            return;
+        }
+
+		let onspawn =
 			 function()
 			 {
 				if( (fname|| tbname)	 &&
@@ -839,15 +666,15 @@ WindowManagement = function(){
 				 c.__trafo = trafo;
 				 c.__msg = msg;
 				if (trafo == undefined){
-					trafo = option
+					trafo = option;
 				}
-				if( tbname ){
-						toolbars = tbname.split(",");
-						for ( var n in toolbars){
-							c._loadToolbar(toolbars[n]);
-						}						
-				}				
-				if( fname ){
+                 if (tbname) {
+                     let toolbars = tbname.split(",");
+                     for (let toolbar of toolbars) {
+                         c._loadToolbar(toolbar);
+                     }
+                 }
+                 if( fname ){
 						c.__saveas = fname;
 						if( option.length > 2 ){
 							c._loadModel(fname);
@@ -978,7 +805,7 @@ WindowManagement = function(){
 		if(ev!=null && ev.keyCode==13) {
 			$('#div_dialog_' + (__dialog_stack.length-1).toString() + " .okbutton").click();
 		}
-        __dialog_stack.pop()
+        __dialog_stack.pop();
 		var dialog = $('#div_dialog_'+__dialog_stack.length);
         dialog.remove();
 		if (!__dialog_stack.length) {

+ 309 - 260
csworker.js

@@ -1,22 +1,7 @@
-/*******************************************************************************
-AToMPM - A Tool for Multi-Paradigm Modelling
-
-Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
-
-This file is part of AToMPM.
-
-AToMPM is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later 
-version.
-
-AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY 
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
-*******************************************************************************/
+/* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
+*  Copyright 2011 by the AToMPM team and licensed under the LGPL
+*  See COPYING.lesser and README.md in the root of this project for full details
+*/
 
 /* NOTES: 
 	it is assumed that csworker _mmmk cud operations NEVER FAIL... 
@@ -192,7 +177,38 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 			SYSOUT message announcing the launching of the rule... a sensible and 
 		  	nice solution would be not to remember such changelogs in
 			__handledSeqNums */
-{
+let {
+    __id_to_uri,
+    __ids2uris, __nextSequenceNumber,
+    __postBadReqErrorMsg,
+    __postForbiddenErrorMsg,
+    __wtype,
+    GET__current_state
+} = require("./__worker");
+
+const {
+	__batchCheckpoint,
+    __errorContinuable,
+    __httpReq,
+	__wHttpReq,
+    __postInternalErrorMsg, __postMessage,
+    __sequenceNumber,
+    __successContinuable,
+	__uri_to_id
+} = require("./__worker");
+
+const _do = require("./___do");
+const _utils = require('./utils');
+const _mmmk = require("./mmmk");
+const _fs = _do.convert(require('fs'), ['readFile', 'writeFile', 'readdir']);
+const _path = require('path');
+const _fspp	= _do.convert(require('./___fs++'), ['mkdirs']);
+const _svg = require('./libsvg').SVG;
+const _mt = require('./libmt');
+
+const _siocl = require('socket.io-client');
+
+module.exports = {
 	'__REGEN_ICON_RETRY_DELAY_MS':200,
 	'__asmm2csmm':{},
 	'__asid2csid':{},
@@ -227,8 +243,8 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 	'__applyASWChanges' :
 		function(changelog,aswSequenceNumber,hitchhiker)
 		{
-			console.error('w#'+__wid+' ++ ('+aswSequenceNumber+') '+
-							_utils.jsons(changelog));
+			//console.error('w#'+__wid+' ++ ('+aswSequenceNumber+') '+
+			//				_utils.jsons(changelog));
 
 
 			if( _utils.sn2int(aswSequenceNumber) > 
@@ -302,76 +318,73 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 										[{'op':step['op'],'id1':csid1,'id2':csid2}]);
 									return __successContinuable();
 								});
-		
-						/* create appropriate CS instance and associate it with new AS
-							instance (remember the association in __asid2csid to 
-							optimize future operations) */
-						else if( step['op'] == 'MKNODE' )
-							actions.push(
-								function()
-								{
-									manageHitchhiker(step['id']);
-	
-									var asid 	  	= step['id'],
-	  									 node   		= _utils.jsonp(step['node']),
-										 isLink		= ('segments' in hitchhiker),
-										 fullastype	= node['$type'],
-										 fullcstype = self.__astype_to_cstype(
-											 							fullastype,
-																		isLink),				
-										 asuri 		= fullastype+'/'+asid+'.instance';
-										 attrs		= {'$asuri':asuri};
-
-									if( 'pos' in hitchhiker )
-										attrs['position'] = hitchhiker['pos'];
-									else if( 'neighborhood' in hitchhiker )
-									{
-										var nc = self.__nodesCenter(
-															hitchhiker['neighborhood']);
-										attrs['position'] = 
-											[(nc[0] || 200), (nc[1] || 200)];
-									}
-									else if( 'clone' in hitchhiker )
-										attrs = _utils.mergeDicts(
-														[attrs,hitchhiker['clone']]);
-									else
-										attrs['position'] = [200,200];
-
-									var res  = _mmmk.create(fullcstype,attrs),
-										 csid	= res['id'];
-				 					self.__asid2csid[asid] = csid;
-				 					cschangelogs.push(res['changelog']);
-	
-									if( isLink )
-									{
-										var s 		 = {},
-											 src		 = 
-												 hitchhiker['src'] ||
-												 self.__asuri_to_csuri(hitchhiker['asSrc']),
-											 dest		 = 
-												 hitchhiker['dest'] ||
-												 self.__asuri_to_csuri(hitchhiker['asDest']),
-											 segments = 
-												 hitchhiker['segments'] ||
-												 self.__defaultSegments(src,dest);
-										s[ src+'--'+__id_to_uri(csid) ]  = segments[0];
-										s[ __id_to_uri(csid)+'--'+dest ] = segments[1];
-	
-										cschangelogs.push( 
-											_mmmk.update(
-												csid,
-												{'$segments':s})['changelog'],
-										  	self.__positionLinkDecorators(csid));
-									}
-	
-									return self.__regenIcon(csid);
-								},
-								function(riChangelog)
-								{
-									cschangelogs.push(riChangelog);
-									return __successContinuable();
-								});
-	
+
+                        /* create appropriate CS instance and associate it with new AS
+                            instance (remember the association in __asid2csid to
+                            optimize future operations) */
+                        else if (step['op'] == 'MKNODE') {
+                            actions.push(
+                                function () {
+                                    manageHitchhiker(step['id']);
+
+                                    let asid = step['id'],
+                                        node = _utils.jsonp(step['node']),
+                                        isLink = ('segments' in hitchhiker),
+                                        fullastype = node['$type'],
+                                        fullcstype = self.__astype_to_cstype(
+                                            fullastype,
+                                            isLink),
+                                        asuri = fullastype + '/' + asid + '.instance',
+                                        attrs = {'$asuri': asuri};
+
+                                    if ('pos' in hitchhiker)
+                                        attrs['position'] = hitchhiker['pos'];
+                                    else if ('neighborhood' in hitchhiker) {
+                                        let nc = self.__nodesCenter(
+                                            hitchhiker['neighborhood']);
+                                        attrs['position'] =
+                                            [(nc[0] || 200), (nc[1] || 200)];
+                                    }
+                                    else if ('clone' in hitchhiker)
+                                        attrs = _utils.mergeDicts(
+                                            [attrs, hitchhiker['clone']]);
+                                    else
+                                        attrs['position'] = [200, 200];
+
+                                    let res = _mmmk.create(fullcstype, attrs),
+                                        csid = res['id'];
+
+                                    self.__asid2csid[asid] = csid;
+                                    cschangelogs.push(res['changelog']);
+
+                                    if (isLink) {
+                                        let s = {},
+                                            src =
+                                                hitchhiker['src'] ||
+                                                self.__asuri_to_csuri(hitchhiker['asSrc']),
+                                            dest =
+                                                hitchhiker['dest'] ||
+                                                self.__asuri_to_csuri(hitchhiker['asDest']),
+                                            segments =
+                                                hitchhiker['segments'] ||
+                                                self.__defaultSegments(src, dest);
+                                        s[src + '--' + __id_to_uri(csid)] = segments[0];
+                                        s[__id_to_uri(csid) + '--' + dest] = segments[1];
+
+                                        cschangelogs.push(
+                                            _mmmk.update(
+                                                csid,
+                                                {'$segments': s})['changelog'],
+                                            self.__positionLinkDecorators(csid));
+                                    }
+
+                                    return self.__regenIcon(csid);
+                                },
+                                function (riChangelog) {
+                                    cschangelogs.push(riChangelog);
+                                    return __successContinuable();
+                                });
+                        }
 						/* remove appropriate CS instance... update __asid2csid for it
 						  	to remain consistent */				
 						else if( step['op'] == 'RMNODE' )
@@ -457,64 +470,78 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 						  	current model, if any... when step['insert'] is specified,
 							adjust $asuris to compensate for offsetting of inserted asm
 							and $segments to compensate for upcoming offsetting of to-
-							be-inserted csm */			
-						else if( step['op'] == 'RESETM' )
-							actions.push(
-								function()
-								{
-									manageHitchhiker(
-										step['old_name'], 
-										{'csm':_mmmk.read()});
-									manageHitchhiker(step['new_name']);
-	
-									var csm = hitchhiker['csm'];
-									if( step['insert'] )
-									{
-										var asoffset = parseInt(step['insert']),
-											 csoffset = _mmmk.next_id,
-											 _csm     = eval('('+ csm +')'),
-											 incUri   = 
-											 	function(oldUri,offset)
-												{
-													var matches = oldUri.
-															match(/(.+\/)(.+)(\.instance)/);
-													return matches[1]+
-															 (parseInt(matches[2])+offset)+
-			 												 matches[3];										
-												};
-
-										for( var id in _csm.nodes )
-										{
-											_csm.nodes[id]['$asuri']['value'] = 
-												incUri(_csm.nodes[id]['$asuri']['value'],
-														 asoffset);
-											
-											if( ! ('$segments' in _csm.nodes[id]) )
-												continue;
-
-											var segments = 
-													_csm.nodes[id]['$segments']['value'],
-												 _segments = {};
-											for( var edgeId in segments )
-											{
-												var uris = edgeId.match(
-														/^(.*\.instance)--(.*\.instance)$/);
-												_segments[incUri(uris[1],csoffset)+'--'+
-															 incUri(uris[2],csoffset)] = 
-													segments[edgeId];											
-											}
-											_csm.nodes[id]['$segments']['value']=_segments;
-										}
-										csm = _utils.jsons(_csm,null,'\t');
-									}
+							be-inserted csm */
+                        else if (step['op'] == 'RESETM')
+                            actions.push(
+                                function () {
+                                    manageHitchhiker(
+                                        step['old_name'],
+                                        {'csm': _mmmk.read()});
+                                    manageHitchhiker(step['new_name']);
+
+                                    var csm = hitchhiker['csm'];
+                                    var _csm = eval('(' + csm + ')');
+                                    if (step['insert']) {
+                                        var asoffset = parseInt(step['insert']),
+                                            csoffset = _mmmk.next_id,
+                                            incUri =
+                                                function (oldUri, offset) {
+                                                    var matches = oldUri.match(/(.+\/)(.+)(\.instance)/);
+                                                    return matches[1] +
+                                                        (parseInt(matches[2]) + offset) +
+                                                        matches[3];
+                                                };
+
+                                        for (var id in _csm.nodes) {
+                                            _csm.nodes[id]['$asuri']['value'] =
+                                                incUri(_csm.nodes[id]['$asuri']['value'],
+                                                    asoffset);
+
+                                            if (!('$segments' in _csm.nodes[id]))
+                                                continue;
+
+                                            var segments =
+                                                    _csm.nodes[id]['$segments']['value'],
+                                                _segments = {};
+                                            for (var edgeId in segments) {
+                                                var uris = edgeId.match(
+                                                    /^(.*\.instance)--(.*\.instance)$/);
+                                                _segments[incUri(uris[1], csoffset) + '--' +
+                                                incUri(uris[2], csoffset)] =
+                                                    segments[edgeId];
+                                            }
+                                            _csm.nodes[id]['$segments']['value'] = _segments;
+                                        }
+                                        csm = _utils.jsons(_csm, null, '\t');
+                                    }
+
+                                    //see if any cs metamodels are missing
+                                    //this fixes issue #28
+                                    //this loading should be done elsewhere in the model loading chain
+                                    for (var i in _csm.metamodels) {
+                                        var mm = _csm.metamodels[i];
+
+                                        if (!(_mmmk.model.metamodels.includes(mm))) {
+                                            console.error("Last-minute loading for CS metamodel: " + mm);
+
+                                            var csmm = _fs.readFile('./users/' + mm, 'utf8');
+                                            _mmmk.loadMetamodel(mm, csmm);
+                                        }
+                                    }
+
+                                    var res = _mmmk.loadModel(
+                                        step['new_name'],
+                                        csm,
+                                        step['insert']);
+
+                                    if (res["$err"] == undefined) {
+                                        cschangelogs.push(res['changelog']);
+                                        return __successContinuable();
+                                    } else {
+                                        return __errorContinuable();
+                                    }
 
-				 					cschangelogs.push(
-											_mmmk.loadModel(
-														step['new_name'], 
-														csm, 
-														step['insert'])['changelog']);
-									return __successContinuable();
-								});
+                                });
 	
 						/* forward this SYSOUT command */
 						else if( step['op'] == 'SYSOUT' ) 
@@ -539,8 +566,8 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 				{
 					var cschangelog = _utils.flatten(cschangelogs);
 
-					console.error('w#'+__wid+' -- ('+aswSequenceNumber+') '+
-						_utils.jsons(cschangelog));
+					//console.error('w#'+__wid+' -- ('+aswSequenceNumber+') '+
+					//	_utils.jsons(cschangelog));
 
 					__postMessage(
 							{'statusCode':200,
@@ -648,7 +675,7 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 												{
 													return self.__sn2int(pc['sequence#']) > 
 																self.__sn2int(
-																	self.__nextASWSequenceNumber)
+																	self.__nextASWSequenceNumber);
 												});
 										callback();
 										self.__applyPendingASWChanges();
@@ -714,7 +741,7 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 					 pp  		= _svg.fns.getPointOnPathAtRatio(path,ldi.xratio);
                      
                 if (pp == undefined)
-                    continue
+                    continue;
                 
 				var yoffset	= new _svg.types.Point(0,ldi.yoffset).rotate(pp.O),
 					endAt	= (ldi.xratio >= 1 ? 
@@ -877,12 +904,12 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 			//			new_node['$linktype'] = this.metamodels[metamodel]['connectorTypes'][type];	
 
 			return [];
-			return [{'op':'SYSOUT',
-			  		  'text':'WARNING :: '+
-						  		'a proper layout constraint solver has yet to be '+
-						  		'implemented... inter-VisualObject relationships are '+
-								'ignored and containers do not resize to fit their '+
-								'contents'}];
+			// return [{'op':'SYSOUT',
+			//   		  'text':'WARNING :: '+
+			// 			  		'a proper layout constraint solver has yet to be '+
+			// 			  		'implemented... inter-VisualObject relationships are '+
+			// 					'ignored and containers do not resize to fit their '+
+			// 					'contents'}];
 		},
 
 
@@ -1346,107 +1373,129 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
                 );
             }
 		},
-	
 
-	/* INTENT :
- 			create a new instance of specified type (if reqData has 'segments', 
-			'src' and 'dest' fields, type is a connector)
-		IN PRACTICE: 
-			adjust uri (and reqData) and forward to asworker
 
-		1. parse + validate parameters
-		2. setup sync/async action chaining
-			a) construct reqData for asworker.POST *.type
-				i.   handle connector ends if applicable
-				ii.  handle 'pos'... pass as hitchhiker and evaluate the to-be
-					  *Icon's parser within a dummy context where 'position' is set
-					  to 'pos'... see NOTES above for more details on this
-		   b) ask asworker to create an instance of appropriate AS type
-		3. launch chain... return success code or error */
-	'POST *.type' :
-		function(resp,uri,reqData/*pos|clone,[segments,src,dest]*/)
-		{
-			var matches = 
-					uri.match(/((.*)\..*Icons)(\.pattern){0,1}\/((.*)Icon)\.type/) ||
-					uri.match(/((.*)\..*Icons)(\.pattern){0,1}\/((.*)Link)\.type/);
-			if( ! matches )
-				return __postBadReqErrorMsg(
-								resp,
-								'bad uri for Icon/Link type :: '+uri);
-
-			var asuri   = matches[2]+(matches[3] || '')+'/'+matches[5]+'.type',
-				 csmm		= matches[1]+(matches[3] || ''),
-				 cstype	= matches[4],
-                 types = _utils.jsonp(_mmmk.readMetamodels(csmm))['types'];
-            if (cstype in types) {
-				 var parser	= 
-					 types[cstype].
-					 	filter( 
-							function(attr) 
-							{
-								return attr['name']=='parser';
-							})[0]['default'],
-				 self = this,
-				 actions  = 
-					[__successContinuable(),
-					 function()
-					 {
-						 if( reqData == undefined || 
-							  (pos = reqData['pos']) == undefined )
-							 return __errorContinuable('missing creation parameters');
-
-						 var hitchhiker = {},
-						 	  reqParams	 = {};
-						 if( (segments = reqData['segments']) != undefined &&
-							  (src = reqData['src']) != undefined 				&&
-							  (dest = reqData['dest']) != undefined )
-						 {
-							 if( (src_asuri = self.__csuri_to_asuri(src))['$err'] )
-								 return __errorContinuable(src_asuri['$err']);
-							 if( (dest_asuri = self.__csuri_to_asuri(dest))['$err'] )
-								 return __errorContinuable(dest_asuri['$err']);
-
-							 hitchhiker = {'segments':segments,
-												'src':src,
-												'dest':dest};
-							 reqParams	= {'src':src_asuri,
-												'dest':dest_asuri};
-						 }
-
-						 hitchhiker['pos'] = pos;
-						 reqParams['attrs'] = 
-								 self.__runParser(
-										 parser,
-										 {'position':pos,
-  										  'orientation':0,
-										  'scale':[1,1]},
-	 									 {});
-
-						 return __successContinuable(
-									 	_utils.mergeDicts(
-											[{'hitchhiker':hitchhiker}, reqParams]) );
-					 },
-					 function(asreqData)
-					 {
-	 					 return __wHttpReq(
-									 'POST',
-									 asuri+'?wid='+self.__aswid,
-									 asreqData);
-					 }];
+    /* INTENT :
+             create a new instance of specified type (if reqData has
+             'src' and 'dest' fields, type is a connector)
+        IN PRACTICE:
+            adjust uri (and reqData) and forward to asworker
+
+        1. parse + validate parameters
+        2. setup sync/async action chaining
+            a) construct reqData for asworker.POST *.type
+                i.   handle connector ends if applicable
+                ii.  handle 'pos'... pass as hitchhiker and evaluate the to-be
+                      *Icon's parser within a dummy context where 'position' is set
+                      to 'pos'... see NOTES above for more details on this
+           b) ask asworker to create an instance of appropriate AS type
+        3. launch chain... return success code or error */
+    'POST *.type':
+        function (resp, uri, reqData/*pos|clone,[segments,src,dest]*/) {
+            let matches =
+                uri.match(/((.*)\..*Icons)(\.pattern){0,1}\/((.*)Icon)\.type/) ||
+                uri.match(/((.*)\..*Icons)(\.pattern){0,1}\/((.*)Link)\.type/);
+            if (!matches)
+                return __postBadReqErrorMsg(
+                    resp,
+                    'bad uri for Icon/Link type :: ' + uri);
 
-                 _do.chain(actions)(
-                        function(res) 	
-                        {
-                            __postMessage({'statusCode':202, 'respIndex':resp});
-                        },
-                        function(err) 	{__postInternalErrorMsg(resp,err);}
-                 );
-            } else {
+            let asuri = matches[2] + (matches[3] || '') + '/' + matches[5] + '.type',
+                csmm = matches[1] + (matches[3] || ''),
+                cstype = matches[4],
+                types = _utils.jsonp(_mmmk.readMetamodels(csmm))['types'];
+
+            if (!(cstype in types)) {
                 return __postBadReqErrorMsg(
-								resp,
-								'no concrete syntax definition found for '+cstype);
+                    resp, 'no concrete syntax definition found for ' + cstype);
             }
-		},
+
+            let parser =
+                    types[cstype].filter(
+                        function (attr) {
+                            return attr['name'] == 'parser';
+                        })[0]['default'],
+                self = this,
+                actions =
+                    [__successContinuable(),
+                        function () {
+                            if (reqData == undefined)
+                                return __errorContinuable('missing creation parameters');
+
+                            let hitchhiker = {},
+                                reqParams = {},
+                                segments = reqData['segments'],
+                                src = reqData['src'],
+                                dest = reqData['dest'],
+                                pos = reqData['pos'];
+
+                            if (src != undefined &&
+                                dest != undefined) {
+
+                                let src_asuri = self.__csuri_to_asuri(src);
+                                if (src_asuri['$err'])
+                                    return __errorContinuable(src_asuri['$err']);
+
+                                let dest_asuri = self.__csuri_to_asuri(dest);
+                                if (dest_asuri['$err'])
+                                    return __errorContinuable(dest_asuri['$err']);
+
+                                if (segments == undefined) {
+                                    segments = self.__defaultSegments(src, dest);
+                                }
+
+                                if (pos == undefined) {
+                                    pos = self.__nodesCenter([src_asuri, dest_asuri]);
+                                }
+
+                                hitchhiker = {
+                                    'segments': segments,
+                                    'src': src,
+                                    'dest': dest
+                                };
+                                reqParams = {
+                                    'src': src_asuri,
+                                    'dest': dest_asuri
+                                };
+
+
+                            }
+
+                            if (pos == undefined)
+                                return __errorContinuable('missing position');
+
+                            hitchhiker['pos'] = pos;
+                            reqParams['attrs'] =
+                                self.__runParser(
+                                    parser,
+                                    {
+                                        'position': pos,
+                                        'orientation': 0,
+                                        'scale': [1, 1]
+                                    },
+                                    {});
+
+                            return __successContinuable(
+                                _utils.mergeDicts(
+                                    [{'hitchhiker': hitchhiker}, reqParams]));
+                        },
+                        function (asreqData) {
+                            return __wHttpReq(
+                                'POST',
+                                asuri + '?wid=' + self.__aswid,
+                                asreqData);
+                        }];
+
+            _do.chain(actions)(
+                function (res) {
+                    __postMessage({'statusCode': 202, 'respIndex': resp, 'reason': res});
+                },
+                function (err) {
+                    __postInternalErrorMsg(resp, err);
+                }
+            );
+
+        },
 
 
 	/* return an AS instance, and optionally also the associated CS instance
@@ -1835,8 +1884,8 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
                             function(result) {
                                 return __wHttpReq('PUT',
                                            uri+'?wid='+aswid,
-                                           ({'csm':_mmmk.read(), 'asmm': asmm}))
-                            }]
+                                           ({'csm':_mmmk.read(), 'asmm': asmm}));
+                            }];
                  } else {
                      actions = [__wHttpReq('PUT',
                                            uri+'?wid='+aswid,
@@ -1877,9 +1926,9 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 					{
 						var sn = asdata['sequence#'];
 						if( self.__nextASWSequenceNumber - 1 > sn )
-							self['PUT *.model'](resp,urin);
+							self['PUT *.model'](resp,uri);
 						else if( self.__nextASWSequenceNumber - 1 < sn )
-							setTimeout(self['PUT *.model'], 200, resp, urin);
+							setTimeout(self['PUT *.model'], 200, resp, uri);
 						else
 						{
 							if( (res = _mmmk.read())['$err'] )
@@ -2248,4 +2297,4 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 		{
 			return parseInt(sn.match(/.*#(\d*)/)[1]);
 		}
-}
+};

+ 10 - 215
doc/Makefile

@@ -1,225 +1,20 @@
-# Makefile for Sphinx documentation
+# Minimal makefile for Sphinx documentation
 #
 
 # You can set these variables from the command line.
 SPHINXOPTS    =
 SPHINXBUILD   = sphinx-build
-PAPER         =
+SPHINXPROJ    = AToMPM
+SOURCEDIR     = .
 BUILDDIR      = _build
 
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help
+# Put it first so that "make" without argument is like "make help".
 help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html       to make standalone HTML files"
-	@echo "  dirhtml    to make HTML files named index.html in directories"
-	@echo "  singlehtml to make a single large HTML file"
-	@echo "  pickle     to make pickle files"
-	@echo "  json       to make JSON files"
-	@echo "  htmlhelp   to make HTML files and a HTML help project"
-	@echo "  qthelp     to make HTML files and a qthelp project"
-	@echo "  applehelp  to make an Apple Help Book"
-	@echo "  devhelp    to make HTML files and a Devhelp project"
-	@echo "  epub       to make an epub"
-	@echo "  epub3      to make an epub3"
-	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
-	@echo "  text       to make text files"
-	@echo "  man        to make manual pages"
-	@echo "  texinfo    to make Texinfo files"
-	@echo "  info       to make Texinfo files and run them through makeinfo"
-	@echo "  gettext    to make PO message catalogs"
-	@echo "  changes    to make an overview of all changed/added/deprecated items"
-	@echo "  xml        to make Docutils-native XML files"
-	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
-	@echo "  linkcheck  to check all external links for integrity"
-	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-	@echo "  coverage   to run coverage check of the documentation (if enabled)"
-	@echo "  dummy      to check syntax errors of document sources"
-
-.PHONY: clean
-clean:
-	rm -rf $(BUILDDIR)/*
-
-.PHONY: html
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-.PHONY: dirhtml
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-.PHONY: singlehtml
-singlehtml:
-	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-	@echo
-	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-.PHONY: pickle
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-.PHONY: json
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-.PHONY: htmlhelp
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-.PHONY: qthelp
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/AToMPM.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/AToMPM.qhc"
-
-.PHONY: applehelp
-applehelp:
-	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
-	@echo
-	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
-	@echo "N.B. You won't be able to view it unless you put it in" \
-	      "~/Library/Documentation/Help or install it in your application" \
-	      "bundle."
-
-.PHONY: devhelp
-devhelp:
-	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-	@echo
-	@echo "Build finished."
-	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/AToMPM"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/AToMPM"
-	@echo "# devhelp"
-
-.PHONY: epub
-epub:
-	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-	@echo
-	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-.PHONY: epub3
-epub3:
-	$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
-	@echo
-	@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
-
-.PHONY: latex
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make' in that directory to run these through (pdf)latex" \
-	      "(use \`make latexpdf' here to do that automatically)."
-
-.PHONY: latexpdf
-latexpdf:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through pdflatex..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-.PHONY: latexpdfja
-latexpdfja:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through platex and dvipdfmx..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-.PHONY: text
-text:
-	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-	@echo
-	@echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-.PHONY: man
-man:
-	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-	@echo
-	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-.PHONY: texinfo
-texinfo:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo
-	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
-	@echo "Run \`make' in that directory to run these through makeinfo" \
-	      "(use \`make info' here to do that automatically)."
-
-.PHONY: info
-info:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo "Running Texinfo files through makeinfo..."
-	make -C $(BUILDDIR)/texinfo info
-	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-.PHONY: gettext
-gettext:
-	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-	@echo
-	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-.PHONY: changes
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-.PHONY: linkcheck
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-.PHONY: doctest
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
-
-.PHONY: coverage
-coverage:
-	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
-	@echo "Testing of coverage in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/coverage/python.txt."
-
-.PHONY: xml
-xml:
-	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-	@echo
-	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 
-.PHONY: pseudoxml
-pseudoxml:
-	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-	@echo
-	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+.PHONY: help Makefile
 
-.PHONY: dummy
-dummy:
-	$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
-	@echo
-	@echo "Build finished. Dummy builder generates no files."
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

BIN
doc/_build/doctrees/environment.pickle


BIN
doc/_build/doctrees/executing_transformation.doctree


BIN
doc/_build/doctrees/index.doctree


BIN
doc/_build/doctrees/installation.doctree


BIN
doc/_build/doctrees/modelling_transformation.doctree


BIN
doc/_build/doctrees/new_language.doctree


BIN
doc/_build/doctrees/overview.doctree


BIN
doc/_build/doctrees/troubleshooting.doctree


BIN
doc/_build/doctrees/using_language.doctree


BIN
doc/_build/doctrees/workflows.doctree


+ 0 - 0
doc/_build/html/.buildinfo


Some files were not shown because too many files changed in this diff