GitHub Pulse Sort By

adds sorting option to repo pulse

  1. // ==UserScript==
  2. // @name GitHub Pulse Sort By
  3. // @namespace faleij
  4. // @description adds sorting option to repo pulse
  5. // @include https://github.com/*
  6. // @version 1.1.1
  7. // @grant none
  8. // @run-at document-end
  9. // ==/UserScript==
  10. /* jshint esnext:true, node:true, browser:true */
  11. /* globals $ */
  12. 'use strict';
  13.  
  14. const menuItems = [{
  15. selected: true,
  16. id: 'sort_changed',
  17. value: 'time',
  18. text: 'Last Changed',
  19. sort: (a, b) => new Date($('time:first', a).attr('datetime')) > new Date($('time:first', b).attr('datetime'))
  20. },{
  21. id: 'sort_assignee',
  22. value: 'assignee',
  23. text: 'Assignee',
  24. sort: (a, b) => $('.assignee:first', a).text().localeCompare($('.assignee:first', b).text())
  25. },{
  26. id: 'sort_created',
  27. value: 'num',
  28. text: 'Created',
  29. sort: (a, b) => parseInt($('.num:first', a).text().substr(1)) > parseInt($('.num:first', b).text().substr(1))
  30. }];
  31.  
  32. let getMenuItemHTML = (args) => `
  33. <div class="select-menu-item js-navigation-item ${args.selected ? 'selected' : ''}">
  34. <input checked="checked" id="${args.id}" name="sortBy" value="${args.value}" type="radio" />
  35. <span class="select-menu-item-icon octicon octicon-check"></span>
  36. <div class="select-menu-item-text">
  37. ${args.text}
  38. </div>
  39. </div>`;
  40.  
  41. const menuHTML = `
  42. <div class="select-menu js-menu-container js-select-menu faleijs-sort-by-menu">
  43. <button class="btn btn-sm select-menu-button js-menu-target" type="button" aria-haspopup="true">
  44. <i>Sort By</i>
  45. </button>
  46.  
  47. <div class="select-menu-modal-holder js-menu-content js-navigation-container" aria-hidden="true">
  48. <div class="select-menu-modal">
  49. <div class="select-menu-header">
  50. <span class="select-menu-title">Sort by</span>
  51. <span class="octicon octicon-remove-close js-menu-close"></span>
  52. </div>
  53.  
  54. <div class="select-menu-list">
  55. ${ menuItems.map(getMenuItemHTML).join('') }
  56. </div>
  57. </div>
  58. </div>
  59. </div>
  60. </li>`;
  61.  
  62. const renderProgressHTML = (total, at) => `
  63. <span class="issue-meta-section task-progress">
  64. <span aria-hidden="true" class="octicon octicon-checklist"></span>
  65. <span class="task-progress-counts">${at} of ${total}</span>
  66. <span class="progress-bar">
  67. <span class="progress" style="width: ${(at/total)*100}%"></span>
  68. </span>
  69. </span>`;
  70.  
  71. const target = document.querySelector('#js-repo-pjax-container');
  72. const mutationHandler = () => {
  73. console.log('event handler', location.pathname.endsWith('/pulse'));
  74. if (/\/pulse\/|$/.test(location.pathname) && !$('.faleijs-sort-by-menu', target).length) create();
  75. };
  76. const observer = new MutationObserver(mutationHandler);
  77.  
  78. observer.observe(target, {
  79. childList: true
  80. });
  81.  
  82. mutationHandler();
  83.  
  84. function create() {
  85. // Load assignees and progress
  86. $('li>.title').each((index, title) => $.get($(title).attr('href')).then(data => {
  87. data = $(data);
  88. let total = $('.comment-body:first input[type=checkbox]', data);
  89.  
  90. if (total.length) {
  91. $(title).parent().append(renderProgressHTML(total.length, total.filter('[checked]').length));
  92. }
  93.  
  94. $(title).parent().append($('.assignee', data).parent().css('float', 'right'));
  95. }));
  96.  
  97. $('input:radio[name="sortBy"]', $(menuHTML).prependTo('.header-with-actions')).change(MenuChangeHandler);
  98. }
  99.  
  100. function MenuChangeHandler() {
  101. let menuItem = menuItems.find(el => el.value === this.value);
  102. $('.repository-content ul').each((index, ul) => $('li', ul).sort(menuItem.sort).appendTo(ul));
  103. }