git-object-browser 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/README.md +17 -0
  2. data/htdocs/{css → common/css}/angular-ui.min.css +0 -0
  3. data/htdocs/{css → common/css}/bootstrap-responsive.css +0 -0
  4. data/htdocs/{css → common/css}/bootstrap-responsive.min.css +0 -0
  5. data/htdocs/{css → common/css}/bootstrap.css +0 -0
  6. data/htdocs/{css → common/css}/bootstrap.min.css +0 -0
  7. data/htdocs/{css → common/css}/font-awesome-ie7.css +0 -0
  8. data/htdocs/{css → common/css}/font-awesome.css +0 -0
  9. data/htdocs/{css → common/css}/main.css +0 -0
  10. data/htdocs/{font → common/font}/fontawesome-webfont.eot +0 -0
  11. data/htdocs/{font → common/font}/fontawesome-webfont.svg +0 -0
  12. data/htdocs/{font → common/font}/fontawesome-webfont.ttf +0 -0
  13. data/htdocs/{font → common/font}/fontawesome-webfont.woff +0 -0
  14. data/htdocs/{img → common/img}/glyphicons-halflings-white.png +0 -0
  15. data/htdocs/{img → common/img}/glyphicons-halflings.png +0 -0
  16. data/htdocs/common/js/main.js +495 -0
  17. data/htdocs/{js → common/js}/vendor/angular-bootstrap-prettify.min.js +0 -0
  18. data/htdocs/{js → common/js}/vendor/angular-bootstrap.min.js +0 -0
  19. data/htdocs/{js → common/js}/vendor/angular-cookies.min.js +0 -0
  20. data/htdocs/{js → common/js}/vendor/angular-loader.min.js +0 -0
  21. data/htdocs/{js → common/js}/vendor/angular-resource.min.js +0 -0
  22. data/htdocs/{js → common/js}/vendor/angular-sanitize.min.js +0 -0
  23. data/htdocs/{js → common/js}/vendor/angular-ui-ieshiv.min.js +0 -0
  24. data/htdocs/{js → common/js}/vendor/angular-ui.min.js +0 -0
  25. data/htdocs/{js → common/js}/vendor/angular.js +0 -0
  26. data/htdocs/{js → common/js}/vendor/angular.min.js +0 -0
  27. data/htdocs/{js → common/js}/vendor/bootstrap.js +0 -0
  28. data/htdocs/{js → common/js}/vendor/bootstrap.min.js +0 -0
  29. data/htdocs/{js → common/js}/vendor/html5shiv.js +0 -0
  30. data/htdocs/{js → common/js}/vendor/jquery-1.8.2.min.js +0 -0
  31. data/htdocs/{templates → common/templates}/directory.html +2 -2
  32. data/htdocs/common/templates/error.html +6 -0
  33. data/htdocs/{templates → common/templates}/file.html +0 -0
  34. data/htdocs/{templates → common/templates}/git.html +0 -0
  35. data/htdocs/{templates → common/templates}/index.html +1 -1
  36. data/htdocs/{templates → common/templates}/index_entry.html +0 -0
  37. data/htdocs/{templates → common/templates}/info_refs.html +2 -2
  38. data/htdocs/{templates → common/templates}/loading.html +0 -0
  39. data/htdocs/{templates → common/templates}/notfound.html +1 -2
  40. data/htdocs/{templates → common/templates}/object.html +1 -1
  41. data/htdocs/{templates → common/templates}/objects.html +3 -3
  42. data/htdocs/{templates → common/templates}/pack_file.html +1 -1
  43. data/htdocs/common/templates/pack_index.html +39 -0
  44. data/htdocs/{templates → common/templates}/packed_object.html +4 -4
  45. data/htdocs/{templates → common/templates}/packed_refs.html +2 -2
  46. data/htdocs/{templates → common/templates}/ref.html +0 -0
  47. data/htdocs/common/templates/reflog.html +29 -0
  48. data/htdocs/config.js +6 -0
  49. data/htdocs/default.js +7 -0
  50. data/htdocs/index.html +52 -20
  51. data/lib/git-object-browser.rb +12 -4
  52. data/lib/git-object-browser/dumper/directories_dumper.rb +42 -0
  53. data/lib/git-object-browser/dumper/index_dumper.rb +20 -7
  54. data/lib/git-object-browser/dumper/info_refs_dumper.rb +37 -0
  55. data/lib/git-object-browser/dumper/main.rb +188 -81
  56. data/lib/git-object-browser/dumper/objects_dumper.rb +46 -0
  57. data/lib/git-object-browser/dumper/pack_indexes_dumper.rb +85 -0
  58. data/lib/git-object-browser/dumper/packed_objects_dumper.rb +36 -0
  59. data/lib/git-object-browser/dumper/packed_refs_dumper.rb +37 -0
  60. data/lib/git-object-browser/dumper/plain_files_dumper.rb +74 -0
  61. data/lib/git-object-browser/dumper/refs_dumper.rb +46 -0
  62. data/lib/git-object-browser/main.rb +22 -6
  63. data/lib/git-object-browser/models/directory.rb +5 -3
  64. data/lib/git-object-browser/models/git_date.rb +26 -0
  65. data/lib/git-object-browser/models/git_object.rb +1 -1
  66. data/lib/git-object-browser/models/pack_file.rb +1 -1
  67. data/lib/git-object-browser/models/pack_index.rb +139 -25
  68. data/lib/git-object-browser/models/packed_object.rb +1 -1
  69. data/lib/git-object-browser/models/reflog.rb +54 -0
  70. data/lib/git-object-browser/models/wrapped_object.rb +40 -0
  71. data/lib/git-object-browser/server/git_servlet.rb +130 -100
  72. data/lib/git-object-browser/server/main.rb +1 -1
  73. data/lib/git-object-browser/version.rb +1 -1
  74. metadata +62 -50
  75. data/htdocs/js/main.js +0 -285
  76. data/htdocs/templates/pack_index.html +0 -37
  77. data/lib/git-object-browser/dumper/object_dumper.rb +0 -20
  78. data/lib/git-object-browser/dumper/pack_index_dumper.rb +0 -21
  79. data/lib/git-object-browser/dumper/packed_object_dumper.rb +0 -19
data/README.md CHANGED
@@ -9,6 +9,23 @@ Browse git raw objects.
9
9
 
10
10
  ## Usage
11
11
 
12
+ To browse .git directory:
13
+
14
+ $ git object-browser ./path/to/project
15
+ $ open http://localhost:8080/#/.git
16
+
17
+ To dump .git directory (dump HTML/JSON/JS app):
18
+
19
+ $ git object-browser --dump path/to/dump/dir path/to/project
20
+ $ ${any_http_server} --port 8080 path/to/dump/dir
21
+ $ open http://localhost:8080/#/step1/.git/
22
+
23
+ To dump .git step by step:
24
+
25
+ $ git object-browser --dump path/to/dump/dir --next path/to/project
26
+
27
+ this command shows .git diff between previous step and current step.
28
+
12
29
  ## Contributing
13
30
 
14
31
  1. Fork it
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,495 @@
1
+ function routingConfig(steps) {
2
+ return function($routeProvider) {
3
+ $routeProvider = angular.extend($routeProvider, {
4
+
5
+ // AngularJS doesn't support regular expressions in routes.
6
+ // http://stackoverflow.com/questions/12685085/angularjs-route-how-to-match-star-as-a-path
7
+ 'whenPath': function(path, depth, route) {
8
+ for (var i = 1; i <= depth; i++) {
9
+ path += '/:path' + i;
10
+ this.when(path, route);
11
+ }
12
+ return this;
13
+ }
14
+ });
15
+ if (steps.length > 0) {
16
+ $routeProvider.
17
+ whenPath('/:basedir/.git', 10, {controller:GitCtrl, templateUrl:'common/templates/git.html'}).
18
+ otherwise({redirectTo:'/' + steps[0].name + '/.git/'});
19
+ } else {
20
+ $routeProvider.
21
+ whenPath('/.git', 10, {controller:GitCtrl, templateUrl:'common/templates/git.html'}).
22
+ otherwise({redirectTo:'/.git/'});
23
+ }
24
+ }
25
+ }
26
+
27
+ angular.module('GitObjectBrowser', ['ngResource'])
28
+ .config(routingConfig(config.steps))
29
+
30
+ .directive('scrollBottom', function() {
31
+ return function(scope, elm, attr) {
32
+ var rawDomElement = elm[0];
33
+ angular.element(window).unbind('scroll');
34
+ angular.element(window).bind('scroll', function() {
35
+ if (! scope.scrollBottomEnabled) return;
36
+
37
+ var rectObject = rawDomElement.getBoundingClientRect();
38
+ if (rectObject.bottom - window.innerHeight < 50) {
39
+ scope.$apply(attr.scrollBottom);
40
+ }
41
+ });
42
+ };
43
+ })
44
+
45
+ .directive('entryIcon', function($parse) {
46
+ return function(scope, element, attrs) {
47
+ var icons = {
48
+ 'directory': 'icon-folder-open',
49
+ 'ref': 'icon-map-marker',
50
+ 'reflog': 'icon-file',
51
+ 'info_refs': 'icon-map-marker',
52
+ 'packed_refs': 'icon-map-marker',
53
+ 'index': 'icon-list',
54
+ 'file': 'icon-file',
55
+ 'object': 'icon-comment',
56
+ 'blob': 'icon-file',
57
+ 'tree': 'icon-folder-open',
58
+ 'commit': 'icon-ok',
59
+ 'tag': 'icon-tag',
60
+ 'ofs_delta': 'icon-arrow-up',
61
+ 'ref_delta': 'icon-arrow-down'
62
+ };
63
+
64
+ var entryType = ($parse(attrs.entryIcon))(scope);
65
+ element.addClass(icons[entryType]);
66
+ }
67
+ })
68
+
69
+ .directive('modeIcon', function($parse) {
70
+ return function(scope, element, attrs) {
71
+ var mode = ($parse(attrs.modeIcon))(scope);
72
+ var iconClass;
73
+ if (120000 <= mode) {
74
+ iconClass = 'icon-share-alt';
75
+ } else if (100000 <= mode) {
76
+ iconClass = 'icon-file';
77
+ } else {
78
+ iconClass = 'icon-folder-open';
79
+ }
80
+ element.addClass(iconClass);
81
+ }
82
+ })
83
+
84
+ .directive('refHref', function($parse, $rootScope) {
85
+ return function(scope, element, attrs) {
86
+ var entry = ($parse(attrs.refHref))(scope);
87
+ var href = "";
88
+ var sha1 = null;
89
+
90
+ if (typeof(entry) == 'string') {
91
+ sha1 = entry;
92
+ } else if (entry && entry.sha1) {
93
+ sha1 = entry.sha1;
94
+ }
95
+
96
+ if (sha1 !== null) {
97
+ href = '#' + $rootScope.basedir + '/.git/objects/' + sha1.substr(0, 2) + '/' + sha1.substr(2);
98
+ } else if (entry && entry.ref) {
99
+ href = '#' + $rootScope.basedir + '/.git/' + entry.ref;
100
+ }
101
+
102
+ element.attr('href', href);
103
+ }
104
+ })
105
+
106
+ .filter('unixtime', function($filter) {
107
+ return function(input, format) {
108
+ return ($filter('date'))(input * 1000, format);
109
+ }
110
+ });
111
+
112
+ function GitCtrl($scope, $location, $routeParams, $rootScope, $resource, $http) {
113
+ if (! $rootScope.diffCache) $rootScope.diffCache = {};
114
+ if (! $rootScope.noteCache) $rootScope.noteCache = {};
115
+
116
+ // reset scrollBottom event handler
117
+ angular.element(window).unbind('scroll');
118
+
119
+ var objectTable = function(entries) {
120
+ var rows = [];
121
+ var hash = {};
122
+
123
+ angular.forEach(entries, function(entry) {
124
+ hash[entry.basename] = entry;
125
+ })
126
+
127
+ var hex = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
128
+ for (var i = 0; i < 16; i++) {
129
+ var cols = [];
130
+ for (var j = 0; j < 16; j++) {
131
+ if (hash[hex[i] + hex[j]]) {
132
+ cols[j] = hash[hex[i] + hex[j]];
133
+ } else {
134
+ cols[j] = {'basename': hex[i] + hex[j], 'type': 'empty'};
135
+ }
136
+ }
137
+ rows[i] = cols;
138
+ }
139
+ return rows;
140
+ }
141
+
142
+ var findIndexEntry = function(sha1) {
143
+ var entries = $scope.object.entries;
144
+ var entry = null;
145
+ angular.forEach(entries, function(value) {
146
+ if (value.sha1 == sha1) {
147
+ entry = value;
148
+ return;
149
+ }
150
+ });
151
+ return entry;
152
+ };
153
+
154
+ var indexEntryKeys = function(version) {
155
+ var keys = [
156
+ 'ctime',
157
+ 'cnano',
158
+ 'mtime',
159
+ 'mnano',
160
+ 'dev',
161
+ 'ino',
162
+ 'object_type',
163
+ 'unix_permission',
164
+ 'uid',
165
+ 'gid',
166
+ 'size',
167
+ 'sha1',
168
+ 'assume_valid_flag',
169
+ 'extended_flag',
170
+ 'stage',
171
+ 'name_length'
172
+ ]
173
+ if (version > 2) {
174
+ keys = keys.concat([
175
+ 'skip_worktree',
176
+ 'intent_to_add',
177
+ ]);
178
+ }
179
+ keys.push('path')
180
+
181
+ return keys;
182
+ };
183
+
184
+ var resourceLoaded = function(json) {
185
+ $scope.workingdir = json.workingdir;
186
+ $scope.root = json.root;
187
+ $scope.gitPath = json.path;
188
+ if (json.path == "") {
189
+ $scope.path = ".git";
190
+ } else {
191
+ $scope.path = ".git/" + json.path;
192
+ }
193
+ $scope.object = json.object;
194
+ $scope.keys = indexEntryKeys($scope.object.version);
195
+ var template;
196
+ if (json.path == "objects") {
197
+ template = "objects";
198
+ $scope.objectTable = objectTable($scope.object.entries);
199
+ loadDiffData();
200
+ } else if (json.type == "index" && $routeParams.sha1) {
201
+ template = "index_entry";
202
+ $scope.entry = findIndexEntry($routeParams.sha1);
203
+ } else if (json.type == "packed_refs" && $routeParams.ref) {
204
+ template = json.type;
205
+ var entries = [];
206
+ angular.forEach($scope.object.entries, function(entry) {
207
+ if (entry.ref == $routeParams.ref) {
208
+ entries.push(entry);
209
+ $scope.limited = true;
210
+ }
211
+ });
212
+ $scope.object.entries = entries;
213
+ } else if (json.type == "packed_object") {
214
+ template = json.type;
215
+ $scope.unpacked = json.unpacked;
216
+ } else if (json.type == "directory") {
217
+ template = json.type;
218
+ loadDiffData();
219
+ } else {
220
+ template = json.type;
221
+ }
222
+
223
+ $scope.template = 'common/templates/' + template + '.html';
224
+ };
225
+
226
+ var loadNote = function() {
227
+ if (! config.loadNote) return;
228
+ var basedir, url;
229
+ if ($rootScope.basedir) {
230
+ basedir = $rootScope.basedir;
231
+ url = 'notes' + basedir + '.html';
232
+ } else {
233
+ basedir = '';
234
+ url = 'note.html';
235
+ }
236
+ if ($rootScope.noteCache[basedir] !== undefined) {
237
+ $rootScope.note = $rootScope.noteCache[basedir];
238
+ return;
239
+ }
240
+ $http.get(url)
241
+ .success(function(data) {
242
+ $rootScope.noteCache[basedir] = data;
243
+ $rootScope.note = data;
244
+ }).error(function() {
245
+ $rootScope.noteCache[basedir] = '';
246
+ $rootScope.note = null;
247
+ });
248
+ };
249
+
250
+ var loadDiffData = function() {
251
+ if (! config.loadDiff) return;
252
+ var stepPath = $rootScope.basedir;
253
+ if (! stepPath) return;
254
+
255
+ $scope.diff = {}
256
+ var base = '.git/';
257
+ if ($scope.object.path !== '') {
258
+ base = base + $scope.object.path + '/';
259
+ }
260
+ angular.forEach($scope.object.entries, function(entry) {
261
+ $scope.diff[base + entry.basename] = null;
262
+ });
263
+
264
+ var cache = $rootScope.diffCache[stepPath];
265
+ if (cache) {
266
+ diffDataLoaded(cache);
267
+ return
268
+ }
269
+ $http.get('json' + stepPath + '/_diff.json').success(function(diffData) {
270
+ $rootScope.diffCache[stepPath] = diffData;
271
+ diffDataLoaded(diffData);
272
+ }).error(function() {
273
+ $rootScope.diffCache[stepPath] = [];
274
+ });
275
+ };
276
+
277
+ var diffDataLoaded = function(data) {
278
+ var newDiffData = {}
279
+ angular.forEach($scope.diff, function(value, key) {
280
+ newDiffData[key] = isDiffEntry(data, key);
281
+ });
282
+ $scope.diff = newDiffData;
283
+ }
284
+
285
+ var isDiffEntry = function(data, key) {
286
+ for(var i = 0; i < data.length; i++) {
287
+ if (('.git/' + data[i]).indexOf(key) === 0) {
288
+ return '#fee';
289
+ }
290
+ }
291
+ return null;
292
+ }
293
+
294
+ $scope.resourceError = function(path) {
295
+ return function(data, status, headers, config) {
296
+ if (status == 404) {
297
+ resourceNotFound(path);
298
+ } else {
299
+ $scope.status = status;
300
+ $scope.path = path;
301
+ $scope.template = 'common/templates/error.html';
302
+ }
303
+ };
304
+ };
305
+
306
+ var packedObjectFinder = function(sha1) {
307
+ var indexes = [];
308
+
309
+ var find = function() {
310
+ $http.get('json' + $rootScope.basedir + '/objects/pack.json')
311
+ .success(startLoadPackDigest)
312
+ .error(showNotFound);
313
+ };
314
+
315
+ var startLoadPackDigest = function(json) {
316
+ angular.forEach(json.object.entries, function(entry) {
317
+ if (entry.basename.match(/\.idx$/)) {
318
+ indexes.push(entry);
319
+ }
320
+ });
321
+ loadPackDigest();
322
+ };
323
+
324
+ var loadPackDigest = function() {
325
+ if (indexes.length == 0) {
326
+ showNotFound();
327
+ return;
328
+ }
329
+ var entry = indexes.shift();
330
+ $http.get('json' + $rootScope.basedir + '/objects/pack/' + entry.basename + '.json')
331
+ .success(findPackObject)
332
+ .error(showNotFound);
333
+ };
334
+
335
+ var findPackObject = function(json) {
336
+ var i = 0;
337
+ angular.forEach(json.object.entries, function(digestSha1) {
338
+ if (sha1 <= digestSha1) {
339
+ return;
340
+ }
341
+ i++;
342
+ });
343
+
344
+ if (i == 0) {
345
+ loadPackDigest();
346
+ return;
347
+ }
348
+
349
+ $http.get('json' + $rootScope.basedir + '/' + json.path + '/sha1/' + i + '.json')
350
+ .success(loadPagedIndex)
351
+ .error(showNotFound);
352
+ };
353
+
354
+ var loadPagedIndex = function(json) {
355
+ var found = false;
356
+ angular.forEach(json.object.entries, function(entry) {
357
+ if (entry.sha1 == sha1) {
358
+ var path = json.path.replace(/.idx$/, '.pack');
359
+ found = true;
360
+ $routeParams.offset = entry.offset;
361
+ loadJson([$rootScope.basedir, path]);
362
+ }
363
+ });
364
+ if (! found) loadPackDigest();
365
+ }
366
+
367
+ find();
368
+ };
369
+
370
+ var resourceNotFound = function(path) {
371
+ $scope.path = path;
372
+ if (path.match(/^json\/[^\/]+\/objects\/([0-9a-f]{2})\/([0-9a-f]{38})\.json$/)) {
373
+ packedObjectFinder(RegExp.$1 + RegExp.$2);
374
+ } else if (path.match(/^json\/[^\/]+\/(refs\/.+)\.json$/)) {
375
+ $routeParams.ref = RegExp.$1;
376
+ loadJson([$rootScope.basedir, 'packed-refs'])
377
+ } else {
378
+ $scope.template = 'common/templates/notfound.html';
379
+ }
380
+ };
381
+
382
+ var showNotFound = function() {
383
+ $scope.template = 'common/templates/notfound.html';
384
+ }
385
+
386
+ // ['', '/.git/xxx'] or
387
+ // ['/basedir', '/.git/xxx']
388
+ var buildPath = function() {
389
+ var path = '';
390
+ var basedir = '';
391
+ if ($routeParams['basedir'] !== undefined) {
392
+ basedir = '/' + $routeParams['basedir'];
393
+ }
394
+ for (var i = 1; i <= 10; i++) {
395
+ if ($routeParams['path' + i]) {
396
+ if (i > 1) path += '/';
397
+ path += $routeParams['path' + i];
398
+ }
399
+ }
400
+ return [basedir, path];
401
+ }
402
+
403
+ var loadJson = function(path) {
404
+ $rootScope.basedir = path[0];
405
+ $scope.template = 'common/templates/loading.html';
406
+
407
+ if (path[1].match(/^objects\/pack\/pack-[0-9a-f]{40}\.pack$/) && $routeParams.offset) {
408
+ var offset = '0000' + $routeParams.offset;
409
+ path = 'json' + path[0] + '/' + path[1]
410
+ + '/' + offset.slice(-2)
411
+ + '/' + offset.slice(-4, -2)
412
+ + '/' + $routeParams.offset + '.json';
413
+ } else if (path[1].match(/^objects\/pack\/pack-[0-9a-f]{40}\.idx$/)) {
414
+ var order = $routeParams.order == 'offset' ? 'offset' : 'sha1';
415
+ var page = $routeParams.page || 1;
416
+ path = 'json' + path[0] + '/' + path[1] + '/' + order + '/' + page + '.json';
417
+ } else {
418
+ if (path[1] == '') path[1] = '_git';
419
+ path = 'json' + path[0] + '/' + path[1] + '.json';
420
+ }
421
+ $http.get(path).success(resourceLoaded).error($scope.resourceError(path));
422
+ };
423
+
424
+ loadJson(buildPath());
425
+ loadNote();
426
+ }
427
+
428
+ function PackFileCtrl($scope, $location, $routeParams) {
429
+ $scope.indexUrl = $scope.path.replace(/.pack$/, '.idx');
430
+ }
431
+
432
+ function PackIndexCtrl($scope, $location, $routeParams, $rootScope, $resource, $http) {
433
+
434
+ $scope.packUrl = $scope.path.replace(/.idx$/, '.pack');
435
+ $scope.lastPage = 1;
436
+ $scope.scrollBottomEnabled = true;
437
+
438
+ var resourceLoaded = function(json) {
439
+ $scope.object.entries = $scope.object.entries.concat(json.object.entries);
440
+ var last_page = Math.ceil(json.entry_count / json.per_page);
441
+ $scope.scrollBottomEnabled = (json.page < last_page);
442
+ $scope.loading = false;
443
+ }
444
+
445
+ $scope.loadNextPage = function() {
446
+ $scope.lastPage += 1;
447
+
448
+ var order = $routeParams.order == 'offset' ? 'offset' : 'sha1';
449
+ var path = 'json' + $rootScope.basedir + '/' + $scope.gitPath + '/' + order + '/' + $scope.lastPage + '.json';
450
+
451
+ $http.get(path).success(resourceLoaded).error($scope.resourceError(path));
452
+ };
453
+
454
+ $scope.scrollBottom = function() {
455
+ $scope.scrollBottomEnabled = false;
456
+ $scope.loading = true;
457
+ $scope.loadNextPage();
458
+ }
459
+
460
+ }
461
+
462
+ function MenuCtrl($scope, $location, $routeParams) {
463
+ $scope.steps = config.steps;
464
+
465
+ $scope.stepPrev = function() {
466
+ var idx = getStepIndex();
467
+ if (idx.index > 0) {
468
+ $location.path('/' + $scope.steps[idx.index - 1].name + '/' + idx.file);
469
+ }
470
+ }
471
+
472
+ $scope.stepNext = function() {
473
+ var idx = getStepIndex();
474
+ if (idx.index < $scope.steps.length - 1) {
475
+ $location.path('/' + $scope.steps[idx.index + 1].name + '/' + idx.file);
476
+ }
477
+ }
478
+
479
+ var getStepIndex = function() {
480
+ var path = $location.path();
481
+ if (! path.match(/\/([^\/]+)\/(.+)/)) return null;
482
+
483
+ var stepName = RegExp.$1;
484
+ var file = RegExp.$2;
485
+ var obj = { stepName: stepName, file: file, index: 0 };
486
+
487
+ for (var i = 0; i < $scope.steps.length; i++) {
488
+ if (stepName == $scope.steps[i].name) {
489
+ obj.index = i;
490
+ return obj;
491
+ }
492
+ }
493
+ return obj;
494
+ }
495
+ }