Expand Code to Fullscreen on StackExchange Site

Toggle fullscreen for code blocks on hover

As of 2024-03-01. See the latest version.

  1. // ==UserScript==
  2. // @name Expand Code to Fullscreen on StackExchange Site
  3. // @namespace http://tampermonkey.net/
  4. // @author aspen138
  5. // @version 0.1.2
  6. // @description Toggle fullscreen for code blocks on hover
  7. // @match *://*.stackexchange.com/*
  8. // @match *://*.stackoverflow.com/questions/*
  9. // @match *://superuser.com/questions/*
  10. // @match *://meta.superuser.com/questions/*
  11. // @match *://serverfault.com/questions/*
  12. // @match *://meta.serverfault.com/questions/*
  13. // @match *://askubuntu.com/questions/*
  14. // @match *://meta.askubuntu.com/questions/*
  15. // @match *://mathoverflow.net/questions/*
  16. // @match *://meta.mathoverflow.net/questions/*
  17. // @match *://*.stackexchange.com/questions/*
  18. // @match *://answers.onstartups.com/questions/*
  19. // @match *://meta.answers.onstartups.com/questions/*
  20. // @match *://stackapps.com/questions/*
  21. // @match *://*.stackoverflow.com/review/*
  22. // @match *://superuser.com/review/*
  23. // @match *://meta.superuser.com/review/*
  24. // @match *://serverfault.com/review/*
  25. // @match *://meta.serverfault.com/review/*
  26. // @match *://askubuntu.com/review/*
  27. // @match *://meta.askubuntu.com/review/*
  28. // @match *://mathoverflow.net/review/*
  29. // @match *://meta.mathoverflow.net/review/*
  30. // @match *://*.stackexchange.com/review/*
  31. // @match *://answers.onstartups.com/review/*
  32. // @match *://meta.answers.onstartups.com/review/*
  33. // @match *://stackapps.com/review/*
  34. // @match *://*.stackoverflow.com/search*
  35. // @match *://superuser.com/search*
  36. // @match *://meta.superuser.com/search*
  37. // @match *://serverfault.com/search*
  38. // @match *://meta.serverfault.com/search*
  39. // @match *://askubuntu.com/search*
  40. // @match *://meta.askubuntu.com/search*
  41. // @match *://mathoverflow.net/search*
  42. // @match *://meta.mathoverflow.net/search*
  43. // @match *://*.stackexchange.com/search*
  44. // @match *://answers.onstartups.com/search*
  45. // @match *://meta.answers.onstartups.com/search*
  46. // @match *://stackapps.com/search*
  47. // @grant none
  48. // @license MIT
  49. // ==/UserScript==
  50.  
  51.  
  52. (function() {
  53. 'use strict';
  54.  
  55. // Function to inject styles
  56. function addStyles() {
  57. const style = document.createElement('style');
  58. style.type = 'text/css';
  59. style.innerHTML = `
  60. .code-wrapper {
  61. position: relative;
  62. }
  63. .button {
  64. position: absolute;
  65. top: 0;
  66. z-index: 10;
  67. padding: 4px 8px;
  68. background-color: #eee;
  69. border: none;
  70. cursor: pointer;
  71. border-radius: 4px;
  72. font-size: 12px;
  73. display: none;
  74. }
  75. .fullscreen-btn {
  76. right: 0;
  77. }
  78. .copy-btn {
  79. right: 80px; /* Adjust based on the size of the fullscreen button */
  80. }
  81. .button:hover {
  82. background-color: #ddd;
  83. }
  84. .code-wrapper:hover .button {
  85. display: block;
  86. }
  87. .code-wrapper.fullscreen {
  88. background: white;
  89. color: black;
  90. width: 100%;
  91. height: 100%;
  92. overflow: auto;
  93. margin: 0;
  94. padding: 20px;
  95. }
  96. .code-wrapper.fullscreen .hljs {
  97. display: block;
  98. overflow-x: auto;
  99. padding: 0.5em;
  100. color: inherit;
  101. background: inherit;
  102. }
  103. `;
  104. document.head.appendChild(style);
  105. }
  106.  
  107. // Function to toggle fullscreen for the specific code block
  108. function toggleFullScreen(codeWrapper) {
  109. if (!document.fullscreenElement && codeWrapper.requestFullscreen) {
  110. codeWrapper.requestFullscreen().then(() => {
  111. codeWrapper.classList.add('fullscreen');
  112. codeWrapper.querySelector('code').classList.forEach(cls => {
  113. codeWrapper.classList.add(cls);
  114. });
  115. });
  116. } else if (document.fullscreenElement && document.exitFullscreen) {
  117. document.exitFullscreen().then(() => {
  118. codeWrapper.classList.remove('fullscreen');
  119. codeWrapper.querySelector('code').classList.forEach(cls => {
  120. codeWrapper.classList.remove(cls);
  121. });
  122. });
  123. }
  124. }
  125.  
  126. // Function to copy code to clipboard
  127. function copyToClipboard(codeWrapper) {
  128. const code = codeWrapper.querySelector('code').innerText;
  129. navigator.clipboard.writeText(code).then(() => {
  130. console.log('Code copied to clipboard!');
  131. // codeWrapper.textContent="Copied";
  132. // setTimeout( ()=>copyBtn.textContent="Copy", 1*1000);
  133.  
  134. }).catch(err => {
  135. console.error('Error copying code to clipboard: ', err);
  136. });
  137. }
  138.  
  139. // Function to create fullscreen and copy buttons for each code block
  140. function addButtons() {
  141. document.querySelectorAll('pre code').forEach((code) => {
  142. let wrapper = code.closest('.code-wrapper');
  143. if (!wrapper) {
  144. wrapper = document.createElement('div');
  145. wrapper.classList.add('code-wrapper');
  146. code.classList.forEach(cls => {
  147. if (cls !== 'hljs') {
  148. wrapper.classList.add(cls);
  149. }
  150. });
  151. code.parentNode.insertBefore(wrapper, code);
  152. wrapper.appendChild(code);
  153. }
  154.  
  155. if (!wrapper.querySelector('.fullscreen-btn')) {
  156. const fullscreenBtn = document.createElement('button');
  157. fullscreenBtn.textContent = 'Fullscreen';
  158. fullscreenBtn.classList.add('fullscreen-btn', 'button');
  159. fullscreenBtn.addEventListener('click', () => toggleFullScreen(wrapper));
  160. wrapper.appendChild(fullscreenBtn);
  161. }
  162.  
  163. // Add copy button
  164. if (!wrapper.querySelector('.copy-btn')) {
  165. const copyBtn = document.createElement('button');
  166. copyBtn.textContent = 'Copy';
  167. copyBtn.classList.add('copy-btn', 'button');
  168. copyBtn.addEventListener('click', () => copyToClipboard(wrapper));
  169. wrapper.appendChild(copyBtn);
  170. }
  171. });
  172. }
  173.  
  174. // Wait for the DOM to be fully loaded
  175. window.addEventListener('load', function() {
  176. addStyles();
  177. setTimeout(addButtons, 0);
  178. });
  179. })();