puppetfactory 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +0 -0
  4. data/bin/pfsh +31 -0
  5. data/bin/puppetfactory +153 -0
  6. data/lib/puppetfactory.rb +300 -0
  7. data/lib/puppetfactory/cli.rb +114 -0
  8. data/lib/puppetfactory/dashboard/rake_tasks.rb +69 -0
  9. data/lib/puppetfactory/dashboard/serverspec_helper.rb +84 -0
  10. data/lib/puppetfactory/dashboard/spec_helper.rb +26 -0
  11. data/lib/puppetfactory/helpers.rb +37 -0
  12. data/lib/puppetfactory/monkeypatches.rb +30 -0
  13. data/lib/puppetfactory/plugins.rb +11 -0
  14. data/lib/puppetfactory/plugins/certificates.rb +28 -0
  15. data/lib/puppetfactory/plugins/classification.rb +75 -0
  16. data/lib/puppetfactory/plugins/code_manager.rb +156 -0
  17. data/lib/puppetfactory/plugins/console_user.rb +62 -0
  18. data/lib/puppetfactory/plugins/dashboard.rb +128 -0
  19. data/lib/puppetfactory/plugins/docker.rb +193 -0
  20. data/lib/puppetfactory/plugins/example.rb +88 -0
  21. data/lib/puppetfactory/plugins/github.rb +102 -0
  22. data/lib/puppetfactory/plugins/gitlab.rb +62 -0
  23. data/lib/puppetfactory/plugins/hooks.rb +46 -0
  24. data/lib/puppetfactory/plugins/login_shell.rb +10 -0
  25. data/lib/puppetfactory/plugins/logs.rb +34 -0
  26. data/lib/puppetfactory/plugins/r10k.rb +112 -0
  27. data/lib/puppetfactory/plugins/shell_user.rb +69 -0
  28. data/lib/puppetfactory/plugins/user_environment.rb +77 -0
  29. data/public/dashboard.js +100 -0
  30. data/public/font-awesome/css/font-awesome.css +2199 -0
  31. data/public/font-awesome/css/font-awesome.min.css +4 -0
  32. data/public/font-awesome/fonts/FontAwesome.otf +0 -0
  33. data/public/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  34. data/public/font-awesome/fonts/fontawesome-webfont.svg +685 -0
  35. data/public/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  36. data/public/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  37. data/public/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  38. data/public/gitviz/LICENSE.md +20 -0
  39. data/public/gitviz/README.md +13 -0
  40. data/public/gitviz/css/explaingit.css +227 -0
  41. data/public/gitviz/css/vendor/1140.css +130 -0
  42. data/public/gitviz/images/forkme_right_red_aa0000.png +0 -0
  43. data/public/gitviz/images/grippy-close.png +0 -0
  44. data/public/gitviz/images/grippy.png +0 -0
  45. data/public/gitviz/images/prompt.gif +0 -0
  46. data/public/gitviz/index.html +734 -0
  47. data/public/gitviz/js/controlbox.js +459 -0
  48. data/public/gitviz/js/explaingit.js +74 -0
  49. data/public/gitviz/js/historyview.js +979 -0
  50. data/public/gitviz/js/main.js +56 -0
  51. data/public/gitviz/js/vendor/d3.min.js +4 -0
  52. data/public/gitviz/js/vendor/jquery-latest.min.js +6 -0
  53. data/public/gitviz/js/vendor/normalize.css +396 -0
  54. data/public/gitviz/js/vendor/require.min.js +35 -0
  55. data/public/gitviz/memtest.html +44 -0
  56. data/public/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  57. data/public/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  58. data/public/images/ui-bg_flat_10_000000_40x100.png +0 -0
  59. data/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  60. data/public/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  61. data/public/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  62. data/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  63. data/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  64. data/public/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  65. data/public/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  66. data/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  67. data/public/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  68. data/public/images/ui-icons_222222_256x240.png +0 -0
  69. data/public/images/ui-icons_228ef1_256x240.png +0 -0
  70. data/public/images/ui-icons_454545_256x240.png +0 -0
  71. data/public/images/ui-icons_ef8c08_256x240.png +0 -0
  72. data/public/images/ui-icons_ffd27a_256x240.png +0 -0
  73. data/public/images/ui-icons_ffffff_256x240.png +0 -0
  74. data/public/jquery-1.11.1.min.js +4 -0
  75. data/public/jquery-ui.css +464 -0
  76. data/public/jquery-ui.min.css +7 -0
  77. data/public/jquery-ui.min.js +13 -0
  78. data/public/jquery-ui.structure.min.css +5 -0
  79. data/public/jquery-ui.theme.min.css +5 -0
  80. data/public/jquery.activity-indicator-1.0.0.min.js +10 -0
  81. data/public/jquery.js +9789 -0
  82. data/public/loginscripts.js +18 -0
  83. data/public/scripts.js +36 -0
  84. data/public/style.css +193 -0
  85. data/public/usermanagement.js +133 -0
  86. data/templates/init_scripts.erb +10 -0
  87. data/templates/puppet.conf.erb +10 -0
  88. data/templates/site.pp.erb +50 -0
  89. data/views/dashboard.erb +62 -0
  90. data/views/home.erb +43 -0
  91. data/views/index.erb +29 -0
  92. data/views/logs.erb +26 -0
  93. data/views/shell.erb +35 -0
  94. data/views/users.erb +69 -0
  95. metadata +256 -0
@@ -0,0 +1,74 @@
1
+ define(['historyview', 'controlbox', 'd3'], function (HistoryView, ControlBox, d3) {
2
+ var prefix = 'ExplainGit',
3
+ openSandBoxes = [],
4
+ open,
5
+ reset,
6
+ explainGit;
7
+
8
+ open = function (args) {
9
+ var name = prefix + args.name,
10
+ containerId = name + '-Container',
11
+ container = d3.select('#' + containerId),
12
+ playground = container.select('.playground-container'),
13
+ historyView, originView = null,
14
+ controlBox;
15
+
16
+ container.style('display', 'block');
17
+
18
+ args.name = name;
19
+ args.height = 525;
20
+ historyView = new HistoryView(args);
21
+
22
+ if (args.originData) {
23
+ originView = new HistoryView({
24
+ name: name + '-Origin',
25
+ width: 300,
26
+ height: 225,
27
+ commitRadius: 15,
28
+ remoteName: 'origin',
29
+ commitData: args.originData
30
+ });
31
+
32
+ originView.render(playground);
33
+ }
34
+
35
+ controlBox = new ControlBox({
36
+ historyView: historyView,
37
+ originView: originView,
38
+ initialMessage: args.initialMessage
39
+ });
40
+
41
+ controlBox.render(playground);
42
+ historyView.render(playground);
43
+
44
+ openSandBoxes.push({
45
+ hv: historyView,
46
+ cb: controlBox,
47
+ container: container
48
+ });
49
+ };
50
+
51
+ reset = function () {
52
+ for (var i = 0; i < openSandBoxes.length; i++) {
53
+ var osb = openSandBoxes[i];
54
+ osb.hv.destroy();
55
+ osb.cb.destroy();
56
+ osb.container.style('display', 'none');
57
+ }
58
+
59
+ openSandBoxes.length = 0;
60
+ d3.selectAll('a.openswitch').classed('selected', false);
61
+ };
62
+
63
+ explainGit = {
64
+ HistoryView: HistoryView,
65
+ ControlBox: ControlBox,
66
+ generateId: HistoryView.generateId,
67
+ open: open,
68
+ reset: reset
69
+ };
70
+
71
+ window.explainGit = explainGit;
72
+
73
+ return explainGit;
74
+ });
@@ -0,0 +1,979 @@
1
+ define(['d3'], function () {
2
+ "use strict";
3
+
4
+ var REG_MARKER_END = 'url(#triangle)',
5
+ MERGE_MARKER_END = 'url(#brown-triangle)',
6
+ FADED_MARKER_END = 'url(#faded-triangle)',
7
+
8
+ preventOverlap,
9
+ applyBranchlessClass,
10
+ cx, cy, fixCirclePosition,
11
+ px1, py1, fixPointerStartPosition,
12
+ px2, py2, fixPointerEndPosition,
13
+ fixIdPosition, tagY;
14
+
15
+ preventOverlap = function preventOverlap(commit, view) {
16
+ var commitData = view.commitData,
17
+ baseLine = view.baseLine,
18
+ shift = view.commitRadius * 4.5,
19
+ overlapped = null;
20
+
21
+ for (var i = 0; i < commitData.length; i++) {
22
+ var c = commitData[i];
23
+ if (c.cx === commit.cx && c.cy === commit.cy && c !== commit) {
24
+ overlapped = c;
25
+ break;
26
+ }
27
+ }
28
+
29
+ if (overlapped) {
30
+ var oParent = view.getCommit(overlapped.parent),
31
+ parent = view.getCommit(commit.parent);
32
+
33
+ if (overlapped.cy < baseLine) {
34
+ overlapped = oParent.cy < parent.cy ? overlapped : commit;
35
+ overlapped.cy -= shift;
36
+ } else {
37
+ overlapped = oParent.cy > parent.cy ? overlapped : commit;
38
+ overlapped.cy += shift;
39
+ }
40
+
41
+ preventOverlap(overlapped, view);
42
+ }
43
+ };
44
+
45
+ applyBranchlessClass = function (selection) {
46
+ if (selection.empty()) {
47
+ return;
48
+ }
49
+
50
+ selection.classed('branchless', function (d) {
51
+ return d.branchless;
52
+ });
53
+
54
+ if (selection.classed('commit-pointer')) {
55
+ selection.attr('marker-end', function (d) {
56
+ return d.branchless ? FADED_MARKER_END : REG_MARKER_END;
57
+ });
58
+ } else if (selection.classed('merge-pointer')) {
59
+ selection.attr('marker-end', function (d) {
60
+ return d.branchless ? FADED_MARKER_END : MERGE_MARKER_END;
61
+ });
62
+ }
63
+ };
64
+
65
+ cx = function (commit, view) {
66
+ var parent = view.getCommit(commit.parent),
67
+ parentCX = parent.cx;
68
+
69
+ if (typeof commit.parent2 === 'string') {
70
+ var parent2 = view.getCommit(commit.parent2);
71
+
72
+ parentCX = parent.cx > parent2.cx ? parent.cx : parent2.cx;
73
+ }
74
+
75
+ return parentCX + (view.commitRadius * 4.5);
76
+ };
77
+
78
+ cy = function (commit, view) {
79
+ var parent = view.getCommit(commit.parent),
80
+ parentCY = parent.cy,
81
+ baseLine = view.baseLine,
82
+ shift = view.commitRadius * 4.5,
83
+ branches = [], // count the existing branches
84
+ branchIndex = 0;
85
+
86
+ for (var i = 0; i < view.commitData.length; i++) {
87
+ var d = view.commitData[i];
88
+
89
+ if (d.parent === commit.parent) {
90
+ branches.push(d.id);
91
+ }
92
+ }
93
+
94
+ branchIndex = branches.indexOf(commit.id);
95
+
96
+ if (parentCY === baseLine) {
97
+ var direction = 1;
98
+ for (var bi = 0; bi < branchIndex; bi++) {
99
+ direction *= -1;
100
+ }
101
+
102
+ shift *= Math.ceil(branchIndex / 2);
103
+
104
+ return parentCY + (shift * direction);
105
+ }
106
+
107
+ if (parentCY < baseLine) {
108
+ return parentCY - (shift * branchIndex);
109
+ } else if (parentCY > baseLine) {
110
+ return parentCY + (shift * branchIndex);
111
+ }
112
+ };
113
+
114
+ fixCirclePosition = function (selection) {
115
+ selection
116
+ .attr('cx', function (d) {
117
+ return d.cx;
118
+ })
119
+ .attr('cy', function (d) {
120
+ return d.cy;
121
+ });
122
+ };
123
+
124
+ // calculates the x1 point for commit pointer lines
125
+ px1 = function (commit, view, pp) {
126
+ pp = pp || 'parent';
127
+
128
+ var parent = view.getCommit(commit[pp]),
129
+ startCX = commit.cx,
130
+ diffX = startCX - parent.cx,
131
+ diffY = parent.cy - commit.cy,
132
+ length = Math.sqrt((diffX * diffX) + (diffY * diffY));
133
+
134
+ return startCX - (view.pointerMargin * (diffX / length));
135
+ };
136
+
137
+ // calculates the y1 point for commit pointer lines
138
+ py1 = function (commit, view, pp) {
139
+ pp = pp || 'parent';
140
+
141
+ var parent = view.getCommit(commit[pp]),
142
+ startCY = commit.cy,
143
+ diffX = commit.cx - parent.cx,
144
+ diffY = parent.cy - startCY,
145
+ length = Math.sqrt((diffX * diffX) + (diffY * diffY));
146
+
147
+ return startCY + (view.pointerMargin * (diffY / length));
148
+ };
149
+
150
+ fixPointerStartPosition = function (selection, view) {
151
+ selection.attr('x1', function (d) {
152
+ return px1(d, view);
153
+ }).attr('y1', function (d) {
154
+ return py1(d, view);
155
+ });
156
+ };
157
+
158
+ px2 = function (commit, view, pp) {
159
+ pp = pp || 'parent';
160
+
161
+ var parent = view.getCommit(commit[pp]),
162
+ endCX = parent.cx,
163
+ diffX = commit.cx - endCX,
164
+ diffY = parent.cy - commit.cy,
165
+ length = Math.sqrt((diffX * diffX) + (diffY * diffY));
166
+
167
+ return endCX + (view.pointerMargin * 1.2 * (diffX / length));
168
+ };
169
+
170
+ py2 = function (commit, view, pp) {
171
+ pp = pp || 'parent';
172
+
173
+ var parent = view.getCommit(commit[pp]),
174
+ endCY = parent.cy,
175
+ diffX = commit.cx - parent.cx,
176
+ diffY = endCY - commit.cy,
177
+ length = Math.sqrt((diffX * diffX) + (diffY * diffY));
178
+
179
+ return endCY - (view.pointerMargin * 1.2 * (diffY / length));
180
+ };
181
+
182
+ fixPointerEndPosition = function (selection, view) {
183
+ selection.attr('x2', function (d) {
184
+ return px2(d, view);
185
+ }).attr('y2', function (d) {
186
+ return py2(d, view);
187
+ });
188
+ };
189
+
190
+ fixIdPosition = function (selection, view) {
191
+ selection.attr('x', function (d) {
192
+ return d.cx;
193
+ }).attr('y', function (d) {
194
+ return d.cy + view.commitRadius + 14;
195
+ });
196
+ };
197
+
198
+ tagY = function tagY(t, view) {
199
+ var commit = view.getCommit(t.commit),
200
+ commitCY = commit.cy,
201
+ tags = commit.tags,
202
+ tagIndex = tags.indexOf(t.name);
203
+
204
+ if (tagIndex === -1) {
205
+ tagIndex = tags.length;
206
+ }
207
+
208
+ if (commitCY < (view.baseLine)) {
209
+ return commitCY - 45 - (tagIndex * 25);
210
+ } else {
211
+ return commitCY + 40 + (tagIndex * 25);
212
+ }
213
+ };
214
+
215
+ /**
216
+ * @class HistoryView
217
+ * @constructor
218
+ */
219
+ function HistoryView(config) {
220
+ var commitData = config.commitData || [],
221
+ commit;
222
+
223
+ for (var i = 0; i < commitData.length; i++) {
224
+ commit = commitData[i];
225
+ !commit.parent && (commit.parent = 'initial');
226
+ !commit.tags && (commit.tags = []);
227
+ }
228
+
229
+ this.name = config.name || 'UnnamedHistoryView';
230
+ this.commitData = commitData;
231
+
232
+ this.branches = [];
233
+ this.currentBranch = config.currentBranch || 'master';
234
+
235
+ this.width = config.width || 886;
236
+ this.height = config.height || 400;
237
+ this.baseLine = this.height * (config.baseLine || 0.6);
238
+
239
+ this.commitRadius = config.commitRadius || 20;
240
+ this.pointerMargin = this.commitRadius * 1.3;
241
+
242
+ this.isRemote = typeof config.remoteName === 'string';
243
+ this.remoteName = config.remoteName;
244
+
245
+ this.initialCommit = {
246
+ id: 'initial',
247
+ parent: null,
248
+ cx: -(this.commitRadius * 2),
249
+ cy: this.baseLine
250
+ };
251
+ }
252
+
253
+ HistoryView.generateId = function () {
254
+ return Math.floor((1 + Math.random()) * 0x10000000).toString(16).substring(1);
255
+ };
256
+
257
+ HistoryView.prototype = {
258
+ /**
259
+ * @method getCommit
260
+ * @param ref {String} the id or a tag name that refers to the commit
261
+ * @return {Object} the commit datum object
262
+ */
263
+ getCommit: function getCommit(ref) {
264
+ var commitData = this.commitData,
265
+ headMatcher = /HEAD(\^+)/.exec(ref),
266
+ matchedCommit = null;
267
+
268
+ if (ref === 'initial') {
269
+ return this.initialCommit;
270
+ }
271
+
272
+ if (headMatcher) {
273
+ ref = 'HEAD';
274
+ }
275
+
276
+ for (var i = 0; i < commitData.length; i++) {
277
+ var commit = commitData[i];
278
+ if (commit === ref) {
279
+ matchedCommit = commit;
280
+ break;
281
+ }
282
+
283
+ if (commit.id === ref) {
284
+ matchedCommit = commit;
285
+ break;
286
+ }
287
+
288
+ if (commit.tags.indexOf(ref) >= 0) {
289
+ matchedCommit = commit;
290
+ break;
291
+ }
292
+ }
293
+
294
+ if (headMatcher && matchedCommit) {
295
+ for (var h = 0; h < headMatcher[1].length; h++) {
296
+ matchedCommit = getCommit.call(this, matchedCommit.parent);
297
+ }
298
+ }
299
+
300
+ return matchedCommit;
301
+ },
302
+
303
+ /**
304
+ * @method getCircle
305
+ * @param ref {String} the id or a tag name that refers to the commit
306
+ * @return {d3 Selection} the d3 selected SVG circle
307
+ */
308
+ getCircle: function (ref) {
309
+ var circle = this.svg.select('#' + this.name + '-' + ref),
310
+ commit;
311
+
312
+ if (circle && !circle.empty()) {
313
+ return circle;
314
+ }
315
+
316
+ commit = this.getCommit(ref);
317
+
318
+ if (!commit) {
319
+ return null;
320
+ }
321
+
322
+ return this.svg.select('#' + this.name + '-' + commit.id);
323
+ },
324
+
325
+ getCircles: function () {
326
+ return this.svg.selectAll('circle.commit');
327
+ },
328
+
329
+ /**
330
+ * @method render
331
+ * @param container {String} selector for the container to render the SVG into
332
+ */
333
+ render: function (container) {
334
+ var svgContainer, svg;
335
+
336
+ svgContainer = container.append('div')
337
+ .classed('svg-container', true)
338
+ .classed('remote-container', this.isRemote);
339
+
340
+ svg = svgContainer.append('svg:svg');
341
+
342
+ svg.attr('id', this.name)
343
+ .attr('width', this.width)
344
+ .attr('height', this.height);
345
+
346
+ if (this.isRemote) {
347
+ svg.append('svg:text')
348
+ .classed('remote-name-display', true)
349
+ .text(this.remoteName)
350
+ .attr('x', 10)
351
+ .attr('y', 25);
352
+ } else {
353
+ svg.append('svg:text')
354
+ .classed('remote-name-display', true)
355
+ .text('Local Repository')
356
+ .attr('x', 10)
357
+ .attr('y', 25);
358
+
359
+ svg.append('svg:text')
360
+ .classed('current-branch-display', true)
361
+ .attr('x', 10)
362
+ .attr('y', 45);
363
+ }
364
+
365
+ this.svgContainer = svgContainer;
366
+ this.svg = svg;
367
+ this.arrowBox = svg.append('svg:g').classed('pointers', true);
368
+ this.commitBox = svg.append('svg:g').classed('commits', true);
369
+ this.tagBox = svg.append('svg:g').classed('tags', true);
370
+
371
+ this.renderCommits();
372
+
373
+ this._setCurrentBranch(this.currentBranch);
374
+ },
375
+
376
+ destroy: function () {
377
+ this.svg.remove();
378
+ this.svgContainer.remove();
379
+
380
+ for (var prop in this) {
381
+ if (this.hasOwnProperty(prop)) {
382
+ this[prop] = null;
383
+ }
384
+ }
385
+ },
386
+
387
+ _calculatePositionData: function () {
388
+ for (var i = 0; i < this.commitData.length; i++) {
389
+ var commit = this.commitData[i];
390
+ commit.cx = cx(commit, this);
391
+ commit.cy = cy(commit, this);
392
+ preventOverlap(commit, this);
393
+ }
394
+ },
395
+
396
+ renderCommits: function () {
397
+ this._calculatePositionData();
398
+ this._calculatePositionData(); // do this twice to make sure
399
+ this._renderCircles();
400
+ this._renderPointers();
401
+ this._renderMergePointers();
402
+ this._renderIdLabels();
403
+ this.checkout(this.currentBranch);
404
+ },
405
+
406
+ _renderCircles: function () {
407
+ var view = this,
408
+ existingCircles,
409
+ newCircles;
410
+
411
+ existingCircles = this.commitBox.selectAll('circle.commit')
412
+ .data(this.commitData, function (d) { return d.id; })
413
+ .attr('id', function (d) {
414
+ return view.name + '-' + d.id;
415
+ })
416
+ .classed('reverted', function (d) {
417
+ return d.reverted;
418
+ })
419
+ .classed('rebased', function (d) {
420
+ return d.rebased;
421
+ });
422
+
423
+ existingCircles.transition()
424
+ .duration(500)
425
+ .call(fixCirclePosition);
426
+
427
+ newCircles = existingCircles.enter()
428
+ .append('svg:circle')
429
+ .attr('id', function (d) {
430
+ return view.name + '-' + d.id;
431
+ })
432
+ .classed('commit', true)
433
+ .classed('merge-commit', function (d) {
434
+ return typeof d.parent2 === 'string';
435
+ })
436
+ .call(fixCirclePosition)
437
+ .attr('r', 1)
438
+ .transition()
439
+ .duration(500)
440
+ .attr('r', this.commitRadius);
441
+
442
+ },
443
+
444
+ _renderPointers: function () {
445
+ var view = this,
446
+ existingPointers,
447
+ newPointers;
448
+
449
+ existingPointers = this.arrowBox.selectAll('line.commit-pointer')
450
+ .data(this.commitData, function (d) { return d.id; })
451
+ .attr('id', function (d) {
452
+ return view.name + '-' + d.id + '-to-' + d.parent;
453
+ });
454
+
455
+ existingPointers.transition()
456
+ .duration(500)
457
+ .call(fixPointerStartPosition, view)
458
+ .call(fixPointerEndPosition, view);
459
+
460
+ newPointers = existingPointers.enter()
461
+ .append('svg:line')
462
+ .attr('id', function (d) {
463
+ return view.name + '-' + d.id + '-to-' + d.parent;
464
+ })
465
+ .classed('commit-pointer', true)
466
+ .call(fixPointerStartPosition, view)
467
+ .attr('x2', function () { return d3.select(this).attr('x1'); })
468
+ .attr('y2', function () { return d3.select(this).attr('y1'); })
469
+ .attr('marker-end', REG_MARKER_END)
470
+ .transition()
471
+ .duration(500)
472
+ .call(fixPointerEndPosition, view);
473
+ },
474
+
475
+ _renderMergePointers: function () {
476
+ var view = this,
477
+ mergeCommits = [],
478
+ existingPointers, newPointers;
479
+
480
+ for (var i = 0; i < this.commitData.length; i++) {
481
+ var commit = this.commitData[i];
482
+ if (typeof commit.parent2 === 'string') {
483
+ mergeCommits.push(commit);
484
+ }
485
+ }
486
+
487
+ existingPointers = this.arrowBox.selectAll('polyline.merge-pointer')
488
+ .data(mergeCommits, function (d) { return d.id; })
489
+ .attr('id', function (d) {
490
+ return view.name + '-' + d.id + '-to-' + d.parent2;
491
+ });
492
+
493
+ existingPointers.transition().duration(500)
494
+ .attr('points', function (d) {
495
+ var p1 = px1(d, view, 'parent2') + ',' + py1(d, view, 'parent2'),
496
+ p2 = px2(d, view, 'parent2') + ',' + py2(d, view, 'parent2');
497
+
498
+ return [p1, p2].join(' ');
499
+ });
500
+
501
+ newPointers = existingPointers.enter()
502
+ .append('svg:polyline')
503
+ .attr('id', function (d) {
504
+ return view.name + '-' + d.id + '-to-' + d.parent2;
505
+ })
506
+ .classed('merge-pointer', true)
507
+ .attr('points', function (d) {
508
+ var x1 = px1(d, view, 'parent2'),
509
+ y1 = py1(d, view, 'parent2'),
510
+ p1 = x1 + ',' + y1;
511
+
512
+ return [p1, p1].join(' ');
513
+ })
514
+ .attr('marker-end', MERGE_MARKER_END)
515
+ .transition()
516
+ .duration(500)
517
+ .attr('points', function (d) {
518
+ var points = d3.select(this).attr('points').split(' '),
519
+ x2 = px2(d, view, 'parent2'),
520
+ y2 = py2(d, view, 'parent2');
521
+
522
+ points[1] = x2 + ',' + y2;
523
+ return points.join(' ');
524
+ });
525
+ },
526
+
527
+ _renderIdLabels: function () {
528
+ var view = this,
529
+ existingLabels,
530
+ newLabels;
531
+
532
+ existingLabels = this.commitBox.selectAll('text.id-label')
533
+ .data(this.commitData, function (d) { return d.id; })
534
+ .text(function (d) { return d.id + '..'; });
535
+
536
+ existingLabels.transition().call(fixIdPosition, view);
537
+
538
+ newLabels = existingLabels.enter()
539
+ .insert('svg:text', ':first-child')
540
+ .classed('id-label', true)
541
+ .text(function (d) { return d.id + '..'; })
542
+ .call(fixIdPosition, view);
543
+ },
544
+
545
+ _parseTagData: function () {
546
+ var tagData = [], i,
547
+ headCommit = null;
548
+
549
+ for (i = 0; i < this.commitData.length; i++) {
550
+ var c = this.commitData[i];
551
+
552
+ for (var t = 0; t < c.tags.length; t++) {
553
+ var tagName = c.tags[t];
554
+ if (tagName.toUpperCase() === 'HEAD') {
555
+ headCommit = c;
556
+ } else if (this.branches.indexOf(tagName) === -1) {
557
+ this.branches.push(tagName);
558
+ }
559
+
560
+ tagData.push({name: tagName, commit: c.id});
561
+ }
562
+ }
563
+
564
+ if (!headCommit) {
565
+ headCommit = this.getCommit(this.currentBranch);
566
+ headCommit.tags.push('HEAD');
567
+ tagData.push({name: 'HEAD', commit: headCommit.id});
568
+ }
569
+
570
+ // find out which commits are not branchless
571
+
572
+
573
+ return tagData;
574
+ },
575
+
576
+ _markBranchlessCommits: function () {
577
+ var branch, commit, parent, parent2, c, b;
578
+
579
+ // first mark every commit as branchless
580
+ for (c = 0; c < this.commitData.length; c++) {
581
+ this.commitData[c].branchless = true;
582
+ }
583
+
584
+ for (b = 0; b < this.branches.length; b++) {
585
+ branch = this.branches[b];
586
+ if (branch.indexOf('/') === -1) {
587
+ commit = this.getCommit(branch);
588
+ parent = this.getCommit(commit.parent);
589
+ parent2 = this.getCommit(commit.parent2);
590
+
591
+ commit.branchless = false;
592
+
593
+ while (parent) {
594
+ parent.branchless = false;
595
+ parent = this.getCommit(parent.parent);
596
+ }
597
+
598
+ // just in case this is a merge commit
599
+ while (parent2) {
600
+ parent2.branchless = false;
601
+ parent2 = this.getCommit(parent2.parent);
602
+ }
603
+ }
604
+ }
605
+
606
+ this.svg.selectAll('circle.commit').call(applyBranchlessClass);
607
+ this.svg.selectAll('line.commit-pointer').call(applyBranchlessClass);
608
+ this.svg.selectAll('polyline.merge-pointer').call(applyBranchlessClass);
609
+ },
610
+
611
+ renderTags: function () {
612
+ var view = this,
613
+ tagData = this._parseTagData(),
614
+ existingTags, newTags;
615
+
616
+ existingTags = this.tagBox.selectAll('g.branch-tag')
617
+ .data(tagData, function (d) { return d.name; });
618
+
619
+ existingTags.exit().remove();
620
+
621
+ existingTags.select('rect')
622
+ .transition()
623
+ .duration(500)
624
+ .attr('y', function (d) { return tagY(d, view); })
625
+ .attr('x', function (d) {
626
+ var commit = view.getCommit(d.commit),
627
+ width = Number(d3.select(this).attr('width'));
628
+
629
+ return commit.cx - (width / 2);
630
+ });
631
+
632
+ existingTags.select('text')
633
+ .transition()
634
+ .duration(500)
635
+ .attr('y', function (d) { return tagY(d, view) + 14; })
636
+ .attr('x', function (d) {
637
+ var commit = view.getCommit(d.commit);
638
+ return commit.cx;
639
+ });
640
+
641
+ newTags = existingTags.enter()
642
+ .append('g')
643
+ .attr('class', function (d) {
644
+ var classes = 'branch-tag';
645
+ if (d.name.indexOf('/') >= 0) {
646
+ classes += ' remote-branch';
647
+ } else if (d.name.toUpperCase() === 'HEAD') {
648
+ classes += ' head-tag';
649
+ }
650
+ return classes;
651
+ });
652
+
653
+ newTags.append('svg:rect')
654
+ .attr('width', function (d) {
655
+ return (d.name.length * 6) + 10;
656
+ })
657
+ .attr('height', 20)
658
+ .attr('y', function (d) { return tagY(d, view); })
659
+ .attr('x', function (d) {
660
+ var commit = view.getCommit(d.commit),
661
+ width = Number(d3.select(this).attr('width'));
662
+
663
+ return commit.cx - (width / 2);
664
+ });
665
+
666
+ newTags.append('svg:text')
667
+ .text(function (d) { return d.name; })
668
+ .attr('y', function (d) {
669
+ return tagY(d, view) + 14;
670
+ })
671
+ .attr('x', function (d) {
672
+ var commit = view.getCommit(d.commit);
673
+ return commit.cx;
674
+ });
675
+
676
+ this._markBranchlessCommits();
677
+ },
678
+
679
+ _setCurrentBranch: function (branch) {
680
+ var display = this.svg.select('text.current-branch-display'),
681
+ text = 'Current Branch: ';
682
+
683
+ if (branch && branch.indexOf('/') === -1) {
684
+ text += branch;
685
+ this.currentBranch = branch;
686
+ } else {
687
+ text += ' DETACHED HEAD';
688
+ this.currentBranch = null;
689
+ }
690
+
691
+ display.text(text);
692
+ },
693
+
694
+ moveTag: function (tag, ref) {
695
+ var currentLoc = this.getCommit(tag),
696
+ newLoc = this.getCommit(ref);
697
+
698
+ if (currentLoc) {
699
+ currentLoc.tags.splice(currentLoc.tags.indexOf(tag), 1);
700
+ }
701
+
702
+ newLoc.tags.push(tag);
703
+ return this;
704
+ },
705
+
706
+ /**
707
+ * @method isAncestor
708
+ * @param ref1
709
+ * @param ref2
710
+ * @return {Boolean} whether or not ref1 is an ancestor of ref2
711
+ */
712
+ isAncestor: function isAncestor(ref1, ref2) {
713
+ var currentCommit = this.getCommit(ref1),
714
+ targetTree = this.getCommit(ref2),
715
+ inTree = false,
716
+ additionalTrees = [];
717
+
718
+ if (!currentCommit) {
719
+ return false;
720
+ }
721
+
722
+ while (targetTree) {
723
+ if (targetTree.id === currentCommit.id) {
724
+ inTree = true;
725
+ targetTree = null;
726
+ } else {
727
+ if (targetTree.parent2) {
728
+ additionalTrees.push(targetTree.parent2);
729
+ }
730
+ targetTree = this.getCommit(targetTree.parent);
731
+ }
732
+ }
733
+
734
+ if (inTree) {
735
+ return true;
736
+ }
737
+
738
+ for (var i = 0; i < additionalTrees.length; i++) {
739
+ inTree = isAncestor.call(this, currentCommit, additionalTrees[i]);
740
+ if (inTree) break;
741
+ }
742
+
743
+ return inTree;
744
+ },
745
+
746
+ commit: function (commit) {
747
+ commit = commit || {};
748
+
749
+ !commit.id && (commit.id = HistoryView.generateId());
750
+ !commit.tags && (commit.tags = []);
751
+
752
+ if (!commit.parent) {
753
+ if (!this.currentBranch) {
754
+ throw new Error('Not a good idea to make commits while in a detached HEAD state.');
755
+ }
756
+
757
+ commit.parent = this.getCommit(this.currentBranch).id;
758
+ }
759
+
760
+ this.commitData.push(commit);
761
+ this.moveTag(this.currentBranch, commit.id);
762
+
763
+ this.renderCommits();
764
+
765
+ this.checkout(this.currentBranch);
766
+ return this;
767
+ },
768
+
769
+ branch: function (name) {
770
+ if (!name || name.trim() === '') {
771
+ throw new Error('You need to give a branch name.');
772
+ }
773
+
774
+ if (name === 'HEAD') {
775
+ throw new Error('You cannot name your branch "HEAD".');
776
+ }
777
+
778
+ if (name.indexOf(' ') > -1) {
779
+ throw new Error('Branch names cannot contain spaces.');
780
+ }
781
+
782
+ if (this.branches.indexOf(name) > -1) {
783
+ throw new Error('Branch "' + name + '" already exists.');
784
+ }
785
+
786
+ this.getCommit('HEAD').tags.push(name);
787
+ this.renderTags();
788
+ return this;
789
+ },
790
+
791
+ deleteBranch: function (name) {
792
+ var branchIndex,
793
+ commit;
794
+
795
+ if (!name || name.trim() === '') {
796
+ throw new Error('You need to give a branch name.');
797
+ }
798
+
799
+ if (name === this.currentBranch) {
800
+ throw new Error('Cannot delete the currently checked-out branch.');
801
+ }
802
+
803
+ branchIndex = this.branches.indexOf(name);
804
+
805
+ if (branchIndex === -1) {
806
+ throw new Error('That branch doesn\'t exist.');
807
+ }
808
+
809
+ this.branches.splice(branchIndex, 1);
810
+ commit = this.getCommit(name);
811
+ branchIndex = commit.tags.indexOf(name);
812
+
813
+ if (branchIndex > -1) {
814
+ commit.tags.splice(branchIndex, 1);
815
+ }
816
+
817
+ this.renderTags();
818
+ },
819
+
820
+ checkout: function (ref) {
821
+ var commit = this.getCommit(ref);
822
+
823
+ if (!commit) {
824
+ throw new Error('Cannot find commit: ' + ref);
825
+ }
826
+
827
+ var previousHead = this.getCircle('HEAD'),
828
+ newHead = this.getCircle(commit.id);
829
+
830
+ if (previousHead && !previousHead.empty()) {
831
+ previousHead.classed('checked-out', false);
832
+ }
833
+
834
+ this._setCurrentBranch(ref === commit.id ? null : ref);
835
+ this.moveTag('HEAD', commit.id);
836
+ this.renderTags();
837
+
838
+ newHead.classed('checked-out', true);
839
+
840
+ return this;
841
+ },
842
+
843
+ reset: function (ref) {
844
+ var commit = this.getCommit(ref);
845
+
846
+ if (!commit) {
847
+ throw new Error('Cannot find ref: ' + ref);
848
+ }
849
+
850
+ if (this.currentBranch) {
851
+ this.moveTag(this.currentBranch, commit.id);
852
+ this.checkout(this.currentBranch);
853
+ } else {
854
+ this.checkout(commit.id);
855
+ }
856
+
857
+ return this;
858
+ },
859
+
860
+ revert: function (ref) {
861
+ var commit = this.getCommit(ref);
862
+
863
+ if (!commit) {
864
+ throw new Error('Cannot find ref: ' + ref);
865
+ }
866
+
867
+ if (this.isAncestor(commit, 'HEAD')) {
868
+ commit.reverted = true;
869
+ this.commit({reverts: commit.id});
870
+ } else {
871
+ throw new Error(ref + 'is not an ancestor of HEAD.');
872
+ }
873
+ },
874
+
875
+ fastForward: function (ref) {
876
+ var targetCommit = this.getCommit(ref);
877
+
878
+ if (this.currentBranch) {
879
+ this.moveTag(this.currentBranch, targetCommit.id);
880
+ this.checkout(this.currentBranch);
881
+ } else {
882
+ this.checkout(targetCommit.id);
883
+ }
884
+ },
885
+
886
+ merge: function (ref) {
887
+ var mergeTarget = this.getCommit(ref),
888
+ currentCommit = this.getCommit('HEAD');
889
+
890
+ if (!mergeTarget) {
891
+ throw new Error('Cannot find ref: ' + ref);
892
+ }
893
+
894
+ if (currentCommit.id === mergeTarget.id) {
895
+ throw new Error('Already up-to-date.');
896
+ } else if (currentCommit.parent2 === mergeTarget.id) {
897
+ throw new Error('Already up-to-date.');
898
+ } else if (this.isAncestor(currentCommit, mergeTarget)) {
899
+ this.fastForward(mergeTarget);
900
+ return 'Fast-Forward';
901
+ } else {
902
+ this.commit({parent2: mergeTarget.id});
903
+ }
904
+ },
905
+
906
+ rebase: function (ref) {
907
+ var rebaseTarget = this.getCommit(ref),
908
+ currentCommit = this.getCommit('HEAD'),
909
+ isCommonAncestor,
910
+ rebaseTreeLoc,
911
+ toRebase = [], rebasedCommit,
912
+ remainingHusk;
913
+
914
+ if (!rebaseTarget) {
915
+ throw new Error('Cannot find ref: ' + ref);
916
+ }
917
+
918
+ if (currentCommit.id === rebaseTarget.id) {
919
+ throw new Error('Already up-to-date.');
920
+ } else if (currentCommit.parent2 === rebaseTarget.id) {
921
+ throw new Error('Already up-to-date.');
922
+ }
923
+
924
+ isCommonAncestor = this.isAncestor(currentCommit, rebaseTarget);
925
+
926
+ if (isCommonAncestor) {
927
+ this.fastForward(rebaseTarget);
928
+ return 'Fast-Forward';
929
+ }
930
+
931
+ rebaseTreeLoc = rebaseTarget.id
932
+
933
+ while (!isCommonAncestor) {
934
+ toRebase.unshift(currentCommit);
935
+ currentCommit = this.getCommit(currentCommit.parent);
936
+ isCommonAncestor = this.isAncestor(currentCommit, rebaseTarget);
937
+ }
938
+
939
+ for (var i = 0; i < toRebase.length; i++) {
940
+ rebasedCommit = toRebase[i];
941
+
942
+ remainingHusk = {
943
+ id: rebasedCommit.id,
944
+ parent: rebasedCommit.parent,
945
+ tags: []
946
+ };
947
+
948
+ for (var t = 0; t < rebasedCommit.tags.length; t++) {
949
+ var tagName = rebasedCommit.tags[t];
950
+ if (tagName !== this.currentBranch && tagName !== 'HEAD') {
951
+ remainingHusk.tags.unshift(tagName);
952
+ }
953
+ }
954
+
955
+ this.commitData.push(remainingHusk);
956
+
957
+ rebasedCommit.parent = rebaseTreeLoc;
958
+ rebaseTreeLoc = HistoryView.generateId()
959
+ rebasedCommit.id = rebaseTreeLoc;
960
+ rebasedCommit.tags.length = 0;
961
+ rebasedCommit.rebased = true;
962
+ }
963
+
964
+ if (this.currentBranch) {
965
+ rebasedCommit.tags.push(this.currentBranch);
966
+ }
967
+
968
+ this.renderCommits();
969
+
970
+ if (this.currentBranch) {
971
+ this.checkout(this.currentBranch);
972
+ } else {
973
+ this.checkout(rebasedCommit.id);
974
+ }
975
+ }
976
+ };
977
+
978
+ return HistoryView;
979
+ });