Sleazy Fork is available in English.

4chan Archive Image Expander

Adds inline image expansion to 4chan archives.

  1. // ==UserScript==
  2. // @name 4chan Archive Image Expander
  3. // @description Adds inline image expansion to 4chan archives.
  4. // @author Hen-Tie
  5. // @homepage http://hen-tie.tumblr.com/
  6. // @namespace https://greasyfork.org/en/users/8336
  7. // @include /https?:\/\/(desuarchive\.org|archived\.moe)\/.*\/thread\/.*/
  8. // @include /https?:\/\/thebarchive\.com\/(b|bant|talk)\/.*/
  9. // @include /https?:\/\/archive\.4plebs\.org\/.*/
  10. // @grant none
  11. // @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
  12. // @require https://greasyfork.org/scripts/39415-jquery-initialize/code/jQuery%20Initialize.js?version=258071
  13. // @icon https://i.imgur.com/80UFdoW.png
  14. // @version 1.9
  15. // ==/UserScript==
  16. var jq = jQuery.noConflict(true);
  17. (function ($) {
  18. //index all images for modal gallery
  19. var fullImage = [];
  20. var thumbnail = [];
  21. var galleryActivated = 0;
  22.  
  23. $("body").append('<modal id="aie"><div id="aie-img-controls">Image Size: <a id="aie-img-native" href="javascript:;">Native</a> | <a id="aie-img-fill" href="javascript:;">Fill</a> | <a id="aie-img-fit" href="javascript:;">Fit</a></div></modal>');
  24.  
  25. //gallery mode keypress listener
  26. $("body").on("keydown", function(e) {
  27. //g to open
  28. if (e.which == 71) {
  29. if (galleryActivated) {
  30. $('modal').show();
  31. } else {
  32. $('modal').show();
  33. $('a.thread_image_link').each(function() {
  34. fullImage.push('<img src="' + $(this).attr('href') + '" />');
  35. thumbnail.push('<img src="' + $(this).children('img').attr('src') + '" />');
  36. });
  37. $.each(fullImage, function(i) {
  38. $("modal").append(fullImage[i]);
  39. });
  40. galleryActivated = 1;
  41. }
  42. }
  43. //esc to close
  44. if (e.which == 27) {
  45. $('modal').hide();
  46. }
  47. });
  48.  
  49. //gallery image contorls
  50. $('#aie-img-native').click(function() {
  51. //$('modal img').each(function() {
  52. // $(this).css({'width':'auto','max-width':'unset'});
  53. //});
  54. imgFit(this);
  55. });
  56. function imgFit(x) {
  57. console.log(x);
  58. }
  59. $('#aie-img-fill').click(function() {
  60. $('modal img').each(function() {
  61. $(this).css({'width':'100%','max-width':'unset'});
  62. });
  63. });
  64. $('#aie-img-fit').click(function() {
  65. $('modal img').each(function() {
  66. $(this).css({'width':'unset','max-width':'100%'});
  67. });
  68. });
  69.  
  70. //catches image load event
  71. $.initialize('.init', function() {
  72. $(this).parent().removeClass('spinner');
  73. });
  74.  
  75. //insert download image links
  76. $('.post_file_filename').each(function() {
  77. $(this).attr('download',true);
  78. });
  79.  
  80. $('<style type="text/css">.spinner:before{content:"";box-sizing:border-box;position:absolute;top:50%;left:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;border-radius:50%;border-top:2px solid #CCC;border-right:2px solid transparent;animation:spinner 0.6s linear infinite}@keyframes spinner{to{transform:rotate(360deg)}}</style>').appendTo('head');
  81. $('<style type="text/css">#aie-img-controls{width:100%;background:#282a2e;padding:0 1em;line-height:2;position:fixed;}modal img{display:block;margin-top:2em;}modal{display:none;position:fixed;top:0;left:0;overflow:auto;width:100%;height:100%;background:#1d1f21;}</style>').appendTo("head");
  82.  
  83. //video settings
  84. var vidAttr = 'loop autoplay controls';
  85. var imgCSS = {
  86. 'max-width': '100%',
  87. 'height': 'auto'
  88. };
  89. var webmCSS = {
  90. 'max-width': '100%',
  91. 'height': 'auto'
  92. };
  93. //indicate webm thumbnail
  94. $('.post_file_filename[href$=".webm"]').parent().next().find('.post_image').css('border', '3px solid #5f89ac');
  95.  
  96. //prevent weird wrapping around expanded images
  97. $('.theme_default .post header').css('display', 'inline-block');
  98.  
  99. $('.thread_image_box a').on('click', function (e) {
  100. var myHref = $(this).attr('href');
  101. var myHeight = $(this).children('img').outerHeight();
  102. var myWidth = $(this).children('img').outerWidth();
  103. $(this).parent().addClass('spinner').css({
  104. 'min-height': myHeight,
  105. 'min-width': myWidth,
  106. 'position': 'relative'
  107. });
  108.  
  109. //new elements containing full size href as src
  110. var img = $('<img />').attr({
  111. 'src': myHref,
  112. 'class': 'openItem'
  113. }).css(imgCSS).load(function(){
  114. $(this).addClass('init');
  115. });
  116.  
  117. var webm = $('<video style="background-color: #222;" poster="data:image/gif,AAAA" ' + vidAttr + ' id="vid"><source type="video/webm" src="' + myHref + '"/></video>').attr('class', 'openItem').css(webmCSS);
  118. //check filetype, hide thumbnail, insert full size file
  119. if (myHref.match(/.gif$|.png$|.jpg$/g)) {
  120. e.preventDefault();
  121. $(this).fadeTo('fast', 0, function () {
  122. $(this).hide();
  123. $(this).after(img);
  124. });
  125. } else if (myHref.match(/.webm$/g)) {
  126. e.preventDefault();
  127. $(this).hide();
  128. $(this).after(webm);
  129. var vid = document.getElementById("vid");
  130. //video proven to be loadable, cancel spinner
  131. vid.ondurationchange = function() {
  132. $(this).parent().removeClass('spinner');
  133. };
  134. } else {
  135. console.log('"4chan Archive Image Expander"\nUnsupported filetype, please report.\nSee @homepage or @namespace for contact info.');
  136. }
  137. //if src is broken insert placeholder, stop infinite spinner
  138. setTimeout(function(){
  139. var checkBroken = $('.openItem');
  140. if (checkBroken.width() === 16 && checkBroken.height() === 16) {
  141. checkBroken.addClass('init').attr('src','http://via.placeholder.com/'+myWidth + 'x' + myHeight+'/1d1f21/888888?text=ERR.');
  142. console.log('broken image');
  143. } else if (checkBroken.is('video')) {
  144. //if readyState has no metadata
  145. if (vid.readyState === 0) {
  146. checkBroken.addClass('init');
  147. console.log('broken video');
  148. }
  149. } else {
  150. return false;
  151. }
  152. }, 3000);
  153. });
  154.  
  155. //on reclick, remove full size, show thumbnail again
  156. $(document).on('click', '.openItem', function () {
  157. //video not loaded or timed out, remove spinner on close
  158. if ($(this).is('video')) {
  159. $(this).parent('.spinner').removeClass('spinner');
  160. }
  161. $(this).prev().show().fadeTo(0, 1);
  162. $(this).remove();
  163. });
  164. }(jq));