futaba lightbox

ふたばの画像表示をギャラリー風にしちゃう

  1. // ==UserScript==
  2. // @name futaba lightbox
  3. // @namespace https://github.com/himuro-majika
  4. // @description ふたばの画像表示をギャラリー風にしちゃう
  5. // @author himuro_majika
  6. // @include http://*.2chan.net/*/res/*
  7. // @include https://*.2chan.net/*/res/*
  8. // @include http://board.futakuro.com/*/res/*
  9. // @require http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js
  10. // @require https://cdn.jsdelivr.net/npm/fancybox@2.1.5/lib/jquery.mousewheel.pack.js
  11. // @require https://cdn.jsdelivr.net/npm/fancybox@2.1.5/dist/js/jquery.fancybox.js
  12. // @resource fancyboxCSS https://cdn.jsdelivr.net/npm/fancybox@2.1.5/dist/css/jquery.fancybox.css
  13. // @resource fancyboxSprite https://cdn.jsdelivr.net/npm/fancybox@2.1.5/dist/img/fancybox_sprite.png
  14. // @version 1.4.0
  15. // @grant GM_getResourceText
  16. // @grant GM_getResourceURL
  17. // @grant GM_addStyle
  18. // @license MIT
  19. // @noframes
  20. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAPUExURYv4i2PQYy2aLUe0R////zorx9oAAAAFdFJOU/////8A+7YOUwAAAElJREFUeNqUj1EOwDAIQoHn/c88bX+2fq0kRsAoUXVAfwzCttWsDWzw0kNVWd2tZ5K9gqmMZB8libt4pSg6YlO3RnTzyxePAAMAzqMDgTX8hYYAAAAASUVORK5CYII=
  21. // ==/UserScript==
  22. this.$ = this.jQuery = jQuery.noConflict(true);
  23.  
  24. (function($) {
  25. /*
  26. * 設定
  27. */
  28. // 閉じるボタンを表示する
  29. var USE_CLOSEBTN = false;
  30. // 末尾から先頭にループさせる
  31. var USE_LOOP = false;
  32. // マウスホイールでのナビゲーションを使用する
  33. var USE_MOUSEWHEEL = true;
  34. // 該当レスにスクロールする
  35. var USE_SCROLL = true;
  36. // スクロールのなめらかさ
  37. var SCROLL_DURATION = 100;
  38. // 動画の幅
  39. var VIDEO_WIDTH = 1280;
  40. // 動画の高さ
  41. var VIDEO_HEIGHT = 720;
  42. // 動画の自動再生
  43. var VIDEO_AUTOPLAY = false;
  44.  
  45.  
  46. var options;
  47. var currentidx;
  48. var reopenflag = false;
  49.  
  50. init();
  51.  
  52. function init() {
  53. // var Start = new Date().getTime();//count parsing time
  54. add_class_and_rel();
  55. add_css();
  56. setup_fancybox();
  57. setKeyDownEvent();
  58. // console.log('Parsing : '+((new Date()).getTime()-Start) +'msec');//log parsing time
  59. }
  60. // スレ内の画像にクラス、rel属性を付加する
  61. function add_class_and_rel() {
  62. var FUTAKURO = false, FUTABOARD = false;
  63. // 赤福が有効か
  64. setTimeout(function() {
  65. if ($("#akahuku_thumbnail").length) {
  66. removeAkahukuThrop();
  67. }
  68. }, 5000)
  69.  
  70. // ふたクロが有効か
  71. if ($("#master").length) { FUTAKURO = true; }
  72. // futaboardか
  73. if ($("#threadsbox").length) { FUTABOARD = true; }
  74. add_class_and_rel_Thread();
  75. add_class_and_rel_Res();
  76. observeInserted();
  77. // スレ画
  78. function add_class_and_rel_Thread() {
  79. var $sure_a = $(".thre").length ?
  80. $(".thre > a > img").parent() :
  81. $("body > form > a > img").parent();
  82. if (FUTAKURO) { // ふたクロ
  83. $sure_a = $("#master > a > img").parent();
  84. }
  85. if (FUTABOARD) { // futaboard
  86. $sure_a = $(".d7 > a > img").parent();
  87. }
  88. if($(".c9-1").length) {
  89. $sure_a = $(".c9-1").parent();
  90. }
  91. addAttr($sure_a);
  92. }
  93. // レス画像
  94. function add_class_and_rel_Res() {
  95. // var Start = new Date().getTime();//count parsing time
  96. var $res_a = $(".rtd > a > img").parent();
  97. if (FUTABOARD) { // futaboard
  98. $res_a = $(".d6 > table img").parent();
  99. }
  100. if($(".c9-10").length) {
  101. $res_a = $(".c9-10 a > img").parent();
  102. }
  103. addAttr($res_a);
  104. // console.log('Parsing : '+((new Date()).getTime()-Start) +'msec');//log parsing time
  105. }
  106. // 続きを読むで挿入される要素を監視
  107. function observeInserted() {
  108. var target = $(".thre").length ?
  109. $(".thre").get(0) :
  110. $("html > body > form[action]:not([enctype])").get(0);
  111. if (FUTABOARD) {
  112. target = $(".d6").get(0); // futaboard
  113. }
  114. if($(".c9-1").length) return;
  115. var observer = new MutationObserver(function(mutations) {
  116. var imgEle;
  117. mutations.forEach(function(mutation) {
  118. var $nodes = $(mutation.addedNodes);
  119. var $res_a_inserted = $nodes.find("td > a > img");
  120. if ($res_a_inserted.length > 0) {
  121. imgEle = $res_a_inserted;
  122. addAttr($res_a_inserted.parent());
  123. }
  124. });
  125. if (imgEle && imgEle.length > 0) {
  126. var video = getIframeVideo();
  127. if (video.length == 0 || (!VIDEO_AUTOPLAY && video[0].paused)) reopenFancybox();
  128. }
  129. });
  130. observer.observe(target, { childList: true });
  131. }
  132. // ノードにクラス、属性を付加
  133. function addAttr(node) {
  134. node.addClass("futaba_lightbox");
  135. node.attr("rel", "futaba_lightbox_gallery");
  136. // 動画
  137. node.each(function() {
  138. if ($(this).attr("href").match(/\.(webm|mp4)$/)) {
  139. $(this).addClass("fancybox.html");
  140. }
  141. })
  142. }
  143. }
  144. // 赤福操作パネル対策
  145. function removeAkahukuThrop() {
  146. var $attb = $("#akahuku_throp_thumbnail_button");
  147. if ($attb.length) {
  148. removeAttr($attb);
  149. }
  150. }
  151. // ノードからfancyboxクラス、属性を削除
  152. function removeAttr(node) {
  153. node.removeClass("futaba_lightbox");
  154. node.attr("rel", "");
  155. }
  156. // CSSを設定
  157. function add_css() {
  158. var css = GM_getResourceText("fancyboxCSS");
  159. GM_addStyle(css);
  160. var sprite = GM_getResourceURL("fancyboxSprite");
  161. GM_addStyle(
  162. "#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {" +
  163. " background-image: url(" + sprite + ");" +
  164. "}" +
  165. "#fancybox-loading div {" +
  166. " display: none;" +
  167. "}" +
  168. ".fancybox-nav {" +
  169. " background: transparent;" +
  170. " width: 45%;" +
  171. " height: 90%;" +
  172. "}" +
  173. // ふたクロ書き込みウィンドウ対応
  174. ".fancybox-opened {" +
  175. " z-index: 2000000016;" +
  176. "}" +
  177. // 画像一覧
  178. ".futaba_lightbox_image_list_overlay img {" +
  179. " margin: 0;" +
  180. " box-shadow: 0 0 15px 5px #222;" +
  181. "}" +
  182. ".futaba-lightbox-image-list-view-button:hover div {" +
  183. " visibility: visible;" +
  184. "}" +
  185. ".futaba-lightbox-image-list-view-button div {" +
  186. " visibility: hidden;" +
  187. " margin: 20px auto;" +
  188. " width: 32px;" +
  189. " height: 32px;" +
  190. " background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAACXBIWXMAAAsTAAALEwEAmpwYAAADMElEQVRIiZ2VXWhbZRyHn3NykjT9sEvV0tXWacXPihO9qDqFwdAhKluHdHrj9ELoovNCVKjWL5h4oWU3onjjqMjEiRMJIgiK4DqGboN2sxsZTrtsadOs7dYkS2tO+vOibz6XudT//+L9cd73eU7O4T1vLKqUermL+2hnNSlinOIn9lqZaivBqkAd+gnp9jEOEmOSRtrpYgPNsJuw9W11SRF/Sn/MaEA3iLL2aqN+0JLcL6LByluW4h/nNKRVFXCxN2hcuUi8B7uqROGkNl0WXu4mheXGYo/huUShj5K65wo4QpaGlY3+cgdOmUJ9OfXWgCPk04gyR2kqUcijY7tqxBFq0wWdGKABOy8IzSpoJoOaNf2cGT/XTpMGC4q3lI4+fCN+LLCB0BBzhW0RNO03YwMBk+oKjzxEpuPdbQSwwVavuvdcYX9UVpownY9wFV6wufMYf61QAN/RcT9B/ODwwIEy9zaTfjPpLClGAThesm4E+OChV+NccGiPlUw0MGzSdj4BYB9/8zIA7/F7YV2CLC3XEwCbtqkVPwBAnOZW/GCz6P9fgjpSwgsOsfbO4uUlxk06b9IZpk1KlOBeriZxERscJtb0FCcy7DJpzKQJkswAmFe5XGuwOBIHQM9Pyy7ssRblq9+M32jIpJ0l2/lFzc3zDN1gs+9a1q34DWzi0Aky/AO2NcNXoRXit7GegxFSLIAD7O3b+iGHAcgSNotOm3SYuEmRguB9Tp5+cz/nyR+02e8PyKn5c35USxrcw5PcSqMRnL3JjX5aI36L5vTzKP08SBu+/G+yElvcqXdqwLsU0fEor/AEN9NYOFIAz/RL2fiXqvtPfL3OKXKm9W36WEtL+blo4Rt9fCH6p56+DHydPpOrkXFeYyv30rp8HpWWhW9758Rud3FMb6i7BK3XFg0rrVNTg1+zg83cXYpbZQov9c927Qh1bVzVkWTS/LW1YuvIyR+Pvv4r54gRI0GaLEKVArDw4KeRlhfWrutpWt18TcqdnJ9ODhwiwzyzzDDLPAu4y/ClArCwcPBRTwMBAviwWSLLAhdJk2GxeO/qgrzExoODg42NyJEjh0uuHAb4F7CMKxAZTkkkAAAAAElFTkSuQmCC);" +
  191. "}" +
  192. ".futaba-lightbox-image-list-view-button {" +
  193. " cursor: pointer;" +
  194. " position: absolute;" +
  195. " top: 0;" +
  196. " width: 100%;" +
  197. " height: 60px;" +
  198. "}"
  199. );
  200. }
  201. // fancyboxの設定
  202. function setup_fancybox() {
  203. options = {
  204. autoSize: false,
  205. width: VIDEO_WIDTH, //動画の幅
  206. height: VIDEO_HEIGHT, //動画の高さ
  207. minWidth : 300, // 画像の最小幅
  208. margin: 15, //画像外側のスペース
  209. padding: 5, //画像内側のスペース(白枠部)
  210. openEffect: "none", //開く時のエフェクト
  211. closeEffect: "none", //閉じる時のエフェクト
  212. prevEffect: "none", //次移動時のエフェクト
  213. nextEffect: "none", //前移動時のエフェクト
  214. preload: 3, //プリロードする画像の数
  215. mouseWheel: USE_MOUSEWHEEL,
  216. closeBtn: USE_CLOSEBTN, //閉じるボタン
  217. loop: USE_LOOP, //末尾から先頭へのループ
  218. helpers: {
  219. overlay: {
  220. speedOut: 100, //閉じる時の背景のフェード時間
  221. // showEarly : false,
  222. fixed: false, //固定表示(falseでスクロール可能)
  223. css: {
  224. // "background" : "rgba(0,0,0,0.85)" //背景色
  225. "background": "none",
  226. "z-index": "2000000015"
  227. }
  228. }
  229. },
  230. // テンプレート
  231. tpl: {
  232. wrap: '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div>' +
  233. '<div title="画像一覧" class="futaba-lightbox-image-list-view-button"><div></div>' +
  234. '</div></div></div>',
  235. image: '<a href="{href}" target="_blank"><img class="fancybox-image" src="{href}" alt="" /></a>',
  236. error: '<p class="fancybox-error">画像がないよ<br>すでに削除されてるかも</p>',
  237. next: '<a title="次" class="fancybox-nav fancybox-next"><span></span></a>',
  238. prev: '<a title="前" class="fancybox-nav fancybox-prev"><span></span></a>'
  239. },
  240. // 読み込み前
  241. beforeLoad: function() {
  242. if (this.element[0].id == "akahuku_throp_thumbnail_button") {
  243. removeAkahukuThrop();
  244. return false;
  245. }
  246. },
  247. // 画像読み込み後イベント
  248. afterLoad: function(current, previous) {
  249. // 動画
  250. if (current.type == "html") {
  251. var ext = current.href.match(/\.(webm|mp4)$/)[1];
  252. var autoplay = VIDEO_AUTOPLAY ? "autoplay=''" : "";
  253. var videohtml = "<video " + autoplay + " controls='' style='width: 100%; height: 100%; background-color: #000;' class='extendWebm'>";
  254. if (ext == "webm") {
  255. videohtml += "<source src='" + current.href + "' type='video/webm'>";
  256. }
  257. videohtml += "<source src='" + current.href + "' type='video/mp4'></video>";
  258. current.content = videohtml;
  259. //デフォルトのプレイヤーを閉じる
  260. var cancelbutton = $(current.element[0]).parent().find(".cancelbk");
  261. var event = new Event("click");
  262. if (cancelbutton[0]) {
  263. cancelbutton[0].dispatchEvent(event);
  264. }
  265. }
  266. makeImageListButton();
  267.  
  268. currentidx = current.index;
  269. if (USE_SCROLL) {
  270. if (reopenflag) {
  271. reopenflag = false;
  272. } else {
  273. scrollToRes(current.href);
  274. }
  275. }
  276. }
  277. };
  278.  
  279. $(".futaba_lightbox").fancybox(options);
  280.  
  281. // ギャラリー表示中の画像を含むレスにスクロール
  282. function scrollToRes(currenthref) {
  283. var $img_a = $(".futaba_lightbox[href='" + currenthref + "']").parent();
  284. if ($img_a.length) {
  285. var img_position = $img_a.offset().top;
  286. $("html,body").animate({
  287. scrollTop: img_position
  288. }, {
  289. duration: SCROLL_DURATION,
  290. queue: false
  291. });
  292. // $("html,body").scrollTop(img_position);
  293. }
  294. }
  295. }
  296.  
  297. function getIframeVideo() {
  298. var video = $(".fancybox-opened video")
  299. return video;
  300. }
  301.  
  302. function reopenFancybox() {
  303. if ($(".fancybox-opened").length == 0) return;
  304.  
  305. var group = $(".futaba_lightbox");
  306. options.index = currentidx;
  307. reopenflag = true;
  308.  
  309. $.fancybox.open(group, options);
  310. }
  311.  
  312. function makeImageListButton() {
  313. var button = $(".futaba-lightbox-image-list-view-button");
  314. button.click(() => {
  315. showImageListView();
  316. });
  317. }
  318.  
  319. function showImageListView() {
  320. closeImageListView();
  321. $.fancybox.close();
  322. var imageList = $(".thre a img").parent().clone();
  323. imageList.attr("rel", "futaba_lightbox_image_list");
  324. // imageList.each(() => {
  325. // $(this).css({
  326. // "flex": "auto"
  327. // })
  328. // })
  329. var imageListContainer = $("<div>");
  330. imageListContainer.addClass("futaba_lightbox_image_list_container");
  331. imageListContainer.css({
  332. "width": "auto",
  333. "height": "100%",
  334. "overflow-y": "scroll",
  335. "padding": "35px 25px",
  336. "display": "flex",
  337. "flex-wrap": "wrap",
  338. "justify-content": "space-around",
  339. "align-items": "center",
  340. "row-gap": "20px",
  341. "column-gap": "20px"
  342. });
  343. imageListContainer.click((event) => {
  344. if ($(event.target)[0].className == "futaba_lightbox_image_list_container") {
  345. closeImageListView();
  346. }
  347. });
  348. imageListContainer.append(imageList);
  349.  
  350. var imageListOverLay = $("<div>");
  351. imageListOverLay.addClass("futaba_lightbox_image_list_overlay");
  352. imageListOverLay.css({
  353. "position": "fixed",
  354. "top": "0",
  355. "bottom": "0",
  356. "left": "0",
  357. "right": "0",
  358. "background": "rgba(0, 0, 0, 0.8)",
  359. "z-index": "2000000014",
  360. });
  361. imageListOverLay.append(imageListContainer);
  362.  
  363. var body = $("body");
  364. body.css({
  365. "overflow": "hidden"
  366. });
  367. body.append(imageListOverLay);
  368. }
  369.  
  370. function closeImageListView() {
  371. var listoverlay = $(".futaba_lightbox_image_list_overlay");
  372. if (listoverlay.length == 0) return;
  373. listoverlay.remove();
  374. $("body").css({
  375. "overflow": ""
  376. })
  377. }
  378.  
  379. function setKeyDownEvent() {
  380. document.addEventListener("keydown", (e) => {
  381. if (e.key == "Escape" && $(".fancybox-opened").length == 0) {
  382. closeImageListView();
  383. }
  384. });
  385. }
  386. })(jQuery);