branch_graph.js 10.9 KB
Newer Older
1
/* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase */
2

3
import $ from 'jquery';
P
Phil Hughes 已提交
4 5 6
import { __ } from '../locale';
import axios from '../lib/utils/axios_utils';
import flash from '../flash';
M
Mike Greiling 已提交
7
import Raphael from './raphael';
8

M
Mike Greiling 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21 22
export default (function() {
  function BranchGraph(element1, options1) {
    this.element = element1;
    this.options = options1;
    this.scrollTop = this.scrollTop.bind(this);
    this.scrollBottom = this.scrollBottom.bind(this);
    this.scrollRight = this.scrollRight.bind(this);
    this.scrollLeft = this.scrollLeft.bind(this);
    this.scrollUp = this.scrollUp.bind(this);
    this.scrollDown = this.scrollDown.bind(this);
    this.preparedCommits = {};
    this.mtime = 0;
    this.mspace = 0;
    this.parents = {};
23
    this.colors = ['#000'];
M
Mike Greiling 已提交
24 25 26 27 28 29 30
    this.offsetX = 150;
    this.offsetY = 20;
    this.unitTime = 30;
    this.unitSpace = 10;
    this.prev_start = -1;
    this.load();
  }
F
Fatih Acet 已提交
31

M
Mike Greiling 已提交
32
  BranchGraph.prototype.load = function() {
33 34
    axios
      .get(this.options.url)
P
Phil Hughes 已提交
35
      .then(({ data }) => {
36
        $('.loading', this.element).hide();
M
Mike Greiling 已提交
37
        this.prepareData(data.days, data.commits);
P
Phil Hughes 已提交
38 39 40
        this.buildGraph();
      })
      .catch(() => __('Error fetching network graph.'));
M
Mike Greiling 已提交
41
  };
F
Fatih Acet 已提交
42

M
Mike Greiling 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
  BranchGraph.prototype.prepareData = function(days, commits) {
    var c, ch, cw, j, len, ref;
    this.days = days;
    this.commits = commits;
    this.collectParents();
    this.graphHeight = $(this.element).height();
    this.graphWidth = $(this.element).width();
    ch = Math.max(this.graphHeight, this.offsetY + this.unitTime * this.mtime + 150);
    cw = Math.max(this.graphWidth, this.offsetX + this.unitSpace * this.mspace + 300);
    this.r = Raphael(this.element.get(0), cw, ch);
    this.top = this.r.set();
    this.barHeight = Math.max(this.graphHeight, this.unitTime * this.days.length + 320);
    ref = this.commits;
    for (j = 0, len = ref.length; j < len; j += 1) {
      c = ref[j];
      if (c.id in this.parents) {
        c.isParent = true;
      }
      this.preparedCommits[c.id] = c;
      this.markCommit(c);
    }
    return this.collectColors();
  };
F
Fatih Acet 已提交
66

M
Mike Greiling 已提交
67 68 69 70 71 72 73 74
  BranchGraph.prototype.collectParents = function() {
    var c, j, len, p, ref, results;
    ref = this.commits;
    results = [];
    for (j = 0, len = ref.length; j < len; j += 1) {
      c = ref[j];
      this.mtime = Math.max(this.mtime, c.time);
      this.mspace = Math.max(this.mspace, c.space);
75 76 77 78 79 80 81 82 83 84 85 86 87
      results.push(
        function() {
          var l, len1, ref1, results1;
          ref1 = c.parents;
          results1 = [];
          for (l = 0, len1 = ref1.length; l < len1; l += 1) {
            p = ref1[l];
            this.parents[p[0]] = true;
            results1.push((this.mspace = Math.max(this.mspace, p[1])));
          }
          return results1;
        }.call(this),
      );
M
Mike Greiling 已提交
88 89 90
    }
    return results;
  };
F
Fatih Acet 已提交
91

M
Mike Greiling 已提交
92 93 94 95 96
  BranchGraph.prototype.collectColors = function() {
    var k, results;
    k = 0;
    results = [];
    while (k < this.mspace) {
97
      this.colors.push(Raphael.getColor(0.8));
M
Mike Greiling 已提交
98 99 100
      // Skipping a few colors in the spectrum to get more contrast between colors
      Raphael.getColor();
      Raphael.getColor();
101
      results.push((k += 1));
M
Mike Greiling 已提交
102 103 104
    }
    return results;
  };
F
Fatih Acet 已提交
105

M
Mike Greiling 已提交
106
  BranchGraph.prototype.buildGraph = function() {
107 108
    var cuday, cumonth, day, j, len, mm, ref;
    const { r } = this;
M
Mike Greiling 已提交
109
    cuday = 0;
110
    cumonth = '';
M
Mike Greiling 已提交
111
    r.rect(0, 0, 40, this.barHeight).attr({
112
      fill: '#222',
M
Mike Greiling 已提交
113 114
    });
    r.rect(40, 0, 30, this.barHeight).attr({
115
      fill: '#444',
M
Mike Greiling 已提交
116 117
    });
    ref = this.days;
G
gfyoung 已提交
118

119
    for (mm = 0, len = ref.length; mm < len; mm += 1) {
M
Mike Greiling 已提交
120 121 122 123
      day = ref[mm];
      if (cuday !== day[0] || cumonth !== day[1]) {
        // Dates
        r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({
124 125
          font: '12px Monaco, monospace',
          fill: '#BBB',
M
Mike Greiling 已提交
126
        });
127
        [cuday] = day;
F
Fatih Acet 已提交
128
      }
M
Mike Greiling 已提交
129 130 131
      if (cumonth !== day[1]) {
        // Months
        r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({
132 133
          font: '12px Monaco, monospace',
          fill: '#EEE',
M
Mike Greiling 已提交
134
        });
135 136

        // eslint-disable-next-line prefer-destructuring
M
Mike Greiling 已提交
137
        cumonth = day[1];
F
Fatih Acet 已提交
138
      }
M
Mike Greiling 已提交
139 140 141 142
    }
    this.renderPartialGraph();
    return this.bindEvents();
  };
F
Fatih Acet 已提交
143

M
Mike Greiling 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  BranchGraph.prototype.renderPartialGraph = function() {
    var commit, end, i, isGraphEdge, start, x, y;
    start = Math.floor((this.element.scrollTop() - this.offsetY) / this.unitTime) - 10;
    if (start < 0) {
      isGraphEdge = true;
      start = 0;
    }
    end = start + 40;
    if (this.commits.length < end) {
      isGraphEdge = true;
      end = this.commits.length;
    }
    if (this.prev_start === -1 || Math.abs(this.prev_start - start) > 10 || isGraphEdge) {
      i = start;
      this.prev_start = start;
      while (i < end) {
        commit = this.commits[i];
        i += 1;
        if (commit.hasDrawn !== true) {
          x = this.offsetX + this.unitSpace * (this.mspace - commit.space);
          y = this.offsetY + this.unitTime * commit.time;
          this.drawDot(x, y, commit);
          this.drawLines(x, y, commit);
          this.appendLabel(x, y, commit);
          this.appendAnchor(x, y, commit);
          commit.hasDrawn = true;
F
Fatih Acet 已提交
170 171
        }
      }
M
Mike Greiling 已提交
172 173 174
      return this.top.toFront();
    }
  };
F
Fatih Acet 已提交
175

M
Mike Greiling 已提交
176
  BranchGraph.prototype.bindEvents = function() {
177 178
    const { element } = this;

179 180 181 182 183 184 185
    return $(element).scroll(
      (function(_this) {
        return function(event) {
          return _this.renderPartialGraph();
        };
      })(this),
    );
M
Mike Greiling 已提交
186
  };
F
Fatih Acet 已提交
187

M
Mike Greiling 已提交
188 189 190 191
  BranchGraph.prototype.scrollDown = function() {
    this.element.scrollTop(this.element.scrollTop() + 50);
    return this.renderPartialGraph();
  };
F
Fatih Acet 已提交
192

M
Mike Greiling 已提交
193 194 195 196
  BranchGraph.prototype.scrollUp = function() {
    this.element.scrollTop(this.element.scrollTop() - 50);
    return this.renderPartialGraph();
  };
F
Fatih Acet 已提交
197

M
Mike Greiling 已提交
198 199 200 201
  BranchGraph.prototype.scrollLeft = function() {
    this.element.scrollLeft(this.element.scrollLeft() - 50);
    return this.renderPartialGraph();
  };
F
Fatih Acet 已提交
202

M
Mike Greiling 已提交
203 204 205 206
  BranchGraph.prototype.scrollRight = function() {
    this.element.scrollLeft(this.element.scrollLeft() + 50);
    return this.renderPartialGraph();
  };
F
Fatih Acet 已提交
207

M
Mike Greiling 已提交
208 209 210
  BranchGraph.prototype.scrollBottom = function() {
    return this.element.scrollTop(this.element.find('svg').height());
  };
F
Fatih Acet 已提交
211

M
Mike Greiling 已提交
212 213 214
  BranchGraph.prototype.scrollTop = function() {
    return this.element.scrollTop(0);
  };
F
Fatih Acet 已提交
215

M
Mike Greiling 已提交
216
  BranchGraph.prototype.appendLabel = function(x, y, commit) {
217 218
    var label, rect, shortrefs, text, textbox, triangle;

M
Mike Greiling 已提交
219 220 221
    if (!commit.refs) {
      return;
    }
222 223

    const { r } = this;
M
Mike Greiling 已提交
224 225 226
    shortrefs = commit.refs;
    // Truncate if longer than 15 chars
    if (shortrefs.length > 17) {
227
      shortrefs = shortrefs.substr(0, 15) + '';
M
Mike Greiling 已提交
228 229
    }
    text = r.text(x + 4, y, shortrefs).attr({
230 231 232 233
      'text-anchor': 'start',
      font: '10px Monaco, monospace',
      fill: '#FFF',
      title: commit.refs,
F
Fatih Acet 已提交
234
    });
M
Mike Greiling 已提交
235 236 237
    textbox = text.getBBox();
    // Create rectangle based on the size of the textbox
    rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr({
238 239 240
      fill: '#000',
      'fill-opacity': 0.5,
      stroke: 'none',
F
Fatih Acet 已提交
241
    });
242 243 244 245
    triangle = r.path(['M', x - 5, y, 'L', x - 15, y - 4, 'L', x - 15, y + 4, 'Z']).attr({
      fill: '#000',
      'fill-opacity': 0.5,
      stroke: 'none',
F
Fatih Acet 已提交
246
    });
M
Mike Greiling 已提交
247
    label = r.set(rect, text);
248
    label.transform(['t', -rect.getBBox().width - 15, 0]);
M
Mike Greiling 已提交
249 250 251 252 253
    // Set text to front
    return text.toFront();
  };

  BranchGraph.prototype.appendAnchor = function(x, y, commit) {
254
    const { r, top, options } = this;
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
    const anchor = r
      .circle(x, y, 10)
      .attr({
        fill: '#000',
        opacity: 0,
        cursor: 'pointer',
      })
      .click(function() {
        return window.open(options.commit_url.replace('%s', commit.id), '_blank');
      })
      .hover(
        function() {
          this.tooltip = r.commitTooltip(x + 5, y, commit);
          return top.push(this.tooltip.insertBefore(this));
        },
        function() {
          return this.tooltip && this.tooltip.remove() && delete this.tooltip;
        },
      );
M
Mike Greiling 已提交
274
    return top.push(anchor);
F
Fatih Acet 已提交
275 276
  };

M
Mike Greiling 已提交
277
  BranchGraph.prototype.drawDot = function(x, y, commit) {
278
    const { r } = this;
M
Mike Greiling 已提交
279 280
    r.circle(x, y, 3).attr({
      fill: this.colors[commit.space],
281
      stroke: 'none',
F
Fatih Acet 已提交
282
    });
283 284 285 286

    const avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10;
    const avatar_box_y = y - 10;

M
Mike Greiling 已提交
287 288
    r.rect(avatar_box_x, avatar_box_y, 20, 20).attr({
      stroke: this.colors[commit.space],
289
      'stroke-width': 2,
F
Fatih Acet 已提交
290
    });
M
Mike Greiling 已提交
291
    r.image(commit.author.icon, avatar_box_x, avatar_box_y, 20, 20);
292 293 294 295 296 297
    return r
      .text(this.offsetX + this.unitSpace * this.mspace + 35, y, commit.message.split('\n')[0])
      .attr({
        'text-anchor': 'start',
        font: '14px Monaco, monospace',
      });
M
Mike Greiling 已提交
298 299 300
  };

  BranchGraph.prototype.drawLines = function(x, y, commit) {
301 302 303 304
    var arrow, color, i, len, offset, parent, parentCommit, parentX1, parentX2, parentY, route;
    const { r } = this;
    const ref = commit.parents;
    const results = [];
G
gfyoung 已提交
305

306
    for (i = 0, len = ref.length; i < len; i += 1) {
M
Mike Greiling 已提交
307 308 309 310 311 312 313 314 315 316
      parent = ref[i];
      parentCommit = this.preparedCommits[parent[0]];
      parentY = this.offsetY + this.unitTime * parentCommit.time;
      parentX1 = this.offsetX + this.unitSpace * (this.mspace - parentCommit.space);
      parentX2 = this.offsetX + this.unitSpace * (this.mspace - parent[1]);
      // Set line color
      if (parentCommit.space <= commit.space) {
        color = this.colors[commit.space];
      } else {
        color = this.colors[parentCommit.space];
F
Fatih Acet 已提交
317
      }
M
Mike Greiling 已提交
318 319 320
      // Build line shape
      if (parent[1] === commit.space) {
        offset = [0, 5];
321
        arrow = 'l-2,5,4,0,-2,-5,0,5';
M
Mike Greiling 已提交
322 323
      } else if (parent[1] < commit.space) {
        offset = [3, 3];
324
        arrow = 'l5,0,-2,4,-3,-4,4,2';
325
      } else {
M
Mike Greiling 已提交
326
        offset = [-3, 3];
327
        arrow = 'l-5,0,2,4,3,-4,-4,2';
M
Mike Greiling 已提交
328 329
      }
      // Start point
330
      route = ['M', x + offset[0], y + offset[1]];
M
Mike Greiling 已提交
331 332 333 334 335 336
      // Add arrow if not first parent
      if (i > 0) {
        route.push(arrow);
      }
      // Circumvent if overlap
      if (commit.space !== parentCommit.space || commit.space !== parent[1]) {
337
        route.push('L', parentX2, y + 10, 'L', parentX2, parentY - 5);
338
      }
M
Mike Greiling 已提交
339
      // End point
340 341 342 343 344 345 346
      route.push('L', parentX1, parentY);
      results.push(
        r.path(route).attr({
          stroke: color,
          'stroke-width': 2,
        }),
      );
M
Mike Greiling 已提交
347 348 349 350 351 352
    }
    return results;
  };

  BranchGraph.prototype.markCommit = function(commit) {
    if (commit.id === this.options.commit_id) {
353 354 355
      const { r } = this;
      const x = this.offsetX + this.unitSpace * (this.mspace - commit.space);
      const y = this.offsetY + this.unitTime * commit.time;
356 357 358 359
      r.path(['M', x + 5, y, 'L', x + 15, y + 4, 'L', x + 15, y - 4, 'Z']).attr({
        fill: '#000',
        'fill-opacity': 0.5,
        stroke: 'none',
M
Mike Greiling 已提交
360 361 362
      });
      // Displayed in the center
      return this.element.scrollTop(y - this.graphHeight / 2);
F
Fatih Acet 已提交
363 364
    }
  };
M
Mike Greiling 已提交
365 366 367

  return BranchGraph;
})();