From 26ebbf82ee4ef892f21cb85d80959e0b744272df Mon Sep 17 00:00:00 2001
From: Richard Gibson <richard.gibson@gmail.com>
Date: Fri, 6 Nov 2015 02:42:03 -0500
Subject: [PATCH 1/2] Support sequenceDiagram "over" notes

---
 .../parser/sequenceDiagram.jison              | 16 +++--
 src/diagrams/sequenceDiagram/sequenceDb.js    |  7 ++-
 .../sequenceDiagram/sequenceDiagram.spec.js   | 58 ++++++++++++++++++-
 .../sequenceDiagram/sequenceRenderer.js       | 33 ++++++-----
 4 files changed, 93 insertions(+), 21 deletions(-)

diff --git a/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison b/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison
index d15204f9aa..455b27df55 100644
--- a/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison
+++ b/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison
@@ -103,8 +103,16 @@ statement
 	;
 
 note_statement
-	: 'note' placement actor text2 {$$=[$3,{type:'addNote', placement:$2, actor:$3.actor, text:$4}];}
-	| 'note' 'over' spaceList actor_pair actor
+	: 'note' placement actor text2
+	{
+		$$ = [$3, {type:'addNote', placement:$2, actor:$3.actor, text:$4}];}
+	| 'note' 'over' actor_pair text2
+	{
+		// Coerce actor_pair into a [to, from, ...] array
+		$2 = [].concat($3, $3).slice(0, 2);
+		$2[0] = $2[0].actor;
+		$2[1] = $2[1].actor;
+		$$ = [$3, {type:'addNote', placement:yy.PLACEMENT.OVER, actor:$2.slice(0, 2), text:$4}];}
 	;
 
 spaceList
@@ -112,8 +120,8 @@ spaceList
     | SPACE
     ;
 actor_pair
-	: actor             { $$ = $1; }
-	| actor ',' actor   { $$ = [$1, $3]; }
+	: actor ',' actor   { $$ = [$1, $3]; }
+	| actor             { $$ = $1; }
 	;
 
 placement
diff --git a/src/diagrams/sequenceDiagram/sequenceDb.js b/src/diagrams/sequenceDiagram/sequenceDb.js
index 5b302cd129..1404ef605d 100644
--- a/src/diagrams/sequenceDiagram/sequenceDb.js
+++ b/src/diagrams/sequenceDiagram/sequenceDb.js
@@ -77,8 +77,11 @@ exports.PLACEMENT = {
 exports.addNote = function (actor, placement, message){
     var note = {actor:actor, placement: placement, message:message};
 
+    // Coerce actor into a [to, from, ...] array
+    var actors = [].concat(actor, actor);
+
     notes.push(note);
-    messages.push({from:actor, to:actor, message:message, type:exports.LINETYPE.NOTE, placement: placement});
+    messages.push({from:actors[0], to:actors[1], message:message, type:exports.LINETYPE.NOTE, placement: placement});
 };
 
 
@@ -132,4 +135,4 @@ exports.apply = function(param){
                 break;
         }
     }
-};
\ No newline at end of file
+};
diff --git a/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js b/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js
index d154e1175f..671acb0f90 100644
--- a/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js
+++ b/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js
@@ -231,6 +231,31 @@ describe('when parsing a sequenceDiagram',function() {
         expect(messages[0].from).toBe('Alice');
         expect(messages[2].from).toBe('John');
     });
+    it('it should handle notes over a single actor', function () {
+        var str = 'sequenceDiagram\n' +
+            'Alice->Bob: Hello Bob, how are you?\n' +
+            'Note over Bob: Bob thinks\n';
+
+        sq.parse(str);
+
+        var messages = sq.yy.getMessages();
+        expect(messages[1].from).toBe('Bob');
+        expect(messages[1].to).toBe('Bob');
+    });
+    it('it should handle notes over multiple actors', function () {
+        var str = 'sequenceDiagram\n' +
+            'Alice->Bob: Hello Bob, how are you?\n' +
+            'Note over Alice,Bob: confusion\n' +
+            'Note over Bob,Alice: resolution\n';
+
+        sq.parse(str);
+
+        var messages = sq.yy.getMessages();
+        expect(messages[1].from).toBe('Alice');
+        expect(messages[1].to).toBe('Bob');
+        expect(messages[2].from).toBe('Bob');
+        expect(messages[2].to).toBe('Alice');
+    });
     it('it should handle loop statements a sequenceDiagram', function () {
         var str = 'sequenceDiagram\n' +
             'Alice->Bob: Hello Bob, how are you?\n\n' +
@@ -623,7 +648,23 @@ describe('when rendering a sequenceDiagram',function() {
         expect(bounds.stopy ).toBe(conf.height);
 
     });
-    it('it should handle one actor and a note', function () {
+    it('it should handle one actor and a centered note', function () {
+        sd.bounds.init();
+        var str = 'sequenceDiagram\n' +
+            'participant Alice\n' +
+            'Note over Alice: Alice thinks\n';
+
+        sq.parse(str);
+        sd.draw(str,'tst');
+
+        var bounds = sd.bounds.getBounds();
+        expect(bounds.startx).toBe(0);
+        expect(bounds.starty).toBe(0);
+        expect(bounds.stopx ).toBe( conf.width);
+        // 10 comes from mock of text height
+        expect(bounds.stopy ).toBe( conf.height + conf.boxMargin + 2*conf.noteMargin +10);
+    });
+    it('it should handle one actor and a note to the left', function () {
         sd.bounds.init();
         var str = 'sequenceDiagram\n' +
             'participant Alice\n' +
@@ -668,7 +709,22 @@ describe('when rendering a sequenceDiagram',function() {
         expect(bounds.starty).toBe(0);
         expect(bounds.stopx ).toBe(conf.width*2 + conf.actorMargin);
         expect(bounds.stopy ).toBe(0 + conf.messageMargin + conf.height);
+    });
+    it('it should handle two actors and two centered shared notes', function () {
+        sd.bounds.init();
+        var str = 'sequenceDiagram\n' +
+            'Alice->Bob: Hello Bob, how are you?\n'+
+            'Note over Alice,Bob: Looks\n' +
+            'Note over Bob,Alice: Looks back\n';
+
+        sq.parse(str);
+        sd.draw(str,'tst');
 
+        var bounds = sd.bounds.getBounds();
+        expect(bounds.startx).toBe(0);
+        expect(bounds.starty).toBe(0);
+        expect(bounds.stopx ).toBe(conf.width*2 + conf.actorMargin);
+        expect(bounds.stopy ).toBe( conf.height + conf.messageMargin + 2*(conf.boxMargin + 2*conf.noteMargin + 10));
     });
     it('it should draw two actors and two messages', function () {
         sd.bounds.init();
diff --git a/src/diagrams/sequenceDiagram/sequenceRenderer.js b/src/diagrams/sequenceDiagram/sequenceRenderer.js
index 7b2f6eb875..59d5fa417d 100644
--- a/src/diagrams/sequenceDiagram/sequenceRenderer.js
+++ b/src/diagrams/sequenceDiagram/sequenceRenderer.js
@@ -129,11 +129,11 @@ exports.bounds = {
  * @param pos The position if the actor in the liost of actors
  * @param description The text in the box
  */
-var drawNote = function(elem, startx, verticalPos, msg){
+var drawNote = function(elem, startx, verticalPos, msg, forceWidth){
     var rect = svgDraw.getNoteRect();
     rect.x = startx;
     rect.y = verticalPos;
-    rect.width = conf.width;
+    rect.width = forceWidth || conf.width;
     rect.class = 'note';
 
     var g = elem.append('g');
@@ -147,21 +147,19 @@ var drawNote = function(elem, startx, verticalPos, msg){
     textObj.text = msg.message;
     textObj.class = 'noteText';
 
-    var textElem = svgDraw.drawText(g,textObj, conf.width-conf.noteMargin);
+    var textElem = svgDraw.drawText(g,textObj, rect.width-conf.noteMargin);
 
     var textHeight = textElem[0][0].getBBox().height;
-    if(textHeight > conf.width){
+    if(!forceWidth && textHeight > conf.width){
         textElem.remove();
         g = elem.append('g');
 
-        //textObj.x = textObj.x - conf.width;
-        //textElem = svgDraw.drawText(g,textObj, 2*conf.noteMargin);
-        textElem = svgDraw.drawText(g,textObj, 2*conf.width-conf.noteMargin);
+        textElem = svgDraw.drawText(g,textObj, 2*rect.width-conf.noteMargin);
         textHeight = textElem[0][0].getBBox().height;
-        rectElem.attr('width',2*conf.width);
-        exports.bounds.insert(startx, verticalPos, startx + 2*conf.width,  verticalPos + 2*conf.noteMargin + textHeight);
+        rectElem.attr('width',2*rect.width);
+        exports.bounds.insert(startx, verticalPos, startx + 2*rect.width,  verticalPos + 2*conf.noteMargin + textHeight);
     }else{
-        exports.bounds.insert(startx, verticalPos, startx + conf.width,  verticalPos + 2*conf.noteMargin + textHeight);
+        exports.bounds.insert(startx, verticalPos, startx + rect.width,  verticalPos + 2*conf.noteMargin + textHeight);
     }
 
     rectElem.attr('height',textHeight+ 2*conf.noteMargin);
@@ -290,6 +288,7 @@ module.exports.draw = function (text, id) {
 
     var startx;
     var stopx;
+    var forceWidth;
 
     // Fetch data from the parsing
     var actors = sq.yy.getActors();
@@ -312,13 +311,19 @@ module.exports.draw = function (text, id) {
                 startx = actors[msg.from].x;
                 stopx = actors[msg.to].x;
 
-                if(msg.placement !== 0){
-                    // Right of
+                if(msg.placement === sq.yy.PLACEMENT.RIGHTOF){
                     drawNote(diagram, startx + (conf.width + conf.actorMargin)/2, exports.bounds.getVerticalPos(), msg);
 
-                }else{
-                    // Left of
+                }else if(msg.placement === sq.yy.PLACEMENT.LEFTOF){
                     drawNote(diagram, startx - (conf.width + conf.actorMargin)/2, exports.bounds.getVerticalPos(), msg);
+                }else if(msg.to === msg.from) {
+                    // Single-actor over
+                    drawNote(diagram, startx, exports.bounds.getVerticalPos(), msg);
+                }else{
+                    // Multi-actor over
+                    forceWidth = Math.abs(startx - stopx) + conf.actorMargin;
+                    drawNote(diagram, (startx + stopx + conf.width - forceWidth)/2, exports.bounds.getVerticalPos(), msg,
+                        forceWidth);
                 }
                 break;
             case sq.yy.LINETYPE.LOOP_START:

From a59a5a24021d5e26325551c756148963f966f1c3 Mon Sep 17 00:00:00 2001
From: Richard Gibson <richard.gibson@gmail.com>
Date: Fri, 6 Nov 2015 10:24:51 -0500
Subject: [PATCH 2/2] Update documentation

---
 docs/content/sequenceDiagram.md | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/docs/content/sequenceDiagram.md b/docs/content/sequenceDiagram.md
index 1ef7ce5ddd..3da5d988ac 100755
--- a/docs/content/sequenceDiagram.md
+++ b/docs/content/sequenceDiagram.md
@@ -74,7 +74,7 @@ There are six types of arrows currently supported:
 
 ## Notes
 It is possible to add notes to a sequence diagram. This is done by the notation
-Note [ right | left ] of [Actor]: Text in note content
+Note [ right of | left of | over ] [Actor]: Text in note content
 
 See the example below:
 ```
@@ -92,18 +92,17 @@ sequenceDiagram
     Note right of John: Text in note
 ```
 
-It is possible to break text into different rows by using &lt;br/> as a line breaker.
+It is also possible to create notes spanning two participants:
 ```
-%% Example of sequence diagram
 sequenceDiagram
-    participant John
-    Note left of John: Text in note spanning several rows.
+    Alice->John: Hello John, how are you?
+    Note over Alice,John: A typical interaction
 ```
 
 ```mermaid
 sequenceDiagram
-    participant John
-    Note left of John: Text in note spanning several rows.
+    Alice->John: Hello John, how are you?
+    Note over Alice,John: A typical interaction
 ```
 
 ## Loops
@@ -309,4 +308,4 @@ mermaid.sequenceConfig = {
 Param | Descriotion | Default value
 --- | --- | ---
 mirrorActor|Turns on/off the rendering of actors below the diagram as well as above it|false
-bottomMarginAdj|Adjusts how far down the graph ended. Wide borders styles with css could generate unwantewd clipping which is why this config param exists.|1
\ No newline at end of file
+bottomMarginAdj|Adjusts how far down the graph ended. Wide borders styles with css could generate unwantewd clipping which is why this config param exists.|1