Greasy Fork is available in English.

Unsolved tab on PSE

Add "Unsolved" tab on Puzzling Stack Exchange

  1. // ==UserScript==
  2. // @name Unsolved tab on PSE
  3. // @version 0.1
  4. // @author Lord of dark (http://stackexchange.com/users/5950528/lord-of-dark)
  5. // @description Add "Unsolved" tab on Puzzling Stack Exchange
  6. // @include http://puzzling.stackexchange.com/*
  7. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  8. // @grant GM_xmlhttpRequest
  9. // @namespace https://greatest.deepsurf.us/users/49190
  10. // ==/UserScript==
  11.  
  12.  
  13. var unanswered = document.querySelectorAll('[id="nav-unanswered"]')[0];
  14. var tabs = unanswered.parentNode.parentNode;
  15. var unsolved = unanswered.cloneNode(true);
  16. unsolved.id="nav-unsolved";
  17. unsolved.innerHTML="Unsolved";
  18. unsolved.setAttribute("href","/unsolved");
  19. var li_unsolved = document.createElement('li');
  20. li_unsolved.appendChild(unsolved);
  21. tabs.appendChild(li_unsolved);
  22.  
  23. var unsolved_url="puzzling.stackexchange.com/unsolved"
  24. if(content.document.location.href.indexOf(unsolved_url) > -1){
  25. // Update page title :
  26. document.title = "Unsolved Questions - Puzzling Stack Exchange";
  27. // Add focus on unsolved tab, remove from other tabs
  28. tabs_youarehere = tabs.querySelectorAll('[class="youarehere"]')
  29. for (var i = 0; i < tabs_youarehere.length; i++) { tabs_youarehere[i].removeAttribute("class");};
  30. unsolved.parentNode.setAttribute("class","youarehere");
  31. //remove every thing in the blank part :
  32. var center_html = document.getElementById("content");
  33. center_html.innerHTML="";
  34. // Check the type of reasearch type :
  35. var researchType = findResearchType();
  36. // Check the page
  37. var page= findPage();
  38. // Call the unanswered and copy it
  39. GM_xmlhttpRequest({
  40. method: "GET",
  41. url: "http://puzzling.stackexchange.com/unanswered",
  42. onload: function(response) {
  43. // Add all the content from UNANSWERED
  44. var tempDiv = document.createElement('html');
  45. tempDiv.innerHTML = response.responseText;
  46. var all_div = tempDiv.getElementsByTagName("div");
  47. for (var i=0;i<all_div.length;i++){
  48. if (all_div[i].getAttribute('id')=="content"){
  49. var content = all_div[i].innerHTML;
  50. center_html.innerHTML=content;
  51. }
  52. }
  53. updateBodyContext(researchType);
  54. //http://puzzling.stackexchange.com/search?page=1&q=hasaccepted%3Afalse+closed%3Afalse+duplicate%3Afalse+locked%3Afalse+hasnotice%3Afalse+answers%3A0
  55. var urlSearch;
  56. switch(researchType) {
  57. case "noanswers":
  58. urlSearch="http://puzzling.stackexchange.com/search?page="+page+"&q=hasaccepted%3Afalse+closed%3Afalse+duplicate%3Afalse+locked%3Afalse+hasnotice%3Afalse+answers%3A0"
  59. break;
  60. default:
  61. urlSearch="http://puzzling.stackexchange.com/search?page="+page+"&tab="+researchType+"&q=hasaccepted%3afalse%20closed%3afalse%20duplicate%3afalse%20locked%3afalse"
  62. }
  63. GM_xmlhttpRequest({
  64. method: "GET",
  65. url: urlSearch,
  66. onload: function(response) {
  67.  
  68. var tempDiv = document.createElement('div');
  69. tempDiv.innerHTML = response.responseText.replace(/<script(.|\s)*?\/script>/g, '');
  70. var nb_questions = tempDiv.getElementsByTagName("h2")[0].firstChild;
  71. document.getElementsByClassName("summarycount al")[0].innerHTML=nb_questions.nodeValue
  72. var questionsText = tempDiv.getElementsByClassName("search-results js-search-results")[0];
  73. var questions_area = document.getElementById("questions");
  74. questions_area.innerHTML= questionsText.innerHTML
  75.  
  76. document.getElementsByClassName("mod-flair").remove();
  77. var all_answer_links = document.querySelectorAll('[data-searchsession]');
  78. for (var i = 0; i < all_answer_links.length; i++) {
  79. all_answer_links[i].removeAttribute("data-searchsession");
  80. all_answer_links[i].setAttribute("class","question-hyperlink");
  81. all_answer_links[i].innerHTML= all_answer_links[i].innerHTML.replace("Q: ","");
  82. var wrapper = document.createElement('h3');
  83. all_answer_links[i].parentNode.appendChild(wrapper);
  84. wrapper.appendChild(all_answer_links[i])
  85. }
  86.  
  87. var alluserid=""
  88. var all_from = document.querySelectorAll('[class="started fr"]');
  89. for (var i = 0; i < all_from.length; i++) {
  90. var relative_time = all_from[i].getElementsByTagName('span')[0];
  91. var a_childs= all_from[i].getElementsByTagName('a');
  92. var user;//= '<a href="/users/0/user">user</a>'
  93. if (a_childs.length>0){
  94. user= a_childs[0];
  95. } else { // if there is no <a> then the user has been deleted
  96. user = document.createElement('a');
  97. user.setAttribute("href","/users/0/user");
  98. user.innerHTML='deleted user'
  99. }
  100. all_from[i].innerHTML= all_from[i].innerHTML.replace("asked","");
  101. all_from[i].innerHTML= all_from[i].innerHTML.replace("by","");
  102. var userName = user.cloneNode(true);
  103. var userid = userName.getAttribute("href").replace("/users/","").replace(/\/.*/gi,"");
  104. alluserid=alluserid+userid+";";
  105. user.innerHTML='<div class="gravatar-wrapper-32"><img src="http://i.stack.imgur.com/Fe45R.png" alt="" height="32" width="32"></div>';
  106. var gravatarDiv = document.createElement('div');
  107. gravatarDiv.setAttribute("class","user-gravatar32");
  108. gravatarDiv.appendChild(user);
  109. all_from[i].innerHTML="";
  110. all_from[i].appendChild(relative_time);
  111. all_from[i].appendChild(gravatarDiv);
  112. var detailsDiv = document.createElement('div');
  113. detailsDiv.setAttribute("class","user-details");
  114. all_from[i].appendChild(detailsDiv);
  115. detailsDiv.appendChild(userName);
  116. }
  117. alluserid = alluserid.replace(/;$/,"");
  118.  
  119. var all_dates = document.querySelectorAll('[class="relativetime"]');
  120. for (var i = 0; i < all_dates.length; i++) {
  121. var infoDiv = document.createElement('div');
  122. infoDiv.setAttribute("class","user-info");
  123. all_dates[i].parentNode.appendChild(infoDiv);
  124. var timeDiv = document.createElement('div');
  125. timeDiv.innerHTML="asked ";
  126. timeDiv.setAttribute("class","user-action-time");
  127. timeDiv.appendChild(all_dates[i]);
  128. infoDiv.appendChild(timeDiv);
  129. infoDiv.appendChild(infoDiv.parentNode.getElementsByClassName("user-gravatar32")[0]);
  130. infoDiv.appendChild(infoDiv.parentNode.getElementsByClassName("user-details")[0]);
  131.  
  132. //add flair
  133. var flairDiv = document.createElement('div');
  134. flairDiv.setAttribute("class","-flair");
  135. infoDiv.getElementsByClassName("user-details")[0].appendChild(flairDiv);
  136. flairDiv.innerHTML='<span class="reputation-score" title="reputation score " dir="ltr">???</span>';
  137. }
  138.  
  139. createPageNav(page,researchType)
  140. updateUserProfil(alluserid);
  141.  
  142. }
  143. });
  144. }
  145. });
  146. // Actions before requesting the questions, applied to context.
  147. function updateBodyContext(researchType) {
  148. // update the title
  149. var title_body = document.getElementById("h-unanswered-questions");
  150. title_body.innerHTML="Unsolved Questions";
  151. // setcontent to empty
  152. var questionsDiv = document.getElementById("questions");
  153. questionsDiv.innerHTML="";
  154. // set question count to 0$
  155. var counter = document.getElementsByClassName('summarycount al')[0];
  156. counter.innerHTML="?";
  157. counter.parentNode.getElementsByTagName("span")[0].innerHTML='questions with no accepted answers';
  158.  
  159. // remove page navigation :
  160. document.getElementsByClassName('pager fl').remove()
  161. // Change tab links
  162. var tabs_link = document.getElementById("tabs");
  163. var links = tabs_link.getElementsByTagName("a");
  164. for (var i = 0; i < links.length; i++) {
  165. links[i].setAttribute("href",links[i].getAttribute("href").replace("unanswered","unsolved"));
  166. if (links[i].getAttribute("href").indexOf(researchType)>-1){
  167. links[i].setAttribute("class","youarehere");
  168. } else {
  169. links[i].removeAttribute("class");
  170. }
  171. if (links[i].getAttribute("href")=="/unsolved/tagged?tab=mytags"){
  172. links[i].innerHTML="";
  173. }
  174. }
  175. }
  176. function findPage() {
  177. var ind1=document.location.href.indexOf("page=");
  178. if (ind1 <1){
  179. return "1"
  180. }
  181. else{
  182. var ind2=document.location.href.indexOf("&",ind1+1);
  183. if (ind2>4){
  184. return document.location.href.substring(ind1+5,ind2);
  185. } else {
  186. return document.location.href.substring(ind1+5);
  187. }
  188. }
  189. return "1"
  190. }
  191. function findResearchType() {
  192. var ind1=document.location.href.indexOf("tab=");
  193. if (ind1 <1){
  194. return "newest"
  195. }
  196. else{
  197. var ind2=document.location.href.indexOf("&",ind1+1);
  198. if (ind2>4){
  199. return document.location.href.substring(ind1+4,ind2);
  200. } else {
  201. return document.location.href.substring(ind1+4);
  202. }
  203. }
  204. return "newest"
  205. }
  206. // Define HTTP client to get the JSON
  207. function httpGetAsync(theUrl, callback)
  208. {
  209. var xmlHttp = new XMLHttpRequest();
  210. xmlHttp.onreadystatechange = function() {
  211. if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
  212. callback(xmlHttp.responseText);
  213. }
  214. xmlHttp.open("GET", theUrl, true); // true for asynchronous
  215. xmlHttp.send(null);
  216. }
  217. // This function update the profil (gravatar / reputation / badges)
  218. // Use API to get this data -> API without login limited at 300 request/day
  219. function updateUserProfil(alluserid){
  220. //var jsonUrl ='https://api.stackexchange.com/2.2/users/'+alluserid+'?page=1&pagesize=50&order=desc&sort=reputation&site=puzzling&filter=!LnNkvq0X3NNP1zDzft3_WF';
  221. var jsonUrl ='https://api.stackexchange.com/2.2/users/'+alluserid+'?page=1&pagesize=50&order=desc&sort=reputation&site=puzzling&filter=!LnO)*RBcCzpAXUnHZRkHoo'
  222. httpGetAsync(jsonUrl, function(response) {
  223. var userJson = JSON.parse(response);
  224. var items = userJson.items;
  225. function jsonHasId(id){
  226. for(var i = 0; i < items.length; i++){
  227. if (items[i].user_id==id){
  228. return true;
  229. }
  230. }
  231. return false;
  232. }
  233. function getImgUrl(id){
  234. for(var i = 0; i < items.length; i++){
  235. if (items[i].user_id==id){
  236. return items[i].profile_image;
  237. }
  238. }
  239. return "";
  240. };
  241. function getRepBadges(id){
  242. for(var i = 0; i < items.length; i++){
  243. if (items[i].user_id==id){
  244. return [items[i].reputation,items[i].badge_counts.gold,items[i].badge_counts.silver,items[i].badge_counts.bronze,items[i].user_type=="moderator"];
  245. }
  246. }
  247. return [1,0,0,0];
  248. };
  249. // Loop on the users to update their profil pictures
  250. var all_gravatars = document.querySelectorAll('[class="user-gravatar32"]');
  251. for (var i = 0; i < all_gravatars.length; i++) {
  252. var userName=all_gravatars[i].getElementsByTagName('a')[0].getAttribute("href").replace("/users/","").replace(/\/.*/gi,"");
  253. if(jsonHasId(userName)){
  254. all_gravatars[i].getElementsByTagName('img')[0].setAttribute("src",getImgUrl(userName));
  255. var flair=all_gravatars[i].nextSibling.lastChild;
  256. var repBadges = getRepBadges(userName);
  257. flair.innerHTML='<span class="reputation-score" title="reputation score " dir="ltr">'+repBadges[0]+'</span>';
  258. if (repBadges[1]>0) {
  259. flair.innerHTML+='<span title="'+repBadges[1]+' gold badges"><span class="badge1"></span><span class="badgecount">'+repBadges[1]+'</span></span>';
  260. }
  261. if (repBadges[2]>0) {
  262. flair.innerHTML+='<span title="'+repBadges[2]+' silver badges"><span class="badge2"></span><span class="badgecount">'+repBadges[2]+'</span></span>';
  263. }
  264. flair.innerHTML+='<span title="'+repBadges[3]+' bronze badges"><span class="badge3"></span><span class="badgecount">'+repBadges[3]+'</span></span>';
  265. // Add moderator Diamonds
  266. if (repBadges[4]){
  267. var mod = document.createElement('span');
  268. mod.setAttribute('class','mod-flair');
  269. mod.setAttribute('title',"moderator");
  270. mod.innerHTML="♦";
  271. flair.parentNode.insertBefore(mod,flair);
  272. }
  273. }
  274. }
  275. });
  276. }
  277. // Create the footer to navigate in other pages
  278. function createPageNav(page,researchType) {
  279. var maxPages = Math.ceil(document.getElementsByClassName('summarycount al')[0].innerHTML /50);
  280. var pagerfl = document.createElement('div');
  281. pagerfl.setAttribute('class','pager fl');
  282. pagerfl.innerHTML="";
  283. if (page > 1) { // add prev button
  284. pagerfl.appendChild(addPage(page-1, researchType, "prev"));
  285. }
  286. if (page > 3) {
  287. pagerfl.appendChild(addPage(1, researchType, "1"));
  288. }
  289. if (page > 4) {
  290. pagerfl.innerHTML+='<span class="page-numbers dots">…</span>';
  291. }
  292. if (page > 2) {
  293. pagerfl.appendChild(addPage(page-2, researchType, page-2));
  294. }
  295. if (page > 1) {
  296. pagerfl.appendChild(addPage(page-1, researchType, page-1));
  297. }
  298. pagerfl.innerHTML+='<span class="page-numbers current">'+page+'</span>' // add current page number
  299.  
  300. if (page < maxPages){
  301. pagerfl.appendChild(addPage(page-(-1), researchType, page-(-1)));
  302. }
  303. if (page < maxPages-1){
  304. pagerfl.appendChild(addPage(page-(-2), researchType, page-(-2)));
  305. }
  306. if (page < maxPages-3){
  307. pagerfl.innerHTML+='<span class="page-numbers dots">…</span>';
  308. }
  309. if (page < maxPages-2){
  310. pagerfl.appendChild(addPage(maxPages, researchType, maxPages));
  311. }
  312.  
  313. if (page < maxPages){
  314. pagerfl.appendChild(addPage(page+1, researchType, "next"));
  315. }
  316. // add next button
  317. var pageplus1 = page+1
  318. document.getElementById('mainbar').appendChild(pagerfl);
  319. }
  320. // function to create a page number span
  321. function addPage(page, researchType, name){
  322. var a_page= document.createElement('a');
  323. a_page.setAttribute("href","/unsolved?page="+page+"&tab="+researchType);
  324. a_page.setAttribute("title","go to page "+page);
  325. var span_page= document.createElement('span');
  326. span_page.setAttribute("class","page-numbers "+page);
  327. span_page.innerHTML=name;
  328. a_page.appendChild(span_page);
  329. a_page.innerHTML+=" ";
  330. return a_page;
  331. }
  332.  
  333. // Remove node list
  334. NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
  335. for(var i = this.length - 1; i >= 0; i--) {
  336. if(this[i] && this[i].parentElement) {
  337. this[i].parentElement.removeChild(this[i]);
  338. }
  339. }
  340. }
  341. }