gitlab-org--gitlab-foss/app/assets/javascripts/branch-graph.js.coffee

341 lines
8.4 KiB
CoffeeScript
Raw Normal View History

2014-08-21 08:14:31 +00:00
class @BranchGraph
2013-03-11 08:52:27 +00:00
constructor: (@element, @options) ->
@preparedCommits = {}
@mtime = 0
@mspace = 0
@parents = {}
@colors = ["#000"]
2013-05-14 10:34:05 +00:00
@offsetX = 150
2013-03-21 12:34:15 +00:00
@offsetY = 20
@unitTime = 30
2013-03-21 12:34:15 +00:00
@unitSpace = 10
2013-04-24 16:49:01 +00:00
@prev_start = -1
2013-03-11 08:52:27 +00:00
@load()
2011-11-13 11:58:45 +00:00
2013-03-11 08:52:27 +00:00
load: ->
$.ajax
url: @options.url
method: "get"
dataType: "json"
success: $.proxy((data) ->
$(".loading", @element).hide()
@prepareData data.days, data.commits
@buildGraph()
, this)
2011-11-15 08:34:30 +00:00
2013-03-11 08:52:27 +00:00
prepareData: (@days, @commits) ->
@collectParents()
2013-04-24 16:49:01 +00:00
@graphHeight = $(@element).height()
@graphWidth = $(@element).width()
ch = Math.max(@graphHeight, @offsetY + @unitTime * @mtime + 150)
cw = Math.max(@graphWidth, @offsetX + @unitSpace * @mspace + 300)
@r = Raphael(@element.get(0), cw, ch)
@top = @r.set()
@barHeight = Math.max(@graphHeight, @unitTime * @days.length + 320)
2013-03-11 08:52:27 +00:00
for c in @commits
c.isParent = true if c.id of @parents
@preparedCommits[c.id] = c
2013-04-24 16:49:01 +00:00
@markCommit(c)
2013-03-11 08:52:27 +00:00
@collectColors()
collectParents: ->
for c in @commits
@mtime = Math.max(@mtime, c.time)
@mspace = Math.max(@mspace, c.space)
for p in c.parents
@parents[p[0]] = true
2013-03-20 09:17:12 +00:00
@mspace = Math.max(@mspace, p[1])
2013-03-11 08:52:27 +00:00
collectColors: ->
k = 0
while k < @mspace
@colors.push Raphael.getColor(.8)
# Skipping a few colors in the spectrum to get more contrast between colors
Raphael.getColor()
Raphael.getColor()
k++
buildGraph: ->
2013-04-24 16:49:01 +00:00
r = @r
2013-03-11 08:52:27 +00:00
cuday = 0
cumonth = ""
2013-03-21 12:34:15 +00:00
r.rect(0, 0, 40, @barHeight).attr fill: "#222"
r.rect(40, 0, 30, @barHeight).attr fill: "#444"
2013-03-11 08:52:27 +00:00
for day, mm in @days
if cuday isnt day[0]
# Dates
r.text(55, @offsetY + @unitTime * mm, day[0])
2013-03-11 08:52:27 +00:00
.attr(
font: "12px Monaco, monospace"
2013-08-05 16:08:51 +00:00
fill: "#BBB"
2013-03-11 08:52:27 +00:00
)
cuday = day[0]
if cumonth isnt day[1]
# Months
r.text(20, @offsetY + @unitTime * mm, day[1])
2013-03-11 08:52:27 +00:00
.attr(
font: "12px Monaco, monospace"
fill: "#EEE"
2013-03-11 08:52:27 +00:00
)
cumonth = day[1]
2013-04-24 16:49:01 +00:00
@renderPartialGraph()
2013-03-11 08:52:27 +00:00
2013-04-24 16:49:01 +00:00
@bindEvents()
2013-03-11 08:52:27 +00:00
2013-04-24 16:49:01 +00:00
renderPartialGraph: ->
start = Math.floor((@element.scrollTop() - @offsetY) / @unitTime) - 10
if start < 0
isGraphEdge = true
start = 0
2013-04-24 16:49:01 +00:00
end = start + 40
if @commits.length < end
isGraphEdge = true
end = @commits.length
2013-03-11 08:52:27 +00:00
if @prev_start == -1 or Math.abs(@prev_start - start) > 10 or isGraphEdge
2013-04-24 16:49:01 +00:00
i = start
2013-04-24 16:49:01 +00:00
@prev_start = start
2013-04-24 16:49:01 +00:00
while i < end
commit = @commits[i]
i += 1
2013-03-11 08:52:27 +00:00
2013-04-24 16:49:01 +00:00
if commit.hasDrawn isnt true
x = @offsetX + @unitSpace * (@mspace - commit.space)
y = @offsetY + @unitTime * commit.time
@drawDot(x, y, commit)
@drawLines(x, y, commit)
@appendLabel(x, y, commit)
@appendAnchor(x, y, commit)
commit.hasDrawn = true
@top.toFront()
2013-03-11 08:52:27 +00:00
bindEvents: ->
element = @element
$(element).scroll (event) =>
@renderPartialGraph()
2013-03-11 08:52:27 +00:00
2014-08-21 08:14:31 +00:00
scrollDown: =>
@element.scrollTop @element.scrollTop() + 50
@renderPartialGraph()
scrollUp: =>
@element.scrollTop @element.scrollTop() - 50
@renderPartialGraph()
scrollLeft: =>
@element.scrollLeft @element.scrollLeft() - 50
@renderPartialGraph()
scrollRight: =>
@element.scrollLeft @element.scrollLeft() + 50
@renderPartialGraph()
scrollBottom: =>
@element.scrollTop @element.find('svg').height()
scrollTop: =>
@element.scrollTop 0
2013-04-24 16:49:01 +00:00
appendLabel: (x, y, commit) ->
return unless commit.refs
2013-03-11 08:52:27 +00:00
2013-03-21 12:34:15 +00:00
r = @r
2013-04-24 16:49:01 +00:00
shortrefs = commit.refs
2013-03-11 08:52:27 +00:00
# Truncate if longer than 15 chars
shortrefs = shortrefs.substr(0, 15) + "" if shortrefs.length > 17
2013-03-26 00:46:10 +00:00
text = r.text(x + 4, y, shortrefs).attr(
2013-03-20 09:17:12 +00:00
"text-anchor": "start"
2013-03-11 08:52:27 +00:00
font: "10px Monaco, monospace"
fill: "#FFF"
2013-04-24 16:49:01 +00:00
title: commit.refs
2013-03-11 08:52:27 +00:00
)
textbox = text.getBBox()
# Create rectangle based on the size of the textbox
2013-03-26 00:46:10 +00:00
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr(
2013-03-11 08:52:27 +00:00
fill: "#000"
2013-03-20 09:17:12 +00:00
"fill-opacity": .5
2013-03-11 08:52:27 +00:00
stroke: "none"
)
2013-03-20 09:17:12 +00:00
triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr(
2013-03-11 08:52:27 +00:00
fill: "#000"
2013-03-20 09:17:12 +00:00
"fill-opacity": .5
2013-03-11 08:52:27 +00:00
stroke: "none"
)
2013-03-20 09:17:12 +00:00
label = r.set(rect, text)
label.transform(["t", -rect.getBBox().width - 15, 0])
2013-03-11 08:52:27 +00:00
# Set text to front
text.toFront()
2012-12-23 21:46:27 +00:00
2013-04-24 16:49:01 +00:00
appendAnchor: (x, y, commit) ->
2013-03-21 12:34:15 +00:00
r = @r
2013-04-24 16:49:01 +00:00
top = @top
2013-03-11 08:52:27 +00:00
options = @options
anchor = r.circle(x, y, 10).attr(
fill: "#000"
opacity: 0
cursor: "pointer"
2013-03-11 08:52:27 +00:00
).click(->
window.open options.commit_url.replace("%s", commit.id), "_blank"
).hover(->
2013-03-20 09:17:12 +00:00
@tooltip = r.commitTooltip(x + 5, y, commit)
2013-03-11 08:52:27 +00:00
top.push @tooltip.insertBefore(this)
, ->
@tooltip and @tooltip.remove() and delete @tooltip
)
top.push anchor
2013-03-19 09:42:46 +00:00
drawDot: (x, y, commit) ->
2013-03-21 12:34:15 +00:00
r = @r
2013-03-19 09:42:46 +00:00
r.circle(x, y, 3).attr(
fill: @colors[commit.space]
stroke: "none"
)
avatar_box_x = @offsetX + @unitSpace * @mspace + 10
avatar_box_y = y - 10
r.rect(avatar_box_x, avatar_box_y, 20, 20).attr(
stroke: @colors[commit.space]
"stroke-width": 2
)
r.image(commit.author.icon, avatar_box_x, avatar_box_y, 20, 20)
r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr(
"text-anchor": "start"
font: "14px Monaco, monospace"
)
2013-03-19 09:42:46 +00:00
drawLines: (x, y, commit) ->
2013-03-21 12:34:15 +00:00
r = @r
for parent, i in commit.parents
2013-03-19 09:42:46 +00:00
parentCommit = @preparedCommits[parent[0]]
2013-03-21 12:34:15 +00:00
parentY = @offsetY + @unitTime * parentCommit.time
parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space)
parentX2 = @offsetX + @unitSpace * (@mspace - parent[1])
2013-03-20 09:17:12 +00:00
# Set line color
if parentCommit.space <= commit.space
color = @colors[commit.space]
else
color = @colors[parentCommit.space]
# Build line shape
if parent[1] is commit.space
2013-03-26 00:46:10 +00:00
offset = [0, 5]
arrow = "l-2,5,4,0,-2,-5,0,5"
2013-03-19 09:42:46 +00:00
else if parent[1] < commit.space
2013-03-26 00:46:10 +00:00
offset = [3, 3]
arrow = "l5,0,-2,4,-3,-4,4,2"
2013-03-19 09:42:46 +00:00
else
2013-03-26 00:46:10 +00:00
offset = [-3, 3]
arrow = "l-5,0,2,4,3,-4,-4,2"
# Start point
2013-03-26 00:46:10 +00:00
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 isnt parentCommit.space or commit.space isnt parent[1]
route.push(
"L", parentX2, y + 10,
"L", parentX2, parentY - 5,
)
# End point
route.push("L", parentX1, parentY)
r
.path(route)
.attr(
stroke: color
"stroke-width": 2)
2013-03-19 09:42:46 +00:00
2013-04-24 16:49:01 +00:00
markCommit: (commit) ->
2013-03-19 09:42:46 +00:00
if commit.id is @options.commit_id
2013-03-21 12:34:15 +00:00
r = @r
2013-04-24 16:49:01 +00:00
x = @offsetX + @unitSpace * (@mspace - commit.space)
y = @offsetY + @unitTime * commit.time
2013-03-20 09:17:12 +00:00
r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr(
2013-03-19 09:42:46 +00:00
fill: "#000"
2013-03-20 09:17:12 +00:00
"fill-opacity": .5
2013-03-19 09:42:46 +00:00
stroke: "none"
)
# Displayed in the center
2013-04-24 16:49:01 +00:00
@element.scrollTop(y - @graphHeight / 2)
2013-03-19 09:42:46 +00:00
2013-03-11 08:52:27 +00:00
Raphael::commitTooltip = (x, y, commit) ->
boxWidth = 300
boxHeight = 200
icon = @image(gon.relative_url_root + commit.author.icon, x, y, 20, 20)
2013-03-11 08:52:27 +00:00
nameText = @text(x + 25, y + 10, commit.author.name)
idText = @text(x, y + 35, commit.id)
messageText = @text(x, y + 50, commit.message)
textSet = @set(icon, nameText, idText, messageText).attr(
"text-anchor": "start"
font: "12px Monaco, monospace"
)
nameText.attr(
font: "14px Arial"
2012-12-28 15:36:42 +00:00
"font-weight": "bold"
2013-03-11 08:52:27 +00:00
)
2012-12-28 15:36:42 +00:00
2013-03-11 08:52:27 +00:00
idText.attr fill: "#AAA"
@textWrap messageText, boxWidth - 50
rect = @rect(x - 10, y - 10, boxWidth, 100, 4).attr(
fill: "#FFF"
stroke: "#000"
"stroke-linecap": "round"
2012-12-28 15:36:42 +00:00
"stroke-width": 2
2013-03-11 08:52:27 +00:00
)
tooltip = @set(rect, textSet)
rect.attr(
height: tooltip.getBBox().height + 10
width: tooltip.getBBox().width + 10
)
tooltip.transform ["t", 20, 20]
tooltip
2012-12-28 15:36:42 +00:00
2013-03-11 08:52:27 +00:00
Raphael::textWrap = (t, width) ->
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 = []
2012-12-28 15:36:42 +00:00
2013-03-11 08:52:27 +00:00
for word in words
if x + (word.length * letterWidth) > width
s.push "\n"
x = 0
x += word.length * letterWidth
s.push word + " "
2012-12-28 15:36:42 +00:00
2013-03-11 08:52:27 +00:00
t.attr text: s.join("")
b = t.getBBox()
h = Math.abs(b.y2) - Math.abs(b.y) + 1
t.attr y: b.y + h