Imgur Upload Posts Grid Layout

Arrange Imgur upload posts in a grid layout

As of 2024-11-17. See the latest version.

  1. // ==UserScript==
  2. // @name Imgur Upload Posts Grid Layout
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Arrange Imgur upload posts in a grid layout
  6. // @author Byakuran
  7. // @match https://imgur.com/a/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=imgur.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14. // Create and inject CSS
  15. const style = document.createElement('style');
  16. style.textContent = `
  17. /* These styles will only be applied when the has-min-posts class is present */
  18. .has-min-posts .UploadPost {
  19. width: auto !important;
  20. margin-left: 20px !important;
  21. }
  22.  
  23. .has-min-posts .Upload-container > :nth-child(2):not(.UploadPost) {
  24. margin-right: 40px !important;
  25. }
  26.  
  27. .has-min-posts .UploadPost-files > .PostContent.UploadPost-file {
  28. flex: 0 0 auto;
  29. width: calc(33.33% - 10px);
  30. min-width: 200px;
  31. margin: 0 !important;
  32. }
  33. .has-min-posts .UploadPost-files {
  34. display: flex;
  35. flex-wrap: wrap;
  36. gap: 10px;
  37. padding: 10px;
  38. }
  39. .has-min-posts .UploadPost-files > :first-child:not(.PostContent.UploadPost-file),
  40. .has-min-posts .UploadPost-files > :last-child:not(.PostContent.UploadPost-file) {
  41. width: 100% !important;
  42. flex: none !important;
  43. }
  44. .ImageDescription {
  45. max-height: 2.4em;
  46. overflow: hidden;
  47. position: relative;
  48. cursor: pointer;
  49. transition: max-height 0.3s ease-out;
  50. padding-right: 25px;
  51. }
  52. .ImageDescription.expanded {
  53. max-height: 1000px;
  54. }
  55. .ImageDescription::after {
  56. content: "▼";
  57. position: absolute;
  58. bottom: 0;
  59. right: 0;
  60. background: linear-gradient(90deg, transparent, #1a3c6e 20%);
  61. padding: 0 5px;
  62. color: white;
  63. }
  64. .ImageDescription.expanded::after {
  65. content: "▲";
  66. background: linear-gradient(90deg, transparent, #1a3c6e 20%);
  67. color: white;
  68. }
  69. `;
  70. document.head.appendChild(style);
  71.  
  72. function makeDescriptionsExpandable() {
  73. const descriptions = document.querySelectorAll('.ImageDescription:not([data-expandable])');
  74. descriptions.forEach(desc => {
  75. desc.setAttribute('data-expandable', 'true');
  76. desc.addEventListener('click', function() {
  77. this.classList.toggle('expanded');
  78. });
  79. });
  80. }
  81.  
  82. // Function to apply layout and make descriptions expandable
  83. function applyChanges() {
  84. const containers = document.querySelectorAll('.UploadPost-files');
  85. containers.forEach(container => {
  86. if (!container.dataset.gridApplied) {
  87. const posts = container.querySelectorAll(':scope > .PostContent.UploadPost-file');
  88. if (posts.length >= 3) { // Check for minimum 3 elements
  89. container.dataset.gridApplied = 'true';
  90. // Add class to body or a parent element to enable styles
  91. document.body.classList.add('has-min-posts');
  92. } else {
  93. container.dataset.gridApplied = 'false';
  94. document.body.classList.remove('has-min-posts');
  95. }
  96. }
  97. });
  98. makeDescriptionsExpandable();
  99. }
  100.  
  101. // Initial application
  102. applyChanges();
  103.  
  104. // Monitor for dynamic content
  105. const observer = new MutationObserver((mutations) => {
  106. mutations.forEach((mutation) => {
  107. if (mutation.addedNodes.length) {
  108. applyChanges();
  109. }
  110. });
  111. });
  112. observer.observe(document.body, {
  113. childList: true,
  114. subtree: true
  115. });
  116. })();