TEXT   272   0
   369 3.83 KB    129

Post Previewer UserScript

By Fillyanon
Created: 2022-01-30 14:21:34
Updated: 2022-01-31 15:02:31
Expiry: Never

  1. 1.
    // ==UserScript==
  2. 2.
    // @name 4chan Post Preview
  3. 3.
  4. 4.
  5. 5.
    // @grant none
  6. 6.
    // @version 1.21
  7. 7.
    // @author Fillyanon
  8. 8.
    // @description 1/30/2022, 1:37:00 PM
  9. 9.
    // ==/UserScript==
  10. 10.
     
  11. 11.
    const css = `
  12. 12.
    .colorizer-preview {
  13. 13.
    border-top: 1px solid #888;
  14. 14.
    margin: 0;
  15. 15.
    margin-top: 0.5rem;
  16. 16.
    max-height: 300px;
  17. 17.
    min-height: 80px;
  18. 18.
    min-width: 300px;
  19. 19.
    overflow-y: auto;
  20. 20.
    word-wrap: break-word;
  21. 21.
    }
  22. 22.
     
  23. 23.
    .colorizer-quote {
  24. 24.
    color: #789922;
  25. 25.
    }
  26. 26.
     
  27. 27.
    .colorizer-reference {
  28. 28.
    color: #d00;
  29. 29.
    }
  30. 30.
     
  31. 31.
    .spoiler {
  32. 32.
    background: #000;
  33. 33.
    color: #fff;
  34. 34.
    display: inline !important;
  35. 35.
    }
  36. 36.
    `;
  37. 37.
     
  38. 38.
    const processPreview = (input) => {
  39. 39.
    const patterns = [
  40. 40.
    [/\[spoiler\]([\s\S]*?)\[\/spoiler\]?/mg, "<s>$1</s>"],
  41. 41.
    [/>>(\d{8})/g, "<a class='quotelink' href='#p$1'>>>$1</a>"],
  42. 42.
    [/(\n>.*)/g, "<span class='quote'>$1</span>"],
  43. 43.
    [/\[spoiler\]([\s\S]*)/mg, "<s>$1</s>"]
  44. 44.
    ];
  45. 45.
     
  46. 46.
    const replaced = patterns.reduce((accumulator, pattern) => {
  47. 47.
    const [rgx, tag] = pattern;
  48. 48.
    return accumulator.replaceAll(rgx, tag)
  49. 49.
    }, input)
  50. 50.
     
  51. 51.
    // First replace removes the \n I added as a hack for quote-detection.
  52. 52.
    // The rest of the \n-s get converted into <br>-s
  53. 53.
    return replaced.replace(/\n/, "").replaceAll(/\n/g, "<br>")
  54. 54.
    }
  55. 55.
     
  56. 56.
    /* YANKED FROM FLAG RANDOMIZER SCRIPT */
  57. 57.
  58. 58.
     
  59. 59.
     
  60. 60.
    const creationObserver = (function () {
  61. 61.
    const observedNodes = [];
  62. 62.
    const callbacks = [];
  63. 63.
    const executeCallback = (fn, node) => {
  64. 64.
    if (observedNodes.includes(node))
  65. 65.
    return;
  66. 66.
    observedNodes.push(node);
  67. 67.
    fn(node);
  68. 68.
    };
  69. 69.
    const obs = new MutationObserver(mutationRecords => {
  70. 70.
    mutationRecords.forEach(mutation => {
  71. 71.
    mutation.addedNodes.forEach(node => {
  72. 72.
    if (!(node instanceof HTMLElement))
  73. 73.
    return;
  74. 74.
    callbacks.forEach(([selector, fn]) => {
  75. 75.
    if (node.matches(selector))
  76. 76.
    executeCallback(fn, node);
  77. 77.
    node.querySelectorAll(selector).forEach(childNode => executeCallback(fn, childNode));
  78. 78.
    });
  79. 79.
    });
  80. 80.
    });
  81. 81.
    });
  82. 82.
    obs.observe(document.body, { childList: true, subtree: true });
  83. 83.
    return function (selector, fn) {
  84. 84.
    document.querySelectorAll(selector).forEach(node => executeCallback(fn, node));
  85. 85.
    callbacks.push([selector, fn]);
  86. 86.
    };
  87. 87.
    })();
  88. 88.
     
  89. 89.
    const selectors = [
  90. 90.
    'form[name="post"]', // 4chan default post
  91. 91.
    'form[name="qrPost"]', // 4chanNX quick reply form
  92. 92.
    'div#qr form', // 4chanX quick reply form
  93. 93.
    ].join(', ');
  94. 94.
     
  95. 95.
     
  96. 96.
    /* YANKING ENDS HERE */
  97. 97.
     
  98. 98.
    // Inject the style.
  99. 99.
    const style = document.createElement("style")
  100. 100.
    style.innerHTML = css
  101. 101.
    document.head.appendChild(style)
  102. 102.
     
  103. 103.
    creationObserver(selectors, form => {
  104. 104.
    if (!(form instanceof HTMLFormElement))
  105. 105.
    return;
  106. 106.
     
  107. 107.
    const textArea = form.querySelector("textarea")
  108. 108.
    const preview = document.createElement("blockquote")
  109. 109.
    preview.className = "colorizer-preview"
  110. 110.
    preview.width = textArea.width;
  111. 111.
     
  112. 112.
    // Thanks >>38150841 !
  113. 113.
    preview.addEventListener('mouseover', Main.onThreadMouseOver);
  114. 114.
    preview.addEventListener('mouseout', Main.onThreadMouseOut);
  115. 115.
     
  116. 116.
    form.appendChild(preview)
  117. 117.
     
  118. 118.
    new ResizeObserver(() => {
  119. 119.
    preview.style.width = textArea.offsetWidth + 'px';
  120. 120.
    preview.style.maxWidth = (textArea.style.width == "" ? "296px" : textArea.style.width);
  121. 121.
    }).observe(textArea)
  122. 122.
     
  123. 123.
    "input,paste".split(",").forEach(ev => textArea.addEventListener(ev, () => {
  124. 124.
    // "\n" + is a hack that simplifies the way I detect quotes.
  125. 125.
    // Probably could be done in a better way, but it doesn't much matter.
  126. 126.
    preview.innerHTML = processPreview("\n" + textArea.value);
  127. 127.
    preview.width = textArea.width;
  128. 128.
    }))
  129. 129.
    });

Fillyanon's Bookshelf

by Fillyanon

Thus passes threadly glory

by Fillyanon

Flag Randomizer Userscript

by Fillyanon

Post Previewer UserScript

by Fillyanon

MLPFicReviews Extras

by Fillyanon