/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-mixed-operators, new-cap, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len */ /* global Raphael */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; this.BranchGraph = (function() { function BranchGraph(element1, options1) { this.element = element1; this.options = options1; this.scrollTop = bind(this.scrollTop, this); this.scrollBottom = bind(this.scrollBottom, this); this.scrollRight = bind(this.scrollRight, this); this.scrollLeft = bind(this.scrollLeft, this); this.scrollUp = bind(this.scrollUp, this); this.scrollDown = bind(this.scrollDown, this); this.preparedCommits = {}; this.mtime = 0; this.mspace = 0; this.parents = {}; this.colors = ["#000"]; this.offsetX = 150; this.offsetY = 20; this.unitTime = 30; this.unitSpace = 10; this.prev_start = -1; this.load(); } BranchGraph.prototype.load = function() { return $.ajax({ url: this.options.url, method: "get", dataType: "json", success: $.proxy(function(data) { $(".loading", this.element).hide(); this.prepareData(data.days, data.commits); return this.buildGraph(); }, this) }); }; 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(); }; 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); 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)); } return results; }; BranchGraph.prototype.collectColors = function() { var k, results; k = 0; results = []; while (k < this.mspace) { this.colors.push(Raphael.getColor(.8)); // Skipping a few colors in the spectrum to get more contrast between colors Raphael.getColor(); Raphael.getColor(); results.push(k += 1); } return results; }; BranchGraph.prototype.buildGraph = function() { var cuday, cumonth, day, j, len, mm, r, ref; r = this.r; cuday = 0; cumonth = ""; r.rect(0, 0, 40, this.barHeight).attr({ fill: "#222" }); r.rect(40, 0, 30, this.barHeight).attr({ fill: "#444" }); ref = this.days; for (mm = j = 0, len = ref.length; j < len; mm = (j += 1)) { day = ref[mm]; if (cuday !== day[0] || cumonth !== day[1]) { // Dates r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({ font: "12px Monaco, monospace", fill: "#BBB" }); cuday = day[0]; } if (cumonth !== day[1]) { // Months r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({ font: "12px Monaco, monospace", fill: "#EEE" }); cumonth = day[1]; } } this.renderPartialGraph(); return this.bindEvents(); }; 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; } } return this.top.toFront(); } }; BranchGraph.prototype.bindEvents = function() { var element; element = this.element; return $(element).scroll((function(_this) { return function(event) { return _this.renderPartialGraph(); }; })(this)); }; BranchGraph.prototype.scrollDown = function() { this.element.scrollTop(this.element.scrollTop() + 50); return this.renderPartialGraph(); }; BranchGraph.prototype.scrollUp = function() { this.element.scrollTop(this.element.scrollTop() - 50); return this.renderPartialGraph(); }; BranchGraph.prototype.scrollLeft = function() { this.element.scrollLeft(this.element.scrollLeft() - 50); return this.renderPartialGraph(); }; BranchGraph.prototype.scrollRight = function() { this.element.scrollLeft(this.element.scrollLeft() + 50); return this.renderPartialGraph(); }; BranchGraph.prototype.scrollBottom = function() { return this.element.scrollTop(this.element.find('svg').height()); }; BranchGraph.prototype.scrollTop = function() { return this.element.scrollTop(0); }; BranchGraph.prototype.appendLabel = function(x, y, commit) { var label, r, rect, shortrefs, text, textbox, triangle; if (!commit.refs) { return; } r = this.r; shortrefs = commit.refs; // Truncate if longer than 15 chars if (shortrefs.length > 17) { shortrefs = shortrefs.substr(0, 15) + "…"; } text = r.text(x + 4, y, shortrefs).attr({ "text-anchor": "start", font: "10px Monaco, monospace", fill: "#FFF", title: commit.refs }); 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({ fill: "#000", "fill-opacity": .5, stroke: "none" }); triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr({ fill: "#000", "fill-opacity": .5, stroke: "none" }); label = r.set(rect, text); label.transform(["t", -rect.getBBox().width - 15, 0]); // Set text to front return text.toFront(); }; BranchGraph.prototype.appendAnchor = function(x, y, commit) { var anchor, options, r, top; r = this.r; top = this.top; options = this.options; 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; }); return top.push(anchor); }; BranchGraph.prototype.drawDot = function(x, y, commit) { var avatar_box_x, avatar_box_y, r; r = this.r; r.circle(x, y, 3).attr({ fill: this.colors[commit.space], stroke: "none" }); avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10; avatar_box_y = y - 10; r.rect(avatar_box_x, avatar_box_y, 20, 20).attr({ stroke: this.colors[commit.space], "stroke-width": 2 }); r.image(commit.author.icon, avatar_box_x, avatar_box_y, 20, 20); return r.text(this.offsetX + this.unitSpace * this.mspace + 35, y, commit.message.split("\n")[0]).attr({ "text-anchor": "start", font: "14px Monaco, monospace" }); }; BranchGraph.prototype.drawLines = function(x, y, commit) { var arrow, color, i, j, len, offset, parent, parentCommit, parentX1, parentX2, parentY, r, ref, results, route; r = this.r; ref = commit.parents; results = []; for (i = j = 0, len = ref.length; j < len; i = (j += 1)) { 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]; } // Build line shape if (parent[1] === commit.space) { offset = [0, 5]; arrow = "l-2,5,4,0,-2,-5,0,5"; } else if (parent[1] < commit.space) { offset = [3, 3]; arrow = "l5,0,-2,4,-3,-4,4,2"; } else { offset = [-3, 3]; arrow = "l-5,0,2,4,3,-4,-4,2"; } // Start point route = ["M", x + offset[0], y + offset[1]]; // Add arrow if not first parent if (i > 0) { route.push(arrow); } // Circumvent if overlap if (commit.space !== parentCommit.space || commit.space !== parent[1]) { route.push("L", parentX2, y + 10, "L", parentX2, parentY - 5); } // End point route.push("L", parentX1, parentY); results.push(r.path(route).attr({ stroke: color, "stroke-width": 2 })); } return results; }; BranchGraph.prototype.markCommit = function(commit) { var r, x, y; if (commit.id === this.options.commit_id) { r = this.r; x = this.offsetX + this.unitSpace * (this.mspace - commit.space); y = this.offsetY + this.unitTime * commit.time; r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr({ fill: "#000", "fill-opacity": .5, stroke: "none" }); // Displayed in the center return this.element.scrollTop(y - this.graphHeight / 2); } }; return BranchGraph; })(); Raphael.prototype.commitTooltip = function(x, y, commit) { var boxHeight, boxWidth, icon, idText, messageText, nameText, rect, textSet, tooltip; boxWidth = 300; boxHeight = 200; icon = this.image(gon.relative_url_root + commit.author.icon, x, y, 20, 20); nameText = this.text(x + 25, y + 10, commit.author.name); idText = this.text(x, y + 35, commit.id); messageText = this.text(x, y + 50, commit.message.replace(/\r?\n/g, " \n ")); textSet = this.set(icon, nameText, idText, messageText).attr({ "text-anchor": "start", font: "12px Monaco, monospace" }); nameText.attr({ font: "14px Arial", "font-weight": "bold" }); idText.attr({ fill: "#AAA" }); messageText.node.style["white-space"] = "pre"; this.textWrap(messageText, boxWidth - 50); rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({ fill: "#FFF", stroke: "#000", "stroke-linecap": "round", "stroke-width": 2 }); tooltip = this.set(rect, textSet); rect.attr({ height: tooltip.getBBox().height + 10, width: tooltip.getBBox().width + 10 }); tooltip.transform(["t", 20, 20]); return tooltip; }; Raphael.prototype.textWrap = function(t, width) { var abc, b, content, h, j, len, letterWidth, s, word, words, x; content = t.attr("text"); abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; t.attr({ text: abc }); letterWidth = t.getBBox().width / abc.length; t.attr({ text: content }); words = content.split(" "); x = 0; s = []; for (j = 0, len = words.length; j < len; j += 1) { word = words[j]; if (x + (word.length * letterWidth) > width) { s.push("\n"); x = 0; } if (word === "\n") { s.push("\n"); x = 0; } else { s.push(word + " "); x += word.length * letterWidth; } } t.attr({ text: s.join("").trim() }); b = t.getBBox(); h = Math.abs(b.y2) + 1; return t.attr({ y: h }); }; }).call(this);