PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/public/js/pullreq.js

https://github.com/rogerschlachter/pullreq
JavaScript | 901 lines | 794 code | 88 blank | 19 comment | 67 complexity | 762eddd8d1bd8896ba0763fd374e3c50 MD5 | raw file
  1. var Pullreq = Pullreq || {};
  2. (function() {
  3. Pullreq.globalEvents = _.extend({}, Backbone.Events);;
  4. Pullreq.VERSION = "0.4.0";
  5. Pullreq.models = {
  6. PullRequest: Backbone.Model.extend({
  7. defaults: {
  8. isExtraInfoLoaded: false,
  9. isWarning: false,
  10. isGood: false,
  11. // Shared array of warning files between objects
  12. testPaths: ['test/']
  13. },
  14. initialize: function() {
  15. if( !this.get('tags') ){
  16. this.set({tags: new Array()});
  17. }
  18. },
  19. fetchExtraInfo: function() {
  20. var pullRequestModel = this;
  21. $.ajax({
  22. type: 'GET',
  23. url: '/api/pullRequests/' + this.get('base').repo.owner.login + '/' + this.get('base').repo.name + '/' + this.get('number') + '/info',
  24. dataType: 'json',
  25. data: {}
  26. }).done(function (json) {
  27. pullRequestModel.addExtraInfo(json)
  28. pullRequestModel.trigger('extraInfoLoaded', pullRequestModel);
  29. Pullreq.globalEvents.trigger('pullRequest:extraInfoLoaded', pullRequestModel);
  30. });
  31. },
  32. addExtraInfo: function(json) {
  33. var pullRequestModel = this;
  34. _.each(json, function(value, attr) {
  35. pullRequestModel.set(attr, value);
  36. });
  37. pullRequestModel.set('isExtraInfoLoaded', true);
  38. pullRequestModel.set('isWarning', this.containsWarningFiles(Pullreq.data.collections.warningPaths));
  39. pullRequestModel.set('isGood', this.isPullRequestGood());
  40. pullRequestModel.set('isMissingTests', !this.areThereTestsFiles());
  41. },
  42. isPullRequestGood: function() {
  43. var comments = this.get('issueComments');
  44. if (comments) {
  45. var count = 0;
  46. _.each(comments, function(comment) {
  47. if (comment.body.indexOf('+1') !== -1) {
  48. count++;
  49. }
  50. });
  51. return count > 1;
  52. }
  53. },
  54. containsWarningFiles: function(warningFilesCollection) {
  55. var files = this.get('files');
  56. var found = false;
  57. if (files && warningFilesCollection) {
  58. _.each(files, function(file) {
  59. file.isWarn = warningFilesCollection.some(function(path) {
  60. return file.filename.indexOf(path.get('path')) !== -1;
  61. });
  62. found = found || file.isWarn;
  63. });
  64. }
  65. return found;
  66. },
  67. areThereTestsFiles: function() {
  68. var files = this.get('files');
  69. var found = false;
  70. if (files) {
  71. var testPaths = this.get('testPaths');
  72. var found = false;
  73. files.every(function(file) {
  74. testPaths.every(function(path) {
  75. found = file.filename.indexOf(path) !== -1;
  76. return !found;
  77. });
  78. return !found;
  79. });
  80. }
  81. return found;
  82. }
  83. }),
  84. User: Backbone.Model.extend({}),
  85. RepoOwner: Backbone.Model.extend({}),
  86. UserRepo: Backbone.Model.extend({}),
  87. WarningPath: Backbone.Model.extend({
  88. defaults: {
  89. path:null
  90. },
  91. idAttribute: '_id'
  92. }),
  93. TeamTag: Backbone.Model.extend({
  94. defaults: {
  95. 'class':'tag'
  96. }
  97. }),
  98. Project: Backbone.Model.extend({
  99. idAttribute: 'id',
  100. initialize: function() {
  101. var pulls = new Pullreq.collections.PullRequests();
  102. pulls.on('extraInfoLoaded', this.determineIfProjectIsReady, this)
  103. this.set('pullRequests', pulls);
  104. },
  105. determineIfProjectIsReady: function(e) {
  106. var pulls = this.get('pullRequests');
  107. //var anyModdedPulls = false;
  108. var res = pulls.every(function(pull) {
  109. //anyModdedPulls = (pull.get('isWarning') || pull.get('isGood')) ? true : anyModdedPulls;
  110. return pull.get('isExtraInfoLoaded');
  111. });
  112. if (res) {
  113. this.trigger('projectReload', this);
  114. }
  115. }
  116. })
  117. };
  118. Pullreq.collections = {
  119. TeamTags: Backbone.Collection.extend({
  120. model: Pullreq.models.TeamTag,
  121. comparator: function(model) {
  122. return model.get('name');
  123. }
  124. }),
  125. RepoOptions: Backbone.Collection.extend({
  126. url: '/api/repoOptions',
  127. model: Pullreq.models.RepoOwner
  128. }),
  129. UserRepos: Backbone.Collection.extend({
  130. url: '/api/userRepos',
  131. model: Pullreq.models.UserRepo
  132. }),
  133. WarningPaths: Backbone.Collection.extend({
  134. url: '/api/warningPaths',
  135. model: Pullreq.models.WarningPath
  136. }),
  137. Projects: Backbone.Collection.extend({
  138. model: Pullreq.models.Project,
  139. comparator: function(model) {
  140. return model.get('name');
  141. }
  142. }),
  143. PullRequests: Backbone.Collection.extend({
  144. model: Pullreq.models.PullRequest,
  145. url: '/api/pullRequests',
  146. initialize: function() {
  147. var that = this;
  148. this.on('reset', function(e) {
  149. that.fetchExtraInfo();
  150. });
  151. },
  152. fetchExtraInfo: function() {
  153. this.each(function(pull) {
  154. pull.fetchExtraInfo();
  155. }, this);
  156. },
  157. comparator: function(model) {
  158. return model.get('title').toLowerCase();
  159. },
  160. getGoodPulls: function() {
  161. var pulls = [];
  162. this.each(function(pull) {
  163. if (pull.get('isGood')) pulls.push(pull);
  164. }, this);
  165. return pulls;
  166. },
  167. getWarnPulls: function() {
  168. var pulls = [];
  169. this.each(function(pull) {
  170. if (pull.get('isWarning') && !pull.get('isGood')) pulls.push(pull);
  171. }, this);
  172. return pulls;
  173. },
  174. getNewPulls: function() {
  175. var pulls = [];
  176. this.each(function(pull) {
  177. if (!pull.get('isWarning') && !pull.get('isGood')) pulls.push(pull);
  178. }, this);
  179. return pulls;
  180. }
  181. })
  182. };
  183. Pullreq.views = {
  184. utils: {
  185. initializeFixedMenu: function(adjustment) {
  186. var top = $('#menu').parent().offset().top - 10 - (adjustment ? adjustment : 0); // -10 for fixed style
  187. $(window).bind('scroll', function() {
  188. var y = $(this).scrollTop();
  189. if (y >= top) {
  190. $('#menu').addClass('fixed');
  191. } else {
  192. $('#menu').removeClass('fixed');
  193. }
  194. });
  195. }
  196. },
  197. RepoOwners: Backbone.View.extend({
  198. //el: $("#owners"),
  199. idAttribute: 'repoOwners',
  200. events: {
  201. 'click input.repoOption': 'repoOptionClick',
  202. 'click button.saveLink': 'saveRepos'
  203. },
  204. data: {
  205. successMessage: '<div class="alert alert-success">' +
  206. '<button type="button" class="close" data-dismiss="alert">&times;</button>' +
  207. '<strong>Bravo!</strong> You have successfully saved your warning paths.' +
  208. '</div>',
  209. warningMessage: '<div class="alert alert-error">' +
  210. '<button type="button" class="close" data-dismiss="alert">&times;</button>' +
  211. '<strong>Error!</strong> Something didn\'t save correctly.' +
  212. '</div>'
  213. },
  214. repoOwnerTemplate: Handlebars.compile($("#repo-owner-template").html()),
  215. template: Handlebars.compile($("#repo-options-template").html()),
  216. initialize: function () {
  217. this.options.loadCount = 0;
  218. this.options.repoOptionsCollection.bind("reset", this.initialLoad, this);
  219. this.options.userReposCollection.bind("reset", this.initialLoad, this);
  220. },
  221. initialLoad: function() {
  222. this.options.loadCount++;
  223. if (this.options.loadCount > 1) {
  224. this.renderRepos();
  225. }
  226. },
  227. saveRepos: function(e) {
  228. e.preventDefault();
  229. var view = this;
  230. var form = view.$el.find('form');
  231. form.find('.alert').remove();
  232. Backbone.sync('create', this.options.userReposCollection, {
  233. success: function() {
  234. form.prepend(view.data.successMessage);
  235. },
  236. error:function() {
  237. form.prepend(view.data.warningMessage);
  238. }
  239. });
  240. },
  241. removeView: function() {
  242. this.options.repoOptionsCollection.unbind();
  243. this.options.userReposCollection.unbind();
  244. this.remove();
  245. },
  246. repoOptionClick: function(e){
  247. var name = $(e.currentTarget).val().split('/');
  248. var owner = name[0];
  249. var repo = name[1];
  250. if (e.currentTarget.checked) {
  251. this.options.userReposCollection.add([{owner:owner, repo:repo}]);
  252. } else {
  253. var userRepo = this.options.userReposCollection.find(function(userRepo) {
  254. return userRepo.get('owner') == owner && userRepo.get('repo') == repo;
  255. });
  256. this.options.userReposCollection.remove(userRepo);
  257. }
  258. },
  259. render: function() {
  260. this.$el.html(this.template());
  261. return this;
  262. },
  263. renderRepos: function() {
  264. var view = this;
  265. this.$el.fadeOut(100, function() {
  266. $('#loadingMessage h1', view.$el).html('Choose your repos');
  267. $('#owners', view.$el).empty();
  268. view.options.repoOptionsCollection.each(view.makeView, view);
  269. view.$el.fadeIn(200, function() {
  270. $('#buttons').show();
  271. });
  272. });
  273. },
  274. makeView: function(repo) {
  275. var json = repo.toJSON();
  276. _.each(json.repos, function(repo) {
  277. repo.isChecked = this.options.userReposCollection.some(function(userRepo) {
  278. return repo.owner.login.toLowerCase() == userRepo.get('owner') && repo.name.toLowerCase() == userRepo.get('repo');
  279. }, this);
  280. }, this);
  281. $('#owners', this.$el).append(this.repoOwnerTemplate(json))
  282. }
  283. }),
  284. WarningPath: Backbone.View.extend({
  285. tagName: 'li',
  286. className: 'warningPath',
  287. events: {
  288. 'click a': 'removePath',
  289. 'mouseenter i': 'viewHover',
  290. 'mouseout i': 'viewHover'
  291. },
  292. template: Handlebars.compile($("#warning-path-template").html()),
  293. initialize: function() {
  294. this._modelBinder = new Backbone.ModelBinder();
  295. },
  296. viewHover: function(e) {
  297. var icon = $(e.target);
  298. if (icon.hasClass('icon-remove-sign')) {
  299. icon.removeClass('icon-remove-sign').addClass('icon-remove-circle');
  300. } else {
  301. icon.removeClass('icon-remove-circle').addClass('icon-remove-sign');
  302. }
  303. },
  304. removePath: function(e) {
  305. e.preventDefault();
  306. this.model.destroy();
  307. this.remove();
  308. },
  309. render: function() {
  310. this.$el.html(this.template(this.model.toJSON()));
  311. this._modelBinder.bind(this.model, this.el);
  312. return this;
  313. }
  314. }),
  315. WarningPaths: Backbone.View.extend({
  316. //el: $("#owners"),
  317. idAttribute: 'warningPaths',
  318. events: {
  319. 'click button.addLink': 'addNewPath',
  320. 'click button.saveLink': 'savePaths'
  321. },
  322. data: {
  323. pathViews: [],
  324. successMessage: '<div class="alert alert-success">' +
  325. '<button type="button" class="close" data-dismiss="alert">&times;</button>' +
  326. '<strong>Bravo!</strong> You have successfully saved your warning paths.' +
  327. '</div>',
  328. warningMessage: '<div class="alert alert-error">' +
  329. '<button type="button" class="close" data-dismiss="alert">&times;</button>' +
  330. '<strong>Error!</strong> Something didn\'t save correctly.' +
  331. '</div>'
  332. },
  333. template: Handlebars.compile($("#warning-paths-template").html()),
  334. removeView: function() {
  335. this.remove();
  336. },
  337. initialize: function () {
  338. this.collection.bind("reset", this.renderPaths, this);
  339. this.collection.bind("add", this.renderNewPath, this);
  340. //this.collection.bind("destroy", this.pathRemoved, this);
  341. },
  342. savePaths: function(e) {
  343. e.preventDefault();
  344. var view = this;
  345. var form = view.$el.find('form');
  346. form.find('.alert').remove();
  347. Backbone.sync('create', this.collection, {
  348. success: function() {
  349. form.prepend(view.data.successMessage);
  350. },
  351. error:function() {
  352. form.prepend(view.data.warningMessage);
  353. }
  354. });
  355. },
  356. addNewPath: function() {
  357. // Add new warning path
  358. this.collection.add([{}]);
  359. },
  360. renderNewPath: function(newPath) {
  361. var warningPathView = new Pullreq.views.WarningPath({
  362. model: newPath
  363. });
  364. this.renderPathView(warningPathView, true);
  365. },
  366. render: function() {
  367. this.$el.html(this.template());
  368. return this;
  369. },
  370. renderPaths: function() {
  371. var view = this;
  372. this.$el.fadeOut(200, function() {
  373. $('#loadingMessage h1', view.$el).html('Edit your warning paths');
  374. $('#warnings', view.$el).empty();
  375. view.collection.each(function(pathModel) {
  376. var warningPathView = new Pullreq.views.WarningPath({
  377. model: pathModel
  378. });
  379. view.renderPathView(warningPathView);
  380. }, view);
  381. view.$el.fadeIn(200, function() {
  382. $('.buttons').show();
  383. });
  384. });
  385. },
  386. renderPathView: function(warningPathView, focus) {
  387. $('#warnings', this.$el).append(warningPathView.render().el);
  388. if (focus) {
  389. $('#warnings li', this.$el).last().find('input').focus();
  390. }
  391. }
  392. }),
  393. PullRequest: Backbone.View.extend({
  394. events: {
  395. 'click a.descriptionLink': 'toggleSummary',
  396. 'click a.filesLink': 'renderFilesModal'
  397. },
  398. templates: {
  399. pullRequest: Handlebars.compile($("#pull-template").html()),
  400. extraInfo: Handlebars.compile($("#pull-request-extra-info-template").html()),
  401. pullComment: Handlebars.compile($("#pull-request-comment-template").html()),
  402. filesModal: Handlebars.compile($("#files-template").html())
  403. },
  404. initialize: function() {
  405. //this.model.bind('extraInfoLoaded', this.renderExtraInfo, this);
  406. },
  407. toggleSummary: function(e) {
  408. e.preventDefault();
  409. var summary = this.$el.find('div.pull-info');
  410. if (summary.is(':visible')) {
  411. var that = this;
  412. summary.slideUp(200, function() {
  413. that.$el.find('ul.pull-comments').empty();
  414. that.$el.find('a.descriptionLink').html('More Info...');
  415. });
  416. } else {
  417. this.renderComments();
  418. summary.slideDown(200);
  419. this.$el.find('a.descriptionLink').html('Less Info...');
  420. }
  421. },
  422. renderFilesModal: function(e) {
  423. e.preventDefault();
  424. var filesModal = this.templates.filesModal(this.model.toJSON()).trim(); // bug with jQuery if leading whitespace.
  425. $(filesModal).modal({
  426. backdrop: true,
  427. keyboard: true
  428. }).css({
  429. 'width': function () {
  430. return ($(document).width() * .6) + 'px';
  431. },
  432. 'margin-left': function () {
  433. return -($(this).width() / 2);
  434. }
  435. });
  436. },
  437. renderComments: function() {
  438. var el = this.$el.find('ul.pull-comments');
  439. var comments = this.model.get('issueComments');
  440. if (comments) {
  441. var that = this;
  442. _.each(comments, function(comment) {
  443. el.append(that.templates.pullComment(comment));
  444. });
  445. }
  446. },
  447. renderExtraInfo: function() {
  448. if (this.model.get('isGood')) {
  449. this.$el.find('a.pull-link')
  450. .prepend('<i class="icon-thumbs-up"></i> ')
  451. .addClass('pull-good');
  452. }
  453. if (this.model.get('isWarning')) {
  454. var link = this.$el.find('a.pull-link')
  455. .prepend('<i class="icon-warning-sign"></i> ')
  456. .addClass('pull-warn');
  457. }
  458. var body = this.model.get('body');
  459. if (body && body.indexOf(':warning:') !== -1) {
  460. this.$el.find('a.pull-link')
  461. .append(' <i class="icon-exclamation-sign" title="This pull request contains a warning note."></i>')
  462. .addClass('pull-desc-warn');
  463. }
  464. if (this.model.get('isMissingTests')) {
  465. var link = this.$el.find('a.pull-link')
  466. .append(' <i class="icon-question-sign" title="This pull request is missing tests"></i> ');
  467. }
  468. this.$el.find('ul.subInfo').html(this.templates.extraInfo( this.model.toJSON()));
  469. },
  470. render: function() {
  471. this.$el.html(this.templates.pullRequest(this.model.toJSON()));
  472. this.renderExtraInfo();
  473. // remove auto wrap div
  474. //this.$el = this.$el.children();
  475. //this.setElement(this.$el);
  476. return this;
  477. }
  478. }),
  479. Project: Backbone.View.extend({
  480. template: Handlebars.compile($("#project-template").html()),
  481. className: 'project',
  482. tagName: 'div',
  483. data: {
  484. pullRequestViews: []
  485. },
  486. initialize: function() {
  487. this.model.on('projectReload', this.render, this);
  488. },
  489. render: function() {
  490. this.$el.html(this.template(this.model.toJSON()));
  491. var pullRequests = this.model.get('pullRequests');
  492. pullRequests.sort();
  493. var pulls = [];
  494. pulls = pulls.concat(pullRequests.getGoodPulls());
  495. pulls = pulls.concat(pullRequests.getWarnPulls());
  496. pulls = pulls.concat(pullRequests.getNewPulls());
  497. _.each(pulls, function(pullRequest) {
  498. if (!Pullreq.data.tag || _.contains(pullRequest.get('tags'), Pullreq.data.tag)) {
  499. var pullView = new Pullreq.views.PullRequest({model:pullRequest});
  500. this.data.pullRequestViews.push(pullView);
  501. this.$('.pull-requests').append(pullView.render().el);
  502. }
  503. }, this);
  504. // remove auto wrap div
  505. //this.$el = this.$el.children();
  506. //this.setElement(this.$el);
  507. return this;
  508. },
  509. removeView: function() {
  510. _.each(this.data.pullRequestViews, function(view) {
  511. view.remove();
  512. });
  513. this.remove();
  514. }
  515. }),
  516. Projects: Backbone.View.extend({
  517. template: Handlebars.compile($("#project-template").html()),
  518. initialize: function () {},
  519. data: {
  520. projectViews: []
  521. },
  522. reset: function() {
  523. this.data.projectViews = [];
  524. },
  525. removeSubViews: function() {
  526. _.each(this.data.projectViews, function(view) {
  527. view.removeView();
  528. });
  529. this.reset();
  530. },
  531. render: function() {
  532. var that = this;
  533. this.$el.fadeOut(150, function() {
  534. that.removeSubViews();
  535. that.$el.empty();
  536. if (that.collection.isEmpty()) {
  537. that.$el.html('<div class="noPullRequests"><h2>There are no open pull requests</h2></div>');
  538. } else {
  539. that.collection.each(that.addProjectView, that);
  540. }
  541. that.$el.fadeIn(150);
  542. });
  543. return this;
  544. },
  545. addProjectView: function(project) {
  546. var projectView = new Pullreq.views.Project({model:project});
  547. this.data.projectViews.push(projectView);
  548. this.$el.append(projectView.render().el);
  549. }
  550. }),
  551. TeamTags: Backbone.View.extend({
  552. template: Handlebars.compile('<li class="{{selected}}"><i class="icon-{{class}}"></i> <a href="#" data-tag="{{value}}">{{name}}</a></li>'),
  553. events: {
  554. 'click a': 'filterTag'
  555. },
  556. filterTag: function(e) {
  557. e.preventDefault();
  558. $('li', this.$el).removeClass('selected');
  559. $(e.target).parents('li').addClass('selected');
  560. Pullreq.globalEvents.trigger('tag:selected', $(e.target).data('tag'));
  561. },
  562. initialize: function () {},
  563. updateView: function() {
  564. this.render();
  565. },
  566. render: function() {
  567. this.$el.empty();
  568. if (this.collection.isEmpty()) {
  569. this.$el.hide();
  570. } else {
  571. this.renderTag({'class':'tags', name:'ALL', value:null, selected: !Pullreq.data.tag ? 'selected':''});
  572. var that = this;
  573. this.collection.each(function(tag) {
  574. var json = tag.toJSON();
  575. json.selected = Pullreq.data.tag == tag.get('value') ? 'selected': '';
  576. that.renderTag(json);
  577. }, this);
  578. }
  579. return this;
  580. },
  581. renderTag: function(json) {
  582. this.$el.append(this.template(json));
  583. }
  584. }),
  585. OptionsMenu: Backbone.View.extend({
  586. events: {
  587. 'click a': 'selectMenuItem'
  588. },
  589. selectMenuItem: function (e) {
  590. $('li', this.$el).removeClass('selected');
  591. $(e.target).parents('li').addClass('selected');
  592. },
  593. initialSelect: function(path) {
  594. this.$el.find('li').each(function() {
  595. var link = $('a', this).attr('href');
  596. if(link.indexOf(path) !== -1) {
  597. $(this).addClass('selected');
  598. }
  599. });
  600. }
  601. }),
  602. MainApp: Backbone.View.extend({
  603. el: $('#main'),
  604. events: {
  605. 'click #refreshLink': 'refreshPage'
  606. },
  607. data: {
  608. progressBarCount: 0,
  609. progressBarComplete: 0,
  610. progressBarPercent: 0,
  611. initialLoadCount:0,
  612. isViewLoaded: false,
  613. isLoading: true
  614. },
  615. initialize: function() {
  616. Pullreq.views.utils.initializeFixedMenu(95); // 95 px for progress bar
  617. Pullreq.data.collections.pullRequests = new Pullreq.collections.PullRequests();
  618. Pullreq.data.collections.teamTags = new Pullreq.collections.TeamTags();
  619. Pullreq.data.collections.warningPaths = new Pullreq.collections.WarningPaths();
  620. Pullreq.data.collections.warningPaths.on('error', this.defaultErrorHandler, this);
  621. Pullreq.data.collections.pullRequests.on('error', this.defaultErrorHandler, this);
  622. Pullreq.data.collections.pullRequests.on('reset', this.initialLoad, this);
  623. Pullreq.data.collections.warningPaths.once('reset', this.initialLoad, this);
  624. Pullreq.globalEvents.on('pullRequest:extraInfoLoaded', this.progressBarUpdateCompleteStatus, this);
  625. Pullreq.globalEvents.on('tag:selected', this.updateTag, this);
  626. this.updateProgressBar(10);
  627. Pullreq.data.collections.warningPaths.fetch({reset:true});
  628. Pullreq.data.collections.pullRequests.fetch({reset:true});
  629. },
  630. defaultErrorHandler: function(model, error) {
  631. if (error.status == 401 || error.status == 403) {
  632. window.location = '/'; // not logged in
  633. }
  634. },
  635. initialLoad: function() {
  636. this.updateProgressBar(this.data.progressBarPercent + 20);
  637. this.data.initialLoadCount++;
  638. if (this.data.initialLoadCount > 1) {
  639. this.collectionLoaded();
  640. }
  641. },
  642. refreshPage: function(e) {
  643. e.preventDefault();
  644. if (!this.data.isLoading) {
  645. this.data.isLoading = true;
  646. this.progressBarBegin();
  647. Pullreq.data.collections.pullRequests.fetch({reset:true});
  648. }
  649. },
  650. updateProgressBar: function(percent) {
  651. this.data.progressBarPercent = percent;
  652. $('#progressBar div').width(percent+'%');
  653. },
  654. progressBarUpdateCompleteStatus: function() {
  655. this.data.progressBarComplete++;
  656. var num = this.data.progressBarComplete / this.data.progressBarCount;
  657. num = num * 50;
  658. this.updateProgressBar(50 + Math.abs(num));
  659. if (this.data.progressBarComplete == this.data.progressBarCount) {
  660. this.progressBarComplete();
  661. }
  662. },
  663. progressBarComplete: function() {
  664. this.updateProgressBar(100);
  665. $('#loadingMessage').slideUp(120, function() {
  666. $('#loadingMessage h1').hide();
  667. });
  668. $('#userOptions').fadeIn(100);
  669. },
  670. progressBarBegin: function() {
  671. this.updateProgressBar(30);
  672. $('#loadingMessage').slideDown(120);
  673. $('#userOptions').fadeIn(100);
  674. this.data.progressBarCount = 0;
  675. this.data.progressBarComplete = 0;
  676. },
  677. collectionLoaded: function() {
  678. this.data.progressBarCount = Pullreq.data.collections.pullRequests.length;
  679. if (Pullreq.data.collections.pullRequests.isEmpty()) {
  680. this.progressBarComplete();
  681. } else {
  682. //this.updateProgressBar(this.data.progressBarPercent + 20);
  683. this.makeTeamTags();
  684. Pullreq.data.collections.teamTags.sort();
  685. }
  686. // Only load tag view once
  687. if (!this.data.isViewLoaded) {
  688. this.render();
  689. this.data.isViewLoaded = true;
  690. }
  691. Pullreq.data.views.tags.updateView();
  692. this.updateViewForTag();
  693. this.data.isLoading = false;
  694. },
  695. makeTeamTags: function() {
  696. var re = /^([A-Za-z]+)\-?(\d+)?.*$/i;
  697. Pullreq.data.collections.pullRequests.each(function(pullRequest) {
  698. var title = pullRequest.get('title');
  699. var res = title.match(re);
  700. if (res) {
  701. var tagName = res[1].toUpperCase();
  702. // Update tags if new tag came in
  703. var tag = Pullreq.data.collections.teamTags.find(function(e) {
  704. return e.get('name') == tagName;
  705. });
  706. if (!tag) {
  707. Pullreq.data.collections.teamTags.add({name:tagName, value:tagName});
  708. }
  709. pullRequest.get('tags').push(tagName);
  710. }
  711. }, this);
  712. },
  713. makeProjectsForTag: function() {
  714. var projects = {};
  715. Pullreq.data.collections.pullRequests.each(function(pull) {
  716. if (Pullreq.data.tag && !_.contains(pull.get('tags'), Pullreq.data.tag)) {
  717. return;
  718. }
  719. var name = pull.get('base').repo.owner.login + '/' + pull.get('base').repo.name;
  720. var project = projects[name];
  721. if (!projects[name]) {
  722. project = new Pullreq.models.Project({name:name});
  723. projects[name] = project;
  724. }
  725. project.get('pullRequests').add(pull);
  726. }, this);
  727. projects = _.map(projects, function(num, key) { return num; });
  728. projects = new Pullreq.collections.Projects(projects);
  729. return projects;
  730. },
  731. updateTag: function(tag) {
  732. Pullreq.data.tag = tag;
  733. this.updateViewForTag();
  734. },
  735. updateViewForTag: function() {
  736. Pullreq.data.views.projects.collection = this.makeProjectsForTag();
  737. Pullreq.data.views.projects.render();
  738. },
  739. render: function() {
  740. Pullreq.data.views.projects = new Pullreq.views.Projects({
  741. el: $("#projects")
  742. });
  743. Pullreq.data.views.tags = new Pullreq.views.TeamTags({
  744. el: $("#teamTags"),
  745. collection: Pullreq.data.collections.teamTags
  746. });
  747. Pullreq.data.views.tags.render();
  748. this.$el.find('#menu').show();
  749. }
  750. })
  751. };
  752. Pullreq.routes = {
  753. OptionsApp: Backbone.Router.extend({
  754. routes: {
  755. 'warningPaths': 'warningPathsView',
  756. '*repos': 'reposView'
  757. },
  758. initialize: function() {
  759. Pullreq.views.utils.initializeFixedMenu();
  760. Pullreq.data.collections.userRepos = new Pullreq.collections.UserRepos();
  761. Pullreq.data.collections.repoOptions = new Pullreq.collections.RepoOptions();
  762. Pullreq.data.collections.warningPaths = new Pullreq.collections.WarningPaths();
  763. // handle errors
  764. Pullreq.data.collections.userRepos.on('error', this.defaultErrorHandler, this);
  765. Pullreq.data.collections.repoOptions.on('error', this.defaultErrorHandler, this);
  766. Pullreq.data.collections.warningPaths.on('error', this.defaultErrorHandler, this);
  767. // load menu
  768. Pullreq.data.views.optionsMenu = new Pullreq.views.OptionsMenu({
  769. el: $('#optionMenu')
  770. });
  771. if (!window.location.hash) {
  772. window.location.hash = "/repos";
  773. }
  774. Backbone.history.start();
  775. Pullreq.data.views.optionsMenu.initialSelect(Backbone.history.fragment);
  776. },
  777. defaultErrorHandler: function(model, error) {
  778. if (error.status == 401 || error.status == 403) {
  779. window.location = '/'; // not logged in
  780. }
  781. },
  782. removeView: function() {
  783. if (Pullreq.data.views.options) {
  784. Pullreq.data.views.options.removeView();
  785. }
  786. },
  787. reposView: function() {
  788. this.removeView();
  789. Pullreq.data.views.options = new Pullreq.views.RepoOwners({
  790. userReposCollection: Pullreq.data.collections.userRepos,
  791. repoOptionsCollection: Pullreq.data.collections.repoOptions
  792. });
  793. $('#content').html(Pullreq.data.views.options.render().el);
  794. Pullreq.data.collections.userRepos.fetch({reset:true});
  795. Pullreq.data.collections.repoOptions.fetch({reset:true});
  796. },
  797. warningPathsView: function() {
  798. this.removeView();
  799. Pullreq.data.views.options = new Pullreq.views.WarningPaths({
  800. collection: Pullreq.data.collections.warningPaths
  801. });
  802. $('#content').html(Pullreq.data.views.options.render().el);
  803. Pullreq.data.collections.warningPaths.fetch({reset:true});
  804. }
  805. })
  806. };
  807. Pullreq.data = {
  808. views: {},
  809. collections: {},
  810. models: {},
  811. tag: null
  812. };
  813. }());