Greasy Fork is available in English.

GreasyFork Code: Syntax Highlight by PrismJS

To syntax highlight GreasyFork Code by PrismJS

Fra 13.01.2024. Se den seneste versjonen.

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