1/** js sequence diagrams
2 *  https://bramp.github.io/js-sequence-diagrams/
3 *  (c) 2012-2017 Andrew Brampton (bramp.net)
4 *  Simplified BSD license.
5 */
6/*global Diagram, Raphael, _ */
7
8if (typeof Raphael != 'undefined') {
9
10  var LINE = {
11    'stroke': '#000000',
12    'stroke-width': 2,
13    'fill': 'none'
14  };
15
16  var RECT = {
17        'stroke': '#000000',
18        'stroke-width': 2,
19        'fill': '#fff'
20      };
21
22  /******************
23   * Raphaël extras
24   ******************/
25  Raphael.fn.line = function(x1, y1, x2, y2) {
26    assert(_.all([x1,x2,y1,y2], _.isFinite), 'x1,x2,y1,y2 must be numeric');
27    return this.path('M{0},{1} L{2},{3}', x1, y1, x2, y2);
28  };
29
30  /******************
31   * RaphaelTheme
32   ******************/
33
34  var RaphaelTheme = function(diagram, options, resume) {
35        this.init(diagram, _.defaults(options, {
36            'font-size': 16,
37            'font-family': 'Andale Mono, monospace'
38          }), resume);
39      };
40
41  _.extend(RaphaelTheme.prototype, BaseTheme.prototype, {
42
43    init: function(diagram, options, resume) {
44      BaseTheme.prototype.init.call(this, diagram);
45
46      this.paper_  = undefined;
47      this.font_   = {
48                  'font-size': options['font-size'],
49                  'font-family': options['font-family']
50                };
51
52      var a = this.arrowTypes_ = {};
53      a[ARROWTYPE.FILLED] = 'block';
54      a[ARROWTYPE.OPEN]   = 'open';
55
56      var l = this.lineTypes_ = {};
57      l[LINETYPE.SOLID]  = '';
58      l[LINETYPE.DOTTED] = '-';
59
60      resume(this);
61    },
62
63    setupPaper: function(container) {
64      this.paper_ = new Raphael(container, 320, 200);
65      this.paper_.setStart();
66    },
67
68    draw: function(container) {
69      BaseTheme.prototype.draw.call(this, container);
70      this.paper_.setFinish();
71    },
72
73    layout: function() {
74      BaseTheme.prototype.layout.call(this);
75      this.paper_.setSize(
76       this.diagram.width,
77       this.diagram.height
78      );
79    },
80
81    /**
82     * Strip whitespace from each newline
83     */
84    cleanText: function(text) {
85      text = _.invoke(text.split('\n'), 'trim');
86      return text.join('\n');
87    },
88
89    /**
90     * Returns the text's bounding box
91     */
92    textBBox: function(text, font) {
93      text = this.cleanText(text);
94      font = font || {};
95      var p;
96      if (font.obj_) {
97        p = this.paper_.print(0, 0, text, font.obj_, font['font-size']);
98      } else {
99        p = this.paper_.text(0, 0, text);
100        p.attr(font);
101      }
102
103      var bb = p.getBBox();
104      p.remove();
105
106      return bb;
107    },
108
109    drawLine: function(x1, y1, x2, y2, linetype, arrowhead) {
110      var line = this.paper_.line(x1, y1, x2, y2).attr(LINE);
111      if (arrowhead !== undefined) {
112        line.attr('arrow-end', this.arrowTypes_[arrowhead] + '-wide-long');
113      }
114      if (arrowhead !== undefined) {
115        line.attr('stroke-dasharray', this.lineTypes_[linetype]);
116      }
117      return line;
118    },
119
120    drawRect: function(x, y, w, h) {
121      return this.paper_.rect(x, y, w, h).attr(RECT);
122    },
123
124    /**
125     * Draws text with a optional white background
126     * x,y (int) x,y top left point of the text, or the center of the text (depending on align param)
127     * text (string) text to print
128     * font (Object)
129     * align (string) ALIGN_LEFT or ALIGN_CENTER
130     */
131    drawText: function(x, y, text, font, align) {
132      text = this.cleanText(text);
133      font = font || {};
134      align = align || ALIGN_LEFT;
135
136      var paper = this.paper_;
137      var bb = this.textBBox(text, font);
138
139      if (align == ALIGN_CENTER) {
140        x = x - bb.width / 2;
141        y = y - bb.height / 2;
142      }
143
144      var t;
145      if (font.obj_) {
146        // When using a font, we have to use .print(..)
147        t = paper.print(x - bb.x, y - bb.y, text, font.obj_, font['font-size']);
148      } else {
149        t = paper.text(x - bb.x - bb.width / 2, y - bb.y, text);
150        t.attr(font);
151        t.attr({'text-anchor': 'start'});
152      }
153
154      return t;
155    }
156  });
157
158  /******************
159   * RaphaelHandTheme
160   ******************/
161
162  var RaphaelHandTheme = function(diagram, options, resume) {
163    this.init(diagram, _.defaults(options, {
164              'font-size': 16,
165              'font-family': 'daniel'
166            }), resume);
167  };
168
169  // Take the standard RaphaelTheme and make all the lines wobbly
170  _.extend(RaphaelHandTheme.prototype, RaphaelTheme.prototype, {
171        setupPaper: function(container) {
172            RaphaelTheme.prototype.setupPaper.call(this, container);
173            this.font_.obj_ = this.paper_.getFont('daniel');
174          },
175
176        drawLine: function(x1, y1, x2, y2, linetype, arrowhead) {
177          var line = this.paper_.path(handLine(x1, y1, x2, y2)).attr(LINE);
178          if (arrowhead !== undefined) {
179            line.attr('arrow-end', this.arrowTypes_[arrowhead] + '-wide-long');
180          }
181          if (arrowhead !== undefined) {
182            line.attr('stroke-dasharray', this.lineTypes_[linetype]);
183          }
184          return line;
185        },
186
187        drawRect: function(x, y, w, h) {
188          return this.paper_.path(handRect(x, y, w, h)).attr(RECT);
189        }
190      });
191
192  registerTheme('raphaelSimple', RaphaelTheme);
193  registerTheme('raphaelHand',   RaphaelHandTheme);
194}
195