Reddit - Subs - default sort

Apply default sort to subs listing (feature missing in SHreddit)

  1. // ==UserScript==
  2. // @name Reddit - Subs - default sort
  3. // @namespace https://github.com/Procyon-b
  4. // @version 0.5.1
  5. // @description Apply default sort to subs listing (feature missing in SHreddit)
  6. // @author Achernar
  7. // @match https://www.reddit.com/*
  8. // @match https://sh.reddit.com/*
  9. // @run-at document-start
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. "use strict";
  15.  
  16. var sort={};
  17.  
  18. const RE=/^(?:https:\/\/(?:sh|www)\.reddit\.com)?(\/(?:r\/[^\/]+\/)?)(?:(best|new|hot|top|rising)\/)?(?:\?.*)?$/;
  19. const REval=/^(best|new|hot|top|rising)?$/;
  20. var SR=[];
  21.  
  22. var done=false;
  23. function init() {
  24. if (done) return;
  25. done=true;
  26. document && document.body && chk();
  27. newObs(document.body);
  28. window.addEventListener('focus', function(){
  29. if (load()) {
  30. chk(null, true);
  31. updLeftNav();
  32. }
  33. });
  34.  
  35. if (window == top) {
  36. var e=document.createElement('div');
  37. e.style='position: fixed; top: 0; left: 0; width: 15px; height: 15px; z-index: 9999;';
  38. document.body.appendChild(e);
  39. e.ondblclick=function(){
  40. var p, s='';
  41.  
  42. do {
  43. p=prompt(s+'Default sort\nChoose between: [empty] - hot - new - top - rising', sort.default);
  44. if (p == null) break;
  45. s='ERROR. Invalid value.\nTry again\n';
  46. } while (!REval.test(p));
  47.  
  48. if (p != null) {
  49. if (p) sort.default=p;
  50. else delete sort.default;
  51. store();
  52. chk(null, true);
  53. updLeftNav();
  54. }
  55.  
  56. p=prompt('Auto-redirect to correct sort ?\nEither: [empty field] - true - force - ask', sort.redir);
  57. if (p != null) {
  58. if (p) sort.redir = p;
  59. else delete sort.redir;
  60. store();
  61. }
  62.  
  63. };
  64. }
  65. }
  66.  
  67. function gen(re) {
  68. var s=sort[re[1]] || '';
  69. if (s) s+='/';
  70. else if ((re[1] != '/') && sort.default) s=sort.default+'/';
  71. return s;
  72. }
  73.  
  74. function chk(r, force=false) {
  75. var a=(r||document).getElementsByTagName("a");
  76. if (r && (r.nodeName == 'A')) a=[r, ...a];
  77. for (let i=0,e; e=a[i]; i++) {
  78. if (!force && e._sortfixed) continue;
  79. e._sortfixed=true;
  80. if (e.hostname == 'www.reddit.com') {
  81. let re;
  82. if (re=RE.exec(e.pathname + (!e.pathname.endsWith('/') ? '/':'' ) )) {
  83. if (e.closest('[slot="dropdown-items"], faceplate-dropdown-menu')) continue;
  84. e.classList.add('_marked_');
  85. let s=gen(re);
  86. e.pathname=re[1]+s;
  87. }
  88. }
  89. }
  90. a=(r||document).querySelectorAll('pdp-back-button[subreddit-prefixed-name]');
  91. for (let i=0,e; e=a[i]; i++) {
  92. if (!force && e._sortfixed) continue;
  93. e._sortfixed=true;
  94. let re, v=e.attributes['subreddit-prefixed-name'].value;
  95. if (re=RE.exec('/'+v+'/')) {
  96. e.classList.add('_marked_');
  97. let s=gen(re);
  98. e.attributes['subreddit-prefixed-name'].value=(re[1]+s).replace(/^\/(.+)\/$/, "$1");
  99. }
  100. }
  101. }
  102.  
  103. var AS=HTMLElement.prototype.attachShadow;
  104. HTMLElement.prototype.attachShadow=function(m){/*[native */
  105. var e=this;
  106. let sr=AS.call(e,m);
  107.  
  108. if (e.tagName == 'REDDIT-RECENT-PAGES') {
  109. SR.push(sr);
  110. newObs(sr, cbRec);
  111. e.SR=true;
  112. }
  113. else if (e.tagName.startsWith('LEFT-NAV-') || (e.tagName == 'SHREDDIT-SUBREDDIT-HEADER') || (e.tagName == 'MOD-NAV') ) {
  114. SR.push(sr);
  115. newObs(sr);
  116. e.SR=true;
  117. }
  118. return sr;
  119. }
  120.  
  121. function updLeftNav() {
  122. for(let r, i=0; r=SR[i]; i++) {
  123. for(let e, j=0; e=r.children[j]; j++) {
  124. chk(e, true);
  125. }
  126. }
  127. }
  128.  
  129. function ds(sub, s) {
  130. let d= sub == '/' ? 'best' : (sort.default?'DEF':'');
  131. return s == d ? '' : s;
  132. }
  133.  
  134. function patchFetch() {
  135. // XHR - Fetch
  136. const _fetch=window.fetch;
  137. window.fetch = async (...args) => {
  138. let [resource, config ] = args;
  139. let response = await _fetch(resource, config);
  140. let re;
  141. if ( re=RE.exec(resource) ) {
  142. let s=ds(re[1], re[2]);
  143. if (s != ds(re[1],sort[re[1]] || '') ) {
  144. if ( (re[1] != '/') && sort.default && (s == sort.default) ) s='';
  145. if (s) sort[re[1]] = s;
  146. else delete sort[re[1]];
  147. store();
  148. chk(null, true);
  149. updLeftNav();
  150. }
  151. }
  152. return response;
  153. };
  154. }
  155.  
  156. function newObs(r, f=cb) {
  157. var o=new MutationObserver(f), config = { attributes: false, childList: true, subtree: true};
  158. o.observe(r, config);
  159. return o;
  160. }
  161.  
  162. function cb(mutL) {
  163. for(let mut of mutL) {
  164. if (mut.type == 'childList') {
  165. for (let e,i=0; e=mut.addedNodes[i]; i++) {
  166. if (e.nodeType == 1) chk(e);
  167. }
  168. }
  169. }
  170. }
  171.  
  172. function cbRec(mutL) {
  173. for(let mut of mutL) {
  174. if (mut.type == 'childList') {
  175. for (let e,i=0; e=mut.addedNodes[i]; i++) {
  176. if (e.nodeType == 1) {
  177. let r=e.closest('details');
  178. if (r) chk(r, true);
  179. else chk(e);
  180. }
  181. }
  182. }
  183. }
  184. }
  185.  
  186. function load() {
  187. var o=sort.upd;
  188. sort=JSON.parse(localStorage.getItem('_sort_') || '{}');
  189. if (o != sort.upd) return true;
  190. }
  191.  
  192. function store() {
  193. sort.upd=Date.now();
  194. localStorage.setItem('_sort_', JSON.stringify(sort) );
  195. }
  196.  
  197. patchFetch();
  198. load();
  199.  
  200. if (document.readyState != 'loading') init();
  201. else {
  202. document.addEventListener('DOMContentLoaded', init);
  203. window.addEventListener('load', init);
  204. }
  205.  
  206. var redir=sort.redir;
  207.  
  208. if (redir) {
  209. let re;
  210. if (re = RE.exec(location.pathname)) {
  211. if ( !re[2] || (redir == 'force') ) {
  212. let l=re[1]+gen(re);
  213. if (l != location.pathname) {
  214. if (redir == 'ask') {
  215. if ( (window == top) && (window.document.visibilityState == 'visible') && confirm('Redirect to correct order ?\n'+location.href+'\n'+l) ) redir=true;
  216. else redir=false;
  217. }
  218. if (redir) {
  219. window.stop();
  220. location.pathname=l;
  221. }
  222. }
  223. }
  224. }
  225. }
  226.  
  227. })();