GreasyFork Code: Syntax Highlight by PrismJS

To syntax highlight GreasyFork Code by PrismJS

As of 2023-12-14. See the latest version.

  1. // ==UserScript==
  2. // @name GreasyFork Code: Syntax Highlight by PrismJS
  3. // @namespace Violentmonkey Scripts
  4. // @grant none
  5. // @version 0.1.2
  6. // @author CY Fung
  7. // @description To syntax highlight GreasyFork Code by PrismJS
  8. // @run-at document-start
  9. // @inject-into page
  10. // @unwrap
  11. // @license MIT
  12. // @match https://greatest.deepsurf.us/*
  13. // @match https://sleazyfork.org/*
  14. // ==/UserScript==
  15.  
  16.  
  17. (() => {
  18. let byPass = true;
  19.  
  20. const resoruces = {
  21. 'prism-core.js': 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.js',
  22. 'prism-clike.js': 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-clike.min.js',
  23. 'prism-javascript.js': 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js',
  24. 'prism-css.js': 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js',
  25. 'prism-stylus.js': 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-stylus.min.js',
  26. 'prism.css': 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css'
  27. }
  28.  
  29. function selectAllWithinElement(element) {
  30. // Clear any current selections
  31. window.getSelection().removeAllRanges();
  32.  
  33. // Create a new range
  34. let range = document.createRange();
  35.  
  36. // Check if the element exists
  37. if (element) {
  38. // Select all content within the element
  39. range.selectNodeContents(element);
  40.  
  41. // Add the range to the selection
  42. window.getSelection().addRange(range);
  43. } else {
  44. console.error('Element not found with ID:', element);
  45. }
  46. }
  47. document.addEventListener('keydown', (e) => {
  48. if (e && e.code === 'KeyA' && e.isTrusted && (e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
  49.  
  50. const target = e.target;
  51. const container = target ? target.closest('div.code-container') : null;
  52. const code = container ? container.querySelector('code') : null;
  53.  
  54. if (container && code) {
  55.  
  56. e.preventDefault();
  57. e.stopPropagation();
  58. e.stopImmediatePropagation();
  59.  
  60. setTimeout(() => {
  61. selectAllWithinElement(code);
  62. }, 1)
  63.  
  64. }
  65.  
  66. }
  67. }, true)
  68.  
  69.  
  70. const Promise = (async function () { })().constructor;
  71.  
  72.  
  73. const PromiseExternal = ((resolve_, reject_) => {
  74. const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
  75. return class PromiseExternal extends Promise {
  76. constructor(cb = h) {
  77. super(cb);
  78. if (cb === h) {
  79. /** @type {(value: any) => void} */
  80. this.resolve = resolve_;
  81. /** @type {(reason?: any) => void} */
  82. this.reject = reject_;
  83. }
  84. }
  85. };
  86. })();
  87.  
  88. const documentReady = new Promise(resolve => {
  89. Promise.resolve().then(() => {
  90. if (document.readyState !== 'loading') {
  91. resolve();
  92. } else {
  93. window.addEventListener("DOMContentLoaded", resolve, false);
  94. }
  95. });
  96. });
  97.  
  98.  
  99. async function doAction() {
  100.  
  101. await new Promise(r => setTimeout(r, 1));
  102.  
  103. document.head.appendChild(document.createElement('style')).textContent = `
  104.  
  105. .code-container{
  106. height:100vh;
  107. }
  108. .code-container .CodeMirror, .code-container textarea{
  109. height:100%;
  110. }
  111. `;
  112.  
  113. if (window.requestIdleCallback) await new Promise(r => !!window.requestIdleCallback(r));
  114. else {
  115. await new Promise(r => !!window.requestAnimationFrame(r));
  116. await new Promise(r => !!window.setTimeout(r, 170));
  117. await new Promise(r => !!window.requestAnimationFrame(r));
  118. }
  119.  
  120. byPass = false;
  121.  
  122. }
  123.  
  124.  
  125. let mgg = 0;
  126. async function mTz() {
  127. if (mgg) return;
  128. mgg = 1;
  129. documentReady.then(doAction);
  130. }
  131.  
  132. function getElementsByTagName(tag) {
  133. if (byPass) {
  134. if (tag === 'pre' || tag === 'code' || tag === 'xmp') {
  135. if (location.pathname.endsWith('/code')) {
  136. setTimeout(mTz, 100)
  137. return [];
  138. }
  139. }
  140. }
  141. return this.getElementsByTagName331(tag);
  142. }
  143.  
  144. async function onBodyHeadReadyAsync() {
  145.  
  146. if (document.body && document.head) {
  147.  
  148.  
  149. } else {
  150.  
  151. const promiseBegin = new Promise(resolve => {
  152.  
  153. let mo = new MutationObserver(() => {
  154. if (document.body && document.head) {
  155. mo.disconnect();
  156. mo.takeRecords();
  157. mo = null;
  158. resolve();
  159. }
  160.  
  161. });
  162. mo.observe(document, { subtree: true, childList: true });
  163.  
  164. });
  165.  
  166. await promiseBegin.then();
  167.  
  168. }
  169.  
  170. }
  171.  
  172.  
  173. // Load CSS
  174. function loadJS(href) {
  175.  
  176. return new Promise(resolve => {
  177.  
  178. const script = document.createElement('script');
  179. script.src = href;
  180. script.onload = () => {
  181. resolve(script);
  182. };
  183. document.head.appendChild(script);
  184.  
  185. });
  186.  
  187. }
  188.  
  189. // Load CSS
  190. function loadCSS(href) {
  191. const link = document.createElement('link');
  192. link.rel = 'stylesheet';
  193. link.href = href;
  194. document.head.appendChild(link);
  195. }
  196.  
  197. const global_css = `
  198.  
  199. html {
  200. line-height: 1.5;
  201. -webkit-text-size-adjust: 100%;
  202. -moz-tab-size: 4;
  203. -o-tab-size: 4;
  204. tab-size: 4;
  205. font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
  206. font-feature-settings: normal;
  207. font-variation-settings: normal
  208. }
  209. /*
  210. body {
  211. margin: 0;
  212. line-height: inherit
  213. }
  214.  
  215. hr {
  216. height: 0;
  217. color: inherit;
  218. border-top-width: 1px
  219. }
  220.  
  221. abbr:where([title]) {
  222. -webkit-text-decoration: underline dotted;
  223. text-decoration: underline dotted
  224. }
  225.  
  226. h1,h2,h3,h4,h5,h6 {
  227. font-size: inherit;
  228. font-weight: inherit
  229. }
  230.  
  231.  
  232. b,strong {
  233. font-weight: bolder
  234. }
  235. */
  236. .code-container code, .code-container kbd, .code-container pre, .code-container samp {
  237. font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;
  238. font-size: 1em
  239. }
  240. /*
  241. small {
  242. font-size: 80%
  243. }
  244.  
  245. sub,sup {
  246. font-size: 75%;
  247. line-height: 0;
  248. position: relative;
  249. vertical-align: baseline
  250. }
  251.  
  252. sub {
  253. bottom: -.25em
  254. }
  255.  
  256. sup {
  257. top: -.5em
  258. }
  259.  
  260. table {
  261. text-indent: 0;
  262. border-color: inherit;
  263. border-collapse: collapse
  264. }
  265.  
  266. button,input,optgroup,select,textarea {
  267. font-family: inherit;
  268. font-feature-settings: inherit;
  269. font-variation-settings: inherit;
  270. font-size: 100%;
  271. font-weight: inherit;
  272. line-height: inherit;
  273. color: inherit;
  274. margin: 0;
  275. padding: 0
  276. }
  277.  
  278. button,select {
  279. text-transform: none
  280. }
  281.  
  282. :-moz-focusring {
  283. outline: auto
  284. }
  285.  
  286. :-moz-ui-invalid {
  287. box-shadow: none
  288. }
  289.  
  290. progress {
  291. vertical-align: baseline
  292. }
  293.  
  294. ::-webkit-inner-spin-button,::-webkit-outer-spin-button {
  295. height: auto
  296. }
  297.  
  298. [type=search] {
  299. -webkit-appearance: textfield;
  300. outline-offset: -2px
  301. }
  302.  
  303. ::-webkit-search-decoration {
  304. -webkit-appearance: none
  305. }
  306.  
  307. ::-webkit-file-upload-button {
  308. -webkit-appearance: button;
  309. font: inherit
  310. }
  311.  
  312. summary {
  313. display: list-item
  314. }
  315.  
  316. blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre {
  317. margin: 0
  318. }
  319.  
  320. fieldset {
  321. margin: 0
  322. }
  323.  
  324. fieldset,legend {
  325. padding: 0
  326. }
  327.  
  328. menu,ol,ul {
  329. list-style: none;
  330. margin: 0;
  331. padding: 0
  332. }
  333. textarea {
  334. resize: vertical
  335. }
  336.  
  337. input::-moz-placeholder,textarea::-moz-placeholder {
  338. opacity: 1;
  339. color: #9ca3af
  340. }
  341.  
  342. input::placeholder,textarea::placeholder {
  343. opacity: 1;
  344. color: #9ca3af
  345. }
  346. */
  347.  
  348. #script-content > .code-container[class] {
  349. width: 100%;
  350. }
  351.  
  352. .code-container[class] {
  353. border-radius: 0;
  354. }
  355.  
  356. .code-container[class] {
  357. border-radius: 0;
  358. }
  359.  
  360. .code-container > pre:only-child{
  361. padding:0;
  362. }
  363.  
  364. code.syntax-highlighted[class] {
  365. /*
  366. font-family: ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace !important;
  367. font-size: 9pt;
  368. */
  369. font-family: monospace;
  370. font-size: 13px;
  371. font-variant-ligatures: contextual;
  372. line-height: 1.15rem;
  373. }
  374.  
  375.  
  376.  
  377.  
  378.  
  379. .hljs-comment[class], .hljs-quote[class] {
  380. font-style: inherit;
  381. color: #259789;
  382. }
  383.  
  384. .hljs-add-marker-width .marker-fixed-width[class] {
  385. user-select: none !important;
  386. width: calc(var(--hljs-marker-width, 0em) + 16px);
  387. background: #f4f4f4;
  388. padding-right: 6px;
  389. margin-right: 4px;
  390. contain: paint style;
  391. }
  392.  
  393. `;
  394.  
  395.  
  396. const cssForCodePage = /\/scripts\/\d+[^\s\/\\]*\/code(\/|$)/.test(location.href) ? `
  397.  
  398. html:not([dkkfv]) div.code-container {
  399. position: absolute !important;
  400. display: none;
  401. }
  402.  
  403. html:not([dkkfv]) div.code-container > pre{
  404. display:none;
  405. }
  406.  
  407.  
  408. html:not([dkkfv]) code:only-child {
  409. display:none;
  410. }
  411.  
  412.  
  413. .code-container,
  414. .code-container pre:only-child,
  415. .code-container pre:only-child code:only-child {
  416. max-height: calc(100vh + 4px);
  417. max-width: calc(100vw + 4px);
  418. }
  419. ` : '';
  420.  
  421.  
  422. const cssAdd = `
  423.  
  424. ${global_css}
  425.  
  426. ${cssForCodePage}
  427.  
  428. .code-container {
  429. max-width: 100%;
  430. display: inline-flex;
  431. flex-direction: column;
  432. overflow: auto;
  433. border-radius: 8px;
  434. max-height: 100%;
  435. overflow: visible;
  436. }
  437. .code-container > pre:only-child {
  438. max-width: 100%;
  439. display: inline-flex;
  440. flex-direction: column;
  441. flex-grow: 1;
  442. height: 0;
  443. }
  444. .code-container > pre:only-child > code:only-child {
  445. max-width: 100%;
  446. flex-grow: 1;
  447. height: 0;
  448. }
  449. .code-container pre code {
  450. padding: 0;
  451. font-family: Consolas;
  452. cursor: text;
  453. overflow: auto;
  454. }
  455. .code-container pre code .marker {
  456. display: inline-block;
  457. color: #636d83;
  458. text-align: right;
  459. padding-right: 20px;
  460. user-select: none;
  461. cursor: auto;
  462. }
  463.  
  464. .code-container[contenteditable]{
  465. outline: 0 !important;
  466. contain: strict;
  467. }
  468.  
  469. .code-container[contenteditable]>pre[contenteditable="false"]{
  470. contain: strict;
  471. }
  472.  
  473.  
  474.  
  475. body .token.comment {
  476. color: #259789;
  477. }
  478.  
  479.  
  480.  
  481.  
  482. body .token.punctuation{
  483. color:#700d0d;
  484. }
  485.  
  486.  
  487. body .token.variable-declaration,
  488. body .token.variable {
  489. color: #0d10cd;
  490. }
  491.  
  492. body .token.selector{
  493. color: #1373bb;
  494. }
  495.  
  496.  
  497. body .token.function {
  498. color:#da204f
  499. }
  500.  
  501.  
  502.  
  503. .language-stylus .token.variable-declaration,
  504. .language-stylus .token.variable {
  505. color: #0d10cd;
  506. }
  507.  
  508. .language-stylus .token.selector{
  509. color: #1373bb;
  510. }
  511.  
  512. .language-stylus .token.punctuation{
  513. color:#700d0d;
  514. }
  515.  
  516.  
  517. .language-stylus .token.function {
  518. color:#da204f
  519. }
  520.  
  521.  
  522.  
  523.  
  524. `;
  525.  
  526.  
  527.  
  528.  
  529. HTMLElement.prototype.getElementsByTagName331 = HTMLElement.prototype.getElementsByTagName
  530. Document.prototype.getElementsByTagName331 = Document.prototype.getElementsByTagName
  531.  
  532. HTMLElement.prototype.getElementsByTagName = getElementsByTagName
  533. Document.prototype.getElementsByTagName = getElementsByTagName
  534.  
  535.  
  536. const pScript = new PromiseExternal();
  537.  
  538. onBodyHeadReadyAsync().then(async () => {
  539.  
  540. document.head.appendChild(document.createElement('style')).textContent = `${cssAdd}`;
  541.  
  542. await loadJS(resoruces['prism-core.js']);
  543. self.Prism = self.Prism || {};
  544. self.Prism.manual = true;
  545. await loadJS(resoruces['prism-clike.js']);
  546. await loadJS(resoruces['prism-javascript.js']);
  547. await loadJS(resoruces['prism-css.js']);
  548. await loadJS(resoruces['prism-stylus.js']);
  549. loadCSS(resoruces['prism.css']);
  550.  
  551. pScript.resolve();
  552.  
  553.  
  554.  
  555.  
  556. });
  557.  
  558.  
  559. /** @param {HTMLElement} pre */
  560. async function prepareCodeAreaAsync(pre) {
  561.  
  562. for (const li of pre.querySelectorAll('li')) {
  563. li.append(document.createTextNode('\n'));
  564. }
  565.  
  566. const codeElement = document.createElement('code');
  567. // codeElement.classList.add('language-javascript');
  568. codeElement.innerHTML = pre.innerHTML;
  569.  
  570. // Clearing the original code container and appending the new one
  571. // pre.classList = '';
  572. pre.innerHTML = '';
  573. // pre.appendChild(codeElement);
  574.  
  575. // if (pre.querySelector('code')) return;
  576. const code = codeElement;
  577.  
  578. const codeContainer = pre.closest('.code-container');
  579. if (codeContainer && codeContainer.querySelector('.code-container>pre:only-child')) {
  580. // avoid selection to the outside by mouse dragging
  581. codeContainer.setAttribute('contenteditable', '');
  582. codeContainer.querySelector('.code-container>pre:only-child').setAttribute('contenteditable', 'false');
  583. }
  584.  
  585.  
  586. // let parentNode = code.parentNode;
  587. // let nextNode = code.nextSibling;
  588.  
  589. // code.remove();
  590. let parentNode = pre;
  591. let nextNode = null;
  592. await Promise.resolve().then();
  593.  
  594. // preset language
  595. /*
  596. const text = codeElement.textContent;
  597. if(/(^|\n)\s*\/\/\s+==UserScript==\s*\n/.test(text)){
  598. codeElement.classList.add('language-javascript');
  599. }else if(/(^|\n)\s*\/\*\s+==UserStyle==\s*\n/.test(text)){
  600. codeElement.classList.add('language-css');
  601. }
  602. */
  603. let className = '';
  604. if (pre.classList.contains('lang-js')) {
  605. className = 'language-javascript';
  606. codeElement.classList.add();
  607. } else if (pre.classList.contains('lang-css')) {
  608.  
  609. const text = codeElement.textContent;
  610. let m = /\n\@preprocessor\s+([-_a-zA-Z]{3,8})\s*\n/.exec(text);
  611. className = 'language-css'
  612. if (m) {
  613. const preprocessor = m[1];
  614. if (preprocessor === 'stylus') {
  615. className = 'language-stylus';
  616. } else if (preprocessor === 'uso') {
  617. className = 'language-stylus';
  618. } else if (preprocessor === 'less') {
  619. className = 'language-less';
  620. } else if (preprocessor === 'default') {
  621. className = 'language-stylus';
  622. } else {
  623. className = 'language-stylus';
  624. }
  625. }
  626.  
  627.  
  628. }
  629.  
  630.  
  631. if (!className) return;
  632.  
  633. codeElement.classList.add(className);
  634. codeElement.classList.add('syntax-highlighted')
  635.  
  636.  
  637. if(className ==='language-javascript'){
  638.  
  639.  
  640. code.innerHTML = Prism.highlight(code.textContent, Prism.languages.javascript, 'javascript');
  641. }else if(className ==='language-stylus'){
  642.  
  643. code.innerHTML = Prism.highlight(code.textContent, Prism.languages.stylus, 'stylus');
  644. }else if(className ==='language-less'){
  645.  
  646. code.innerHTML = Prism.highlight(code.textContent, Prism.languages.less, 'less');
  647.  
  648. }else{
  649.  
  650. code.innerHTML = Prism.highlight(code.textContent, Prism.languages.css, 'css');
  651. }
  652.  
  653.  
  654. // Highlighting
  655. //hljs.highlightElement(code);
  656.  
  657. await Promise.resolve().then();
  658.  
  659. // Adding line numbers
  660. const html = code.innerHTML || '';
  661. const htmlSplit = html ? html.split('\n') : [];
  662. const totalLines = htmlSplit.length;
  663.  
  664. if (totalLines >= 1) {
  665. code.classList.add('hljs-add-marker-width');
  666. const len = `${totalLines}`.length * 0.5;
  667. code.style.setProperty('--hljs-marker-width', `${len}em`);
  668. code.innerHTML = htmlSplit.map((n, i) => `<span class="marker marker-fixed-width">${i + 1}</span>${n}`).join('\n');
  669. } else {
  670. code.classList.remove('hljs-add-marker-width');
  671. code.style.setProperty('--hljs-marker-width', '');
  672. }
  673.  
  674. await Promise.resolve().then();
  675. parentNode.insertBefore(code, nextNode);
  676.  
  677. }
  678. documentReady.then(async () => {
  679.  
  680. if (!location.pathname.endsWith('/code')) {
  681. byPass = false;
  682. }
  683.  
  684. await pScript.then();
  685.  
  686. // Code highlighting
  687. const promises = [...document.querySelectorAll('.code-container pre.lang-js, .code-container pre.lang-css')].map(prepareCodeAreaAsync)
  688. Promise.all(promises).then(() => {
  689. setTimeout(() => {
  690. document.documentElement.setAttribute('dkkfv', '');
  691. }, 1);
  692. })
  693.  
  694. });
  695.  
  696.  
  697. })();
  698.