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 grammar _ */
7
8function Diagram() {
9  this.title   = undefined;
10  this.actors  = [];
11  this.signals = [];
12}
13/*
14 * Return an existing actor with this alias, or creates a new one with alias and name.
15 */
16Diagram.prototype.getActor = function(alias, name) {
17  alias = alias.trim();
18
19  var i;
20  var actors = this.actors;
21  for (i in actors) {
22    if (actors[i].alias == alias) {
23      return actors[i];
24    }
25  }
26  i = actors.push(new Diagram.Actor(alias, (name || alias), actors.length));
27  return actors[ i - 1 ];
28};
29
30/*
31 * Parses the input as either a alias, or a "name as alias", and returns the corresponding actor.
32 */
33Diagram.prototype.getActorWithAlias = function(input) {
34  input = input.trim();
35
36  // We are lazy and do some of the parsing in javascript :(. TODO move into the .jison file.
37  var s = /([\s\S]+) as (\S+)$/im.exec(input);
38  var alias;
39  var name;
40  if (s) {
41    name  = s[1].trim();
42    alias = s[2].trim();
43  } else {
44    name = alias = input;
45  }
46  return this.getActor(alias, name);
47};
48
49Diagram.prototype.setTitle = function(title) {
50  this.title = title;
51};
52
53Diagram.prototype.addSignal = function(signal) {
54  this.signals.push(signal);
55};
56
57Diagram.Actor = function(alias, name, index) {
58  this.alias = alias;
59  this.name  = name;
60  this.index = index;
61};
62
63Diagram.Signal = function(actorA, signaltype, actorB, message) {
64  this.type       = 'Signal';
65  this.actorA     = actorA;
66  this.actorB     = actorB;
67  this.linetype   = signaltype & 3;
68  this.arrowtype  = (signaltype >> 2) & 3;
69  this.message    = message;
70};
71
72Diagram.Signal.prototype.isSelf = function() {
73  return this.actorA.index == this.actorB.index;
74};
75
76Diagram.Note = function(actor, placement, message) {
77  this.type      = 'Note';
78  this.actor     = actor;
79  this.placement = placement;
80  this.message   = message;
81
82  if (this.hasManyActors() && actor[0] == actor[1]) {
83    throw new Error('Note should be over two different actors');
84  }
85};
86
87Diagram.Note.prototype.hasManyActors = function() {
88  return _.isArray(this.actor);
89};
90
91Diagram.unescape = function(s) {
92  // Turn "\\n" into "\n"
93  return s.trim().replace(/^"(.*)"$/m, '$1').replace(/\\n/gm, '\n');
94};
95
96Diagram.LINETYPE = {
97  SOLID: 0,
98  DOTTED: 1
99};
100
101Diagram.ARROWTYPE = {
102  FILLED: 0,
103  OPEN: 1
104};
105
106Diagram.PLACEMENT = {
107  LEFTOF: 0,
108  RIGHTOF: 1,
109  OVER: 2
110};
111
112// Some older browsers don't have getPrototypeOf, thus we polyfill it
113// https://github.com/bramp/js-sequence-diagrams/issues/57
114// https://github.com/zaach/jison/issues/194
115// Taken from http://ejohn.org/blog/objectgetprototypeof/
116if (typeof Object.getPrototypeOf !== 'function') {
117  /* jshint -W103 */
118  if (typeof 'test'.__proto__ === 'object') {
119    Object.getPrototypeOf = function(object) {
120      return object.__proto__;
121    };
122  } else {
123    Object.getPrototypeOf = function(object) {
124      // May break if the constructor has been tampered with
125      return object.constructor.prototype;
126    };
127  }
128  /* jshint +W103 */
129}
130
131/** The following is included by preprocessor */
132// #include "build/grammar.js"
133
134/**
135 * jison doesn't have a good exception, so we make one.
136 * This is brittle as it depends on jison internals
137 */
138function ParseError(message, hash) {
139  _.extend(this, hash);
140
141  this.name = 'ParseError';
142  this.message = (message || '');
143}
144ParseError.prototype = new Error();
145Diagram.ParseError = ParseError;
146
147Diagram.parse = function(input) {
148  // TODO jison v0.4.17 changed their API slightly, so parser is no longer defined:
149
150  // Create the object to track state and deal with errors
151  parser.yy = new Diagram();
152  parser.yy.parseError = function(message, hash) {
153    throw new ParseError(message, hash);
154  };
155
156  // Parse
157  var diagram = parser.parse(input);
158
159  // Then clean up the parseError key that a user won't care about
160  delete diagram.parseError;
161  return diagram;
162};
163
164