Overflow extended blog

Redirecting from localized versions of the site to an English-language domain with a search for the current question.

  1. // ==UserScript==
  2. // @name Overflow extended blog
  3. // @namespace https://github.com/XelaNimed
  4. // @version 1.1
  5. // @description Redirecting from localized versions of the site to an English-language domain with a search for the current question.
  6. // @author XelaNimed
  7. // @copyright 2021, XelaNimed (https://github.com/XelaNimed)
  8. // @match https://*.stackoverflow.com/*
  9. // @match https://*.meta.stackoverflow.com/*
  10. // @grant none
  11. // @homepageURL https://raw.githubusercontent.com/XelaNimed/ruSO
  12. // @supportURL https://github.com/XelaNimed/ruSO/issues
  13. // @iconURL https://raw.githubusercontent.com/XelaNimed/ruSO/master/stackoverflow.ico
  14. // @license MIT
  15. // ==/UserScript==
  16. const $ = window.jQuery;
  17. window.addEventListener('load', function () {
  18. ruSO
  19. .initLocalStorage()
  20. .addButtons()
  21. .addAuthorQuestionsLinks();
  22. }, false);
  23. var ruSO = {
  24. $sidebar: $('#sidebar'),
  25. $content: $('#content'),
  26. $container: $('body>.container'),
  27. $fullWidthBtn: null,
  28. params: {
  29. animationSpeed: 250
  30. },
  31. keys: {
  32. showMetasKey: 'showMetaPosts',
  33. contentMaxWidth: 'contentMaxWidth',
  34. containerMaxWidth: 'containerMaxWidth',
  35. fooFullWidth: 'fooFullWidth'
  36. },
  37. strings: {
  38. watchedTagsText: 'Отслеживаемые метки',
  39. clickToToggle: 'Скрыть/показать',
  40. setFullWidth: 'Растянуть',
  41. resetFullWidth: 'Восстановить'
  42. },
  43. initLocalStorage: function initLocalStorage() {
  44. localStorage[this.keys.showMetasKey] || localStorage.setItem(this.keys.showMetasKey, true);
  45. localStorage[this.keys.containerMaxWidth] = this.$container.css('max-width');
  46. localStorage[this.keys.contentMaxWidth] = this.$content.css('max-width');
  47. localStorage[this.keys.fooFullWidth] = 'setFullWidth';
  48. return this;
  49. },
  50. addButtons: function () {
  51. var self = this,
  52. addWatchedTags = function () {
  53. let tags = [],
  54. urlPrefix = window.location.origin + '/questions/tagged/';
  55. $('.js-watched-tag-list a.user-tag').each(function (idx, itm) {
  56. let url = itm.href;
  57. tags.push(url.substring(url.lastIndexOf('/') + 1));
  58. });
  59. if (tags.length) {
  60. let url = urlPrefix + tags.join('+or+');
  61. let spanArr = self.$sidebar.find("span:contains('" + self.strings.watchedTagsText + "')");
  62. self.$sidebar.find('span.grid--cell.mr4').hide();
  63. if (spanArr.length > 0) {
  64. spanArr[0].innerHTML = '<a class="post-tag user-tag" href="' + url + '">' + self.strings.watchedTagsText + '</a>';
  65. }
  66. }
  67. },
  68. addMetaToggles = function () {
  69. let showHideMetas = function ($elem) {
  70. let isVisible = localStorage.getItem(self.keys.showMetasKey) === 'true';
  71. $elem.parent().children('li')[isVisible ? 'show' : 'hide'](ruSO.params.animationSpeed);
  72. };
  73. self.$sidebar
  74. .find('div.s-sidebarwidget:first div.s-sidebarwidget--header, #how-to-format, #how-to-title')
  75. .each(function (idx, itm) {
  76. let $itm = $(itm);
  77. $itm
  78. .attr('title', ruSO.strings.clickToToggle)
  79. .css('cursor', 'pointer')
  80. .on('click', function (e) {
  81. let isVisible = localStorage.getItem(self.keys.showMetasKey) === 'true';
  82. localStorage.setItem(self.keys.showMetasKey, !isVisible);
  83. showHideMetas($(e.target));
  84. });
  85. showHideMetas($itm);
  86. });
  87. },
  88. addFullWidth = function() {
  89. let $header = $('#question-header');
  90. self.$fullWidthBtn = $header.find('div').clone();
  91. self.$fullWidthBtn.attr('id', 'set-full-width-btn').find('a')
  92. .removeClass('s-btn__primary')
  93. .addClass('s-btn__filled')
  94. .attr('href', '#')
  95. .text(self.strings.setFullWidth)
  96. .on('click', function() {
  97. self[localStorage[self.keys.fooFullWidth]]();
  98. });
  99. $header.append(self.$fullWidthBtn);
  100. },
  101. addRedirectToSO = function(){
  102. let localPrefix = "ru.";
  103. let isLocalSO = location.host.substr(0,3) === localPrefix;
  104. let btnText = isLocalSO ? "en" : "ru";
  105. let $btn = $(`<div class="print:d-none"><a href="#" class="s-btn s-btn__filled s-btn__xs s-btn__icon ws-nowrap">${btnText}</a></div>`);
  106. $btn.insertAfter($("#search"));
  107. $btn.on('click', function() {
  108. location.host = isLocalSO
  109. ? location.host.substr(localPrefix.length)
  110. : localPrefix + location.host;
  111. });
  112. };
  113. addWatchedTags();
  114. addMetaToggles();
  115. addFullWidth();
  116. addRedirectToSO();
  117. return this;
  118. },
  119. addAuthorQuestionsLinks: function(){
  120. let $userDetails = $('div.owner > div.user-info > div.user-details');
  121. if($userDetails.length > 0){
  122. let $postTags = $('div.post-taglist').find('a.post-tag');
  123. let tags = [];
  124. for(let i = 0; i < $postTags.length; i++){
  125. tags.push('[' + $postTags[i].href.split('/').slice(-1).pop() + ']');
  126. }
  127. let tagsUrl = tags.join('+or+');
  128. for(let i = 0; i < $userDetails.length; i++){
  129. let $userDetail = $($userDetails[i]);
  130. let $userUrl = $userDetail.find('a');
  131. let userName = $userUrl.text();
  132. let userId = $userUrl[0].href.split('/')[4];
  133. let baseSearhcUrl = 'https://ru.stackoverflow.com/search?tab=newest&q=user%3A' + userId + '+is%3Aq';
  134. let elem = '<span>? <a href="' + baseSearhcUrl + '" title="Все вопросы ' + userName + '">все</a>';
  135. if(tags.length > 0){
  136. elem += ', <a href="' + baseSearhcUrl + '+' + tagsUrl+ '" title="Вопросы ' + userName + ' с метками текущего вопроса">с такими-же метками</a>';
  137. }
  138. elem += '</span>';
  139. $(elem).insertAfter($userDetail);
  140. }
  141. }
  142. return this;
  143. },
  144. setFullWidth: function() {
  145. this.$container.add(this.$content).css({'max-width':'none'});
  146. this.$fullWidthBtn.find('a').text(this.strings.resetFullWidth);
  147. localStorage[this.keys.fooFullWidth] = 'resetFullWidth';
  148. return this;
  149. },
  150. resetFullWidth: function() {
  151. this.$container.css({'max-width': localStorage[this.keys.containerMaxWidth]});
  152. this.$content.css({'max-width': localStorage[this.keys.contentMaxWidth]});
  153. this.$fullWidthBtn.find('a').text(this.strings.setFullWidth);
  154. localStorage[this.keys.fooFullWidth] = 'setFullWidth';
  155. return this;
  156. }
  157. };