@kangsicj/dcinsideenhancer 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <br><br>
6
6
 
7
- <a href="https://cdn.jsdelivr.net/npm/@kangsicj/dcinsideenhancer@latest/dist/DCInsideEnhancer.user.js"><img src="https://img.shields.io/badge/Install-v1.0.2-3b4890?style=for-the-badge&logo=tampermonkey&logoColor=white" alt="Install"></a>
7
+ <a href="https://cdn.jsdelivr.net/npm/@kangsicj/dcinsideenhancer@latest/dist/DCInsideEnhancer.user.js"><img src="https://img.shields.io/npm/v/%40kangsicj%2Fdcinsideenhancer?label=Install&color=3b4890&style=for-the-badge&logo=tampermonkey&logoColor=white&cacheSeconds=1800" alt="Install"></a>
8
8
  <img src="https://img.shields.io/badge/iPhone%20%C2%B7%20Android%20%C2%B7%20PC-supported-5865c7?style=for-the-badge" alt="iPhone · Android · PC">
9
9
  <img src="https://img.shields.io/badge/ads%20%26%20tracking-none-2e7d5b?style=for-the-badge" alt="No ads or tracking">
10
10
  <a href="https://fairy.hada.io/@jinny#support"><img src="https://img.shields.io/badge/Fairy-Support-ff6fb5?style=for-the-badge&logo=githubsponsors&logoColor=white" alt="Support on Fairy"></a>
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <br><br>
6
6
 
7
- <a href="https://cdn.jsdelivr.net/npm/@kangsicj/dcinsideenhancer@latest/dist/DCInsideEnhancer.user.js"><img src="https://img.shields.io/badge/%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-v1.0.2-3b4890?style=for-the-badge&logo=tampermonkey&logoColor=white" alt="설치하기"></a>
7
+ <a href="https://cdn.jsdelivr.net/npm/@kangsicj/dcinsideenhancer@latest/dist/DCInsideEnhancer.user.js"><img src="https://img.shields.io/npm/v/%40kangsicj%2Fdcinsideenhancer?label=%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0&color=3b4890&style=for-the-badge&logo=tampermonkey&logoColor=white&cacheSeconds=1800" alt="설치하기"></a>
8
8
  <img src="https://img.shields.io/badge/iPhone%20%C2%B7%20Android%20%C2%B7%20PC-%EC%A7%80%EC%9B%90-5865c7?style=for-the-badge" alt="iPhone · Android · PC 지원">
9
9
  <img src="https://img.shields.io/badge/%EA%B4%91%EA%B3%A0%C2%B7%EC%B6%94%EC%A0%81-%EC%97%86%EC%9D%8C-2e7d5b?style=for-the-badge" alt="광고·추적 없음">
10
10
  <a href="https://fairy.hada.io/@jinny#support"><img src="https://img.shields.io/badge/Fairy-%ED%9B%84%EC%9B%90%ED%95%98%EA%B8%B0-ff6fb5?style=for-the-badge&logo=githubsponsors&logoColor=white" alt="Fairy로 후원하기"></a>
@@ -2,7 +2,7 @@
2
2
  // @name DCInside Enhancer
3
3
  // @name:ko 디시 인핸서
4
4
  // @namespace https://github.com/kangsicj/DCInsideEnhancer
5
- // @version 1.2.0
5
+ // @version 1.2.2
6
6
  // @description DCInside filter & enhancer — mobile & PC. Block users/words/heads/notices/images & GIFs across posts, infinite list, zoom viewer, theme sync, encrypted multi-device sync.
7
7
  // @description:ko 디시인사이드 차단·편의 기능 (모바일·PC) - 유저·단어(제목/본문)·말머리·공지 차단, 같은/비슷한 이미지·움짤 모든 글 차단, 디시콘 제거, 차단 미디어 데이터 절약, 목록 자동 보충·무한 스크롤·뒤로 가기 위치 복원, 스크랩, 이미지 확대뷰 개선, 야간모드 자동 연동, 암호화 기기 간 동기화
8
8
  // @author kangsicj
@@ -2,7 +2,7 @@
2
2
  // @name DCInside Enhancer
3
3
  // @name:ko 디시 인핸서
4
4
  // @namespace https://github.com/kangsicj/DCInsideEnhancer
5
- // @version 1.2.0
5
+ // @version 1.2.2
6
6
  // @description DCInside filter & enhancer — mobile & PC. Block users/words/heads/notices/images & GIFs across posts, infinite list, zoom viewer, theme sync, encrypted multi-device sync.
7
7
  // @description:ko 디시인사이드 차단·편의 기능 (모바일·PC) - 유저·단어(제목/본문)·말머리·공지 차단, 같은/비슷한 이미지·움짤 모든 글 차단, 디시콘 제거, 차단 미디어 데이터 절약, 목록 자동 보충·무한 스크롤·뒤로 가기 위치 복원, 스크랩, 이미지 확대뷰 개선, 야간모드 자동 연동, 암호화 기기 간 동기화
8
8
  // @author kangsicj
@@ -75,7 +75,7 @@
75
75
  // 0. 설정·상수
76
76
  // =========================================================
77
77
 
78
- const SCRIPT_VERSION = '1.2.0';
78
+ const SCRIPT_VERSION = '1.2.2';
79
79
  const PROJECT_URL = 'https://github.com/kangsicj/DCInsideEnhancer';
80
80
  const DONATE_URL = 'https://fairy.hada.io/@jinny#support';
81
81
 
@@ -412,67 +412,93 @@
412
412
  html.dc-home-media-off .main-wrapping section.grid.media-group { display: none !important; }
413
413
  html.dc-footer-info-off footer.footer > .ft-btm { display: none !important; }
414
414
 
415
- html.dc-pc-fit { --dc-fit-gap: clamp(12px, 2vw, 32px); }
416
- html.dc-pc-fit body,
417
- html.dc-pc-fit .dcwrap { min-width: 0 !important; }
418
- html.dc-pc-fit .dcwrap .dchead,
419
- html.dc-pc-fit .dcwrap .gnb,
420
- html.dc-pc-fit .dcwrap .gnb_depth2_list,
421
- html.dc-pc-fit .dcwrap .info_policy,
422
- html.dc-pc-fit .dcwrap .copyright,
423
- html.dc-pc-fit .dcwrap .visit_bookmark {
424
- width: auto !important;
425
- max-width: none !important;
426
- margin-left: var(--dc-fit-gap) !important;
427
- margin-right: var(--dc-fit-gap) !important;
428
- }
429
-
430
- html.dc-pc-fit .dcwrap .gnb_bar { min-width: 0 !important; width: auto !important; margin: 0 !important; }
431
- html.dc-pc-fit .dcwrap .gnb_list,
432
- html.dc-pc-fit .dcwrap .dc_all { width: auto !important; }
433
- html.dc-pc-fit .dcwrap .wrap_inner { width: auto !important; }
434
- html.dc-pc-fit .dcwrap #container {
435
- width: auto !important;
436
- margin-left: var(--dc-fit-gap) !important;
437
- margin-right: var(--dc-fit-gap) !important;
438
- }
439
- html.dc-pc-fit .dcwrap #container:has(> .left_content),
440
- html.dc-pc-fit .dcwrap #container > .main_content:has(> .left_content) {
441
- display: flex;
442
- flex-wrap: wrap;
443
- align-items: flex-start;
444
- gap: 20px;
445
- width: auto !important;
446
- }
447
- html.dc-pc-fit .dcwrap #container:has(> .left_content)::before,
448
- html.dc-pc-fit .dcwrap #container:has(> .left_content)::after,
449
- html.dc-pc-fit .dcwrap .main_content:has(> .left_content)::before,
450
- html.dc-pc-fit .dcwrap .main_content:has(> .left_content)::after { display: none !important; }
451
- html.dc-pc-fit .dcwrap #container:has(> .left_content) > :not(.left_content):not(.right_content),
452
- html.dc-pc-fit .dcwrap .main_content:has(> .left_content) > :not(.left_content):not(.right_content) {
453
- flex: 0 0 100%;
454
- float: none !important;
455
- }
456
- html.dc-pc-fit .dcwrap :is(#container, .main_content) > .left_content {
457
- float: none !important;
458
- flex: 1 1 0;
459
- width: auto !important;
460
- min-width: 0;
461
- }
462
- html.dc-pc-fit .dcwrap :is(#container, .main_content) > .right_content {
463
- float: none !important;
464
- flex: 0 0 300px;
465
- margin-left: 0 !important;
466
- }
467
- html.dc-pc-fit .dcwrap .issuebox,
468
- html.dc-pc-fit .dcwrap .newvisit_history,
469
- html.dc-pc-fit .dcwrap .trendalign { width: auto !important; }
470
- html.dc-pc-fit .dcwrap .list_array_option { width: 100% !important; }
471
- html.dc-pc-fit .dcwrap .stickyunit { display: none !important; }
472
- html.dc-pc-fit .dcwrap .darkmodebox .pop_tipbox { left: auto !important; right: 0 !important; }
473
-
474
- @media (max-width: 1240px) {
475
- html.dc-pc-fit .dcwrap :is(#container, .main_content) > .right_content { display: none !important; }
415
+ @media (max-width: 1519.98px) {
416
+ html.dc-pc-fit { --dc-fit-gap: clamp(10px, 1.6vw, 24px); }
417
+ html.dc-pc-fit body,
418
+ html.dc-pc-fit .dcwrap { min-width: 0 !important; }
419
+ html.dc-pc-fit body { overflow-x: hidden; }
420
+ html.dc-pc-fit .dcwrap .dchead,
421
+ html.dc-pc-fit .dcwrap .gnb,
422
+ html.dc-pc-fit .dcwrap .gnb_depth2_list,
423
+ html.dc-pc-fit .dcwrap .info_policy,
424
+ html.dc-pc-fit .dcwrap .copyright,
425
+ html.dc-pc-fit .dcwrap .visit_bookmark {
426
+ width: auto !important;
427
+ max-width: none !important;
428
+ margin-left: var(--dc-fit-gap) !important;
429
+ margin-right: var(--dc-fit-gap) !important;
430
+ }
431
+
432
+ html.dc-pc-fit .dcwrap .gnb_bar { min-width: 0 !important; width: auto !important; margin: 0 !important; }
433
+ html.dc-pc-fit .dcwrap .gnb_list,
434
+ html.dc-pc-fit .dcwrap .dc_all,
435
+ html.dc-pc-fit .dcwrap .wrap_inner { width: auto !important; }
436
+ html.dc-pc-fit .dcwrap #container {
437
+ width: auto !important;
438
+ margin-left: var(--dc-fit-gap) !important;
439
+ margin-right: var(--dc-fit-gap) !important;
440
+ }
441
+
442
+ html.dc-pc-fit .dcwrap :is(#container, .main_content, #bottom_listwrap) > .right_content { display: none !important; }
443
+ html.dc-pc-fit .dcwrap :is(#container, .main_content, #bottom_listwrap) > .left_content {
444
+ float: none !important;
445
+ width: auto !important;
446
+ max-width: none !important;
447
+ min-width: 0;
448
+ }
449
+ html.dc-pc-fit .dcwrap .main_content { width: auto !important; }
450
+
451
+ html.dc-pc-fit .dcwrap :is(.view_content_wrap, .gallview_head, .writing_view_box, .view_comment, .comment_wrap,
452
+ .comment_box, .cmt_write_box, #bottom_listwrap, .listwrap, .gall_listwrap, .page_head, .issuebox,
453
+ .newvisit_history, .trendalign, .bottom_paging_wrap, .bottom_paging_box, .bottom_search, .btn_recommend_box) {
454
+ width: auto !important;
455
+ max-width: 100% !important;
456
+ box-sizing: border-box;
457
+ }
458
+ html.dc-pc-fit .dcwrap .write_div { max-width: 100% !important; }
459
+ html.dc-pc-fit .dcwrap .write_div img,
460
+ html.dc-pc-fit .dcwrap .write_div video,
461
+ html.dc-pc-fit .dcwrap .write_div iframe { max-width: 100% !important; }
462
+ html.dc-pc-fit .dcwrap .list_array_option { width: 100% !important; }
463
+ html.dc-pc-fit .dcwrap .stickyunit { display: none !important; }
464
+ html.dc-pc-fit .dcwrap .darkmodebox .pop_tipbox { left: auto !important; right: 0 !important; }
465
+
466
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) { display: flex !important; align-items: flex-start; }
467
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info)::before,
468
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info)::after { display: none !important; }
469
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) > .addbox { display: flex; flex: 1 1 auto; min-width: 0; width: auto !important; }
470
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) > .addbox > .cmt_nickbox { flex: 0 0 auto; float: none !important; }
471
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) > .addbox > .cmt_txtbox { flex: 1 1 auto; min-width: 0; float: none !important; width: auto !important; }
472
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) .cmt_txtbox .usertxt { float: none !important; width: auto !important; max-width: 100% !important; }
473
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) .cmt_txtbox .comment_dccon { float: none !important; width: auto !important; max-width: 100% !important; }
474
+ html.dc-pc-fit .dcwrap :is(.cmt_info, .reply_info) > .fr { flex: 0 0 auto; float: none !important; margin-left: 10px; }
475
+
476
+ html.dc-pc-fit .dcwrap .writing_view_box { float: none !important; width: auto !important; }
477
+ html.dc-pc-fit .dcwrap .writing_view_box .write_div { width: auto !important; }
478
+
479
+ html.dc-pc-fit .dcwrap .bottom_paging_wrap {
480
+ display: flex !important;
481
+ justify-content: center;
482
+ align-items: center;
483
+ gap: 10px;
484
+ width: 100% !important;
485
+ padding-left: 0 !important;
486
+ padding-right: 0 !important;
487
+ box-sizing: border-box;
488
+ position: relative;
489
+ }
490
+ html.dc-pc-fit .dcwrap .bottom_paging_wrap > .bottom_paging_box { margin: 0 !important; }
491
+
492
+ html.dc-pc-fit .dcwrap .cmt_write_box > .fl { width: 142px; }
493
+ html.dc-pc-fit .dcwrap .cmt_write_box > .cmt_txt_cont {
494
+ float: right !important;
495
+ width: calc(100% - 158px) !important;
496
+ }
497
+ html.dc-pc-fit .dcwrap .cmt_write_box > .cmt_txt_cont .cmt_write,
498
+ html.dc-pc-fit .dcwrap .cmt_write_box > .cmt_txt_cont textarea,
499
+ html.dc-pc-fit .dcwrap .cmt_write_box > .cmt_txt_cont .cmt_cont_bottm { width: 100% !important; box-sizing: border-box; }
500
+
501
+ html.dc-pc-fit .dcwrap .cmt_write_box:not(:has(> .fl)) > .cmt_txt_cont { width: 100% !important; }
476
502
  }
477
503
 
478
504
  html.dc-footer-info-off .dcwrap footer.dcfoot,
@@ -1856,40 +1882,52 @@
1856
1882
 
1857
1883
  #dc-preview {
1858
1884
  position: fixed;
1885
+ top: 50%;
1886
+ left: 50%;
1859
1887
  z-index: 99997;
1860
1888
  display: flex;
1861
1889
  flex-direction: column;
1862
1890
  box-sizing: border-box;
1891
+ width: clamp(520px, 52vw, 980px);
1892
+ height: clamp(420px, 84vh, 1100px);
1893
+ max-width: calc(100vw - 32px);
1894
+ max-height: calc(100vh - 32px);
1863
1895
  border: 1px solid var(--dc-border);
1864
- border-radius: 14px;
1896
+ border-radius: 16px;
1897
+ overflow: hidden;
1865
1898
  background: var(--dc-surface);
1866
1899
  color: var(--dc-text);
1867
- box-shadow: var(--dc-shadow);
1900
+ box-shadow: 0 18px 60px rgba(0,0,0,.28), var(--dc-shadow);
1868
1901
  font-size: 14px;
1869
- line-height: 1.65;
1902
+ line-height: 1.6;
1870
1903
  opacity: 0;
1871
1904
  visibility: hidden;
1872
1905
  pointer-events: none;
1873
- transform: translateY(4px);
1874
- transition: opacity .15s ease, transform .15s ease, visibility 0s linear .15s;
1906
+ transform: translate(-50%, -50%) scale(.97);
1907
+ transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s;
1875
1908
  }
1876
1909
 
1877
1910
  #dc-preview.show {
1878
1911
  opacity: 1;
1879
1912
  visibility: visible;
1880
1913
  pointer-events: auto;
1881
- transform: none;
1882
- transition: opacity .15s ease, transform .15s ease, visibility 0s;
1914
+ transform: translate(-50%, -50%);
1915
+ transition: opacity .16s ease, transform .16s ease, visibility 0s;
1883
1916
  }
1884
1917
 
1885
1918
  #dc-preview .dc-pv-head {
1886
1919
  flex: 0 0 auto;
1887
- padding: 14px 16px 10px;
1920
+ padding: 14px 18px 10px;
1888
1921
  border-bottom: 1px solid var(--dc-border);
1922
+ background: var(--dc-surface);
1889
1923
  }
1890
1924
 
1891
1925
  #dc-preview .dc-pv-title {
1892
- font-size: 15px;
1926
+ display: -webkit-box;
1927
+ overflow: hidden;
1928
+ -webkit-line-clamp: 2;
1929
+ -webkit-box-orient: vertical;
1930
+ font-size: 16px;
1893
1931
  font-weight: 800;
1894
1932
  line-height: 1.4;
1895
1933
  word-break: break-all;
@@ -1906,21 +1944,25 @@
1906
1944
  flex-wrap: wrap;
1907
1945
  align-items: center;
1908
1946
  gap: 6px;
1909
- margin-top: 8px;
1947
+ margin-top: 9px;
1910
1948
  }
1911
1949
 
1950
+ #dc-preview .dc-pv-hint { margin-left: auto; color: var(--dc-text-dim); font-size: 11.5px; }
1951
+ #dc-preview .dc-pv-notes:empty { display: none; }
1952
+ #dc-preview .dc-pv-notes { margin-top: 6px; }
1953
+
1912
1954
  #dc-preview .dc-btn {
1913
1955
  display: inline-flex;
1914
1956
  align-items: center;
1915
1957
  justify-content: center;
1916
1958
  box-sizing: border-box;
1917
- height: 28px;
1918
- padding: 0 11px;
1959
+ height: 30px;
1960
+ padding: 0 12px;
1919
1961
  border: 1px solid var(--dc-border);
1920
1962
  border-radius: 999px;
1921
1963
  background: var(--dc-surface-2);
1922
1964
  color: var(--dc-text);
1923
- font-size: 12px;
1965
+ font-size: 12.5px;
1924
1966
  font-weight: 700;
1925
1967
  line-height: 1;
1926
1968
  text-decoration: none;
@@ -1928,43 +1970,37 @@
1928
1970
  }
1929
1971
 
1930
1972
  #dc-preview .dc-btn:hover { border-color: var(--dc-accent-ink); color: var(--dc-accent-ink); }
1973
+ #dc-preview .dc-btn.primary { border-color: var(--dc-accent); background: var(--dc-accent); color: var(--dc-on-accent); }
1974
+ #dc-preview .dc-btn.primary:hover { color: var(--dc-on-accent); filter: brightness(1.08); }
1931
1975
  #dc-preview .dc-btn.danger-ghost { color: var(--dc-danger-ink); }
1932
- #dc-preview .dc-btn.dc-pv-close { margin-left: auto; width: 28px; padding: 0; }
1976
+ #dc-preview .dc-btn.dc-pv-close { width: 30px; padding: 0; font-size: 14px; }
1933
1977
 
1934
1978
  #dc-preview .dc-pv-body {
1979
+ position: relative;
1980
+ display: flex;
1981
+ flex-direction: column;
1935
1982
  flex: 1 1 auto;
1936
- min-height: 60px;
1937
- padding: 12px 16px 18px;
1938
- overflow-y: auto;
1939
- overscroll-behavior: contain;
1940
- word-break: break-word;
1941
- }
1942
-
1943
- #dc-preview .dc-pv-body img,
1944
- #dc-preview .dc-pv-body video { max-width: 100% !important; height: auto !important; border-radius: 6px; }
1945
- #dc-preview .dc-pv-body p { margin: 0 0 4px; }
1946
- #dc-preview .dc-pv-body { overflow-x: hidden; overflow-wrap: anywhere; }
1947
- #dc-preview .dc-pv-body .write_div,
1948
- #dc-preview .dc-pv-body [style*="width"] { width: auto !important; max-width: 100% !important; }
1949
- #dc-preview .dc-pv-body * { white-space: normal !important; }
1950
- #dc-preview .dc-pv-body pre, #dc-preview .dc-pv-body pre * { white-space: pre-wrap !important; }
1951
- #dc-preview .dc-pv-body a { color: var(--dc-accent-ink); }
1952
- #dc-preview .dc-pv-body table { max-width: 100%; }
1953
-
1954
- html.dc-dark #dc-preview .dc-pv-body [style*="background"] { background: transparent !important; }
1955
- html.dc-dark #dc-preview .dc-pv-body [style*="color: rgb(0, 0, 0)"],
1956
- html.dc-dark #dc-preview .dc-pv-body [style*="color:#000"],
1957
- html.dc-dark #dc-preview .dc-pv-body [style*="color: #000"],
1958
- html.dc-dark #dc-preview .dc-pv-body [style*="color:black"],
1959
- html.dc-dark #dc-preview .dc-pv-body font[color="#000000"] { color: inherit !important; }
1983
+ min-height: 0;
1984
+ background: var(--dc-surface);
1985
+ }
1986
+
1987
+ #dc-preview .dc-pv-frame {
1988
+ flex: 1 1 auto;
1989
+ width: 100%;
1990
+ min-height: 0;
1991
+ border: 0;
1992
+ background: var(--dc-surface);
1993
+ }
1960
1994
 
1961
1995
  #dc-preview .dc-pv-loading,
1962
1996
  #dc-preview .dc-pv-note {
1963
- padding: 10px 0;
1997
+ padding: 12px 18px;
1964
1998
  color: var(--dc-text-dim);
1965
1999
  font-size: 13px;
1966
2000
  }
1967
2001
 
2002
+ #dc-preview .dc-pv-notes .dc-pv-note { padding: 0; font-size: 12px; }
2003
+
1968
2004
  #dc-preview .dc-pv-loading::before {
1969
2005
  content: '';
1970
2006
  display: inline-block;
@@ -1985,6 +2021,7 @@
1985
2021
  flex-wrap: wrap;
1986
2022
  align-items: center;
1987
2023
  gap: 8px;
2024
+ margin: 14px 18px;
1988
2025
  padding: 10px 12px;
1989
2026
  border-radius: 10px;
1990
2027
  background: var(--dc-danger-soft);
@@ -1993,18 +2030,6 @@
1993
2030
  font-weight: 700;
1994
2031
  }
1995
2032
 
1996
- #dc-preview .dc-pv-blocked {
1997
- margin: 6px 0;
1998
- padding: 14px;
1999
- border: 1px dashed var(--dc-border);
2000
- border-radius: 8px;
2001
- color: var(--dc-text-dim);
2002
- font-size: 12px;
2003
- text-align: center;
2004
- }
2005
-
2006
- #dc-preview .dc-pv-embed { display: inline-block; margin: 6px 0; font-weight: 700; }
2007
-
2008
2033
  .dc-pv-anchor { text-decoration: underline !important; text-decoration-color: var(--dc-accent-ink) !important; }
2009
2034
 
2010
2035
  .dc-key-cursor { outline: 2px solid var(--dc-accent-ink) !important; outline-offset: -2px; background: var(--dc-accent-soft) !important; }
@@ -14296,6 +14321,10 @@
14296
14321
  let keyCursor = null;
14297
14322
 
14298
14323
  const moveKeyCursor = step => {
14324
+ if (previewOpen()) {
14325
+ closePreview();
14326
+ }
14327
+
14299
14328
  const rows = visibleListRows();
14300
14329
 
14301
14330
  if (!rows.length) {
@@ -14695,19 +14724,25 @@
14695
14724
 
14696
14725
  // =========================================================
14697
14726
 
14698
- const PREVIEW_DELAY = 450;
14727
+ const PREVIEW_DELAY = 650;
14728
+ const PREVIEW_SWITCH_DELAY = 320;
14729
+ const PREVIEW_CLOSE_DELAY = 380;
14699
14730
  const PREVIEW_CACHE_MS = 5 * 60 * 1000;
14700
14731
  const PREVIEW_CACHE_MAX = 30;
14701
14732
  const PREVIEW_TITLE_SELECTOR = 'table.gall_list td.gall_tit > a:not(.reply_numbox)';
14733
+ const PREVIEW_MOVIE_RE = /^https:\/\/gall\.dcinside\.com\/board\/movie\//i;
14702
14734
 
14703
14735
  const previewCache = new Map();
14704
14736
  let previewEl = null;
14705
14737
  let previewKey = '';
14738
+ let previewHref = '';
14706
14739
  let previewAnchor = null;
14707
14740
  let previewHoverTimer = null;
14708
14741
  let previewCloseTimer = null;
14742
+ let previewThemeObserver = null;
14709
14743
 
14710
14744
  const previewEnabled = () => IS_PC_SITE && settings.hoverPreview !== false;
14745
+ const previewOpen = () => !!previewEl && previewEl.classList.contains('show');
14711
14746
 
14712
14747
  const ensurePreviewEl = () => {
14713
14748
  if (previewEl && previewEl.isConnected) {
@@ -14723,6 +14758,7 @@
14723
14758
  '<div class="dc-pv-title"></div>' +
14724
14759
  '<div class="dc-pv-meta"></div>' +
14725
14760
  '<div class="dc-pv-actions"></div>' +
14761
+ '<div class="dc-pv-notes"></div>' +
14726
14762
  '</div>' +
14727
14763
  '<div class="dc-pv-body"></div>';
14728
14764
  previewEl.addEventListener('mouseenter', () => clearTimeout(previewCloseTimer));
@@ -14732,40 +14768,24 @@
14732
14768
  return previewEl;
14733
14769
  };
14734
14770
 
14735
- const placePreview = anchor => {
14736
- const el = ensurePreviewEl();
14737
- const rect = anchor.getBoundingClientRect();
14738
- const vw = window.innerWidth;
14739
- const vh = window.innerHeight;
14740
- const width = Math.min(640, Math.max(360, Math.round(vw * 0.42)));
14741
- const height = Math.min(Math.round(vh * 0.78), 860);
14742
-
14743
- el.style.width = `${width}px`;
14744
- el.style.maxHeight = `${height}px`;
14745
-
14746
- let left = rect.right + 16;
14747
-
14748
- if (left + width > vw - 12) {
14749
- left = Math.max(12, rect.left - width - 16);
14750
- }
14771
+ const stopPreviewTheme = () => {
14772
+ previewThemeObserver?.disconnect();
14773
+ previewThemeObserver = null;
14774
+ };
14751
14775
 
14752
- if (left + width > vw - 12) {
14753
- left = Math.max(12, vw - width - 12);
14776
+ const closePreview = ({ keepHover = false } = {}) => {
14777
+ if (!keepHover) {
14778
+ clearTimeout(previewHoverTimer);
14754
14779
  }
14755
14780
 
14756
- const top = Math.min(Math.max(12, rect.top - 60), Math.max(12, vh - height - 12));
14757
-
14758
- el.style.left = `${Math.round(left)}px`;
14759
- el.style.top = `${Math.round(top)}px`;
14760
- };
14761
-
14762
- const closePreview = () => {
14763
- clearTimeout(previewHoverTimer);
14764
14781
  clearTimeout(previewCloseTimer);
14782
+ stopPreviewTheme();
14765
14783
 
14766
14784
  if (previewEl) {
14767
14785
  previewEl.classList.remove('show');
14786
+
14768
14787
  previewEl.querySelector('.dc-pv-body').textContent = '';
14788
+ previewEl.querySelector('.dc-pv-notes').textContent = '';
14769
14789
  }
14770
14790
 
14771
14791
  previewAnchor?.classList.remove('dc-pv-anchor');
@@ -14775,57 +14795,54 @@
14775
14795
 
14776
14796
  const schedulePreviewClose = () => {
14777
14797
  clearTimeout(previewCloseTimer);
14778
- previewCloseTimer = setTimeout(closePreview, 260);
14798
+
14799
+ previewCloseTimer = setTimeout(() => closePreview({ keepHover: true }), PREVIEW_CLOSE_DELAY);
14779
14800
  };
14780
14801
 
14781
- let previewHref = '';
14802
+ const goToPreviewPost = () => {
14803
+ const href = previewHref;
14782
14804
 
14783
- const withPostReferrer = (href, fn) => {
14784
- const back = location.href;
14785
- let swapped = false;
14805
+ closePreview();
14786
14806
 
14787
- try {
14788
- const target = new URL(href, location.href);
14789
-
14790
- if (target.origin === location.origin && target.href !== back) {
14791
- history.replaceState(history.state, '', target.pathname + target.search);
14792
- swapped = true;
14793
- }
14794
- } catch {
14795
- }
14796
-
14797
- try {
14798
- return fn();
14799
- } finally {
14800
- if (swapped) {
14801
- setTimeout(() => {
14802
- try {
14803
- history.replaceState(history.state, '', back);
14804
- } catch {
14805
- }
14806
- }, 60);
14807
- }
14807
+ if (href) {
14808
+ location.href = href;
14808
14809
  }
14809
14810
  };
14810
14811
 
14811
- const cleanPreviewBody = source => withPostReferrer(previewHref, () => cleanPreviewBodyRaw(source));
14812
-
14813
- const cleanPreviewBodyRaw = source => {
14814
- const body = document.importNode(source, true);
14812
+ const cleanPreviewBody = source => {
14813
+ const body = source.cloneNode(true);
14815
14814
  let hiddenMedia = 0;
14816
14815
 
14817
- body.querySelectorAll('script, style, link, iframe, object, embed, form, noscript, .dc-ui').forEach(node => {
14818
- if (node.tagName === 'IFRAME') {
14819
- const src = node.getAttribute('src') || '';
14820
- const link = document.createElement('a');
14816
+ body.removeAttribute('style');
14821
14817
 
14822
- link.href = src;
14823
- link.textContent = '🎬 삽입된 동영상 열기';
14824
- link.className = 'dc-pv-embed';
14825
- node.replaceWith(link);
14826
- } else {
14827
- node.remove();
14818
+ body.querySelectorAll('script, style, link, object, embed, form, noscript, .dc-ui, #zzbang_div').forEach(node => node.remove());
14819
+
14820
+ body.querySelectorAll('iframe').forEach(node => {
14821
+ let src = node.getAttribute('src') || '';
14822
+
14823
+ try {
14824
+ src = new URL(src, 'https://gall.dcinside.com/').href;
14825
+ } catch {
14826
+ src = '';
14827
+ }
14828
+
14829
+ if (PREVIEW_MOVIE_RE.test(src)) {
14830
+ node.setAttribute('src', src);
14831
+ node.removeAttribute('style');
14832
+ node.removeAttribute('width');
14833
+ node.removeAttribute('height');
14834
+ node.setAttribute('allowfullscreen', '');
14835
+ node.setAttribute('allow', 'autoplay; fullscreen; picture-in-picture');
14836
+ node.className = 'dc-pv-movie';
14837
+ return;
14828
14838
  }
14839
+
14840
+ const link = body.ownerDocument.createElement('a');
14841
+
14842
+ link.href = src;
14843
+ link.textContent = '🎬 삽입된 동영상 열기';
14844
+ link.className = 'dc-pv-embed';
14845
+ node.replaceWith(link);
14829
14846
  });
14830
14847
 
14831
14848
  body.querySelectorAll('*').forEach(node => {
@@ -14837,9 +14854,9 @@
14837
14854
  });
14838
14855
 
14839
14856
  body.querySelectorAll('img, video').forEach(media => {
14840
- const lazy = media.getAttribute('data-original') || media.getAttribute('data-src');
14857
+ const lazy = media.getAttribute('data-original') || media.getAttribute('data-src') || media.getAttribute('data-lazy-src');
14841
14858
 
14842
- if (lazy && !media.getAttribute('src')) {
14859
+ if (media.tagName === 'IMG' && lazy && /^https?:\/\//.test(lazy)) {
14843
14860
  media.setAttribute('src', lazy);
14844
14861
  }
14845
14862
 
@@ -14853,10 +14870,8 @@
14853
14870
  return;
14854
14871
  }
14855
14872
 
14856
- const blocked = findBlockedMediaKey(resolveMediaIds(media, candidates));
14857
-
14858
- if (blocked) {
14859
- const stub = document.createElement('div');
14873
+ if (findBlockedMediaKey(resolveMediaIds(media, candidates))) {
14874
+ const stub = body.ownerDocument.createElement('div');
14860
14875
 
14861
14876
  stub.className = 'dc-pv-blocked';
14862
14877
  stub.textContent = '🚫 차단한 이미지';
@@ -14870,18 +14885,134 @@
14870
14885
  media.removeAttribute('height');
14871
14886
 
14872
14887
  if (media.tagName === 'VIDEO') {
14873
- media.setAttribute('controls', '');
14874
- media.setAttribute('preload', 'metadata');
14875
- media.removeAttribute('autoplay');
14888
+ media.setAttribute('playsinline', '');
14889
+
14890
+ if (media.hasAttribute('loop') && media.hasAttribute('muted')) {
14891
+ media.setAttribute('autoplay', '');
14892
+ media.muted = true;
14893
+ } else {
14894
+ media.removeAttribute('autoplay');
14895
+ media.setAttribute('controls', '');
14896
+ media.setAttribute('preload', 'metadata');
14897
+ }
14876
14898
  }
14877
14899
  });
14878
14900
 
14879
- body.querySelectorAll('a[href]').forEach(link => {
14880
- link.target = '_blank';
14881
- link.rel = 'noopener noreferrer';
14901
+ return { body, hiddenMedia };
14902
+ };
14903
+
14904
+ const previewFrameCss = () => {
14905
+ const root = getComputedStyle(document.documentElement);
14906
+ const token = name => root.getPropertyValue(name).trim();
14907
+ const font = getComputedStyle(document.body).fontFamily || 'sans-serif';
14908
+ const dark = document.documentElement.classList.contains('dc-dark');
14909
+
14910
+ return `
14911
+ html { color-scheme: ${dark ? 'dark' : 'light'}; }
14912
+ html, body { margin: 0; padding: 0; background: ${token('--dc-surface')}; color: ${token('--dc-text')}; }
14913
+ body { padding: 14px 18px 28px; font: 14px/1.7 ${font}; word-break: break-word; overflow-wrap: anywhere; cursor: pointer; }
14914
+ ::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-thumb { background: ${token('--dc-border')}; border-radius: 4px; }
14915
+ * { max-width: 100% !important; box-sizing: border-box; white-space: normal !important; }
14916
+ pre, pre * { white-space: pre-wrap !important; }
14917
+ [style*="width"] { width: auto !important; }
14918
+ p { margin: 0 0 4px; }
14919
+ img, video { display: block; height: auto !important; margin: 6px 0; border-radius: 6px; }
14920
+ a { color: ${token('--dc-accent-ink')}; cursor: pointer; }
14921
+ video, iframe, a, button { cursor: auto; }
14922
+ a { cursor: pointer; }
14923
+ .dc-pv-movie { display: block; width: 100% !important; aspect-ratio: 16 / 9; height: auto; border: 0; border-radius: 8px; background: #000; }
14924
+ .dc-pv-embed { display: inline-block; margin: 6px 0; font-weight: 700; }
14925
+ .dc-pv-blocked { margin: 6px 0; padding: 14px; border: 1px dashed ${token('--dc-border')}; border-radius: 8px; color: ${token('--dc-text-dim')}; font-size: 12px; text-align: center; }
14926
+ ${dark ? `[style*="background"] { background: transparent !important; }
14927
+ [style*="color: rgb(0, 0, 0)"], [style*="color:#000"], [style*="color: #000"], [style*="color:black"], font[color="#000000"] { color: inherit !important; }` : ''}
14928
+ `;
14929
+ };
14930
+
14931
+ const mountPreviewFrame = (container, data, href) => {
14932
+ const { body, hiddenMedia } = cleanPreviewBody(data.source);
14933
+ const frame = document.createElement('iframe');
14934
+
14935
+ frame.className = 'dc-pv-frame';
14936
+ frame.setAttribute('title', '글 본문');
14937
+ container.appendChild(frame);
14938
+
14939
+ const win = frame.contentWindow;
14940
+ const doc = frame.contentDocument;
14941
+
14942
+ if (!win || !doc) {
14943
+ return hiddenMedia;
14944
+ }
14945
+
14946
+ doc.open();
14947
+ doc.write('<!doctype html><html><head><meta charset="utf-8"><base target="_blank"><style id="dc-pv-css"></style></head><body></body></html>');
14948
+ doc.close();
14949
+
14950
+ try {
14951
+ const target = new URL(href, location.href);
14952
+
14953
+ win.history.replaceState(null, '', target.pathname + target.search);
14954
+ } catch {
14955
+ }
14956
+
14957
+ doc.getElementById('dc-pv-css').textContent = previewFrameCss();
14958
+ doc.body.appendChild(doc.importNode(body, true));
14959
+
14960
+ doc.querySelectorAll('video[data-src]').forEach(video => {
14961
+ const gif = video.getAttribute('data-src');
14962
+ let swapped = false;
14963
+ const toGif = () => {
14964
+ if (swapped || !video.isConnected) {
14965
+ return;
14966
+ }
14967
+
14968
+ swapped = true;
14969
+
14970
+ const img = doc.createElement('img');
14971
+
14972
+ img.src = gif;
14973
+ img.alt = '움짤';
14974
+ video.replaceWith(img);
14975
+ };
14976
+
14977
+ if (!video.canPlayType('video/mp4')) {
14978
+ toGif();
14979
+ return;
14980
+ }
14981
+
14982
+ video.addEventListener('error', toGif, true);
14983
+ video.querySelectorAll('source').forEach(source => source.addEventListener('error', toGif));
14882
14984
  });
14883
14985
 
14884
- return { body, hiddenMedia };
14986
+ doc.addEventListener('click', event => {
14987
+ const target = event.target;
14988
+
14989
+ if (target && target.closest && target.closest('a[href], video, iframe, button, input, textarea, select, summary')) {
14990
+ return;
14991
+ }
14992
+
14993
+ event.preventDefault();
14994
+ goToPreviewPost();
14995
+ });
14996
+
14997
+ doc.addEventListener('keydown', event => {
14998
+ if (event.key === 'Escape') {
14999
+ closePreview();
15000
+ }
15001
+ });
15002
+
15003
+ doc.documentElement.style.overscrollBehavior = 'contain';
15004
+
15005
+ stopPreviewTheme();
15006
+ previewThemeObserver = new MutationObserver(() => {
15007
+ const css = doc.getElementById('dc-pv-css');
15008
+
15009
+ if (css) {
15010
+ css.textContent = previewFrameCss();
15011
+ }
15012
+ });
15013
+ previewThemeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
15014
+
15015
+ return hiddenMedia;
14885
15016
  };
14886
15017
 
14887
15018
  const readPreviewDoc = (html, key) => {
@@ -14917,17 +15048,17 @@
14917
15048
 
14918
15049
  const renderPreview = (data, href, key) => {
14919
15050
  const el = ensurePreviewEl();
14920
-
14921
- previewHref = href;
14922
15051
  const titleEl = el.querySelector('.dc-pv-title');
14923
15052
  const metaEl = el.querySelector('.dc-pv-meta');
14924
15053
  const actions = el.querySelector('.dc-pv-actions');
15054
+ const notes = el.querySelector('.dc-pv-notes');
14925
15055
  const bodyEl = el.querySelector('.dc-pv-body');
14926
15056
 
15057
+ previewHref = href;
14927
15058
  actions.textContent = '';
15059
+ notes.textContent = '';
14928
15060
  bodyEl.textContent = '';
14929
- bodyEl.scrollTop = 0;
14930
- el.scrollTop = 0;
15061
+ stopPreviewTheme();
14931
15062
 
14932
15063
  if (data.missing) {
14933
15064
  titleEl.textContent = '본문을 불러올 수 없습니다';
@@ -14936,29 +15067,36 @@
14936
15067
  titleEl.textContent = `${data.head ? `[${data.head}] ` : ''}${data.title || '(제목 없음)'}`;
14937
15068
  metaEl.textContent = [data.nick && `${data.nick}${data.ip ? `(${data.ip})` : ''}`, data.date, data.stats].filter(Boolean).join(' · ');
14938
15069
 
14939
- if (data.word) {
14940
- const note = h('div', 'dc-pv-warn', `🚫 본문에 차단 단어 "${data.word}" 가 있어 가렸습니다${settings.prefetchBodies !== false ? ' · 목록에서도 가려집니다' : ''}`);
14941
- const reveal = makeButton('그래도 보기', 'mini', () => {
14942
- note.remove();
14943
- bodyEl.appendChild(cleanPreviewBody(data.source).body);
14944
- });
14945
-
14946
- note.appendChild(reveal);
14947
- bodyEl.appendChild(note);
14948
- } else {
14949
- const { body, hiddenMedia } = cleanPreviewBody(data.source);
15070
+ const showBody = () => {
15071
+ const hidden = mountPreviewFrame(bodyEl, data, href);
14950
15072
 
14951
- if (hiddenMedia) {
14952
- bodyEl.appendChild(h('div', 'dc-pv-note', `차단한 이미지 ${hiddenMedia}개를 뺐습니다`));
15073
+ if (hidden) {
15074
+ notes.appendChild(h('div', 'dc-pv-note', `차단한 이미지 ${hidden}개를 뺐습니다`));
14953
15075
  }
15076
+ };
15077
+
15078
+ if (data.word) {
15079
+ const warn = h('div', 'dc-pv-warn', `🚫 본문에 차단 단어 "${data.word}" 가 있어 가렸습니다${settings.prefetchBodies !== false ? ' · 목록에서도 가려집니다' : ''}`);
14954
15080
 
14955
- bodyEl.appendChild(body);
15081
+ warn.appendChild(makeButton('그래도 보기', 'mini', () => {
15082
+ warn.remove();
15083
+ showBody();
15084
+ }));
15085
+ bodyEl.appendChild(warn);
15086
+ } else {
15087
+ showBody();
14956
15088
  }
14957
15089
  }
14958
15090
 
14959
- const open = h('a', 'dc-btn mini', '글 열기');
15091
+ const open = h('a', 'dc-btn mini primary', '글 열기');
14960
15092
 
14961
15093
  open.href = href;
15094
+ open.addEventListener('click', event => {
15095
+ if (!event.ctrlKey && !event.metaKey && !event.shiftKey && event.button === 0) {
15096
+ event.preventDefault();
15097
+ goToPreviewPost();
15098
+ }
15099
+ });
14962
15100
  actions.appendChild(open);
14963
15101
 
14964
15102
  if (!data.missing && key) {
@@ -14992,6 +15130,8 @@
14992
15130
  }
14993
15131
  }
14994
15132
 
15133
+ actions.appendChild(h('span', 'dc-pv-hint', '본문을 누르면 글로 이동'));
15134
+
14995
15135
  const close = makeButton('✕', 'mini dc-pv-close', closePreview);
14996
15136
 
14997
15137
  close.setAttribute('aria-label', '미리보기 닫기');
@@ -15005,7 +15145,7 @@
15005
15145
 
15006
15146
  const row = anchor.closest('tr');
15007
15147
 
15008
- if (row && (row.classList.contains('dc-user-blocked') && !row.classList.contains('dc-user-revealed') || row.classList.contains('dc-head-hidden'))) {
15148
+ if (row && ((row.classList.contains('dc-user-blocked') && !row.classList.contains('dc-user-revealed')) || row.classList.contains('dc-head-hidden'))) {
15009
15149
  return;
15010
15150
  }
15011
15151
 
@@ -15019,11 +15159,11 @@
15019
15159
  previewAnchor?.classList.remove('dc-pv-anchor');
15020
15160
  previewAnchor = anchor;
15021
15161
  previewKey = key;
15162
+ previewHref = href;
15022
15163
  anchor.classList.add('dc-pv-anchor');
15023
15164
 
15024
15165
  const el = ensurePreviewEl();
15025
15166
 
15026
- placePreview(anchor);
15027
15167
  el.classList.add('show');
15028
15168
 
15029
15169
  const cached = previewCache.get(key);
@@ -15033,19 +15173,23 @@
15033
15173
  return;
15034
15174
  }
15035
15175
 
15036
- const paused = netPausedUntil();
15037
-
15176
+ stopPreviewTheme();
15038
15177
  el.querySelector('.dc-pv-title').textContent = cleanText(anchor.textContent);
15039
15178
  el.querySelector('.dc-pv-meta').textContent = '';
15040
15179
  el.querySelector('.dc-pv-actions').textContent = '';
15041
- el.querySelector('.dc-pv-body').textContent = '';
15180
+ el.querySelector('.dc-pv-notes').textContent = '';
15181
+
15182
+ const body = el.querySelector('.dc-pv-body');
15183
+ const paused = netPausedUntil();
15184
+
15185
+ body.textContent = '';
15042
15186
 
15043
15187
  if (paused) {
15044
- el.querySelector('.dc-pv-body').appendChild(h('div', 'dc-pv-note', `⏸ 디시 요청 제한 중이라 ${Math.max(1, Math.ceil((paused - Date.now()) / 60000))}분 뒤에 미리볼 수 있습니다`));
15188
+ body.appendChild(h('div', 'dc-pv-note', `⏸ 디시 요청 제한 중이라 ${Math.max(1, Math.ceil((paused - Date.now()) / 60000))}분 뒤에 미리볼 수 있습니다`));
15045
15189
  return;
15046
15190
  }
15047
15191
 
15048
- el.querySelector('.dc-pv-body').appendChild(h('div', 'dc-pv-loading', '불러오는 중…'));
15192
+ body.appendChild(h('div', 'dc-pv-loading', '불러오는 중…'));
15049
15193
 
15050
15194
  try {
15051
15195
  const html = await siteFetchText(href, { credentials: 'same-origin', cache: 'no-cache' });
@@ -15069,8 +15213,6 @@
15069
15213
  return;
15070
15214
  }
15071
15215
 
15072
- const body = el.querySelector('.dc-pv-body');
15073
-
15074
15216
  body.textContent = '';
15075
15217
  body.appendChild(h('div', 'dc-pv-note', error instanceof NetPausedError
15076
15218
  ? '⏸ 디시 요청 제한 중이라 잠시 뒤에 미리볼 수 있습니다'
@@ -15083,27 +15225,72 @@
15083
15225
  return;
15084
15226
  }
15085
15227
 
15086
- document.addEventListener('mouseover', event => {
15087
- if (!previewEnabled() || (event.pointerType && event.pointerType !== 'mouse')) {
15228
+ let hoverAnchor = null;
15229
+ let pressedAnchor = null;
15230
+
15231
+ const armHover = anchor => {
15232
+ clearTimeout(previewHoverTimer);
15233
+
15234
+ if (!previewEnabled() || anchor === pressedAnchor) {
15088
15235
  return;
15089
15236
  }
15090
15237
 
15238
+ const switching = previewOpen() && anchor !== previewAnchor;
15239
+
15240
+ previewHoverTimer = setTimeout(() => {
15241
+ if (hoverAnchor !== anchor || pressedAnchor === anchor || !anchor.isConnected) {
15242
+ return;
15243
+ }
15244
+
15245
+ clearTimeout(previewCloseTimer);
15246
+ showPreviewFor(anchor);
15247
+ }, switching ? PREVIEW_SWITCH_DELAY : PREVIEW_DELAY);
15248
+ };
15249
+
15250
+ document.addEventListener('mouseover', event => {
15091
15251
  const anchor = event.target instanceof Element ? event.target.closest(PREVIEW_TITLE_SELECTOR) : null;
15092
15252
 
15093
15253
  if (!anchor || anchor.closest('.dc-ui')) {
15094
15254
  return;
15095
15255
  }
15096
15256
 
15097
- clearTimeout(previewCloseTimer);
15257
+ if (anchor === hoverAnchor) {
15258
+ return;
15259
+ }
15098
15260
 
15099
- if (anchor === previewAnchor) {
15261
+ hoverAnchor = anchor;
15262
+
15263
+ if (anchor === previewAnchor && previewOpen()) {
15264
+ clearTimeout(previewCloseTimer);
15100
15265
  return;
15101
15266
  }
15102
15267
 
15103
- clearTimeout(previewHoverTimer);
15104
- previewHoverTimer = setTimeout(() => showPreviewFor(anchor), PREVIEW_DELAY);
15268
+ if (previewOpen()) {
15269
+ clearTimeout(previewCloseTimer);
15270
+ }
15271
+
15272
+ armHover(anchor);
15105
15273
  });
15106
15274
 
15275
+ document.addEventListener('mousemove', event => {
15276
+ if (!hoverAnchor || previewAnchor === hoverAnchor) {
15277
+ return;
15278
+ }
15279
+
15280
+ if (event.target instanceof Node && hoverAnchor.contains(event.target)) {
15281
+ armHover(hoverAnchor);
15282
+ }
15283
+ }, { passive: true });
15284
+
15285
+ document.addEventListener('mousedown', event => {
15286
+ const anchor = event.target instanceof Element ? event.target.closest(PREVIEW_TITLE_SELECTOR) : null;
15287
+
15288
+ if (anchor) {
15289
+ pressedAnchor = anchor;
15290
+ clearTimeout(previewHoverTimer);
15291
+ }
15292
+ }, true);
15293
+
15107
15294
  document.addEventListener('mouseout', event => {
15108
15295
  const anchor = event.target instanceof Element ? event.target.closest(PREVIEW_TITLE_SELECTOR) : null;
15109
15296
 
@@ -15111,30 +15298,46 @@
15111
15298
  return;
15112
15299
  }
15113
15300
 
15301
+ if (hoverAnchor === anchor) {
15302
+ hoverAnchor = null;
15303
+ }
15304
+
15305
+ if (pressedAnchor === anchor) {
15306
+ pressedAnchor = null;
15307
+ }
15308
+
15114
15309
  clearTimeout(previewHoverTimer);
15115
15310
 
15116
- if (previewEl && previewEl.classList.contains('show')) {
15311
+ if (previewOpen() && !(event.relatedTarget instanceof Node && previewEl.contains(event.relatedTarget))) {
15117
15312
  schedulePreviewClose();
15118
15313
  }
15119
15314
  });
15120
15315
 
15121
15316
  document.addEventListener('mousedown', event => {
15122
- if (previewEl && previewEl.classList.contains('show') && !(event.target instanceof Node && previewEl.contains(event.target))) {
15317
+ if (previewOpen() && !(event.target instanceof Node && previewEl.contains(event.target))) {
15123
15318
  closePreview();
15124
15319
  }
15125
15320
  }, true);
15126
15321
 
15127
15322
  window.addEventListener('scroll', () => {
15128
- if (previewEl && previewEl.classList.contains('show') && previewAnchor && !previewEl.matches(':hover')) {
15323
+ if (previewOpen() && !previewEl.matches(':hover')) {
15129
15324
  closePreview();
15130
15325
  }
15131
15326
  }, { passive: true });
15132
15327
 
15328
+ window.addEventListener('resize', () => {
15329
+ if (previewOpen()) {
15330
+ closePreview();
15331
+ }
15332
+ });
15333
+
15133
15334
  document.addEventListener('keydown', event => {
15134
- if (event.key === 'Escape' && previewEl && previewEl.classList.contains('show')) {
15335
+ if (event.key === 'Escape' && previewOpen()) {
15135
15336
  closePreview();
15136
15337
  }
15137
15338
  });
15339
+
15340
+ window.addEventListener('pagehide', closePreview);
15138
15341
  };
15139
15342
 
15140
15343
  const togglePreviewForCursor = () => {
@@ -15142,12 +15345,12 @@
15142
15345
  return false;
15143
15346
  }
15144
15347
 
15145
- if (previewEl && previewEl.classList.contains('show')) {
15348
+ if (previewOpen()) {
15146
15349
  closePreview();
15147
15350
  return true;
15148
15351
  }
15149
15352
 
15150
- const anchor = keyCursor && keyCursor.isConnected ? keyCursor.querySelector(PREVIEW_TITLE_SELECTOR.replace('table.gall_list ', '')) : null;
15353
+ const anchor = keyCursor && keyCursor.isConnected ? keyCursor.querySelector('td.gall_tit > a:not(.reply_numbox)') : null;
15151
15354
 
15152
15355
  if (!anchor) {
15153
15356
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kangsicj/dcinsideenhancer",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "DCInside filter & enhancer userscript for mobile and PC (디시인사이드 차단·편의 유저스크립트)",
5
5
  "license": "MIT",
6
6
  "author": "kangsicj",