Skip to content
Browse files

initial commit (actually branched from v20120911 because of renewal)

  • Loading branch information...
0 parents commit 29116c6908e9298b712937143cb7068124e0877b @tmtk75 committed Sep 11, 2012
Showing with 18,312 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +9 −0 .gitmodules
  3. +6 −0 .heroku.js
  4. 0 .npmignore
  5. +1 −0 .setenv
  6. +63 −0 Cakefile
  7. +1 −0 Procfile
  8. +73 −0 README.md
  9. +70 −0 app.coffee
  10. +5 −0 lib/jumly/css/Mac.less
  11. +17 −0 lib/jumly/css/Win.less
  12. +43 −0 lib/jumly/css/class.less
  13. +59 −0 lib/jumly/css/commons.less
  14. 0 lib/jumly/css/docs.less
  15. +34 −0 lib/jumly/css/icon.less
  16. +9 −0 lib/jumly/css/jumly.less
  17. +173 −0 lib/jumly/css/note.less
  18. +27 −0 lib/jumly/css/object.less
  19. +8 −0 lib/jumly/css/relationship.less
  20. +151 −0 lib/jumly/css/sequence.less
  21. +18 −0 lib/jumly/css/spacing.less
  22. +34 −0 lib/jumly/css/theme/cool.scss
  23. +25 −0 lib/jumly/css/theme/monotone.scss
  24. +121 −0 lib/jumly/css/theme/pop.scss
  25. +43 −0 lib/jumly/css/usecase-a.less
  26. +34 −0 lib/jumly/css/usecase.less
  27. +107 −0 lib/jumly/js/0.1.0.coffee
  28. +88 −0 lib/jumly/js/ClassDiagram.coffee
  29. +24 −0 lib/jumly/js/Diagram.coffee
  30. +39 −0 lib/jumly/js/DiagramBuilder.coffee
  31. +17 −0 lib/jumly/js/ElementLayout.coffee
  32. +14 −0 lib/jumly/js/HTMLElement.coffee
  33. +17 −0 lib/jumly/js/HTMLElementLayout.coffee
  34. +9 −0 lib/jumly/js/Note.coffee
  35. +43 −0 lib/jumly/js/Object.coffee
  36. +45 −0 lib/jumly/js/Position.coffee
  37. +99 −0 lib/jumly/js/Relationship.coffee
  38. +523 −0 lib/jumly/js/SequenceDiagram.coffee
  39. +230 −0 lib/jumly/js/SequenceDiagramBuilder.coffee
  40. +132 −0 lib/jumly/js/SequenceDiagramLayout.coffee
  41. +178 −0 lib/jumly/js/UsecaseDiagram.coffee
  42. +149 −0 lib/jumly/js/core.coffee
  43. +170 −0 lib/jumly/js/icon.coffee
  44. +53 −0 lib/jumly/js/jquery.ext.coffee
  45. +188 −0 lib/jumly/js/jquery.g2d.coffee
  46. +24 −0 lib/jumly/js/jumly.coffee
  47. +39 −0 lib/jumly/js/legacy/0.1.0/class-diagramSpec.coffee
  48. +114 −0 lib/jumly/js/legacy/0.1.0/jumly.DSLSpec.coffee
  49. +85 −0 lib/jumly/js/legacy/0.1.0/jumly.attrsSpec.coffee
  50. +187 −0 lib/jumly/js/legacy/0.1.0/jumly.commonsSpec.coffee
  51. +105 −0 lib/jumly/js/legacy/0.1.0/jumly.diagramSpec.coffee
  52. +36 −0 lib/jumly/js/legacy/0.1.0/jumly.iconSpec.coffee
  53. +134 −0 lib/jumly/js/legacy/0.1.0/jumly.identificationSpec.coffee
  54. +146 −0 lib/jumly/js/legacy/0.1.0/jumly.objectSpec.coffee
  55. +78 −0 lib/jumly/js/legacy/0.1.0/jumly.propertySpec.coffee
  56. +442 −0 lib/jumly/js/legacy/0.1.0/jumlySpec.coffee
  57. +1,030 −0 lib/jumly/js/legacy/0.1.0/sequence-diagramSpec.coffee
  58. +268 −0 lib/jumly/js/legacy/0.1.0/sequence.DSLSpec.coffee
  59. +62 −0 lib/jumly/js/legacy/0.1.0/sequence.fragment.altSpec.coffee
  60. +23 −0 lib/jumly/js/legacy/0.1.0/sequence.fragment.loopSpec.coffee
  61. +352 −0 lib/jumly/js/legacy/0.1.0/sequence.fragmentSpec.coffee
  62. +590 −0 lib/jumly/js/legacy/0.1.0/sequence.interactionSpec.coffee
  63. +173 −0 lib/jumly/js/legacy/0.1.0/sequence.lifelineSpec.coffee
  64. +117 −0 lib/jumly/js/legacy/0.1.0/sequence.message.createSpec.coffee
  65. +43 −0 lib/jumly/js/legacy/0.1.0/sequence.message.returnSpec.coffee
  66. +590 −0 lib/jumly/js/legacy/0.1.0/sequence.messageSpec.coffee
  67. +380 −0 lib/jumly/js/legacy/0.1.0/sequence.refSpec.coffee
  68. +23 −0 lib/jumly/js/legacy/0.1.0/spec.coffee
  69. +75 −0 lib/jumly/js/legacy/0.1.0/usecase-diagramSpec.coffee
  70. +302 −0 lib/jumly/js/legacy/0.1.0/usecase.DSLSpec.coffee
  71. +200 −0 lib/jumly/js/legacy/0.1.1/JUMLYSpec.coffee
  72. +115 −0 lib/jumly/js/legacy/0.1.1/builderSpec.coffee
  73. +97 −0 lib/jumly/js/legacy/0.1.1/classSpec.coffee
  74. +34 −0 lib/jumly/js/legacy/0.1.1/diagramSpec.coffee
  75. +25 −0 lib/jumly/js/legacy/0.1.1/elementSpec.coffee
  76. +132 −0 lib/jumly/js/legacy/0.1.1/referingSpec.coffee
  77. +186 −0 lib/jumly/js/legacy/0.1.1/sequenceSpec.coffee
  78. +8 −0 lib/jumly/js/legacy/0.1.1/spec.coffee
  79. +65 −0 lib/jumly/js/legacy/0.1.1/usecaseSpec.coffee
  80. +10 −0 lib/jumly/js/legacy/0.1.2/JUMLYHTMLElementSpec.coffee
  81. +23 −0 lib/jumly/js/legacy/0.1.2/sequenceSpec.coffee
  82. +2 −0 lib/jumly/js/legacy/0.1.2/spec.coffee
  83. +8 −0 lib/jumly/js/legacy/NOTE.md
  84. +34 −0 lib/jumly/js/legacy/helper.coffee
  85. +235 −0 lib/jumly/js/legacy/jasmine-story.coffee
  86. +37 −0 lib/jumly/js/plugin.coffee
  87. +32 −0 lib/jumly/js/spec/layout/ElementLayoutSpec.coffee
  88. +36 −0 lib/jumly/js/spec/looks/fullSpec.coffee
  89. +5 −0 lib/jumly/js/spec/position/BrowserSpec.coffee
  90. +160 −0 lib/jumly/js/spec/position/PositionSpec.coffee
  91. +38 −0 lib/jumly/js/spec/position/SequenceDiagramSpec.coffee
  92. +63 −0 lib/jumly/js/spec/position/jQuerySpec.coffee
  93. +4 −0 lib/jumly/js/spec/requires.coffee
  94. +13 −0 lib/jumly/js/spec/struct/SequenceDiagramSpec.coffee
  95. +14 −0 lib/jumly/js/spec/struct/jQuerySpec.coffee
  96. +19 −0 lib/jumly/js/spec/struct/jasmine-node-helper.coffee
  97. +25 −0 lib/jumly/js/spec/struct/jumlySpec.coffee
  98. +20 −0 lib/jumly/js/spec/struct/metaSpec.coffee
  99. +6 −0 lib/jumly/js/spec/struct/notationSpec.coffee
  100. +7 −0 lib/jumly/js/support.coffee
  101. +1 −0 lib/jumly/version
  102. +39 −0 package.json
  103. +1 −0 public/bootstrap
  104. BIN public/images/TryJUMLY-overview.png
  105. BIN public/images/grid_c.gif
  106. +136 −0 public/javascripts/TryJUMLY.Tutorial.coffee
  107. +77 −0 public/javascripts/TryJUMLY.Tutorial.sample.coffee
  108. +5 −0 public/javascripts/TryJUMLY.utils.coffee
  109. +97 −0 public/javascripts/base64.js
  110. +6 −0 public/javascripts/bitly.coffee
  111. +12 −0 public/javascripts/btn.js
  112. +8 −0 public/javascripts/coffee-script.js
  113. +5 −0 public/javascripts/cssua.min.js
  114. +20 −0 public/javascripts/jasmine/MIT.LICENSE
  115. +616 −0 public/javascripts/jasmine/jasmine-html.js
  116. +81 −0 public/javascripts/jasmine/jasmine.css
  117. +2,529 −0 public/javascripts/jasmine/jasmine.js
  118. +123 −0 public/javascripts/jquery.client.js
  119. +41 −0 public/javascripts/jquery.cookie.js
  120. +4 −0 public/javascripts/jquery.js
  121. +10 −0 public/javascripts/jquery.tmpl.min.js
  122. +7 −0 public/javascripts/jquery.typing.min.js
  123. +465 −0 public/javascripts/jwerty.js
  124. +86 −0 public/javascripts/knockout.js
  125. +687 −0 public/javascripts/knockout.mapping.js
  126. +9 −0 public/javascripts/less.js
  127. +1 −0 public/javascripts/syntaxhighlighter/VERSION
  128. +52 −0 public/javascripts/syntaxhighlighter/scripts/shBrushJScript.js
  129. +69 −0 public/javascripts/syntaxhighlighter/scripts/shBrushXml.js
  130. +17 −0 public/javascripts/syntaxhighlighter/scripts/shCore.js
  131. +226 −0 public/javascripts/syntaxhighlighter/styles/shCore.css
  132. +117 −0 public/javascripts/syntaxhighlighter/styles/shThemeDefault.css
  133. +84 −0 public/javascripts/tryjumly.coffee
  134. +2 −0 public/javascripts/tw.js
  135. +1 −0 public/javascripts/twb.js
  136. +2 −0 public/latest/jumly.min.css
  137. +2 −0 public/latest/jumly.min.js
  138. +2 −0 public/latest/latest.sha1
  139. +8 −0 public/min-working.html
  140. +2 −0 public/sh.css
  141. 0 public/stylesheets/style.less
  142. +35 −0 views/_credit.jade
  143. +23 −0 views/_example.jade
  144. +55 −0 views/_grammer.haml
  145. +139 −0 views/_introduction.jade
  146. +20 −0 views/_license.jade
  147. +10 −0 views/_navbar.jade
  148. +84 −0 views/_overview.jade
  149. +42 −0 views/_tutorial.jade
  150. +61 −0 views/examples/arch.jade
  151. +61 −0 views/examples/codec.jade
  152. +40 −0 views/examples/demo_sequence.jade
  153. +6 −0 views/examples/full-feature-class
  154. +47 −0 views/examples/full-feature-sequence
  155. +11 −0 views/examples/full-feature-usecase
  156. +292 −0 views/examples/javascripts/codec.js
  157. +21 −0 views/examples/javascripts/order.coffee
  158. +66 −0 views/examples/javascripts/osgi.coffee
  159. +22 −0 views/examples/javascripts/restaurant.coffee
  160. +66 −0 views/examples/slide.html.haml
  161. +95 −0 views/grammer.rb
  162. BIN views/images/TryJUMLY-overview.png
  163. BIN views/images/grid_a.gif
  164. BIN views/images/grid_b.gif
  165. BIN views/images/grid_c.gif
  166. BIN views/images/lined.png
  167. BIN views/images/square.png
  168. +53 −0 views/index.jade
  169. +31 −0 views/layout.jade
  170. +26 −0 views/legacy.jade
  171. +18 −0 views/reference.jade
  172. +341 −0 views/reference.md
  173. +22 −0 views/spec.jade
  174. +26 −0 views/stylesheets/3rd-party.styl
  175. +59 −0 views/stylesheets/commons.styl
  176. +105 −0 views/stylesheets/docs.styl
  177. +165 −0 views/stylesheets/tryjumly.styl
  178. +186 −0 views/tryjumly.i18n
  179. +210 −0 views/tryjumly.jade
4 .gitignore
@@ -0,0 +1,4 @@
+.project
+node_modules
+public/*.css
+*.swp
9 .gitmodules
@@ -0,0 +1,9 @@
+[submodule "vendor/slippy"]
+ path = vendor/slippy
+ url = https://github.com/Seldaek/slippy.git
+[submodule "vendor/node-canvas"]
+ path = vendor/node-canvas
+ url = https://github.com/LearnBoost/node-canvas
+[submodule "public/bootstrap"]
+ path = public/bootstrap
+ url = https://github.com/twitter/bootstrap.git
6 .heroku.js
@@ -0,0 +1,6 @@
+// $ heroku create --stack cedar
+// $ heroku config:add NODE_ENV=heroku
+var coffee = require('coffee-script');
+var fs = require('fs');
+var app = coffee.compile(fs.readFileSync('./app.coffee').toString());
+eval(app);
0 .npmignore
No changes.
1 .setenv
@@ -0,0 +1 @@
+PATH=`pwd`/node_modules/.bin:$PATH
63 Cakefile
@@ -0,0 +1,63 @@
+util = require "util"
+fs = require "fs"
+path = require "path"
+muffin = require "muffin"
+glob = require "glob"
+_ = require "underscore"
+
+tmpdir = ".build" #(require "temp").mkdirSync()
+basepath = "lib/jumly/build/jumly"
+dstpath = "#{basepath}.js"
+minpath = "#{basepath}.min.js"
+version = fs.readFileSync("lib/jumly/version").toString().trim() #replace /\n/, ""
+header = """
+// JUMLY v#{version}, 2011-#{new Date().getFullYear()} copyright(c), all rights reserved.\n
+"""
+
+#option "-w", "--watch", "How do I specify argument?"
+
+
+task "compile", "compile *.coffee", ->
+ muffin.run
+ files: ["./lib/jumly/js/*.coffee"]
+ options: {}
+ map:
+ "([^/]+).coffee": (matches)->
+ src = "lib/jumly/js/#{matches[1]}.coffee"
+ dst = "#{tmpdir}/#{matches[1]}.js"
+ a = (fs.statSync src).mtime
+ b = (fs.statSync dst).mtime if path.existsSync dst
+ dirty = a > b or not(a and b)
+ muffin.compileScript src, dst, bare:false if dirty
+
+
+task "concat", "concatenate", ->
+ bodies = _.map (glob.sync "#{tmpdir}/*.js"), (e)-> (fs.readFileSync e).toString()
+ muffin.writeFile dstpath, [header].concat(bodies).join ""
+
+
+task "minify", "minify", ->
+ muffin.run
+ files: [dstpath]
+ options: {}
+ map:
+ ".*": (matches)-> muffin.minifyScript matches[0]
+ after: ->
+ body = (fs.readFileSync minpath).toString()
+ muffin.writeFile minpath, header + body
+
+#task "doc", "", (opts)-> muffin.doccoFile("./lib/jumly/js/core.coffee", opts)
+
+task "spec", "print command line to run spec", ->
+ invoke "spec::struct"
+
+cmd = "jasmine-node --coffee lib/jumly/js"
+task "spec::struct", "print command line to run spec::struct", ->
+ console.log "#{cmd}/spec/struct"
+
+task "spec::position", "print command line to run spec::position", ->
+ console.log "#{cmd}/spec/position"
+
+task "spec::legacy", "print command line to run legacy spec::legacy", ->
+ console.log "#{cmd}/legacy"
+
1 Procfile
@@ -0,0 +1 @@
+web: node .heroku.js
73 README.md
@@ -0,0 +1,73 @@
+# README
+
+JUMLY is a JavaScript library.
+Using JUMLY, you can easily embed UML diagram on your HTML document.
+All you need are just two in order to use JUMLY.
+
+- Text editor you get used to use.
+- Modern browser like WebKit-base brwoser and Opera.
+ (planning it works on other browsers, especially Firefox.)
+
+
+## Getting Started
+Copy following code and put it at the place of your HTML document.
+
+ <link href='http://tmtk75.github.com/jumly/latest/jumly.min.css' rel="stylesheet"/>
+ <script src='http://code.jquery.com/jquery-1.7.1.min.js'></script>
+ <script src='http://jashkenas.github.com/coffee-script/extras/coffee-script.js'></script>
+ <script src='http://tmtk75.github.com/jumly/latest/jumly.min.js'></script>
+ <script type='text/jumly+sequence'>
+ @found "You", ->
+ @message "meet", "JUMLY"
+ </script>
+
+[Here is same one](/min-working.html), minimal working example.
+
+
+## Features
+
+- Easily embed some of UML diagrams with HTML5/CSS3.
+- Rendered diagrams are composed in HTMLElement,
+ which means all known ways for HTML/CSS are available.
+- DSL based on coffeescript.
+- Works on just client side.
+
+
+## Links
+
+- Website: <http://jumly.heroku.com>
+- Blog: <http://tmtk75.github.com>
+
+
+## License
+**NOTE: License will be changed in the future.**
+
+Now I'm contemplating which license I should choose.
+
+Tentatively CC3.0.
+
+<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-nd/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">jumly</span> by <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Tomotaka Sakuma</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License</a>.
+
+Tomotaka Sakuma 2011-2012 copyright(c), all rights reserved.
+
+
+## Changelog
+
+### 0.1.2
+- Re-write site and spec in coffee-script on Node.js
+- Configure for heroku
+
+### 0.1.2a
+- No feature improvement
+- Refine directory layout
+
+### 0.1.1
+- Implement JUMLY root namespace.
+- Implement JUMLY.DiagramBuilder class, and its sub-classes.
+- Enhance way to refer in JUMLY DSL.
+- Improve website. Especially, TryJUMLY page.
+
+### 0.1.0 <small>Initial release.</small>
+- Enable to render sequence diagram.
+- Able to try prototype of use-case diagram.
+- Able to try prototype of class diagram.
70 app.coffee
@@ -0,0 +1,70 @@
+#!/usr/bin/env coffee
+fs = require "fs"
+express = require "express"
+assets = require "connect-assets"
+jade = require "jade"
+stylus = require 'stylus'
+_ = require 'underscore'
+yaml = require 'js-yaml'
+
+jumly = (str, type)->
+ str = str.replace /\\n/g, '\n'
+ js = str.replace(/\\/g, '\\\\').replace /\n/g, '\\n'
+ """<script type="text/jumly+#{type}">\\n#{js}</script>"""
+
+jade.filters["jumly_sequence"] = (str)-> jumly str, "sequence"
+jade.filters["jumly_class"] = (str)-> jumly str, "class"
+jade.filters["jumly_usecase"] = (str)-> jumly str, "usecase"
+jade.filters["code"] = (str)->
+ str = str.replace /\\n/g, '\n'
+ js = str.replace(/\\/g, '\\\\').replace /\n/g, '\\n'
+ """<pre class="brush: html">#{js}</pre>"""
+
+
+app = module.exports = express.createServer()
+app.configure ->
+ app.set "views", __dirname + "/views"
+ app.set "view engine", "jade"
+ app.use stylus.middleware
+ src:"#{__dirname}/views/stylesheets"
+ dest:"#{__dirname}/public"
+ app.use express.bodyParser()
+ app.use express.methodOverride()
+ app.use app.router
+ app.use express.static __dirname + "/public"
+ app.use assets src:"lib/jumly"
+app.configure "development", -> app.use express.errorHandler dumpExceptions: true, showStack: true
+app.configure "production", -> app.use express.errorHandler()
+
+
+lang_resources = yaml.load fs.readFileSync("views/tryjumly.i18n").toString()
+
+conf =
+ VERSION: fs.readFileSync("lib/jumly/version").toString()
+ markdown: (path)-> require("markdown-js").parse fs.readFileSync(path).toString()
+ title: "JUMLY"
+ layout: true
+
+i18n =
+ value: (key, lang="en")->
+ a = lang_resources[key.toLowerCase()]
+ return key unless a
+ a[lang] || key
+
+app.get "/", (req, res)-> res.render 'index', conf
+app.get /^\/([^;.]+)(;([a-z]+))?$/, (req, res)->
+ mkparams = (req, opts)-> _.extend {}, conf, {i18n:_:(key)->i18n.value key, req.params[2]}, opts
+ name = req.params[0]
+ layouts =
+ tryjumly :false
+ spec :false
+ reference:true
+ legacy :false
+ res.render name, mkparams req, layout:layouts[name]
+
+port = 3000
+if process.env.NODE_ENV is "heroku"
+ console.log "run on heroku"
+ port = process.env.PORT
+app.listen port, ->
+ console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env
5 lib/jumly/css/Mac.less
@@ -0,0 +1,5 @@
+/* ---------------- Mac ---------------- */
+.Mac {
+ .ua-webkit {
+ }
+}
17 lib/jumly/css/Win.less
@@ -0,0 +1,17 @@
+/* ---------------- Windows XP SP3 ---------------- */
+.Windows {
+ font-weight: bold;
+ .ua-webkit {
+ font-size: 77%;
+ color: blue;
+ }
+ .ua-firefox {
+ font-size: 80%;
+ color: #FF7F2A; /* orange */
+ }
+ .ua-opera {
+ font-size: 80%;
+ color: red;
+ }
+}
+
43 lib/jumly/css/class.less
@@ -0,0 +1,43 @@
+@import "commons";
+.class-diagram {
+ .class .icon {
+ .primary_border;
+ .box_shadow;
+ .border_radius(3px);
+ float: left;
+ min-width: 120px;
+ background-color: white;
+ .stereotype, .name {
+ display: block;
+ text-align: center;
+ }
+ .stereotype {
+ &:before { content: '<<'; }
+ &:after { content: '>>'; }
+ }
+ .name {
+ margin: 0.2em 0.5em 0.2em 0.5em;
+ font-weight: bold;
+ }
+ .attrs, .methods {
+ > * {
+ display: block;
+ margin: 0px;
+ padding: 0px;
+ }
+ border-top: solid 2px gray;
+ min-height: 8px;
+ }
+ ul {
+ -webkit-margin-before: 0px;
+ -webkit-margin-after : 0px;
+ -webkit-margin-start : 0px;
+ -webkit-margin-end : 0px;
+ -webkit-padding-start: 0px;
+ padding-left: 1em;
+ padding-right: 1em;
+ }
+ margin-left: 2px;
+ margin-bottom: 2px;
+ }
+}
59 lib/jumly/css/commons.less
@@ -0,0 +1,59 @@
+.border_radius(@pd: 2px) {
+ -moz-border-radius: @pd;
+ -webkit-border-radius: @pd;
+ -o-border-radius: @pd;
+ border-radius: @pd;
+}
+.transform_rotate(@deg: 45deg) {
+ -moz-transform: rotate(@deg);
+ -webkit-transform: rotate(@deg);
+ -o-transform: rotate(@deg);
+ transform: rotate(@deg);
+}
+
+@black_half: rgba(0, 0, 0, 0.33);
+.box_shadow {
+ -webkit-box-shadow: 10px 5px 5px @black_half;
+ -moz-box-shadow: 10px 5px 5px @black_half;
+ -o-box-shadow: 10px 5px 5px @black_half;
+ box-shadow: 10px 5px 5px @black_half;
+}
+.box_shadow2 {
+ -webkit-box-shadow: 5px 2.5px 2.5px @black_half;
+ -moz-box-shadow: 5px 2.5px 2.5px @black_half;
+ -o-box-shadow: 5px 2.5px 2.5px @black_half;
+ box-shadow: 5px 2.5px 2.5px @black_half;
+}
+.text_shadow {
+ -text-shadow: rgba(0,0,0,0.33) 1px 0px 0px;
+ -text-shadow: 1px 0px 0px rgba(0,0,0,0.33);
+ -text-shadow: 4px 3px 0px #fff, 9px 8px 0px rgba(0,0,0,0.15);
+}
+.centering {
+ margin-left: auto;
+ margin-right: auto;
+}
+@unimportant_z_index: 0;
+.unimportant {
+ z-index: @unimportant_z_index;
+}
+@primary_border_width: 2px;
+@primary_border_color: gray;
+@primary_border_style: solid;
+.primary_border {
+ border: @primary_border_width @primary_border_style @primary_border_color;
+}
+@golden_ratio: (1 + 2.2360679)/2;
+@silver_ratio: (1.41421356);
+
+
+// I think helvetical is the best... :-)
+//@import url(http://fonts.googleapis.com/css?family=Numans|Carme|Michroma|Varela|Open+Sans:400,800);
+/*
+font-family: 'Numans', sans-serif;
+font-family: 'Questrial', sans-serif;
+font-family: 'Carme', sans-serif;
+font-family: 'Michroma', sans-serif;
+font-family: 'Varela', sans-serif;
+font-family: 'Open Sans', sans-serif;
+*/
0 lib/jumly/css/docs.less
No changes.
34 lib/jumly/css/icon.less
@@ -0,0 +1,34 @@
+@import "commons";
+/*-------- icon.css --------*/
+.icon-squaresize {
+ width : 24px;
+ height: 24px;
+}
+.icon {
+ @bgcolor: gray;
+ .square {
+ .icon-squaresize;
+ margin-left: 6px;
+ position: relative;
+ background-color: @bgcolor;
+ .border_radius;
+ &.cross {
+ width: 4px;
+ margin-left: 4px;
+ }
+ &::before {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 100%;
+ .transform_rotate(90deg);
+ background-color: @bgcolor;
+ }
+ }
+ .stop {
+ .icon-squaresize;
+ .square.cross {
+ .transform_rotate;
+ }
+ }
+}
9 lib/jumly/css/jumly.less
@@ -0,0 +1,9 @@
+@import "commons";
+@import "object";
+@import "relationship";
+@import "icon";
+//@import "note";
+@import "usecase";
+@import "class";
+@import "sequence";
+@import "spacing";
173 lib/jumly/css/note.less
@@ -0,0 +1,173 @@
+@import "_commons";
+
+$default-note-width: 192;
+/* $golden_ratio: 1.618; */
+$default-min-height: $default-note-width/1.618/1.618;
+@mixin default-note-size {
+ width: #{$default-note-width}px;
+ min-height: #{$default-min-height}px;
+}
+@mixin default-note-padding {
+ padding: 0.4em 1em 0.4em 1em;
+}
+@mixin default-note-boarder {
+ border: 2px solid rgba(128,128,128,0.77);
+}
+@mixin default-note-margin {
+ margin-top: 4px;
+ margin-left: 4px;
+}
+@mixin default-gradient {
+ background: -webkit-gradient(linear, 100% 100%, 50% 10%, from(#fff), to(#f3f3f3), color-stop(.1,#fff));
+}
+
+@mixin default-note {
+ @include default-note-size;
+ @include default-note-padding;
+ @include default-note-boarder;
+ @include default-note-margin;
+ position: relative;
+}
+
+@mixin plain-note {
+ @include default-note;
+}
+
+/*-------- Folded up note -------- */
+@mixin foldedup-effect {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ right: 0;
+}
+@mixin foldedup {
+ $foldw: 40;
+ $gs: 128;
+ $foldbd: 2px solid rgba($gs,$gs,$gs,0.5);
+ border: {top:$foldbd; right:$foldbd}
+ border: {bottom:$foldbd; left:$foldbd}
+ -webkit-border-bottom-right-radius: #{$foldw}px #{$foldw}px;
+ -webkit-box-shadow: -1px 2px 2px rgba(0, 0, 0, 0.2);
+ &:before { /* folded corner */
+ @include foldedup-effect;
+ width: #{$foldw/(60/25)}px;
+ height:#{$foldw/(60/20)}px;
+ -webkit-border-bottom-right-radius: #{$foldw/2}px;
+ -webkit-box-shadow: -2px -2px 5px rgba(0, 0, 0, 0.3);
+ -webkit-transform: rotate(-20deg) skew(-40deg,-3deg) translate(-#{$foldw/(60/13)}px,-#{$foldw/(60/13)}px);
+ }
+ &:after { /* shadow */
+ @include foldedup-effect;
+ z-index: -1;
+ width: #{$foldw/(60/50)}px;
+ height: #{$foldw/(60/50)}px;
+ background: rgba(0, 0, 0, 0.2);
+ display: inline-block;
+ -webkit-box-shadow: 20px 20px 8px rgba(0, 0, 0, 0.2);
+ -webkit-transform: rotate(0deg) translate(-#{$foldw/(60/45)}px,-#{$foldw/(60/30)}px) skew(20deg);
+ }
+}
+@mixin stack-effect {
+ content: '';
+ width: 98%;
+ z-index: -1;
+ border: 2px solid gray;
+ bottom: 0;
+ right: 0;
+ padding: 0 0 1px 0;
+ position: absolute;
+}
+@mixin piled {
+ -webkit-box-shadow: 1px 1px 4px rgba(0,0,0, 0.1);
+ -webkit-border-bottom-right-radius: 60px 5px;
+ &:before {
+ @include stack-effect;
+ height: 100%;
+ background: -webkit-gradient(linear, 0% 20%, 0% 92%, from(#fff), to(#f9f9f9), color-stop(.1,#fff));
+ -webkit-box-shadow: 1px 1px 8px rgba(0,0,0, 0.1);
+ -webkit-border-bottom-right-radius: 60px 5px;
+ -webkit-transform: skew(2deg,2deg) translate(3px,8px)
+ }
+ &:after {
+ @include stack-effect;
+ height: 98%;
+ background: -webkit-gradient(linear, 0% 20%, 0% 100%, from(#f3f3f3), to(#f6f6f6), color-stop(.1,#fff));
+ -webkit-box-shadow: 0px 0px 8px rgba(0,0,0, 0.1);
+ -webkit-transform: skew(2deg,2deg) translate(-1px,2px)
+ }
+}
+
+.diagram .note {
+ position: absolute;
+}
+.plain-note {
+ @include plain-note;
+}
+.folded-note {
+ @include plain-note;
+ @include default-gradient;
+ @include foldedup;
+}
+.piled-note {
+ @include plain-note;
+ @include default-gradient;
+ @include piled;
+}
+
+.note {
+ $bwidth: 2px;
+ $bstyle: solid;
+ $bcolor: rgba(128,128,128,1);
+ $brad: 3px;
+ $bgcolor: rgba(0,64,128,0.4);
+ $foldc: rgba(0,64,128,0.6);
+ $pad: 4;
+ $foldw: 16;
+ $notewidth: 180;
+ $innerheight: 64;
+ $bdstyle: $bstyle $bwidth $bcolor;
+
+ width: #{$notewidth}px;
+ position: absolute;
+ z-index: 1;
+ .inner {
+ color: #fff5f3;
+ border: $bdstyle;
+ border-radius: $brad;
+ background-color: $bgcolor;
+ width: #{$notewidth - $foldw}px;
+ min-height: #{$innerheight}px;
+ padding-left: 4px;
+ }
+ &.fold.top-right {
+ .inner {
+ border-right: none;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ &::before {
+ position: absolute;
+ content: '';
+ top: 1px;
+ right: -13px;
+ margin-top: 21px;
+ border-right: $bdstyle;
+ border-bottom: $bdstyle;
+ border-bottom-right-radius: $brad;
+ width: 21px;
+ min-height: #{$innerheight - 20}px;
+ background-color: $bgcolor;
+ }
+ &::after {
+ $bdstyle: $bstyle #{$foldw}px transparent;
+ position: absolute;
+ content: '';
+ top: 0px;
+ right: -4px;
+ border-top: $bdstyle;
+ border-bottom: $bdstyle;
+ border-right: $bstyle #{$foldw}px $foldc;
+ -webkit-transform: rotate(-45deg);
+ }
+ }
+ }
+}
27 lib/jumly/css/object.less
@@ -0,0 +1,27 @@
+@import "commons";
+.diagram {
+ //font-size: 77%;
+ //color: black;
+ position: relative;
+ font-size:10pt;
+ padding-bottom: 8px;
+ .object.iconified {
+ text-align: center;
+ .name {
+ height: auto;
+ padding-top: 0.4em;
+ padding-bottom: 0.4em;
+ }
+ .icon-container {
+ position: relative;
+ margin-bottom: 5px;
+ @include centering;
+ @include unimportant;
+ }
+ canvas.icon {
+ border: none;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ }
+ }
+}
8 lib/jumly/css/relationship.less
@@ -0,0 +1,8 @@
+.diagram {
+ .relationship {
+ position: absolute;
+ canvas {
+ -border: 1px solid red;
+ }
+ }
+}
151 lib/jumly/css/sequence.less
@@ -0,0 +1,151 @@
+@import "commons";
+.sequence-diagram {
+ @min_width: 88px;
+ @min_height: 31px;
+ font-family: Tahoma,Verdana;
+ .object-lane, .stop {
+ position: relative;
+ }
+ // ---------------- Object ----------------
+ .object {
+ position: absolute;
+ .name {
+ .primary_border;
+ text-align: center;
+ min-width: @min_width;
+ padding: 0 4px;
+ height: @min_height;
+ display: table-cell;
+ vertical-align: middle;
+ background-color: white;
+ }
+ }
+ // ---------------- Lifeline ----------------
+ .lifeline,
+ .lifeline .line {
+ position: absolute;
+ }
+ .lifeline .line {
+ margin-left: 51%;
+ .unimportant;
+ border-left: dashed 1px gray;
+ }
+ // ---------------- Occurrence ----------------
+ .occurrence {
+ position: relative;
+ width: 12px;
+ padding: 1em 0 1em 0;
+ margin-top: 4px;
+ .primary_border;
+ background-color: lightgray;
+ z-index: 1;
+ }
+ .message.create + .occurrence {
+ padding: 0.5em 0 0.5em 0;
+ }
+ // ---------------- Interaction ----------------
+ .interaction {
+ position: relative;
+ }
+ .interaction.lost {
+ .occurrence.icon {
+ height: 20px;
+ width: 20px;
+ .border_radius(20px);
+ padding: 0;
+ margin: 0;
+ border-color: #999;
+ background-color: #aaa;
+ }
+ .occurrence.icon::before {
+ content: '';
+ }
+ }
+ // ---------------- Message ----------------
+ .message,
+ .message canvas {
+ position: absolute;
+ }
+ .message {
+ height: 20px;
+ .name {
+ margin-top: -10px;
+ text-align: center;
+ }
+ }
+ .message.lost .icon {
+ background-color: gray;
+ border: 1px solid #444;
+ }
+ .message.create .name::before {
+ content: '<<create>>';
+ }
+ .message.return {
+ margin-top: -10px;
+ }
+ .fragment {
+ > .reply {
+ margin-bottom: 0.5em; // padding b/w .reply and .fragment inset.
+ }
+ }
+ .message.return .name::before {
+ content: '<<return>>';
+ }
+ .message.destroy .name::before {
+ content: '<<destroy>>';
+ }
+ // ---------------- Fragment ----------------
+ .fragment {
+ @inset_padding: 4px;
+ margin: 4px 4px 8px 4px;
+ padding: @inset_padding;
+ border: solid 1px #aaa;
+ .border_radius(2px);
+ @condition_color: black;
+ @condition_font_weight: normal;
+ .header {
+ .name {
+ color: black;
+ font-weight: bold;
+ }
+ .condition {
+ color: @condition_color;
+ font-weight: @condition_font_weight;
+ }
+ }
+ &.alt {
+ > .condition {
+ color: @condition_color;
+ font-weight: @condition_font_weight;
+ //height: 0px;
+ margin-bottom: -0.5em;
+ }
+ div.divider {
+ border-bottom: 1px dashed #555;
+ margin: 4px -@inset_padding 4px -@inset_padding;
+ }
+ }
+ }
+ // ---------------- Ref ----------------
+ .ref {
+ position: relative;
+ margin: 4px;
+ padding: 4px;
+ padding-bottom: 1em;
+ background-color: white;
+ border: solid 1px black;
+ .tag {
+ color: black;
+ font-weight: bold;
+ }
+ .name {
+ text-align: center;
+ }
+ }
+ .object .name, .occurrence, .ref {
+ .box_shadow;
+ }
+ .object {
+ .text_shadow;
+ }
+}
18 lib/jumly/css/spacing.less
@@ -0,0 +1,18 @@
+.spacing {
+ position: absolute;
+}
+
+.sequence-diagram {
+ .horizontal {
+ width: 150px - (88px + 4px);
+ }
+}
+
+.sequence-diagram {
+ .horizontal {
+ border: solid rgb(255,128,128,0.5) 1px;
+ height: 1em;
+ background-color: rgba(255,128,128,0.33);
+ }
+}
+
34 lib/jumly/css/theme/cool.scss
@@ -0,0 +1,34 @@
+@import "../_commons";
+
+$c0: #527bc6;
+$c1: lighten($c0, 10%);
+$c2: lighten($c1, 10%);
+$c3: lighten($c2, 10%);
+$c4: lighten($c3, 10%);
+
+.sequence-diagram.theme-cool {
+ .object .name {
+ color: white;
+ text-shadow: rgba(0,0,0,0.8) 3px 0px 2px;
+ font-weight: bold;
+ white-space: nowrap;
+ padding: {
+ left: 0.5em;
+ right: 0.5em;
+ }
+ &:hover {
+ color: complement($c0);
+ }
+ }
+ .object .name, .occurrence {
+ background-color: $c0;
+ @include border_radius(4px);
+ white-space: nowrap;
+ }
+ .message .name {
+ &:hover {
+ color: invert($c0);
+ }
+ }
+}
+
25 lib/jumly/css/theme/monotone.scss
@@ -0,0 +1,25 @@
+/* Monotone-style */
+@import "../_commons";
+
+$darkgray: #444;
+$c1: lighten($darkgray, 65%);
+
+.sequence-diagram.theme-monotone {
+ .object .name {
+ @include border_radius(6px);
+ background-color: $c1;
+ text-shadow: 2px 0px white;
+ }
+ .message {
+ .name {
+ color: white;
+ background-color: $darkgray;
+ padding: 0.25em 0.5em 0.5em 0.5em;
+ @include border_radius(6px);
+ opacity: 0.66;
+ @include box_shadow;
+ border: 1px solid black;
+ }
+ }
+}
+
121 lib/jumly/css/theme/pop.scss
@@ -0,0 +1,121 @@
+@import "../_object";
+@import "../_relationship";
+
+.component-diagram {
+ $min_width: 88*2;
+ $min_height: $min_width*5/8;
+ $r: 16;
+
+ .component {
+ > .frame {
+ padding-top: 0.5em;
+ padding-left: 1em;
+ @include primary_border;
+ min-width: #{$min_width}px;
+ min-height: #{$min_height}px;
+ @include box_shadow;
+ margin-left: #{$r*2 + $primary_border_width}px; // Space to put interfaces
+ margin-right: #{$r*2 + $primary_border_width}px; // Space to put interfaces
+ background-color: white;
+ -webkit-border-radius: 4px;
+ > .stereotype {
+ text-align: center;
+ }
+ > .name {
+ text-align: center;
+ font-weight: bold;
+ }
+ > .icon {
+ $t: $r;
+ width: #{$t*8/5}px;
+ height: #{$t*8/5}px;
+ margin-left: auto;
+ margin-right: 1em;
+ @include primary_border;
+ @include box_shadow2;
+ &:before, &:after {
+ content: ' ';
+ position: absolute;
+ width: #{$t}px;
+ height: #{$t/2}px;
+ @include primary_border;
+ margin-top : #{$primary_border_width}px;
+ margin-left: -#{$t/2 + $primary_border_width}px;
+ background-color: white;
+ }
+ &:after {
+ margin-top: #{$t*8/5 - $t/2 - $primary_border_width*3}px;
+ }
+ }
+ }
+ .provided-interface, .required-interface {
+ position: absolute;
+ -z-index: -1; // ignore in order to show whole of name.
+ > .icon {
+ width: #{$r*2}px;
+ height: #{$r + $primary_border_width*2}px;
+ > * {
+ position: absolute;
+ @include box_shadow;
+ }
+ &:before, &:after { // 'after' is line.
+ content: ' ';
+ position: absolute;
+ width: #{$r}px;
+ }
+ &:before {
+ height: #{$r}px;
+ @include box_shadow;
+ }
+ &:after {
+ border-top: #{$primary_border_width}px $primary_border_style $primary_border_color;
+ margin-top: #{$r/2}px;
+ margin-left: #{$r+$primary_border_width}px;
+ }
+ }
+ &.to-right .name {
+ margin-left: 1em;
+ }
+ }
+ .provided-interface > .icon {
+ &:before {
+ @include primary_border;
+ background-color: white;
+ -webkit-border-radius: #{$r}px;
+ }
+ }
+ .required-interface > .icon { // 'U' shape icon
+ &:before {
+ border-right: #{$primary_border_width}px $primary_border_style $primary_border_color;
+ border-top: #{$primary_border_width}px $primary_border_style $primary_border_color;
+ border-bottom: #{$primary_border_width}px $primary_border_style $primary_border_color;
+ -webkit-border-top-right-radius : #{$r}px;
+ -webkit-border-bottom-right-radius: #{$r}px;
+ }
+ }
+ .port {
+ display: none;
+ > .icon {
+ width: #{$r*3/4}px;
+ height: #{$r*3/4}px;
+ @include primary_border;
+ }
+ }
+ }
+ .component {
+ $mg: 10%;
+ &:nth-child(odd) {
+ float: left;
+ margin-right: $mg;
+ margin-left: $mg;
+ margin-top: 8px;
+ }
+ &:nth-child(even) {
+ float: right;
+ margin-left: $mg;
+ margin-right: $mg;
+ margin-top: 24px;
+ }
+ }
+}
+
43 lib/jumly/css/usecase-a.less
@@ -0,0 +1,43 @@
+.usecase-diagram.layout-a {
+ .usecase {
+ &:nth-child(odd) > .icon {margin-left: auto;}
+ &:nth-child(even) > .icon {margin-right: auto;}
+ }
+ .actor {
+ &:nth-child(odd) {float: left;}
+ &:nth-child(even) {float: right;}
+ position: relative;
+ }
+ .extend, .include, .use {
+ .name {
+ &:before {
+ left: 45%; top: 45%;
+ position: absolute;
+ }
+ }
+ }
+ .system-boundary {
+ width: 55%;
+ margin-left: auto;
+ margin-right: auto;
+ > .name {
+ font-weight: bold;
+ }
+ .usecase:nth-child(4n + 2) .icon {margin-left:4px;}
+ .usecase:nth-child(3) .icon {margin-right:4px;}
+ .system-boundary {
+ margin-top: 4px;
+ width: 95%;
+ }
+ &.out-of-bounds {
+ border-width: 0;
+ border-color: transparent;
+ box-shadow: none;
+ width: 0px;
+ height: 0px;
+ padding: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ }
+ }
+}
34 lib/jumly/css/usecase.less
@@ -0,0 +1,34 @@
+@import "commons";
+
+.usecase-diagram {
+ @h: 44*3px;
+ @v: 17*3px;
+ .usecase {
+ .icon {
+ min-width: @h;
+ min-height: @v;
+ .primary_border;
+ .border_radius(~"@{h}px / @{v}px");
+ .box_shadow;
+ background-color: white;
+ .name {
+ text-align: left;
+ vertical-align: middle;
+ }
+ }
+ }
+ .use .name:before {content:'<<use>>';}
+ .extend .name:before {content:'<<extend>>';}
+ .include .name:before {content:'<<include>>';}
+ .system-boundary {
+ .primary_border;
+ .box_shadow;
+ padding:4px;
+ background-color: white;
+ .system-boundary {
+ min-width: @h*1.5;
+ }
+ }
+}
+
+@import "usecase-a";
107 lib/jumly/js/0.1.0.coffee
@@ -0,0 +1,107 @@
+jumly = $.jumly
+
+jumly.identify = (e)->
+ unless e then return null
+ if (p for p of e).length is 1 and p is "id"
+ switch typeof e.id
+ when "number", "string" then e.id
+ when "function" then e.id()
+ else return null
+
+jumly.normalize = (a, b) ->
+ return a if a is undefined or a is null
+ switch typeof a
+ when "string" then return $.extend name:a, b
+ when "boolean", "number" then return null
+ return null if $.isArray a
+ return a if a.hasOwnProperty "id"
+ r = {}
+ for key, val of a
+ if typeof key is "string" and key.match(/[1-9][0-9]*/) and typeof val is "string"
+ r.id = parseInt key
+ r.name = val
+ else
+ r[key] = val
+ if r.hasOwnProperty "name"
+ r
+ else
+ r.name = key
+ attrs = r[key]
+ delete r[key]
+ $.extend r, attrs
+
+jumly.lang =
+ _gives: (a, dic)->
+ gives = (query)->
+ r = dic[query]
+ if r then r else null
+ if !a.gives then return gives
+ f = a.gives
+ (query)->
+ r = f(query)
+ if r.length > 0 or r.of or r.as
+ return r
+ gives(query)
+ _as: (m)-> as:(e)-> m[e]
+ _of: (nodes, query)->
+ (unode)->
+ n = nodes.filter (i, e)->
+ e = jumly(e)[0]
+ s = e.gives(unode.jprops().type)
+ if s is unode then e else null
+ if n.length > 0 then jumly(n)[0] else []
+
+SCRIPT_TYPE_PATTERN = /text\/jumly-(.*)-diagram|text\/jumly\+(.*)|application\/jumly\+(.*)/
+_to_type_string = (type)->
+ unless type.match SCRIPT_TYPE_PATTERN then throw "Illegal type: #{type}"
+ kind = RegExp.$1 + RegExp.$2 + RegExp.$3
+
+window.JUMLY.evalHTMLScriptElement = (script) ->
+ script = $ script
+ type = script.attr("type")
+ throw "Not found: type attribute in script" unless type
+ kind = _to_type_string type
+ compiler = _dsl ".#{kind}-diagram"
+ throw "Not found: compiler for '.#{kind}'" unless compiler
+ throw "Not found: compileScript" unless compiler.compileScript
+ diag = compiler.compileScript script
+
+ _before = (diag, target, script)->
+ if target[0]?.localName is "head" then diag.appendTo $ "body"
+ else if script.attr("target-id") then target.html diag
+ else diag.insertAfter script
+
+ _determine = (script)->
+ targetid = script.attr "target-id"
+ if targetid then $ "##{targetid}"
+ else script.parent()
+
+ target = _determine script
+ _before diag, target, script
+ diag.compose()
+
+dsl_ = {}
+_dsl = (args) ->
+ throw "It MUST NOT be null." if args is null
+ return dsl_[args] if typeof args is "string"
+ throw "DSL can only accept an object." unless typeof args is "object" and not $.isArray args
+ throw "type property is required." unless args.type
+ throw "compileScript property is required." unless args.compileScript
+ dsl_[args.type] = {compileScript:args.compileScript, version:args.version}
+
+window.JUMLY.DSL = _dsl
+
+_runScripts = ->
+ return null if _runScripts.done
+ scripts = document.getElementsByTagName 'script'
+ diagrams = (s for s in scripts when s.type.match /text\/jumly+(.*)/)
+ for script in diagrams
+ JUMLY.evalHTMLScriptElement script
+ _runScripts.done = true
+ null
+
+# Listen for window load, both in browsers and in IE.
+if window.addEventListener
+ window.addEventListener 'DOMContentLoaded', _runScripts
+else
+ throw "window.addEventListener is not supported"
88 lib/jumly/js/ClassDiagram.coffee
@@ -0,0 +1,88 @@
+##
+JUMLY = window.JUMLY
+class JUMLYClassDiagram extends JUMLY.Diagram
+
+JUMLYClassDiagram::member = (kind, clz, normval)->
+ holder = clz.find(".#{kind}s")
+ $(normval["#{kind}s"]).each (i, e)->
+ id = "#{normval.id}-#{kind}-#{e}"
+ throw new Error("Already exists #{e}") if holder.find(".#{e}").length > 0
+ holder.append $("<li>").addClass(e).attr("id", id).html e
+
+JUMLYClassDiagram::declare = (normval) ->
+ clz = $.jumly ".class", normval
+ if normval.stereotype
+ clz.find(".stereotype").html normval.stereotype
+ else
+ clz.find(".stereotype").hide()
+
+ @member(kind, clz, normval) for kind in ["attr", "method"]
+
+ ref = @_regByRef_ normval.id, clz
+ eval "#{ref} = clz"
+ @append clz
+
+JUMLYClassDiagram::preferredWidth = ->
+ @find(".class .icon").mostLeftRight().width() + 16 ##WORKAROUND: 16 is magic number.
+
+JUMLYClassDiagram::preferredHeight = ->
+ @find(".class .icon").mostTopBottom().height()
+
+JUMLYClassDiagram::compose = ->
+ @trigger "beforeCompose", [this]
+ ## Resize for looks
+ @find(".class .icon").each (i, e) ->
+ e = $ e
+ return null if e.width() > e.height()
+ e.width e.height() * (1 + Math.sqrt 2)/2
+ @trigger "afterCompose", [this]
+ @width @preferredWidth()
+ @height @preferredHeight()
+ this
+
+###
+<div class="class icon">
+ <span class="stereotype">abstract</span>
+ <span class="name">UMLObject</span>
+ <ul class="attrs">
+ <li>name</li>
+ <li>stereotypes</li>
+ </ul>
+ <ul class="methods">
+ <li>activate</li>
+ <li>isLeftAt(a)</li>
+ <li>isRightAt(a)</li>
+ <li>iconify(fixture, styles)</li>
+ <li>lost</li>
+ </ul>
+</div>
+###
+class JUMLYClass extends JUMLY.HTMLElement
+JUMLYClass::_build_ = (div)->
+ icon = $("<div>")
+ .addClass("icon")
+ .append($("<div>").addClass "stereotype")
+ .append($("<div>").addClass "name")
+ .append($("<ul>").addClass "attrs")
+ .append($("<ul>").addClass "methods")
+ div.addClass("object")
+ .append(icon)
+
+JUMLY.def ".class-diagram", JUMLYClassDiagram
+JUMLY.def ".class", JUMLYClass
+
+
+class JUMLYClassDiagramBuilder extends JUMLY.DiagramBuilder
+ constructor: (@diagram) ->
+
+JUMLYClassDiagramBuilder::def = (props)->
+ @diagram.declare JUMLY.Identity.normalize props
+
+##Deprecated
+JUMLYClassDiagramBuilder::start = (acts)-> acts.apply this, []
+
+JUMLY.DSL type:".class-diagram", compileScript: (script) ->
+ b = new JUMLYClassDiagramBuilder
+ b.build script.html()
+
+JUMLY.ClassDiagramBuilder = JUMLYClassDiagramBuilder
24 lib/jumly/js/Diagram.coffee
@@ -0,0 +1,24 @@
+JUMLY = window.JUMLY
+
+class JUMLYDiagram extends JUMLY.HTMLElement
+ ## v0.1.1 Tenatative Implementation.
+ @isIDExisting = (id, diag)->
+ if JUMLY.Preferences "document.id.validation.enable"
+ $("##{id}").length > 0
+ else
+ false
+
+JUMLYDiagram::_build_ = (div)-> div.addClass "diagram"
+
+JUMLYDiagram::_def_ = (varname, e)-> eval "#{varname} = e"
+
+JUMLYDiagram::_regByRef_ = (id, obj)->
+ ref = JUMLY.Naming.toRef id
+ throw new Error("Already exists for '#{ref}' in the " + $.kindof(this)) if this[ref]
+ throw new Error("Element which has same ID(#{id}) already exists in the document.") if JUMLY.Diagram.isIDExisting? id, this
+ this[ref] = obj
+ ref
+
+JUMLY.Diagram = JUMLYDiagram
+
+
39 lib/jumly/js/DiagramBuilder.coffee
@@ -0,0 +1,39 @@
+JUMLY = window.JUMLY
+
+class DiagramBuilder
+ @selectHTMLScriptElements = (e)->
+ $("script", e).not(".ignored")
+
+DiagramBuilder::beforeCompose = (f)->
+ @diagram.bind "beforeCompose", f
+ this
+
+DiagramBuilder::afterCompose = (f)->
+ @diagram.bind "afterCompose", f
+ this
+
+DiagramBuilder::before = -> @beforeCompose.apply this, arguments
+
+DiagramBuilder::after = -> @afterCompose.apply this, arguments
+
+DiagramBuilder::accept = (f)-> f.apply this, []
+
+DiagramBuilder::build = (jumlipt)->
+ try
+ typename = this.constructor.name.replace(/^JUMLY/, "")
+ .replace(/Diagram(Builder)?$/, "")
+ .toLowerCase()
+ @diagram = $.jumly ".#{typename}-diagram"
+ @accept -> eval CoffeeScript.compile jumlipt
+ @diagram.trigger "build.after"
+ @diagram
+ catch ex
+ console.error ex.stack, jumlipt
+ throw new JUMLY.Error "failed_to_build", "Failed to build", [], ex, jumlipt
+
+DiagramBuilder::note = (text)->
+ $.jumly(".note").find(".content").html(text).end().appendTo @diagram
+
+JUMLY.DiagramBuilder = DiagramBuilder
+
+
17 lib/jumly/js/ElementLayout.coffee
@@ -0,0 +1,17 @@
+class JUMLYHorizontalSpacing
+ constructor: (a, b)->
+ $.extend this, $("<span>")
+ @data("a", a)
+ @data("b", b)
+ @addClass "horizontal"
+ @addClass "spacing"
+
+JUMLYHorizontalSpacing::apply = ->
+ a = @data("a")
+ b = @data("b")
+ a.after this
+ @offset left:a.offset().left + a.outerWidth(), top:a.offset().top
+ b.offset left:@offset().left + @outerWidth() + 1
+
+JUMLY.HorizontalSpacing = JUMLYHorizontalSpacing
+
14 lib/jumly/js/HTMLElement.coffee
@@ -0,0 +1,14 @@
+JUMLY = window.JUMLY
+
+class JUMLYHTMLElement
+ constructor: ->
+ cls = JUMLY.Naming.toCSSClass @constructor.name
+ p = $.extend this, $("<div>").addClass cls
+ a = Array.prototype.slice.apply arguments
+ a.unshift p
+ @_build_?.apply this, a
+ this
+
+JUMLY.HTMLElement = JUMLYHTMLElement
+
+
17 lib/jumly/js/HTMLElementLayout.coffee
@@ -0,0 +1,17 @@
+class JUMLYHorizontalSpacing
+ constructor: (a, b)->
+ $.extend this, $("<span>")
+ @data("a", a)
+ @data("b", b)
+ @addClass "horizontal"
+ @addClass "spacing"
+
+JUMLYHorizontalSpacing::apply = ->
+ a = @data("a")
+ b = @data("b")
+ a.after this
+ @offset left:a.offset().left + a.outerWidth(), top:a.offset().top
+ b.offset left:@offset().left + @outerWidth() + 1
+
+JUMLY.HorizontalSpacing = JUMLYHorizontalSpacing
+
9 lib/jumly/js/Note.coffee
@@ -0,0 +1,9 @@
+JUMLY = window.JUMLY
+class JUMLYNote extends JUMLY.HTMLElement
+
+JUMLYNote::_build_ = (div, a)->
+ div.addClass("note")
+ .append($("<div>").addClass("inner")
+ .append($("<div>").addClass("content").html a))
+
+JUMLY.def ".note", JUMLYNote
43 lib/jumly/js/Object.coffee
@@ -0,0 +1,43 @@
+JUMLY = window.JUMLY
+class JUMLYObject extends JUMLY.HTMLElement
+
+JUMLYObject::_build_ = (div)->
+ div.addClass("object")
+ .append($("<div>").addClass("name"))
+
+JUMLYObject::activate = ->
+ _as = $.jumly.lang._as
+ occurr = $.jumly(type:".occurrence", ".object":this)
+ iact = $.jumly(type:".interaction", ".occurrence":_as(".actor":null, ".actee":occurr), ".actor":null, ".actee":occurr)
+ iact.addClass "activated"
+ iact.find(".message").remove()
+ iact.append(occurr)
+ @parent().append(iact)
+ occurr
+
+JUMLYObject::isLeftAt = (a)-> @offset().left < a.offset().left
+
+JUMLYObject::isRightAt = (a)-> (a.offset().left + a.width()) < @offset().left
+
+JUMLYObject::iconify = (fixture, styles)->
+ unless typeof fixture is "function"
+ fixture = $.jumly.icon["." + fixture] || $.jumly.icon[".actor"]
+ canvas = $("<canvas>").addClass("icon")
+ container = $("<div>").addClass("icon-container")
+ @addClass("iconified").prepend(container.append canvas)
+
+ {size, styles} = fixture canvas[0], styles
+ container.css height:size.height #, width:size.width ##FIXME: Way to decide the width.
+ render = =>
+ name = @find(".name")
+ styles.fillStyle = name.css("background-color")
+ styles.strokeStyle = name.css("border-top-color")
+ fixture canvas[0], styles
+ this.renderIcon = -> render()
+ this
+
+JUMLYObject::lost =-> @activate().interact(null, {stereotype:".lost"})
+
+JUMLY.def ".object", JUMLYObject
+
+
45 lib/jumly/js/Position.coffee
@@ -0,0 +1,45 @@
+class JUMLYPosition
+ constructor: (@attrs)->
+ @div = $("<div>").addClass(@attrs.css)
+ .css position:"absolute"
+JUMLYPosition::_coordinate = (target)->
+ if @attrs.coordinate
+ @attrs.coordinate.append @div
+ else
+ target.after @div
+
+class JUMLYPositionRightLeft extends JUMLYPosition
+class JUMLYPositionLeftRight extends JUMLYPosition
+class JUMLYPositionLeft extends JUMLYPosition
+class JUMLYPositionTop extends JUMLYPosition
+
+JUMLYPositionRightLeft::apply = ->
+ src = @attrs.src
+ @_coordinate src
+ @div.offset left:src.offset().left + src.outerWidth()
+ @attrs.dst.offset left:@div.offset().left + @div.outerWidth()
+
+JUMLYPositionLeftRight::apply = ->
+ src = @attrs.src
+ dst = @attrs.dst
+ @_coordinate src
+ @div.offset left:src.offset().left - @div.outerWidth()
+ @attrs.dst.offset left:@div.offset().left - @attrs.dst.outerWidth()
+
+JUMLYPositionLeft::apply = ->
+ dst = @attrs.dst
+ @_coordinate dst
+ @attrs.dst.offset left:(@div.offset().left + @div.outerWidth())
+
+JUMLYPositionTop::apply = ->
+ dst = @attrs.dst
+ @_coordinate dst
+ @attrs.dst.offset top:(@div.offset().top + @div.outerHeight())
+
+
+JUMLYPosition.RightLeft = JUMLYPositionRightLeft
+JUMLYPosition.LeftRight = JUMLYPositionLeftRight
+JUMLYPosition.Left = JUMLYPositionLeft
+JUMLYPosition.Top = JUMLYPositionTop
+
+JUMLY.Position = JUMLYPosition
99 lib/jumly/js/Relationship.coffee
@@ -0,0 +1,99 @@
+JUMLY = window.JUMLY
+class JUMLYRelationship extends JUMLY.HTMLElement
+
+JUMLYRelationship::_build_ = (div, opts)->
+ @src = opts.src
+ @dst = opts.dst
+ div.append($("<canvas>").addClass("icon"))
+ .append($("<div>").addClass("name"))
+
+MESSAGE_STYLE =
+ width : 1
+ base : 6
+ height : 10
+ lineWidth : 1.5
+ shape : "dashed"
+ pattern : [8, 8]
+ strokeStyle: 'gray'
+ fillStyle : 'gray'
+ lineJoin : 'round'
+
+Math.sign = (x) ->
+ if x is 0
+ return 0
+ else if x > 0
+ return 1
+ return -1
+
+JUMLYRelationship::render = ->
+ margin_left = $("body").cssAsInt "margin-left"
+ margin_top = $("body").cssAsInt "margin-top"
+ pt = (obj) ->
+ s = obj.offset()
+ dh = 0*(obj.cssAsInt "margin-left") - margin_left
+ dv = 0*obj.css("margin-top").toInt() - margin_top
+ p = left:s.left + obj.outerWidth()/2 + dh, top:s.top + obj.outerHeight()/2 + dv
+
+ rect = (p, q) ->
+ a = left:Math.min(p.left, q.left), top:Math.min(p.top, q.top)
+ b = left:Math.max(p.left, q.left), top:Math.max(p.top, q.top)
+ w = b.left - a.left + 1
+ h = b.top - a.top + 1
+ hs = Math.sign(q.left - p.left)
+ vs = Math.sign(q.top - p.top)
+ l = Math.sqrt w*w + h*h
+ r = left:a.left, top:a.top, width:w, height:h, hsign:hs, vsign:vs, hunit:hs*w/l, vunit:vs*h/l
+
+ expand = (rect, val) ->
+ if typeof val is "number" then return expand rect, left:val, top:val, right:val, bottom:val
+ r = $.extend {}, rect
+ for d of val
+ switch d
+ when "left"
+ r.left -= val[d]
+ r.width += val[d]
+ when "top"
+ r.top -= val[d]
+ r.height += val[d]
+ when "right"
+ r.width += val[d]
+ when "bottom"
+ r.height += val[d]
+ r
+
+ srcicon = @src.find(".icon")
+ dsticon = @dst.find(".icon")
+ p = pt srcicon
+ q = pt dsticon
+ r = rect p, q
+
+ cr = 2
+ aa = r.hunit*dsticon.outerWidth()/cr
+ bb = r.vunit*dsticon.outerHeight()/cr
+ cc = r.hunit*srcicon.outerWidth()/cr
+ dd = r.vunit*srcicon.outerHeight()/cr
+ s = x:p.left - r.left + cc, y:p.top - r.top + dd
+ t = x:q.left - r.left - aa, y:q.top - r.top - bb
+
+ margin = 4
+ r = expand r, margin
+ @width r.width
+ @height r.height
+ @offset left:r.left, top:r.top
+
+ ctxt = @find("canvas").css( width:r.width, height:r.height)
+ .attr(width:r.width, height:r.height)[0]
+ .getContext "2d"
+ ctxt.save()
+ ctxt.translate margin, margin
+ style = $.extend {}, MESSAGE_STYLE, pattern:[4, 4], shape:'line'
+ if @hasClass("extend")
+ style = $.extend style, shape:'dashed'
+ #if @hasClass("use")
+ # style = $.extend style, width:0, base:0, height:0
+ $.g2d.arrow ctxt, s, t, style
+ ctxt.restore()
+
+JUMLY.def ".relationship", JUMLYRelationship
+JUMLY.Relationship = JUMLYRelationship
+
523 lib/jumly/js/SequenceDiagram.coffee
@@ -0,0 +1,523 @@
+JUMLY = window.JUMLY
+class JUMLYMessage extends JUMLY.HTMLElement
+class JUMLYInteraction extends JUMLY.HTMLElement
+class JUMLYLifeline extends JUMLY.HTMLElement
+class JUMLYOccurrence extends JUMLY.HTMLElement
+class JUMLYFragment extends JUMLY.HTMLElement
+class JUMLYRef extends JUMLY.HTMLElement
+types =
+ ".message" : JUMLYMessage
+ ".interaction" : JUMLYInteraction
+ ".lifeline" : JUMLYLifeline
+ ".occurrence" : JUMLYOccurrence
+ ".fragment" : JUMLYFragment
+ ".ref" : JUMLYRef
+JUMLY.def e, types[e] for e of types
+jumly = $.jumly
+
+JUMLYMessage::_build_ = (div)->
+ div.append($("<canvas>").addClass "arrow")
+ .append($("<div>").addClass "name")
+
+JUMLYMessage::_lineToNextOccurr = (canvas) ->
+ if false #@hasClass("destroy")) {
+ ##FIXME: Destroy message
+ console.log "FIXME: to avoid runtime error."
+ {src:{x:0, y:0}, dst:{x:400, y:0}}
+ srcll = @_srcOccurr()
+ dstll = @_dstOccurr()
+ @_toLine srcll, dstll, canvas
+
+JUMLYMessage::_toLine = (srcll, dstll, canvas) ->
+ # Lost message is always toward right.
+ if !@parent().hasClass("lost") and @isTowardLeft()
+ src:
+ x: srcll.offset().left - @offset().left
+ y: canvas.outerHeight()/2
+ dst:
+ x: dstll.outerWidth()
+ y: canvas.outerHeight()/2
+ else
+ src:
+ x: srcll.outerWidth()
+ y: canvas.outerHeight()/2
+ dst:
+ x: dstll.offset().left - srcll.offset().left
+ y: canvas.outerHeight()/2
+
+JUMLYMessage::_srcOccurr = -> @parents(".occurrence:eq(0)").self()
+
+JUMLYMessage::_dstOccurr = -> (if @hasClass "return" then @prev ".occurrence" else $ "~ .occurrence", this).self()
+
+JUMLYMessage::_prefferedCanvas = ->
+ @find("canvas:eq(0)")
+ .attr(width:@width(), height:@height())
+ .css (width:@width(), height:@height())
+
+JUMLYMessage::_toCreateLine = (canvas)->
+ e = @_toLine @_srcOccurr(), @_dstOccurr().gives(".object"), canvas
+ if @isTowardLeft()
+ src = @_srcOccurr()
+ e.dst.x = src.gives(".object").outerRight() - src.offset().left
+ e
+
+JUMLYMessage::_findOccurr = (actee)->
+ occurr = null
+ @parents(".occurrence").each (i, e)=>
+ e = $(e).self()
+ if e.gives(".object") is actee
+ occurr = e
+ occurr
+
+MESSAGE_STYLE =
+ width : 1
+ base : 6
+ height : 10
+ lineWidth : 1.5
+ shape : "line2"
+ pattern : [8, 8]
+ strokeStyle: 'gray'
+ fillStyle : 'gray'
+
+STEREOTYPE_STYLES = # From streotype to style object
+ create : {shape: "dashed"}
+ asynchronous: {shape: "line"}
+ synchronous : {shape: "line2", fillStyle: 'gray'}
+ destroy : {shape: "line2", fillStyle: 'gray'}
+
+_determine_primary_stereotype = (jqnode) ->
+ for e in ["create", "asynchronous", "synchronous", "destroy"]
+ return e if jqnode.hasClass e
+
+JUMLYMessage::repaint = (style) ->
+ arrow = jQuery.extend {}, MESSAGE_STYLE, style, STEREOTYPE_STYLES[_determine_primary_stereotype this]
+ # Canvas element has width x height property of CSS and posseses width x height attribute as DOM element.
+ # So if you don't set same value to both, the rendered result may be inconsistent.
+ canvas = @_current_canvas = @_prefferedCanvas()
+
+ if style?.inherit
+ p = @parents(".occurrence:eq(0)")
+ arrow.fillStyle = p.css "background-color"
+ arrow.strokeStyle = p.css "border-top-color"
+ (p.css "box-shadow").match /(rgba\(.*\)) ([0-9]+)px ([0-9]+)px ([0-9]+)px ([0-9]+)px/
+ arrow.shadowColor = RegExp.$1
+ arrow.shadowOffsetX = RegExp.$2
+ arrow.shadowOffsetY = RegExp.$3
+ arrow.shadowBlur = RegExp.$4
+
+ if arrow.self
+ ctxt = canvas[0].getContext '2d'
+ gap = 2
+ rcx = @width() - (gap + 4)
+ rey = @height() - (arrow.height/2 + 4)
+ llw = @_dstOccurr().outerWidth()
+ $.g2d.arrow ctxt, {x:rcx, y:rey}, {x:llw + gap, y:rey}, arrow
+ arrow.base = 0
+ $.g2d.arrow ctxt, {x:llw/2 + gap, y:gap}, {x:rcx, y:gap}, arrow
+ $.g2d.arrow ctxt, {x:rcx, y:gap}, {x:rcx, y:rey}, arrow
+ return this
+
+ if @hasClass "create"
+ line = @_toCreateLine canvas
+ else if @gives ".actee"
+ newsrc = @_findOccurr @gives ".actee"
+ newdst = @_dstOccurr()
+ line = @_toLine newsrc, newdst, canvas
+ else
+ line = @_lineToNextOccurr canvas
+
+ if arrow.reverse
+ a = line.src
+ line.src = line.dst
+ line.dst = a
+ arrow.shape = 'dashed'
+
+ jQuery.g2d.arrow canvas[0].getContext('2d'), line.src, line.dst, arrow
+ this
+
+JUMLYMessage::isToward = (dir) ->
+ iact = @gives(".interaction")
+ see = (a)-> iact.gives(".occurrence").as(a).gives(".object")
+ actor = see ".actor"
+ actee = see ".actee"
+ if "right" is dir
+ actor.isLeftAt(actee)
+ else if "left" is dir
+ actor.isRightAt(actee)
+
+JUMLYMessage::isTowardRight = -> @isToward "right"
+
+JUMLYMessage::isTowardLeft = -> @isToward "left"
+
+JUMLYMessage::_composeLooksOfCreation = ->
+ srcoccur = @_srcOccurr()
+ dstoccur = @_dstOccurr()
+ render = (msg) ->
+ msg.repaint()
+ .gives(".interaction").gives(".occurrence").as(".actee")
+
+ preffered_width = (msg) ->
+ l = msg._toLine srcoccur, dstoccur.gives(".object"), msg
+ Math.abs l.src.x - l.dst.x
+
+ centering_name = (msg, newwidth) ->
+ return if msg.isTowardLeft()
+ newleft = srcoccur.outerWidth() + msg.offset().left + (newwidth - msg.find(".name").outerWidth())/2
+ msg.find(".name").offset(left:newleft)
+
+ shift_down_lifeline = (obj) ->
+ diag = obj.parents ".sequence-diagram"
+ ll = $ ".lifeline .line:eq(1)", diag # Should be derrived from obj.
+ prevtop = ll.offset().top
+ ll.offset(top:obj.offset().top + obj.outerHeight())
+ ll.height ll.height() - (ll.offset().top - prevtop)
+
+ shift_downward = (msg) ->
+ created.offset top:msg.offset().top - created.height()/3
+ y = created.outerBottom() + parseInt dstoccur.css "margin-top"
+ dstoccur.offset(top:y)
+ iact = msg.parents(".interaction:eq(0)")
+ dy = iact.outerBottom() - dstoccur.outerBottom() - parseInt dstoccur.css "margin-top"
+ iact.css "margin-bottom", (Math.abs dy)
+
+ created = dstoccur.gives ".object"
+ w = preffered_width this
+ shift_downward this
+ render this
+ centering_name this, w
+ shift_down_lifeline created
+
+JUMLYInteraction::_build_ = (div, props)->
+ msg = jumly type:".message", ".interaction":this
+ props[".message"] = msg ## FIXME: Can I remove this?
+ div.append(msg)
+
+JUMLYInteraction::interact = (obj) -> @awayfrom().interact obj
+JUMLYInteraction::forward = (obj) -> @toward()
+
+JUMLYInteraction::to = (func) ->
+ occurrs = @gives(".occurrence")
+ tee = occurrs.as(".actee")
+ tor = occurrs.as(".actor")
+ func(tee, tor)
+
+JUMLYInteraction::forwardTo = -> @gives(".occurrence").as ".actee"
+JUMLYInteraction::backwardTo = -> @gives(".occurrence").as ".actor"
+JUMLYInteraction::toward = -> @forwardTo()
+JUMLYInteraction::awayfrom = (obj) ->
+ return @backwardTo() unless obj
+ for e in @parents(".occurrence").not(".activated")
+ e = $(e).self()
+ return e if e?.gives(".object") is obj
+ obj.activate()
+
+JUMLYInteraction::_compose_ = ->
+ that = this
+ src = that.gives(".occurrence").as ".actor"
+ dst = that.gives(".occurrence").as ".actee"
+ msg = jumly($ "> .message", that)[0]
+ # Self-invokation case
+ if @isToSelf()
+ @_buildSelfInvocation src, dst, msg
+ return
+
+ # Determine the width of interaction for normal message
+ w = src.offset().left - (dst.offset().left + $(".occurrence:eq(0)", that).width())
+ if @hasClass("lost")
+ msg.height dst.outerHeight()
+ else if msg.isTowardLeft()
+ w = dst.offset().left - (src.offset().left + $(".occurrence:eq(0)", that).width())
+ msg.width(Math.abs(w))
+ .offset(left:Math.min(src.offset().left, dst.offset().left))
+ .repaint()
+
+ # Locate the name of message
+ # Normal message
+ #TODO: Move this centering logic for name to .message class.
+
+ # Return message
+ rmsg = $("> .message.return:last", that).self()
+ if rmsg
+ x = msg.offset().left
+ actee = rmsg.gives ".actee"
+ if actee
+ newdst = rmsg._findOccurr actee
+ unless newdst
+ console.error "Not found occurrence for", actee
+ throw new Error("Not found occurrence #{actee.html()}")
+ w = dst.offset().left - newdst.offset().left
+ x = Math.min dst.offset().left, newdst.offset().left
+ rmsg.width(Math.abs w)
+ .offset(left:x)
+ .repaint(reverse:true)
+
+JUMLYInteraction::_buildSelfInvocation = (a, b, msg) ->
+ w = @find(".occurrence:eq(0)").outerWidth() ## It's based on the width of occurrence.
+ dx = w*2
+ dy = w*1
+ b.css top:0 + dy # Shift the actee occurrence to y-positive
+ @css "padding-bottom":dy # To expand the height of occurrence of actor
+
+ msg.css(top:0)
+ .width(b.width() + dx)
+ .height(b.offset().top - msg.offset().top + dy + w/8)
+ .offset left:b.offset().left
+
+ msg.repaint self:true
+
+ arrow = msg.find ".arrow"
+ msg.find(".name").offset
+ left: arrow.offset().left + arrow.outerWidth()
+ top : arrow.offset().top
+
+JUMLYInteraction::reply = (p) ->
+ @addClass "reply