gitoe 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.gitmodules +3 -0
  4. data/Gemfile +7 -0
  5. data/Gemfile.lock +91 -0
  6. data/Guardfile +8 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +30 -0
  9. data/Rakefile +35 -0
  10. data/Rules +47 -0
  11. data/bin/gitoe +36 -0
  12. data/content/gitoe-draw.coffee +342 -0
  13. data/content/gitoe-repo.coffee +546 -0
  14. data/content/gitoe.coffee +182 -0
  15. data/content/index.haml +71 -0
  16. data/content/jquery/jquery-1.9.1.min.js +5 -0
  17. data/content/jquery/jquery.scrollTo.min.js +7 -0
  18. data/content/raphael-min.js +10 -0
  19. data/content/reset.sass +46 -0
  20. data/content/style.sass +109 -0
  21. data/gitoe.gemspec +34 -0
  22. data/lib/gitoe.rb +16 -0
  23. data/lib/gitoe/httpserver/public/gitoe-draw.js +399 -0
  24. data/lib/gitoe/httpserver/public/gitoe-repo.js +618 -0
  25. data/lib/gitoe/httpserver/public/gitoe.js +249 -0
  26. data/lib/gitoe/httpserver/public/index.html +49 -0
  27. data/lib/gitoe/httpserver/public/jquery/jquery-1.9.1.min.js +5 -0
  28. data/lib/gitoe/httpserver/public/jquery/jquery.scrollTo.min.js +7 -0
  29. data/lib/gitoe/httpserver/public/raphael-min.js +10 -0
  30. data/lib/gitoe/httpserver/public/reset.css +45 -0
  31. data/lib/gitoe/httpserver/public/style.css +106 -0
  32. data/lib/gitoe/httpserver/repos.rb +71 -0
  33. data/lib/gitoe/httpserver/static.rb +17 -0
  34. data/lib/gitoe/repo/repo.rb +174 -0
  35. data/lib/gitoe/repo/rugged.rb +121 -0
  36. data/lib/gitoe/version.rb +3 -0
  37. data/nanoc.yaml +77 -0
  38. data/test/test.rb +9 -0
  39. data/todo.markdown +16 -0
  40. data/vendor/jquery-1.9.1.min.js +5 -0
  41. data/vendor/raphael-min.js +10 -0
  42. metadata +239 -0
data/lib/gitoe.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "gitoe/version"
2
+
3
+ start = Time.now
4
+
5
+ $gitoe_debug = (ENV['RACK_ENV'] == 'development')
6
+ $gitoe_log = lambda do |str|
7
+ if $gitoe_debug
8
+ $stderr.puts "#{ Time.now - start }s : #{str}"
9
+ true
10
+ else
11
+ false
12
+ end
13
+ end
14
+
15
+ module Gitoe
16
+ end
@@ -0,0 +1,399 @@
1
+ (function() {
2
+ var $, DAGLayout, GitoeCanvas, R, clone, strcmp, _ref,
3
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4
+
5
+ $ = jQuery || (function() {
6
+ throw "demand jQuery";
7
+ })();
8
+
9
+ R = Raphael || (function() {
10
+ throw "demand Raphael";
11
+ })();
12
+
13
+ clone = function(obj) {
14
+ return $.extend({}, obj);
15
+ };
16
+
17
+ strcmp = exports.gitoe.strcmp;
18
+
19
+ DAGLayout = (function() {
20
+ function DAGLayout(cb) {
21
+ this.cb = cb;
22
+ this.query_pos = __bind(this.query_pos, this);
23
+ this.query_parents = __bind(this.query_parents, this);
24
+ this.add_node = __bind(this.add_node, this);
25
+ this.children = {};
26
+ this.parents = {};
27
+ this.layer = {};
28
+ this.position = {};
29
+ this.grid = {};
30
+ this.layer_span = {};
31
+ }
32
+
33
+ DAGLayout.prototype.add_node = function(id, parents) {
34
+ var layer, layer_span, pos;
35
+
36
+ this.topo(id, parents);
37
+ layer = this.get_layer(id);
38
+ layer_span = this.get_layer_span(id, layer);
39
+ pos = this.get_position(id, layer, layer_span);
40
+ return this.cb.draw_node(id, layer, pos, layer_span);
41
+ };
42
+
43
+ DAGLayout.prototype.get_layer = function(id) {
44
+ var layer, parent, parent_layer, _base, _i, _len, _ref, _ref1;
45
+
46
+ layer = 0;
47
+ _ref = this.parents[id];
48
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
49
+ parent = _ref[_i];
50
+ parent_layer = this.layer[parent];
51
+ if (parent_layer >= layer) {
52
+ layer = parent_layer + 1;
53
+ }
54
+ }
55
+ if ((_ref1 = (_base = this.grid)[layer]) == null) {
56
+ _base[layer] = [];
57
+ }
58
+ return this.layer[id] = layer;
59
+ };
60
+
61
+ DAGLayout.prototype.get_layer_span = function(id, layer) {
62
+ var l, layer_span, parent, parent_layer, _i, _len, _ref;
63
+
64
+ layer_span = 1;
65
+ l = {};
66
+ _ref = this.parents[id];
67
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
68
+ parent = _ref[_i];
69
+ parent_layer = this.layer[parent];
70
+ l[parent] = parent_layer;
71
+ if (parent_layer + layer_span < layer) {
72
+ layer_span = layer - parent_layer;
73
+ }
74
+ }
75
+ return this.layer_span[id] = layer_span;
76
+ };
77
+
78
+ DAGLayout.prototype.get_position = function(id, layer, layer_span) {
79
+ var conflict, grid, layers_to_check, occupied, position, _i, _j, _len, _ref, _ref1, _results;
80
+
81
+ position = -1;
82
+ conflict = true;
83
+ layers_to_check = (function() {
84
+ _results = [];
85
+ for (var _i = _ref = layer - layer_span + 1, _ref1 = layer; _ref <= _ref1 ? _i <= _ref1 : _i >= _ref1; _ref <= _ref1 ? _i++ : _i--){ _results.push(_i); }
86
+ return _results;
87
+ }).apply(this);
88
+ grid = this.grid;
89
+ while (conflict) {
90
+ position++;
91
+ occupied = layers_to_check.filter(function(layer) {
92
+ return grid[layer][position];
93
+ });
94
+ if (occupied.length === 0) {
95
+ conflict = false;
96
+ }
97
+ }
98
+ for (_j = 0, _len = layers_to_check.length; _j < _len; _j++) {
99
+ layer = layers_to_check[_j];
100
+ this.grid[layer][position] = id;
101
+ }
102
+ return this.position[id] = position;
103
+ };
104
+
105
+ DAGLayout.prototype.topo = function(id, parents) {
106
+ var cs, parent, _i, _len;
107
+
108
+ for (_i = 0, _len = parents.length; _i < _len; _i++) {
109
+ parent = parents[_i];
110
+ cs = this.children[parent];
111
+ if (cs === void 0) {
112
+ throw "<" + id + "> added before its parent <" + parent + ">";
113
+ } else {
114
+ cs.push(id);
115
+ }
116
+ }
117
+ if (this.parents[id]) {
118
+ throw "<" + id + "> added more than once";
119
+ }
120
+ this.parents[id] = parents;
121
+ return this.children[id] = [];
122
+ };
123
+
124
+ DAGLayout.prototype.query_parents = function(id) {
125
+ return this.parents[id];
126
+ };
127
+
128
+ DAGLayout.prototype.query_pos = function(id) {
129
+ return {
130
+ layer: this.layer[id],
131
+ pos: this.position[id]
132
+ };
133
+ };
134
+
135
+ return DAGLayout;
136
+
137
+ })();
138
+
139
+ GitoeCanvas = (function() {
140
+ GitoeCanvas.CONST = {
141
+ canvas: {
142
+ width: 300,
143
+ height: 100
144
+ },
145
+ padding_left: 60,
146
+ padding_top: 40,
147
+ outer_width: 80,
148
+ outer_height: 60,
149
+ commit_handle: 10,
150
+ box: {
151
+ width: 60,
152
+ height: 20,
153
+ radius: 2,
154
+ attr: {
155
+ fill: "lightblue",
156
+ "stroke-width": 2,
157
+ stroke: 'black'
158
+ }
159
+ },
160
+ text_attr: {
161
+ 'font-family': 'mono',
162
+ 'font-size': 12
163
+ },
164
+ path_style: {
165
+ fill: 'pink',
166
+ strokeWidth: 3
167
+ }
168
+ };
169
+
170
+ function GitoeCanvas(id_container) {
171
+ this.draw_async = __bind(this.draw_async, this);
172
+ this.add_commit_async = __bind(this.add_commit_async, this); this.dag = new DAGLayout({
173
+ draw_node: this.draw_async
174
+ });
175
+ this.constant = clone(GitoeCanvas.CONST);
176
+ this.init_canvas(id_container);
177
+ this.objs = {};
178
+ this.div = $("#" + id_container);
179
+ this.ref_objs = {};
180
+ }
181
+
182
+ GitoeCanvas.prototype.add_commit_async = function(commit) {
183
+ return setTimeout(this.add_commit.bind(this, commit), 500);
184
+ };
185
+
186
+ GitoeCanvas.prototype.draw_async = function(sha1, layer, pos) {
187
+ return setTimeout(this.draw.bind(this, sha1, layer, pos), 0);
188
+ };
189
+
190
+ GitoeCanvas.prototype.add_commit = function(commit) {
191
+ return this.dag.add_node(commit.sha1, commit.parents);
192
+ };
193
+
194
+ GitoeCanvas.prototype.draw = function(sha1, layer, pos) {
195
+ var commit_box, coord, need_focus, parents, paths, text;
196
+
197
+ if (this.objs[sha1]) {
198
+ this.destroy(sha1);
199
+ }
200
+ coord = this.commit_coord(layer, pos);
201
+ parents = this.dag.query_parents(sha1).map(this.dag.query_pos);
202
+ commit_box = this.draw_commit_box(coord);
203
+ text = this.draw_commit_text(coord, sha1);
204
+ paths = this.draw_paths(coord, parents);
205
+ need_focus = !!(this.canvas_inc_height(coord.top) + this.canvas_inc_width(coord.left));
206
+ if (need_focus) {
207
+ this.focus(coord);
208
+ }
209
+ return this.objs[sha1] = {
210
+ commit_box: commit_box,
211
+ text: text,
212
+ paths: paths
213
+ };
214
+ };
215
+
216
+ GitoeCanvas.prototype.clear_refs = function() {
217
+ var obj, objs, ref_name, _ref, _results;
218
+
219
+ _ref = this.ref_objs;
220
+ _results = [];
221
+ for (ref_name in _ref) {
222
+ objs = _ref[ref_name];
223
+ _results.push((function() {
224
+ var _i, _len, _ref1, _results1;
225
+
226
+ _ref1 = objs || [];
227
+ _results1 = [];
228
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
229
+ obj = _ref1[_i];
230
+ _results1.push(obj.remove());
231
+ }
232
+ return _results1;
233
+ })());
234
+ }
235
+ return _results;
236
+ };
237
+
238
+ GitoeCanvas.prototype.set_refs = function(refs) {
239
+ var ref_index, ref_name, ref_names_sorted, _i, _len, _results;
240
+
241
+ this.clear_refs();
242
+ ref_names_sorted = Object.keys(refs).sort(strcmp);
243
+ _results = [];
244
+ for (ref_index = _i = 0, _len = ref_names_sorted.length; _i < _len; ref_index = ++_i) {
245
+ ref_name = ref_names_sorted[ref_index];
246
+ _results.push(this.ref_objs[ref_name] = this.draw_ref(ref_index, ref_name, refs[ref_name]));
247
+ }
248
+ return _results;
249
+ };
250
+
251
+ GitoeCanvas.prototype.draw_ref = function(ref_index, ref_name, sha1s) {
252
+ var coord_last, p_last, sha1_last;
253
+
254
+ sha1_last = sha1s[sha1s.length - 1];
255
+ p_last = this.dag.query_pos(sha1_last);
256
+ coord_last = this.commit_coord(p_last.layer, p_last.pos);
257
+ this.focus(this.commit_coord(p_last.layer, p_last.pos));
258
+ return [this.draw_ref_path(ref_index, sha1s), this.draw_ref_text(ref_index, coord_last, ref_name), this.draw_ref_pointer(ref_index, coord_last)];
259
+ };
260
+
261
+ GitoeCanvas.prototype.draw_ref_pointer = function(ref_index, p_last) {
262
+ return this.canvas.path();
263
+ };
264
+
265
+ GitoeCanvas.prototype.draw_ref_text = function(ref_index, coord_last, ref_name) {
266
+ console.log(ref_index, coord_last, ref_name);
267
+ return this.canvas.text(coord_last.left + this.constant.box.width + (0.6 + ref_index) * 30 + ref_name.length * 2, coord_last.top + this.constant.box.height * (1 + ref_index) / 3, ref_name).attr(this.constant.text_attr);
268
+ };
269
+
270
+ GitoeCanvas.prototype.draw_ref_path = function(ref_index, sha1s, textbox_width) {
271
+ var command_array, current, handler_top, index, prev, sha1, _i, _len;
272
+
273
+ command_array = [];
274
+ current = prev = null;
275
+ handler_top = this.constant.box.height * (1 + ref_index) / 3;
276
+ for (index = _i = 0, _len = sha1s.length; _i < _len; index = ++_i) {
277
+ sha1 = sha1s[index];
278
+ prev = current;
279
+ current = this.commit_coord_by_sha1(sha1);
280
+ if (index === 0) {
281
+ command_array.push.apply(command_array, ['M', current.left + this.constant.box.width, current.top + handler_top]);
282
+ } else {
283
+ command_array.push.apply(command_array, ['Q', 50 + ref_index * 40 + this.constant.box.width + Math.max(current.left, prev.left), (current.top + prev.top) / 2 + handler_top - 35, this.constant.box.width + current.left, current.top + handler_top]);
284
+ }
285
+ }
286
+ return this.canvas.path(command_array.join(' '));
287
+ };
288
+
289
+ GitoeCanvas.prototype.commit_coord = function(layer, pos) {
290
+ return {
291
+ left: this.constant.padding_left + this.constant.outer_width * pos,
292
+ top: this.constant.padding_top + this.constant.outer_height * layer
293
+ };
294
+ };
295
+
296
+ GitoeCanvas.prototype.commit_coord_by_sha1 = function(sha1) {
297
+ var p;
298
+
299
+ p = this.dag.query_pos(sha1);
300
+ return this.commit_coord(p.layer, p.pos);
301
+ };
302
+
303
+ GitoeCanvas.prototype.draw_commit_box = function(coord) {
304
+ return this.canvas.rect(coord.left, coord.top, this.constant.box.width, this.constant.box.height, this.constant.box.radius).attr(this.constant.box.attr);
305
+ };
306
+
307
+ GitoeCanvas.prototype.draw_commit_text = function(coord, sha1) {
308
+ return this.canvas.text(coord.left + this.constant.box.width / 2, coord.top + this.constant.box.height / 2, sha1.slice(0, 8)).attr(this.constant.text_attr);
309
+ };
310
+
311
+ GitoeCanvas.prototype.draw_paths = function(coord, parents_pos) {
312
+ var coord_p, p, path_command, paths, start, _i, _len;
313
+
314
+ paths = [];
315
+ start = ['M', coord.left + this.constant.box.width / 2, coord.top].join(' ');
316
+ for (_i = 0, _len = parents_pos.length; _i < _len; _i++) {
317
+ p = parents_pos[_i];
318
+ coord_p = this.commit_coord(p.layer, p.pos);
319
+ path_command = this.path_command(coord, coord_p);
320
+ if (path_command) {
321
+ paths.push(this.canvas.path(start + path_command));
322
+ }
323
+ }
324
+ return paths;
325
+ };
326
+
327
+ GitoeCanvas.prototype.path_command = function(coord, coord_p) {
328
+ var bottom_of_parent, ratio, top_of_highest_fake_node, vertical_distance;
329
+
330
+ bottom_of_parent = {
331
+ x: coord_p.left + this.constant.box.width / 2,
332
+ y: coord_p.top + this.constant.box.height
333
+ };
334
+ top_of_highest_fake_node = {
335
+ x: coord.left + this.constant.box.width / 2,
336
+ y: coord_p.top + this.constant.outer_height
337
+ };
338
+ if (coord.left === coord_p.left) {
339
+ return ['L', bottom_of_parent.x, bottom_of_parent.y].join(' ');
340
+ } else {
341
+ vertical_distance = this.constant.outer_height - this.constant.box.height;
342
+ ratio = 0.3;
343
+ return ['L', top_of_highest_fake_node.x, top_of_highest_fake_node.y, 'C', top_of_highest_fake_node.x, this.mix(top_of_highest_fake_node.y, bottom_of_parent.y, ratio), bottom_of_parent.x, this.mix(top_of_highest_fake_node.y, bottom_of_parent.y, 1 - ratio), bottom_of_parent.x, bottom_of_parent.y].join(' ');
344
+ }
345
+ };
346
+
347
+ GitoeCanvas.prototype.mix = function(a, b, ratio) {
348
+ return a * ratio + b * (1 - ratio);
349
+ };
350
+
351
+ GitoeCanvas.prototype.focus = function(coord) {
352
+ return this.div.scrollTo({
353
+ left: coord.left - 200,
354
+ top: coord.top - 200
355
+ });
356
+ };
357
+
358
+ GitoeCanvas.prototype.init_canvas = function(id_canvas) {
359
+ this.canvas_size = clone(this.constant.canvas);
360
+ return this.canvas = R(id_canvas, this.canvas_size.width, this.canvas_size.height);
361
+ };
362
+
363
+ GitoeCanvas.prototype.canvas_inc_width = function(left) {
364
+ if (left + this.constant.outer_width > this.canvas_size.width) {
365
+ this.canvas_size.width += 1 * this.constant.outer_width;
366
+ this.canvas_resize();
367
+ return true;
368
+ } else {
369
+ return false;
370
+ }
371
+ };
372
+
373
+ GitoeCanvas.prototype.canvas_inc_height = function(top) {
374
+ if (top + this.constant.outer_height > this.canvas_size.height) {
375
+ this.canvas_size.height += 1 * this.constant.outer_height;
376
+ this.canvas_resize();
377
+ return true;
378
+ } else {
379
+ return false;
380
+ }
381
+ };
382
+
383
+ GitoeCanvas.prototype.canvas_resize = function() {
384
+ return this.canvas.setSize(this.canvas_size.width, this.canvas_size.height);
385
+ };
386
+
387
+ return GitoeCanvas;
388
+
389
+ })();
390
+
391
+ if ((_ref = this.exports) == null) {
392
+ this.exports = {
393
+ gitoe: {}
394
+ };
395
+ }
396
+
397
+ exports.gitoe.GitoeCanvas = GitoeCanvas;
398
+
399
+ }).call(this);
@@ -0,0 +1,618 @@
1
+ (function() {
2
+ var $, DAGtopo, GitoeChange, GitoeHistorian, GitoeRepo, OrderedSet, clone, exec_callback, local, strcmp, uniq, url_root, _ref,
3
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4
+
5
+ $ = jQuery || (function() {
6
+ throw "demand jQuery";
7
+ })();
8
+
9
+ url_root = "/repo";
10
+
11
+ exec_callback = function(context, fun, args) {
12
+ return fun.apply(context, args);
13
+ };
14
+
15
+ clone = function(obj) {
16
+ return $.extend({}, obj);
17
+ };
18
+
19
+ local = '##??!';
20
+
21
+ uniq = function(old_array, ignore_list) {
22
+ var elem, i, ignore, new_array, _i, _j, _len, _len1;
23
+
24
+ if (ignore_list == null) {
25
+ ignore_list = ['0000000000000000000000000000000000000000'];
26
+ }
27
+ ignore = {};
28
+ for (_i = 0, _len = ignore_list.length; _i < _len; _i++) {
29
+ i = ignore_list[_i];
30
+ ignore[i] = true;
31
+ }
32
+ new_array = [];
33
+ for (_j = 0, _len1 = old_array.length; _j < _len1; _j++) {
34
+ elem = old_array[_j];
35
+ if (!ignore[elem]) {
36
+ ignore[elem] = true;
37
+ new_array.push(elem);
38
+ }
39
+ }
40
+ return new_array;
41
+ };
42
+
43
+ strcmp = function(str1, str2, pos) {
44
+ var c1, c2;
45
+
46
+ if (pos == null) {
47
+ pos = 0;
48
+ }
49
+ c1 = str1.charAt(pos);
50
+ c2 = str2.charAt(pos);
51
+ if (c1 < c2) {
52
+ return 1;
53
+ } else if (c1 > c2) {
54
+ return -1;
55
+ } else if (c1 === '') {
56
+ return 0;
57
+ } else {
58
+ return strcmp(str1, str2, pos + 1);
59
+ }
60
+ };
61
+
62
+ OrderedSet = (function() {
63
+ function OrderedSet() {
64
+ this.elems = [];
65
+ this.hash = {};
66
+ }
67
+
68
+ OrderedSet.prototype.push = function(new_elem) {
69
+ if (!this.hash[new_elem]) {
70
+ this.hash[new_elem] = true;
71
+ this.elems.push(new_elem);
72
+ return true;
73
+ } else {
74
+ return false;
75
+ }
76
+ };
77
+
78
+ OrderedSet.prototype.length = function() {
79
+ return this.elems.length;
80
+ };
81
+
82
+ OrderedSet.prototype.shift = function() {
83
+ var ret;
84
+
85
+ if (!(this.elems.length > 0)) {
86
+ throw "empty";
87
+ }
88
+ ret = this.elems.shift();
89
+ delete this.hash[ret];
90
+ return ret;
91
+ };
92
+
93
+ return OrderedSet;
94
+
95
+ })();
96
+
97
+ DAGtopo = (function() {
98
+ function DAGtopo() {
99
+ this.edges = {};
100
+ }
101
+
102
+ DAGtopo.prototype.add_edge = function(from, to) {
103
+ var _base, _base1, _ref, _ref1;
104
+
105
+ if ((_ref = (_base = this.edges)[from]) == null) {
106
+ _base[from] = [];
107
+ }
108
+ if ((_ref1 = (_base1 = this.edges)[to]) == null) {
109
+ _base1[to] = [];
110
+ }
111
+ return this.edges[from].push(to);
112
+ };
113
+
114
+ DAGtopo.prototype.sort = function() {
115
+ var from, in_degree, node, nodes_whose_in_degree_is_0, sorted, to, to_s, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
116
+
117
+ in_degree = {};
118
+ _ref = this.edges;
119
+ for (from in _ref) {
120
+ to_s = _ref[from];
121
+ if ((_ref1 = in_degree[from]) == null) {
122
+ in_degree[from] = 0;
123
+ }
124
+ for (_i = 0, _len = to_s.length; _i < _len; _i++) {
125
+ to = to_s[_i];
126
+ if ((_ref2 = in_degree[to]) == null) {
127
+ in_degree[to] = 0;
128
+ }
129
+ in_degree[to]++;
130
+ }
131
+ }
132
+ sorted = [];
133
+ nodes_whose_in_degree_is_0 = Object.keys(in_degree).filter(function(node) {
134
+ return in_degree[node] === 0;
135
+ });
136
+ while (nodes_whose_in_degree_is_0.length > 0) {
137
+ node = nodes_whose_in_degree_is_0.shift();
138
+ delete in_degree[node];
139
+ sorted.push(node);
140
+ _ref3 = this.edges[node];
141
+ for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
142
+ to = _ref3[_j];
143
+ if (--in_degree[to] === 0) {
144
+ nodes_whose_in_degree_is_0.push(to);
145
+ }
146
+ }
147
+ }
148
+ return sorted;
149
+ };
150
+
151
+ return DAGtopo;
152
+
153
+ })();
154
+
155
+ GitoeChange = (function() {
156
+ GitoeChange.parse = function(repos) {
157
+ var change, changes, grouped_changes, ref_content, ref_name, repo_content, repo_name, _i, _len, _ref;
158
+
159
+ changes = [];
160
+ for (repo_name in repos) {
161
+ repo_content = repos[repo_name];
162
+ for (ref_name in repo_content) {
163
+ ref_content = repo_content[ref_name];
164
+ _ref = ref_content.log;
165
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
166
+ change = _ref[_i];
167
+ change['repo_name'] = repo_name;
168
+ change["ref_name"] = ref_name;
169
+ changes.push(change);
170
+ }
171
+ }
172
+ }
173
+ changes.sort(function(a, b) {
174
+ return (a.committer.time - b.committer.time) || strcmp(a.repo_name, b.repo_name) || -((a.ref_name === "HEAD") - (b.ref_name === "HEAD")) || strcmp(a.ref_name, b.ref_name);
175
+ });
176
+ grouped_changes = this.group(changes);
177
+ console.log(changes, grouped_changes);
178
+ return grouped_changes.map(function(group) {
179
+ return new GitoeChange(group);
180
+ });
181
+ };
182
+
183
+ GitoeChange.group = function(changes) {
184
+ var begin, change, end, groups, next, _i, _len;
185
+
186
+ groups = [];
187
+ begin = 0;
188
+ for (end = _i = 0, _len = changes.length; _i < _len; end = ++_i) {
189
+ change = changes[end];
190
+ next = changes[end + 1];
191
+ if ((change.ref_name !== "HEAD") || (/^rebase: aborting/.test(change.message)) || (end === changes.length - 1) || (next.repo_name !== change.repo_name) || (/^checkout:/.test(change.message) && !/^(rebase|cherry-pick)/.test(next.message))) {
192
+ groups.push(changes.slice(begin, +end + 1 || 9e9));
193
+ begin = end + 1;
194
+ }
195
+ }
196
+ return groups;
197
+ };
198
+
199
+ function GitoeChange(changes) {
200
+ this.main = changes[changes.length - 1];
201
+ if (changes.length > 1) {
202
+ this.rest = changes.slice(0, +(changes.length - 2) + 1 || 9e9);
203
+ } else {
204
+ this.rest = [];
205
+ }
206
+ if (this.main.repo_name === local) {
207
+ this.is_local = true;
208
+ } else {
209
+ this.is_local = false;
210
+ }
211
+ }
212
+
213
+ GitoeChange.prototype.to_html = function() {
214
+ var html, matched, pattern, regex, rules, _ref;
215
+
216
+ rules = GitoeChange.message_rules;
217
+ html = GitoeChange.html;
218
+ _ref = rules.patterns;
219
+ for (pattern in _ref) {
220
+ regex = _ref[pattern];
221
+ if (matched = this.main.message.match(regex)) {
222
+ return rules.actions[pattern].apply(html, [matched, this.main, this.rest]).addClass("reflog");
223
+ }
224
+ }
225
+ console.log("not recognized change : ", this.main, this.rest);
226
+ return $('<li>').text("???").addClass("unknown");
227
+ };
228
+
229
+ GitoeChange.prototype.on_click = function() {
230
+ var change, fullname, ref_fullname, refs, sha1_s, _i, _len, _ref, _ref1, _ref2;
231
+
232
+ refs = {};
233
+ ref_fullname = GitoeChange.html.ref_fullname;
234
+ _ref = this.rest;
235
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
236
+ change = _ref[_i];
237
+ fullname = ref_fullname(change);
238
+ if ((_ref1 = refs[fullname]) == null) {
239
+ refs[fullname] = [];
240
+ }
241
+ refs[fullname].push(change.oid_old);
242
+ refs[fullname].push(change.oid_new);
243
+ }
244
+ fullname = ref_fullname(this.main);
245
+ if ((_ref2 = refs[fullname]) == null) {
246
+ refs[fullname] = [];
247
+ }
248
+ refs[fullname].push(this.main.oid_old);
249
+ refs[fullname].push(this.main.oid_new);
250
+ for (fullname in refs) {
251
+ sha1_s = refs[fullname];
252
+ refs[fullname] = uniq(sha1_s);
253
+ }
254
+ return function() {
255
+ return this.set_refs(refs);
256
+ };
257
+ };
258
+
259
+ GitoeChange.html = {
260
+ span: function(text, classes) {
261
+ return $("<span>").text(text).addClass(classes);
262
+ },
263
+ li: function(content, classes) {
264
+ return $("<li>").append(content).addClass(classes);
265
+ },
266
+ ref: function(text) {
267
+ return this.span(text, 'ref_name');
268
+ },
269
+ ref_fullname: function(change) {
270
+ if (change.repo_name === local) {
271
+ return change.ref_name;
272
+ } else {
273
+ return "" + change.repo_name + "/" + change.ref_name;
274
+ }
275
+ },
276
+ git_command: function(text) {
277
+ return this.span(text, "git_command");
278
+ },
279
+ ref_realname: function(ref_name) {
280
+ var splited;
281
+
282
+ splited = ref_name.split("/");
283
+ if (splited[0] === "HEAD") {
284
+ return "HEAD";
285
+ } else if (splited[0] === "refs") {
286
+ switch (splited[1]) {
287
+ case "heads":
288
+ return splited[2];
289
+ case "remotes":
290
+ return "" + splited[2] + "/" + splited[3];
291
+ case "tags":
292
+ return splited[2];
293
+ default:
294
+ console.log("not recognized", ref_name);
295
+ return "???";
296
+ }
297
+ } else {
298
+ console.log("not recognized", ref_name);
299
+ return "???";
300
+ }
301
+ },
302
+ sha1_commit: function(sha1) {
303
+ return this.span(sha1, "sha1_commit");
304
+ },
305
+ br: function() {
306
+ return $('<br>');
307
+ }
308
+ };
309
+
310
+ GitoeChange.message_rules = {
311
+ patterns: {
312
+ clone: /^clone: from (.*)/,
313
+ branch: /^branch: Created from (.*)/,
314
+ commit: /^commit: /,
315
+ commit_amend: /^commit \(amend\): /,
316
+ merge_commit: /^commit \(merge\): Merge branch '?([^ ]+)'? into '?([^ ]+)'?$/,
317
+ merge_ff: /^merge ([^:]*):/,
318
+ reset: /^reset: moving to (.*)/,
319
+ push: /^update by push/,
320
+ pull: /^pull: /,
321
+ fetch: /^fetch/,
322
+ checkout: /^checkout: moving from ([^ ]+) to ([^ ]+)/,
323
+ rename_remote: /^remote: renamed ([^ ]+) to ([^ ]+)/,
324
+ rebase_finish: /^rebase (-[^ ]+)? \(finish\): returning to (.*)/,
325
+ rebase_finish2: /^rebase (-[^ ]+)? \(finish\): ([^ ]+) onto/,
326
+ rebase_finish3: /^rebase (-[^ ]+ )?finished: ([^ ]+) onto/,
327
+ rebase_abort: /^rebase: aborting/
328
+ },
329
+ actions: {
330
+ clone: function(matched, change) {
331
+ return this.li([this.git_command("git clone"), this.span(": create "), this.ref(this.ref_fullname(change)), this.span(" at "), this.sha1_commit(change.oid_new)]);
332
+ },
333
+ branch: function(matched, change) {
334
+ return this.li([this.git_command("git branch"), this.span(": branch out "), this.ref(this.ref_fullname(change)), this.span(" at "), this.sha1_commit(change.oid_new), /^refs/.test(matched[1]) ? this.span(" (was ") : void 0, /^refs/.test(matched[1]) ? this.ref(this.ref_realname(matched[1])) : void 0, /^refs/.test(matched[1]) ? this.span(" )") : void 0]);
335
+ },
336
+ commit: function(matched, change) {
337
+ return this.li([this.git_command("git commit"), this.span(": move "), this.ref(this.ref_fullname(change)), this.span(" from "), this.sha1_commit(change.oid_old), this.span(" to "), this.sha1_commit(change.oid_new)]);
338
+ },
339
+ merge_commit: function(matched, change) {
340
+ return this.li([this.git_command("git merge"), this.span(": move "), this.span(matched[2], "ref_name"), this.span(' to '), this.sha1_commit(change.oid_new), this.span(' by merging '), this.span(matched[1], "ref_name")]);
341
+ },
342
+ commit_amend: function(matched, change) {
343
+ return this.li([this.git_command("git commit --amend"), this.span(": move "), this.ref(this.ref_fullname(change)), this.span(" from "), this.sha1_commit(change.oid_old), this.span(" to "), this.sha1_commit(change.oid_new)]);
344
+ },
345
+ merge_ff: function(matched, change) {
346
+ return this.li([this.git_command("git merge"), this.span(": move "), this.ref(this.ref_fullname(change)), this.span(' to '), this.sha1_commit(change.oid_new), this.span(' by merging '), this.span(matched[1], "ref_name")]);
347
+ },
348
+ reset: function(matched, change) {
349
+ return this.li([this.git_command("git reset"), this.span(": point "), this.ref(this.ref_fullname(change)), this.span(" to "), this.sha1_commit(change.oid_new), this.span(" ( was "), this.sha1_commit(change.oid_old), this.span(" )")]);
350
+ },
351
+ push: function(matched, change) {
352
+ return this.li([this.git_command("git push"), this.span(": update "), this.ref(this.ref_fullname(change)), this.span(" to "), this.sha1_commit(change.oid_new), change.oid_old !== "0000000000000000000000000000000000000000" ? this.span(" ( was ") : void 0, change.oid_old !== "0000000000000000000000000000000000000000" ? this.sha1_commit(change.oid_old) : void 0, change.oid_old !== "0000000000000000000000000000000000000000" ? this.span(" )") : void 0]);
353
+ },
354
+ fetch: function(matched, change) {
355
+ return this.li([this.git_command("git fetch"), this.span(": update "), this.ref(this.ref_fullname(change)), this.span(" to "), this.sha1_commit(change.oid_new), change.oid_old !== "0000000000000000000000000000000000000000" ? this.span(" ( was ") : void 0, change.oid_old !== "0000000000000000000000000000000000000000" ? this.sha1_commit(change.oid_old) : void 0, change.oid_old !== "0000000000000000000000000000000000000000" ? this.span(" )") : void 0]);
356
+ },
357
+ pull: function(matched, change) {
358
+ return this.li([this.git_command("git pull"), this.span(": update "), this.ref(this.ref_fullname(change)), this.span(" from "), this.sha1_commit(change.oid_old), this.span(" to "), this.sha1_commit(change.oid_new)]);
359
+ },
360
+ checkout: function(matched, change, rest) {
361
+ return this.li([this.git_command("git checkout"), this.span(": checkout "), this.ref(matched[2]), this.span(" at "), this.sha1_commit(change.oid_new)]);
362
+ },
363
+ rename_remote: function(matched, change) {
364
+ return this.li([this.git_command("git remote rename"), this.span(": rename "), this.ref(this.ref_realname(matched[1])), this.span(" to "), this.ref(this.ref_realname(matched[2]))]);
365
+ },
366
+ rebase_finish: function(matched, change) {
367
+ return this.li([matched[1] ? this.git_command("git rebase " + matched[1]) : this.git_command("git rebase"), this.span(": rebase "), this.ref(this.ref_realname(matched[2])), this.span(" to "), this.sha1_commit(change.oid_new)]);
368
+ },
369
+ rebase_finish2: function(matched, change) {
370
+ return this.li([matched[1] ? this.git_command("git rebase " + matched[1]) : this.git_command("git rebase"), this.span(": rebase "), this.ref(this.ref_fullname(change)), this.span(" to "), this.sha1_commit(change.oid_new)]);
371
+ },
372
+ rebase_finish3: function(matched, change) {
373
+ return this.li([matched[1] ? this.git_command("git rebase " + matched[1]) : this.git_command("git rebase"), this.span(": rebase "), this.ref(this.ref_fullname(change)), this.span(" to "), this.sha1_commit(change.oid_new)]);
374
+ },
375
+ rebase_abort: function(matched, change, rest) {
376
+ var matched_head, real_ref;
377
+
378
+ if (rest.length > 0) {
379
+ if (matched_head = rest[0].message.match(/^checkout: moving from ([^ ]+)/)) {
380
+ real_ref = matched_head[1];
381
+ }
382
+ }
383
+ return this.li([this.git_command("git rebase --abort"), this.span(": didn't rebase "), real_ref ? this.ref(real_ref) : void 0]);
384
+ }
385
+ }
386
+ };
387
+
388
+ return GitoeChange;
389
+
390
+ })();
391
+
392
+ GitoeHistorian = (function() {
393
+ function GitoeHistorian() {
394
+ this.cb = {};
395
+ }
396
+
397
+ GitoeHistorian.prototype.set_cb = function(new_cb) {
398
+ var fun, name, _results;
399
+
400
+ _results = [];
401
+ for (name in new_cb) {
402
+ fun = new_cb[name];
403
+ _results.push(this.cb[name] = fun);
404
+ }
405
+ return _results;
406
+ };
407
+
408
+ GitoeHistorian.prototype.parse = function(refs) {
409
+ var changes, classified, _base, _base1;
410
+
411
+ classified = this.classify(clone(refs));
412
+ console.log(classified);
413
+ if (typeof (_base = this.cb).update_num_tags === "function") {
414
+ _base.update_num_tags(Object.keys(classified.tags).length);
415
+ }
416
+ changes = GitoeChange.parse(classified.repos);
417
+ return typeof (_base1 = this.cb).update_reflog === "function" ? _base1.update_reflog(changes) : void 0;
418
+ };
419
+
420
+ GitoeHistorian.prototype.classify = function(refs) {
421
+ var ref_content, ref_name, repos, splited, tags, _name, _ref, _ref1, _ref2;
422
+
423
+ repos = {};
424
+ tags = {};
425
+ for (ref_name in refs) {
426
+ ref_content = refs[ref_name];
427
+ splited = ref_name.split("/");
428
+ if (splited[0] === "HEAD" && splited.length === 1) {
429
+ if ((_ref = repos[local]) == null) {
430
+ repos[local] = {};
431
+ }
432
+ repos[local]["HEAD"] = ref_content;
433
+ } else if (splited[0] === "refs") {
434
+ switch (splited[1]) {
435
+ case "heads":
436
+ if ((_ref1 = repos[local]) == null) {
437
+ repos[local] = {};
438
+ }
439
+ repos[local][splited[2]] = ref_content;
440
+ break;
441
+ case "remotes":
442
+ if ((_ref2 = repos[_name = splited[2]]) == null) {
443
+ repos[_name] = {};
444
+ }
445
+ repos[splited[2]][splited[3]] = ref_content;
446
+ break;
447
+ case "tags":
448
+ tags[splited[2]] = ref_content;
449
+ break;
450
+ default:
451
+ console.log("not recognized", ref_name);
452
+ }
453
+ } else {
454
+ console.log("not recognized", ref_name);
455
+ }
456
+ }
457
+ return {
458
+ repos: repos,
459
+ tags: tags
460
+ };
461
+ };
462
+
463
+ return GitoeHistorian;
464
+
465
+ })();
466
+
467
+ GitoeRepo = (function() {
468
+ function GitoeRepo() {
469
+ this.ajax_error = __bind(this.ajax_error, this);
470
+ this.ajax_fetch_status_success = __bind(this.ajax_fetch_status_success, this);
471
+ this.ajax_fetch_commits_success = __bind(this.ajax_fetch_commits_success, this);
472
+ this.ajax_open_success = __bind(this.ajax_open_success, this);
473
+ this.fetch_commits = __bind(this.fetch_commits, this);
474
+ this.open = __bind(this.open, this); this.commits_to_fetch = {};
475
+ this.commits_fetched = {};
476
+ this.cb = {};
477
+ this.commits_ignored = {
478
+ "0000000000000000000000000000000000000000": true
479
+ };
480
+ }
481
+
482
+ GitoeRepo.prototype.set_cb = function(new_cb) {
483
+ var fun, name, _results;
484
+
485
+ _results = [];
486
+ for (name in new_cb) {
487
+ fun = new_cb[name];
488
+ _results.push(this.cb[name] = fun);
489
+ }
490
+ return _results;
491
+ };
492
+
493
+ GitoeRepo.prototype.open = function(path, cb) {
494
+ if (cb == null) {
495
+ cb = {};
496
+ }
497
+ return $.post("" + url_root + "/new", {
498
+ path: path
499
+ }).fail(this.ajax_error, cb.fail).done(this.ajax_open_success, cb.success);
500
+ };
501
+
502
+ GitoeRepo.prototype.fetch_commits = function(cb) {
503
+ var param, to_query;
504
+
505
+ if (cb == null) {
506
+ cb = {};
507
+ }
508
+ if (!this.path) {
509
+ throw "not opened";
510
+ }
511
+ to_query = Object.keys(this.commits_to_fetch).slice(0, 10);
512
+ param = {
513
+ limit: 1000
514
+ };
515
+ return $.get("" + this.path + "/commits/" + (to_query.join()), param).fail(this.ajax_error, cb.fail).done(this.ajax_fetch_commits_success, cb.success);
516
+ };
517
+
518
+ GitoeRepo.prototype.fetch_status = function(cb) {
519
+ if (cb == null) {
520
+ cb = {};
521
+ }
522
+ return $.get("" + this.path + "/").fail(this.ajax_error, cb.fail).done(this.ajax_fetch_status_success, cb.success);
523
+ };
524
+
525
+ GitoeRepo.prototype.fetch_alldone = function() {
526
+ var child, content, parent, sha1, sorted_commits, sorter, _base, _i, _j, _len, _len1, _ref, _ref1, _results;
527
+
528
+ sorter = new DAGtopo;
529
+ _ref = this.commits_fetched;
530
+ for (child in _ref) {
531
+ content = _ref[child];
532
+ _ref1 = content.parents;
533
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
534
+ parent = _ref1[_i];
535
+ sorter.add_edge(parent, child);
536
+ }
537
+ }
538
+ sorted_commits = sorter.sort();
539
+ _results = [];
540
+ for (_j = 0, _len1 = sorted_commits.length; _j < _len1; _j++) {
541
+ sha1 = sorted_commits[_j];
542
+ _results.push(typeof (_base = this.cb).yield_commit === "function" ? _base.yield_commit(this.commits_fetched[sha1]) : void 0);
543
+ }
544
+ return _results;
545
+ };
546
+
547
+ GitoeRepo.prototype.ajax_open_success = function(json) {
548
+ if (this.path) {
549
+ throw "already opened";
550
+ }
551
+ return this.path = "" + url_root + "/" + json.id;
552
+ };
553
+
554
+ GitoeRepo.prototype.ajax_fetch_commits_success = function(json) {
555
+ var content, fetched, sha1, sha1_parent, to_fetch, _base, _base1, _i, _len, _ref;
556
+
557
+ for (sha1 in json) {
558
+ content = json[sha1];
559
+ delete this.commits_to_fetch[sha1];
560
+ (_base = this.commits_fetched)[sha1] || (_base[sha1] = content);
561
+ _ref = content.parents;
562
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
563
+ sha1_parent = _ref[_i];
564
+ if (!(this.commits_fetched[sha1] || this.commits_ignored[sha1])) {
565
+ this.commits_to_fetch[sha1_parent] = true;
566
+ }
567
+ }
568
+ }
569
+ to_fetch = Object.keys(this.commits_to_fetch).length;
570
+ fetched = Object.keys(this.commits_fetched).length;
571
+ return typeof (_base1 = this.cb).fetched_commit === "function" ? _base1.fetched_commit(to_fetch, fetched) : void 0;
572
+ };
573
+
574
+ GitoeRepo.prototype.ajax_fetch_status_success = function(response) {
575
+ var change, field, ref, ref_name, sha1, _base, _i, _j, _len, _len1, _ref, _ref1, _ref2;
576
+
577
+ _ref = response.refs;
578
+ for (ref_name in _ref) {
579
+ ref = _ref[ref_name];
580
+ _ref1 = ref.log;
581
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
582
+ change = _ref1[_i];
583
+ _ref2 = ['oid_new', 'oid_old'];
584
+ for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
585
+ field = _ref2[_j];
586
+ sha1 = change[field];
587
+ if (!(this.commits_fetched[sha1] || this.commits_ignored[sha1])) {
588
+ this.commits_to_fetch[sha1] = true;
589
+ }
590
+ }
591
+ }
592
+ }
593
+ return typeof (_base = this.cb).fetch_status === "function" ? _base.fetch_status(response) : void 0;
594
+ };
595
+
596
+ GitoeRepo.prototype.ajax_error = function(jqXHR) {
597
+ var _base;
598
+
599
+ return typeof (_base = this.cb).ajax_error === "function" ? _base.ajax_error(jqXHR) : void 0;
600
+ };
601
+
602
+ return GitoeRepo;
603
+
604
+ })();
605
+
606
+ if ((_ref = this.exports) == null) {
607
+ this.exports = {
608
+ gitoe: {}
609
+ };
610
+ }
611
+
612
+ exports.gitoe.strcmp = strcmp;
613
+
614
+ exports.gitoe.GitoeRepo = GitoeRepo;
615
+
616
+ exports.gitoe.GitoeHistorian = GitoeHistorian;
617
+
618
+ }).call(this);