branch-graph.js.coffee 8.5 KB
Newer Older
R
Robert Schilling 已提交
1
class @BranchGraph
S
Sato Hiroyuki 已提交
2 3 4 5 6 7
  constructor: (@element, @options) ->
    @preparedCommits = {}
    @mtime = 0
    @mspace = 0
    @parents = {}
    @colors = ["#000"]
S
Sato Hiroyuki 已提交
8
    @offsetX = 150
S
Sato Hiroyuki 已提交
9
    @offsetY = 20
10
    @unitTime = 30
S
Sato Hiroyuki 已提交
11
    @unitSpace = 10
S
Sato Hiroyuki 已提交
12
    @prev_start = -1
S
Sato Hiroyuki 已提交
13
    @load()
V
Valery Sizov 已提交
14

S
Sato Hiroyuki 已提交
15 16 17 18 19 20 21 22 23 24
  load: ->
    $.ajax
      url: @options.url
      method: "get"
      dataType: "json"
      success: $.proxy((data) ->
        $(".loading", @element).hide()
        @prepareData data.days, data.commits
        @buildGraph()
      , this)
N
Nihad Abbasov 已提交
25

S
Sato Hiroyuki 已提交
26 27
  prepareData: (@days, @commits) ->
    @collectParents()
S
Sato Hiroyuki 已提交
28 29 30 31 32 33 34
    @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)
S
Sato Hiroyuki 已提交
35 36 37 38

    for c in @commits
      c.isParent = true  if c.id of @parents
      @preparedCommits[c.id] = c
S
Sato Hiroyuki 已提交
39
      @markCommit(c)
S
Sato Hiroyuki 已提交
40 41 42 43 44 45 46 47 48

    @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
S
Sato Hiroyuki 已提交
49
        @mspace = Math.max(@mspace, p[1])
S
Sato Hiroyuki 已提交
50 51 52 53 54 55 56 57 58 59 60

  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: ->
S
Sato Hiroyuki 已提交
61
    r = @r
S
Sato Hiroyuki 已提交
62 63
    cuday = 0
    cumonth = ""
S
Sato Hiroyuki 已提交
64

65 66
    r.rect(0, 0, 40, @barHeight).attr fill: "#222"
    r.rect(40, 0, 30, @barHeight).attr fill: "#444"
S
Sato Hiroyuki 已提交
67 68

    for day, mm in @days
69
      if cuday isnt day[0] || cumonth isnt day[1]
S
Sato Hiroyuki 已提交
70
        # Dates
71
        r.text(55, @offsetY + @unitTime * mm, day[0])
S
Sato Hiroyuki 已提交
72 73
          .attr(
            font: "12px Monaco, monospace"
74
            fill: "#BBB"
S
Sato Hiroyuki 已提交
75 76 77 78 79
          )
        cuday = day[0]

      if cumonth isnt day[1]
        # Months
80
        r.text(20, @offsetY + @unitTime * mm, day[1])
S
Sato Hiroyuki 已提交
81 82
          .attr(
            font: "12px Monaco, monospace"
83
            fill: "#EEE"
S
Sato Hiroyuki 已提交
84 85 86
          )
        cumonth = day[1]

S
Sato Hiroyuki 已提交
87
    @renderPartialGraph()
S
Sato Hiroyuki 已提交
88

S
Sato Hiroyuki 已提交
89
    @bindEvents()
S
Sato Hiroyuki 已提交
90

S
Sato Hiroyuki 已提交
91 92
  renderPartialGraph: ->
    start = Math.floor((@element.scrollTop() - @offsetY) / @unitTime) - 10
93 94 95
    if start < 0
      isGraphEdge = true
      start = 0
S
Sato Hiroyuki 已提交
96
    end = start + 40
97 98 99
    if @commits.length < end
      isGraphEdge = true
      end = @commits.length
S
Sato Hiroyuki 已提交
100

101
    if @prev_start == -1 or Math.abs(@prev_start - start) > 10 or isGraphEdge
S
Sato Hiroyuki 已提交
102
      i = start
103

S
Sato Hiroyuki 已提交
104
      @prev_start = start
105

S
Sato Hiroyuki 已提交
106 107 108
      while i < end
        commit = @commits[i]
        i += 1
S
Sato Hiroyuki 已提交
109

S
Sato Hiroyuki 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
        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()
S
Sato Hiroyuki 已提交
125 126 127

  bindEvents: ->
    element = @element
128 129 130

    $(element).scroll (event) =>
      @renderPartialGraph()
S
Sato Hiroyuki 已提交
131

R
Robert Schilling 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
  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
S
Sato Hiroyuki 已提交
153 154 155

  appendLabel: (x, y, commit) ->
    return unless commit.refs
S
Sato Hiroyuki 已提交
156

S
Sato Hiroyuki 已提交
157
    r = @r
S
Sato Hiroyuki 已提交
158
    shortrefs = commit.refs
S
Sato Hiroyuki 已提交
159 160
    # Truncate if longer than 15 chars
    shortrefs = shortrefs.substr(0, 15) + "…"  if shortrefs.length > 17
S
Sato Hiroyuki 已提交
161
    text = r.text(x + 4, y, shortrefs).attr(
S
Sato Hiroyuki 已提交
162
      "text-anchor": "start"
S
Sato Hiroyuki 已提交
163 164
      font: "10px Monaco, monospace"
      fill: "#FFF"
S
Sato Hiroyuki 已提交
165
      title: commit.refs
S
Sato Hiroyuki 已提交
166 167 168
    )
    textbox = text.getBBox()
    # Create rectangle based on the size of the textbox
S
Sato Hiroyuki 已提交
169
    rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr(
S
Sato Hiroyuki 已提交
170
      fill: "#000"
S
Sato Hiroyuki 已提交
171
      "fill-opacity": .5
S
Sato Hiroyuki 已提交
172 173
      stroke: "none"
    )
S
Sato Hiroyuki 已提交
174
    triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr(
S
Sato Hiroyuki 已提交
175
      fill: "#000"
S
Sato Hiroyuki 已提交
176
      "fill-opacity": .5
S
Sato Hiroyuki 已提交
177 178
      stroke: "none"
    )
S
Sato Hiroyuki 已提交
179 180 181 182

    label = r.set(rect, text)
    label.transform(["t", -rect.getBBox().width - 15, 0])

S
Sato Hiroyuki 已提交
183 184
    # Set text to front
    text.toFront()
K
Koen Punt 已提交
185

S
Sato Hiroyuki 已提交
186
  appendAnchor: (x, y, commit) ->
S
Sato Hiroyuki 已提交
187
    r = @r
S
Sato Hiroyuki 已提交
188
    top = @top
S
Sato Hiroyuki 已提交
189 190 191 192
    options = @options
    anchor = r.circle(x, y, 10).attr(
      fill: "#000"
      opacity: 0
K
Koen Punt 已提交
193
      cursor: "pointer"
S
Sato Hiroyuki 已提交
194 195 196
    ).click(->
      window.open options.commit_url.replace("%s", commit.id), "_blank"
    ).hover(->
S
Sato Hiroyuki 已提交
197
      @tooltip = r.commitTooltip(x + 5, y, commit)
S
Sato Hiroyuki 已提交
198 199 200 201 202 203
      top.push @tooltip.insertBefore(this)
    , ->
      @tooltip and @tooltip.remove() and delete @tooltip
    )
    top.push anchor

S
Sato Hiroyuki 已提交
204
  drawDot: (x, y, commit) ->
S
Sato Hiroyuki 已提交
205
    r = @r
S
Sato Hiroyuki 已提交
206 207 208 209
    r.circle(x, y, 3).attr(
      fill: @colors[commit.space]
      stroke: "none"
    )
J
Jeroen van Baarsen 已提交
210 211 212 213

    avatar_box_x = @offsetX + @unitSpace * @mspace + 10
    avatar_box_y = y - 10
    r.rect(avatar_box_x, avatar_box_y, 20, 20).attr(
214 215 216
      stroke: @colors[commit.space]
      "stroke-width": 2
    )
217
    r.image(commit.author.icon, avatar_box_x, avatar_box_y, 20, 20)
218 219 220 221
    r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr(
      "text-anchor": "start"
      font: "14px Monaco, monospace"
    )
S
Sato Hiroyuki 已提交
222 223

  drawLines: (x, y, commit) ->
S
Sato Hiroyuki 已提交
224
    r = @r
225
    for parent, i in commit.parents
S
Sato Hiroyuki 已提交
226
      parentCommit = @preparedCommits[parent[0]]
S
Sato Hiroyuki 已提交
227 228 229
      parentY = @offsetY + @unitTime * parentCommit.time
      parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space)
      parentX2 = @offsetX + @unitSpace * (@mspace - parent[1])
S
Sato Hiroyuki 已提交
230

231 232 233 234 235 236 237 238 239
      # 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
S
Sato Hiroyuki 已提交
240 241
        offset = [0, 5]
        arrow = "l-2,5,4,0,-2,-5,0,5"
S
Sato Hiroyuki 已提交
242

243
      else if parent[1] < commit.space
S
Sato Hiroyuki 已提交
244 245
        offset = [3, 3]
        arrow = "l5,0,-2,4,-3,-4,4,2"
S
Sato Hiroyuki 已提交
246 247

      else
S
Sato Hiroyuki 已提交
248 249
        offset = [-3, 3]
        arrow = "l-5,0,2,4,3,-4,-4,2"
250 251

      # Start point
S
Sato Hiroyuki 已提交
252
      route = ["M", x + offset[0], y + offset[1]]
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272

      # 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)
S
Sato Hiroyuki 已提交
273

S
Sato Hiroyuki 已提交
274
  markCommit: (commit) ->
S
Sato Hiroyuki 已提交
275
    if commit.id is @options.commit_id
S
Sato Hiroyuki 已提交
276
      r = @r
S
Sato Hiroyuki 已提交
277 278
      x = @offsetX + @unitSpace * (@mspace - commit.space)
      y = @offsetY + @unitTime * commit.time
S
Sato Hiroyuki 已提交
279
      r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr(
S
Sato Hiroyuki 已提交
280
        fill: "#000"
S
Sato Hiroyuki 已提交
281
        "fill-opacity": .5
S
Sato Hiroyuki 已提交
282 283 284
        stroke: "none"
      )
      # Displayed in the center
S
Sato Hiroyuki 已提交
285
      @element.scrollTop(y - @graphHeight / 2)
S
Sato Hiroyuki 已提交
286

S
Sato Hiroyuki 已提交
287 288 289
Raphael::commitTooltip = (x, y, commit) ->
  boxWidth = 300
  boxHeight = 200
290
  icon = @image(gon.relative_url_root + commit.author.icon, x, y, 20, 20)
S
Sato Hiroyuki 已提交
291 292 293 294 295 296 297 298 299
  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"
K
Koen Punt 已提交
300
    "font-weight": "bold"
S
Sato Hiroyuki 已提交
301
  )
K
Koen Punt 已提交
302

S
Sato Hiroyuki 已提交
303 304 305 306 307 308
  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"
K
Koen Punt 已提交
309
    "stroke-width": 2
S
Sato Hiroyuki 已提交
310 311 312 313 314 315 316 317 318
  )
  tooltip = @set(rect, textSet)
  rect.attr(
    height: tooltip.getBBox().height + 10
    width: tooltip.getBBox().width + 10
  )

  tooltip.transform ["t", 20, 20]
  tooltip
K
Koen Punt 已提交
319

S
Sato Hiroyuki 已提交
320 321 322 323 324 325 326 327 328
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 = []
K
Koen Punt 已提交
329

S
Sato Hiroyuki 已提交
330 331 332 333 334 335
  for word in words
    if x + (word.length * letterWidth) > width
      s.push "\n"
      x = 0
    x += word.length * letterWidth
    s.push word + " "
K
Koen Punt 已提交
336

S
Sato Hiroyuki 已提交
337 338 339 340
  t.attr text: s.join("")
  b = t.getBBox()
  h = Math.abs(b.y2) - Math.abs(b.y) + 1
  t.attr y: b.y + h