Layouts Of MoonHouse

Mh Justice Layout V 1...

Mh Sideways Layout

Mh Gallery Layout & M...

문하우스 Simple 레이...

Mh Thomsander Layout ...

Mh White Layout V1.1

Mh LeftMenu 레이아웃 ...

MH FullPage 레이아웃 ...

MH Pointy Slider 레이...

MH_Sliding 레이아웃

사이트선택 전용 레이...

XE기본 Xedition레이아...

XE기본 Xedition레이아...

Widgets Of MoonHouse

Data Of MoonHouse

1 2 3 4 5

Layouts Of MoonHouse

    • Mh Justice Layout V 1.6 [ 2025. 06. 09 ] - Sale 제품
    • Mh Justice Layout is a one page html5 bootstrap template. This template comes with smooth a...
    • 2023.03.26
    • Mh Sideways Layout
    • [특징] •화면전환이 가로 방향으로 움직입니다. •홍보용 사이트 레이아웃에 적합합니다. •화면 좌...
    • 2022.05.16
    • Mh Gallery Layout & Mh Gallery Widget V2.2 [2022.05.22] - Sale 제품
    • [ V2.2 특징 ] •Colorbox Skin 추가 - 첫화면에 박스출력 - Auto Slide 지원 •본문 메뉴(GNB)를 ...
    • 2022.05.02
    • 문하우스 Simple 레이아웃 V 1.4 (2020.12.13 Up)
    • V1.4 수정내용(2020.12.13) 1. 코드정리 2. 자체 로그인 탑재 3. 메뉴 아래 "mh_simple 소식"은 M...
    • 2015.05.18
    • Mh Thomsander Layout V1.0
    • [특징] - 페이지안에서 부메뉴가 Box형식으로 열립니다. - 배경이미지를 슬라이드로 출력합니다. ...
    • 2019.09.12
    • Mh White Layout V1.1
    • [ V1.1 특징 ] 1. 본문 상단에 Accordions / TiltedContent 이미지박스 출력선택 - mwea.html 와 ...
    • 2019.04.04
    • Mh LeftMenu 레이아웃 V1.2
    • [특징] - Sub메뉴를 좌측 Wing영역에 배치하여 Sub메뉴의 활용도를 높였습니다. - 최대한으로 코...
    • 2019.01.07
    • MH FullPage 레이아웃 V1.2
    • 마우스 휠 한번 작동시 전체 화면 스크롤되는 FullPage 레이아웃입니다. [Tip] 페이지에 Youtube...
    • 2018.12.04
    • MH Pointy Slider 레이아웃
    • MH_Pointy-Slider 레이아웃을 공개합니다. 소스는 아래에서 제공 받아서 XE에서 작동하게끔 수정...
    • 2018.10.27
    • MH_Sliding 레이아웃
    • MH_Sliding 레이아웃을 공개합니다. 소스는 아래에서 제공 받아서 XE에서 작동하게끔 수정했습니...
    • 2018.10.20

XE Of MoonHouse

에디터에서 탭(책갈피) 형태의 콘텐츠 박스를 만드는 에디터 컴포넌트입니다. 탭 제목과 HTML 내용, 선택/비선택 탭 색상, 탭 순서, 제목/내용 폰트 크기와 색상을 지정할 수 있습니다. Mh Tab 특징Pop Up View버전 정보 다운받은 파일 압축을 풀어서 mh_tab 폴더를 ./modules/editor/components/mh_tab 에 업로더하고 관리자 설정 에디터에서 항목 체크해 주면 됩니다. 그럼 게시판 글쓰기 항목에 "탭 박스" 추가됩니다. • 탭 박스 — 본문내용에 책갈피 형태의 탭 박스가 생성됩니다. • 팝업창에서 입력 — 팝업창에서 탭의 제목과 내용을 입력 할 수 있습니다. • 에디터 창에서 입력 — 에디터 입력 창에서 이미지나 기타 항목을 추가할 수 있습니다. • 탭 순서 변경 — 탭의 순서를 임의로 바꿀 수 있습니다. • 여러 항목 선택 — 탭의 색상, 폰터 크기등을 지정 할 수 있습니다. Ver 1.3 - 2026.07.24최신기능추가개선 • 기능추가Tab 색상 — 각 Tab에 색과 배경을 지정 할 수 있습니다. • 개선Tab의 형태 지정 — Tab의 형태도 둥근 형태(기존)와 마름모 모양 중 선택 가능 합니다. • 기능추가Tab 자동전환 — Tab AutoPlay를 지원합니다. Ver 1.2 - 2026.07.17버그수정개선 • 버그수정Enter키 적용 — 에디터 편집창에서 Enter시 오류 수정 • 개선Tab 제목과 내용 영역 분리 — 에디터 편집창에서 Tab 제목과 내용 영역 사이의 구분을 확실하게 개선(안내 문구 삽입) Ver 1.1 - 2026.07.16기능추가개선 • 개선탭 형태 수정 — 탭 형태를 좀더 책갈피 모습에 가깝게 수정 했습니다. • 기능추가내용글 배경색 지정 — 내용글 박스의 배경색을 지정할 수 있습니다. Ver 1.0 - 2026.07.16첫버전 • 탭 박스 — 본문내용에 책갈피 형태의 탭 박스가 생성됩니다. • 팝업창에서 입력 — 팝업창에서 탭의 제목과 내용을 입력 할 수 있습니다. • 에디터 창에서 입력 — 에디터 입력 창에서 이미지나 기타 항목을 추가할 수 있습니다. • 탭 순서 변경 — 탭의 순서를 임의로 바꿀 수 있습니다. • 여러 항목 선택 — 탭의 색상, 폰터 크기등을 지정 할 수 있습니다. CKEditor 에서만 정상 작동 합니다. 다른 에디터에서는 문제가 있습니다. CKEditor5 의 경우는 편집 화면에서는 정상적인 출력이 되지는 않으나 본문 출력시는 청상 출력됩니다. 그 외 다른 에디터 (TinyMCE·STARIS) 같은 경우는 본문 출력도 되지 않습니다. [A.I 의 답변] CKEditor5·TinyMCE·STARIS 같은 에디터별 미리보기 스타일 차이는 각 에디터의 HTML 필터 설정(GHS, valid_elements 등) 영역이라 사이트 관리자 쪽에서 편집기 설정을 조정하지 않는 한 컴포넌트 코드로는 더 손댈 부분이 없어요.
업로드한 이미지로 다양한 전환효과의 슬라이드쇼를 만드는 에디터 컴포넌트입니다. Ver 1.2 - 2026.07.27최신버그수정 • 버그수정파일 삭제 — PopUp 에서 이미지만 추가 시의 파일들이 게시글 삭제 시 파일들 삭제가 안되던 버그 수정 Ver 1.1 - 2026.07.12버그수정개선 • 버그수정반응형 화면 전환시 이미지 사이즈 문제 - 반응형/모바일에서 그리드 전환효과가 화면 크기에 맞춰 정확히 스케일되도록 개선 (어긋남 없음) - contain 모드에서 작은 이미지가 전환 중 크게 보였다가 끝나고 작아지는 현상 수정 - 팝업에 "반응형/모바일에서도 전환효과 사용" 옵션 추가 • 개선마우스 Hover시 정지 - 이미지에 마우스 오버(PC) / 터치(모바일) 시 자동 슬라이딩 정지, 벗어나면 재개 - 전환 애니메이션 도중 hover가 시작돼도 멈춤이 확실히 유지되도록 수정 Mh Slide 특징 - 2026.07.09첫버전 • 이미지 사이즈 — 원본 이미지의 가로·세로 비율이 일정한 이미지를 업로드하면 슬라이드가 더욱 깔끔하게 출력됩니다. - 가로/세로 중 한쪽을 비워두면, 입력한 값을 기준으로 각 이미지의 원본 비율에 맞게 나머지 값이 자동으로 계산됩니다. Sample - 둘 다 비워두면 가로 100%(반응형)로 출력되며, 첫 번째 이미지의 비율에 맞춰 세로 높이가 자동 조정됩니다. - 가로·세로 사이즈 둘 다 지정해 주어야 전환 효과가 작동을 합니다. - 썸네일도 지정한 비율로 출력됩니다. • 재생 옵션 — 자동 재생, 전환 효과 주기, 랜덤 재생, 썸네일 표시등을 선택 할 수 있습니다. • 이미지 업로드 — 업로드 방식을 직접 PopUp 에서 추가하거나, 기존에 올려진 파일에서 선택 가능합니다. - 파일 선택창에서 Shift(범위 선택) 또는 Ctrl(개별 선택, Mac은 Cmd) 키를 누른 채 여러 장을 한 번에 선택하면, 선택한 순서대로 모두 추가되어 순차적으로 업로드됩니다. - 각 항목 좌측 상단의 ☰ 손잡이를 눌러 드래그하면 이미지 순서를 바꿀 수 있습니다. - PopUp 에서 추가한 이미지도 에디터 안에 파일 첨부됩니다. • 이미지 설명 — 각각의 이미지에 Caption를 달 수 있습니다. 다운받은 파일 압축을 풀어서 mh_zoom 폴더를 ./modules/editor/components/mh_slide 에 업로더하고 관리자 설정 에디터에서 항목 체크해 주면 됩니다. 그럼 게시판 글쓰기 항목에 "이미지 슬라이드" 추가됩니다.
완성도 높은 쇼핑몰 모듈이 될 수 있도록 많은 피드백 부탁드립니다. https://ddgglabs.org/ddggshop1 에서 쇼핑몰 모듈을 체험하실 수 있습니다. 현재 토스페이먼츠와 무통장입금 결제방식만 지원하나, 추후 확장 가능하도록 설계되어있고. 업데이트 예정입니다. ============================================================ ver 1.1.3(2026.06.12) - 쇼핑몰 인스턴스가 생성되기전, 구버전 콘솔이 뜨는 오류 및 파일첨부 오류 해결 ver 1.1.1(2026.06.12) - 관리자 주문 통계 페이지 +엑셀 export - 적립금(포인트) 사용기능 추가 - 주문 알림 - 배송 추적(배송조회 링크) - 상품 엑셀 일괄관리 기능 추가 - 회원 등급별 적립률 기능 추가 - 쿠폰 자동발급 + 쿠폰함 - 토스페이먼츠 결제 안정성강화 - 위시리스트(찜), 연관상품, 포토리뷰 기능추가 ver 1.0.0 (2026.06.12) - 별도 레이아웃 관리자 콘솔 구성 - 회원공개 폐쇄몰 기능 - 도서산간등 배송비 커스텀 및 상품별 배송비설정 기능 - 쿠폰기능추가 - 컬러셋 커스텀가능 ============================================================ 핵심 기능 - 상품 & 옵션 관리 색상·사이즈 등 조합형 옵션을 자유롭게 구성하고 조합별 재고·추가금액까지 관리. 대표/추가 이미지 첨부와 상세 에디터를 지원합니다. - 간편한 결제 토스페이먼츠 카드결제와 무통장입금을 기본 제공. 추후 이니시스, KCP 확장예정 - 다단계 카테고리 최대 3단계 계층형 카테고리. 헤더 hover 메뉴와 상품목록 좌측 필터로 고객이 원하는 상품을 빠르게 찾습니다. - 리뷰 & 상품문의 별점 리뷰와 비밀글 가능한 1:1 상품문의(Q&A). 판매자 답변까지 관리자에서 한 번에 처리합니다. - 반응형 지원 모바일 햄버거 메뉴·필터 오버레이·검색까지 모바일 UX 최적화. 회원은 모달 로그인으로 끊김 없이 구매합니다. 관리자 대시보드 — 매출·신규주문·품절 현황 요약 주문 관리 — 기간·상태·결제수단 필터, 송장 입력, 입금 확인, 주문 취소·환불 상품 관리 — 계단식 카테고리 선택, 옵션 조합 생성, 태그·배너 설정 쇼핑몰 설정 — 홈 화면·결제·배송·기타(로고/적립률)까지 탭으로 한 곳에 관리자 대시보드 상품관리 페이지 쇼핑몰 홈 상품 상세페이지 장바구니 모바일 반응형
2025.11.06 구충제 모듈 v2 file 39
ALL-BAN-dazole anti-parasite module for Rhymix 귀중한 서버 자원과 트래픽을 쪽쪽 빨아먹는 기생충 같은 로봇들을 차단합니다. 암 예방에 도움이 될지도 모릅니다. 1. 널리 알려진 불량 로봇 user-agent 목록이 기본 적재되어 있습니다. 불량 로봇으로 간주하는 기준은 1) 검색 노출을 통한 방문자 유입, SNS 공유, 광고 수익 등 명백한 혜택을 주지 않으면서 2) 운영자의 동의 없이 국내 홈페이지를 대량으로 크롤링하여 3) 서버 부하 증가, 해외 트래픽 요금 등의 피해를 입힌 이력이 있는 user-agent 또는 IP 대역입니다. 통계 작성이나 마케팅을 위한 데이터 수집, AI 학습 데이터 수집은 대부분 해당됩니다. (만약 특정 업체의 크롤링에 동의하셨다면 해당 user-agent나 IP 대역을 목록에서 제외하세요.) 물론 .htaccess나 서버 설정으로 차단하는 것이 훨씬 더 효율적이겠지만, 웹호스팅에서는 서버 설정을 임의로 수정하기도 어렵고, .htaccess를 수정하면 코어 업데이트시 덮어씌워질 수 있으므로 좀더 관리하기 편한 방법을 제공합니다. User-agent 목록을 수정하실 경우, 정상적인 방문자를 차단하지 않도록 조심하세요. 2. 해외 접속을 국가별로 통제할 수 있습니다. (스크린샷은 예시이며, 해당 국가들을 차단하라는 뜻이 아닙니다.) 위의 1번과 같은 user-agent 기반의 AI 봇 차단이 보편화되면서, 최근에는 일반 브라우저로 user-agent를 위장한 채 다수의 IP에서 무작위로 접속하여 서버 부하를 일으키고 해외 트래픽을 유발하는 AI 봇이 늘어나고 있습니다. 어느 나라 IP가 많이 등장하는지는 그때그때 다릅니다. 중국일 때도 있고 브라질일 때도 있고... 그렇다고 해외 접속을 모두 차단하면 해외 회원들이 불편을 겪는 것은 물론, 구글 노출과 광고에도 악영향이 있겠지요? 그래서 구충제 모듈 v2부터는 해외 특정 국가에서 접속할 경우 클라우드플레어처럼 캡챠를 표시하거나, 로그인을 강제하도록 설정할 수 있도록 개선했습니다. 즉, 특정 국가에서만 회원제 사이트로 전환하는 효과를 얻을 수 있습니다. Googlebot, bingbot, facebookexternalhit 등 널리 알려진 검색로봇이나 SNS 공유 로봇은 예외처리할 수 있습니다. 3. 클라우드 서버 IP를 차단할 수 있습니다. (스크린샷은 예시이며, 해당 업체들을 차단하라는 뜻이 아닙니다.) 국내외 클라우드의 VPS나 서버리스 상품을 사용하여 다수의 IP를 돌려쓰며 무단 크롤링하는 봇을 일괄 차단할 수 있습니다. 개발자를 위한 API 서비스를 운영하지 않는 사이트라면, 클라우드 서버 IP를 통한 접속은 대부분 봇이라고 가정해도 무방합니다. 아마존이나 구글처럼 자사의 클라우드 서비스에 사용하는 IP 대역 목록을 공식적으로 제공하는 업체는 그 정보를 활용하여 차단하고, 그렇지 않은 업체는 ASN 단위로 차단합니다. 차단된 사용자에게는 아래와 같은 화면이 표시되며, 안내 문구는 커스터마이징할 수 있습니다. 이미 대부분의 사람들이 익숙해진 클라우드플레어의 주황색 안내 화면처럼, 해외 접속자분들은 이 화면에도 곧 익숙해지지 않을까 합니다.^^ 4. 국가 및 클라우드 IP 대역 DB를 제공합니다. 국가나 기업이 사용하는 IP 대역은 종종 변경될 수 있으므로, 구충제 모듈이 사용하는 IP 대역 DB는 모듈과 별도로 종종 업데이트할 예정입니다. 한 달에 한 번 정도 IP 대역 업데이트 버튼을 클릭해 주시는 것을 추천합니다. 모든 차단 기능은 서버 부하와 트래픽을 최소화하는 방법으로 구현되어 있습니다. (차단 화면을 예쁘게 꾸밀 수도 있겠지만, 추천하지 않습니다.) 원치 않는 AI 봇을 차단하는 과정에서 오히려 더 많은 부하와 트래픽이 발생한다면 곤란하니까요. 작동 환경: 라이믹스 2.1.x (2024년 하반기 이후 릴리즈된 버전)
Rhymix - 스킨에 쓰지 않는 부분 삭제하면 속도 향상에 도움이 될까요? 스케치북 스킨 같은거에서 html 소스의 사용하지 않는 기능 부분들을 다 삭제해버리면 로딩 속도나 서버 부하에 조금 도움이 될까요? 아니면 용량이 조금 줄어드는 정도 말고는 의미가 없는건지요 from RHYMIX.ORG 게시판을 꾸미다 보면 게시판 설정을 자주 사용합니다. 그래서 설정 Wizard 버턴을 적용하여 사용하면 편하죠. 근데 위의 글 에서 처럼 부하가 걸린다고 하니, 이 부분을 해소 하고자 게시판 상단에 Wizard 설정을 출력해서 해결하는 팁 입니다. 기능상 "사용 안함"만 적용되며 Wizard를 사용 설정 할려면 해당 게시판 설정으로 이동 합니다. 사용 안함 체크하면 Wizard 버턴 사라짐, 근데 사용할려면 "사용중"체크시 게시판 설정으로 이동합니다. "위저드 사용 > 관리자만(기본)"에 체크하면 됩니다. 설정 화면은 10초후 자동으로 사라집니다. <!--// Wizard --> <block cond="$grant->manager && !$mi->wizard=='N' && !$mo"> <load target="css/wizard.css" /> <include target="_wizard.php" /> </block> <block cond="!$oDocument->isExists() && $grant->manager && !$mo"> <!--// Wizard 사용 설정 --> <include target="_wizardsetting.html" /> </block> <div class="wizard-setting-panel"> <span class="setting-label">위저드 설정</span> <span class="radio-group"> <label class="radio-label"> <input type="radio" name="wizard_opt" value="" checked="checked"|cond="$mi->wizard != 'N'" onchange="location.href='<?php echo getUrl('act', 'dispBoardAdminSkinInfo'); ?>'"> <span>사용중(✔ 설정 바로가기)</span> </label> <label class="radio-label"> <input type="radio" name="wizard_opt" value="N" checked="checked"|cond="$mi->wizard == 'N'" onchange="exeWizardUpdate(this.value)"> <span>사용 안함</span> </label> </span> </div> <style> .wizard-setting-panel { display: inline-flex; align-items: center; padding: 5px 10px; margin:5px; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; width: fit-content; } .wizard-setting-panel span { font-size:13px; } .setting-label { font-weight: 600; margin-right: 20px; color: #333; } .radio-group { display: flex; gap: 15px; } .radio-label { display: flex; align-items: center; cursor: pointer; font-size: 13px; } .radio-label input { margin-right: 5px; } </style> <script> function exeWizardUpdate(val) { if(!confirm('설정을 변경하시겠습니까?')) return; var $ = jQuery; // 1. _wizard.php가 로드한 원본 폼을 찾습니다. var $originForm = $('#bd_wizard_scroll').find('form').first(); if(!$originForm.length) { alert('원본 설정 폼을 찾을 수 없습니다. _wizard.php가 정상 로드되었는지 확인해주세요.'); return; } // 2. 폼 데이터를 복사하여 객체화합니다. var formData = $originForm.serializeArray(); // 3. 복사된 데이터에서 wizard 값만 사용자가 선택한 값으로 강제 교체합니다. var isReplaced = false; for (var i = 0; i < formData.length; i++) { if (formData[i].name === 'wizard') { formData[i].value = val; isReplaced = true; break; } } if(!isReplaced) formData.push({ name: 'wizard', value: val }); // 4. 전송 (가장 범용적인 $.post 사용) $.post(request_uri, formData, function(data) { // 성공 시 페이지를 새로고침하여 적용된 내용을 확인합니다. alert('설정이 저장되었습니다.'); location.reload(); }).fail(function() { // 오류가 나더라도 실제로는 저장되었을 수 있으므로 확인을 위해 새로고침합니다. alert('요청을 완료했습니다.'); location.reload(); }); } // 10초 후 패널 제거 setTimeout(function() { var panel = document.querySelector('.wizard-setting-panel'); if(panel) { panel.style.transition = 'opacity 0.5s'; panel.style.opacity = '0'; setTimeout(function() { panel.remove(); }, 500); } }, 10000); </script>
https://moonhouse.co.kr/xetip/588063 위의 글에서 적용된 폴딩 기능에서 좀 더 발전한 옵션을 주는 벙법입니다. <div style="float:left; border-top:1px solid #ddd; width:100%"> <!--@if($mi->default_style=='list')--><!--@else--><table class="bd_lst bd_tb_lst bd_tb common_notice"><!--@end--> <tr loop="$notice_list=>$no,$document" class="notice"> <td $list_config['no']><span class="sbtn_mini sbtn-orange">{$lang->notice}</span></td> <td scope="col" class="m_no" style="color:{$category_list[$document->get('category_srl')]->color}"|cond="$category_list[$document->get('category_srl')]->color!='transparent'"><span>{$category_list[$document->get('category_srl')]->title}</span></td> <td class="title"> <!--// 공지사항 일반 출력 --> <a cond="$mi->notice_view_slide ==''" href="{getUrl('document_srl',$document->document_srl, 'listStyle', $listStyle, 'cpage','')}"> <strong><include cond="$mi->notice_view_slide ==''" target="title.html" /></strong> </a> <!--// 공지사항 슬라이드 출력 --> <details cond="!$mi->notice_view_slide ==''" class="accordion"> <summary class="notice_fold"> <strong><include target="title.html" /></strong> </summary> <div class="slide-content"> <p style="margin-top:10px"> <span>{$document->getContent(false)}</span> <div style="text-align:right; float:left; padding-bottom:5px; width:100%"> <div style="float:right"> <a href="{getUrl('document_srl',$document->document_srl, 'listStyle', $listStyle, 'cpage','')}" class="btn"><span>공지글 보기</span></a> <!--@if($grant->write_comment && $oDocument->isEnableComment() )--> <a href="{getUrl('', 'mid', $mid, 'act','dispBoardWriteComment','document_srl',$document->document_srl)}" class="btn"><span>{$lang->cmd_comment_registration}</span></a> <!--@end--> <!--@if($document->isEditable())--> <!--@if(!( !$grant->manager && $document->getCommentCount()))--> <a href="{getUrl('', 'mid', $mid, 'act','dispBoardWrite','document_srl',$document->document_srl,'comment_srl','')}" class="btn"><span>{$lang->cmd_modify}</span></a> <!--@end--> <!--@if(!( !$grant->manager && $oDocument->getCommentCount()))--> <a href="{getUrl('', 'mid', $mid, 'act','dispBoardDelete','document_srl',$document->document_srl,'comment_srl','')}" class="btn"><span>{$lang->cmd_delete}</span></a> <!--@end--> <!--@end--> </div> </div> </p> <!-- 내용글 하단 감지용 마커 추가 --> <div class="scroll-end-marker"></div> </div> <style> summary.notice_fold::-webkit-details-marker { display: none; } summary.notice_fold { display: block; } summary.notice_fold { position: relative; padding-left: 1.2em; cursor: pointer; } summary.notice_fold::before { content: ""; position: absolute; border-top: 0.5em solid transparent; border-bottom: 0.5em solid transparent; border-left: 0.75em solid yellowgreen; top: 0.2em; left: 0; transition: .25s transform; margin-top:2px; } details[open] summary:before { transform: rotateZ(90deg); } details[open] > summary::-webkit-details-marker { transform:rotate3d(0, 0, 1, 180deg);} .slide-content { max-height:0; overflow:hidden; transition:max-height 0.5s ease-out, opacity 0.5s ease-out; opacity:0; } details[open] .slide-content { opacity:1; } .scroll-end-marker { height:1px; width:100%; } </style> </details> </td> <td cond="$list_config['nick_name']"><a href="#popup_menu_area" class="member_{$document->get('member_srl')}" onclick="return false">{$document->getNickName()}</a></td> <td cond="$list_config['regdate']">{$document->getRegdate('Y.m.d')}</td> <td cond="$list_config['last_update']">{getTimeGap($document->get('last_update'), "y.m.d")}</td> <td cond="$list_config['readed_count'] && $val->idx==-1">{$document->get('readed_count')>0?$document->get('readed_count'):'0'}</td> <td cond="$grant->manager" class="check m_no"><input type="checkbox" name="cart" value="{$document->document_srl}" class="iCheck" title="Check This Article" onclick="doAddDocumentCart(this)" checked="checked"|cond="$document->isCarted()" /></td> </tr> <!--@if($mi->default_style=='list')--><!--@else--></table><!--@end--> </div> <script cond="!$mi->notice_view_slide ==''" > document.addEventListener('DOMContentLoaded', function() { const detailsElements = document.querySelectorAll('details.accordion'); const closeTimers = new Map(); // 각 details 요소별로 타이머 관리 // Intersection Observer 설정 (긴 글용) const observerOptions = { root: null, // 뷰포트를 기준으로 관찰 rootMargin: '0px', threshold: 0.1 // 마커가 뷰포트의 10% 이상 보일 때 감지 }; const scrollEndObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { const details = entry.target.closest('details.accordion'); if (!details || !details.open) return; const slideContent = details.querySelector('.slide-content'); // "긴 글"로 판단된 경우에만 Intersection Observer 로직 적용 const viewportHeight = window.innerHeight || document.documentElement.clientHeight; if (slideContent && slideContent.scrollHeight > viewportHeight * 0.8) { if (entry.isIntersecting) { // 마커가 뷰포트에 들어왔을 때 (내용의 끝까지 스크롤됨) if (closeTimers.has(details)) { clearTimeout(closeTimers.get(details)); } // 3초 후에 닫히도록 타이머 설정 const timerId = setTimeout(() => { if (details.open) { details.open = false; const slideContent = details.querySelector('.slide-content'); if (slideContent) { slideContent.style.maxHeight = '0'; slideContent.style.opacity = '0'; } closeTimers.delete(details); setTimeout(() => { const detailsTop = details.getBoundingClientRect().top; window.scrollTo({ top: window.pageYOffset + detailsTop - 200, behavior: 'smooth', }); }, 300); } }, 3000); // 3초 = 3000ms closeTimers.set(details, timerId); } else { // 마커가 뷰포트를 벗어나면 (다시 위로 스크롤하면) 타이머 취소 if (closeTimers.has(details)) { clearTimeout(closeTimers.get(details)); closeTimers.delete(details); } } } }); }, observerOptions); let currentOpenDetails = null; let lastScrollDirection = 0; // 0: 정지, 1: 아래, -1: 위 // window 스크롤 이벤트 리스너 (짧은 글용) window.addEventListener('wheel', function(e) { if (currentOpenDetails && currentOpenDetails.open) { const slideContent = currentOpenDetails.querySelector('.slide-content'); const viewportHeight = window.innerHeight || document.documentElement.clientHeight; // "짧은 글"로 판단된 경우에만 wheel 로직 적용 if (slideContent && slideContent.scrollHeight <= viewportHeight * 0.8) { // 스크롤 방향 감지 const newScrollDirection = e.deltaY > 0 ? 1 : (e.deltaY < 0 ? -1 : 0); // 스크롤 방향이 아래쪽일 때 if (newScrollDirection === 1) { // 기존 타이머가 있다면 취소 (새로운 스크롤 이벤트 발생 시) if (closeTimers.has(currentOpenDetails)) { clearTimeout(closeTimers.get(currentOpenDetails)); } // 1초 후에 닫히도록 타이머 설정 const timerId = setTimeout(() => { if (currentOpenDetails && currentOpenDetails.open) { // 타이머 만료 시에도 열려있는지 확인 currentOpenDetails.open = false; if (slideContent) { slideContent.style.maxHeight = '0'; slideContent.style.opacity = '0'; } closeTimers.delete(currentOpenDetails); setTimeout(() => { const detailsTop = currentOpenDetails.getBoundingClientRect().top; window.scrollTo({ top: window.pageYOffset + detailsTop - 200, behavior: 'smooth', }); }, 300); currentOpenDetails = null; // 닫혔으므로 초기화 } }, 1000); // 1초 = 1000ms closeTimers.set(currentOpenDetails, timerId); } else if (newScrollDirection === -1) { // 스크롤 방향이 위쪽일 때 // 위로 스크롤하면 타이머 취소 if (closeTimers.has(currentOpenDetails)) { clearTimeout(closeTimers.get(currentOpenDetails)); closeTimers.delete(currentOpenDetails); } } } } }); detailsElements.forEach(details => { const slideContent = details.querySelector('.slide-content'); const scrollEndMarker = details.querySelector('.scroll-end-marker'); details.addEventListener('toggle', function() { // details가 닫힐 때 타이머 및 currentOpenDetails 정리 if (!this.open) { if (closeTimers.has(this)) { clearTimeout(closeTimers.get(this)); closeTimers.delete(this); } if (currentOpenDetails === this) { currentOpenDetails = null; } // 닫힐 때 Intersection Observer도 해제 if (scrollEndMarker) { scrollEndObserver.unobserve(scrollEndMarker); } } if (this.open) { currentOpenDetails = this; // 다른 details 닫기 detailsElements.forEach(otherDetails => { if (otherDetails !== this && otherDetails.open) { if (closeTimers.has(otherDetails)) { clearTimeout(closeTimers.get(otherDetails)); closeTimers.delete(otherDetails); } otherDetails.open = false; const otherSlideContent = otherDetails.querySelector('.slide-content'); if (otherSlideContent) { otherSlideContent.style.maxHeight = '0'; otherSlideContent.style.opacity = '0'; } // 다른 details가 닫힐 때 해당 Observer도 해제 const otherMarker = otherDetails.querySelector('.scroll-end-marker'); if (otherMarker) { scrollEndObserver.unobserve(otherMarker); } } }); // 슬라이딩 효과 if (slideContent) { slideContent.style.maxHeight = 'none'; const scrollHeight = slideContent.scrollHeight; slideContent.style.maxHeight = '0'; requestAnimationFrame(() => { slideContent.style.maxHeight = scrollHeight + 'px'; slideContent.style.opacity = '1'; // details가 열린 후, 내용의 길이를 판단하여 Observer 등록 const viewportHeight = window.innerHeight || document.documentElement.clientHeight; if (scrollHeight > viewportHeight * 0.8) { // 긴 글인 경우 if (scrollEndMarker) { scrollEndObserver.observe(scrollEndMarker); } } }); } // 열린 details로 스크롤 이동 requestAnimationFrame(() => { const detailsTop = this.getBoundingClientRect().top; window.scrollTo({ top: window.pageYOffset + detailsTop - 200, behavior: 'smooth', }); }); } else { // details가 닫힐 때 if (slideContent) { slideContent.style.maxHeight = '0'; slideContent.style.opacity = '0'; } } }); }); }); </script> 위의 스크립트는 공지글의 내용이 길어서 한 화면을 넘어서면 가장 아래줄에 도달시 3초가 지나면 자동으로 닫히고, 내용글이 짧아서 화면을 벗어나지 않으면 내용이 닫히지 않고, 마우스 스크롤시 1초가 지나면 닫히게 만든 스크립트 입니다. 시간은 자신에게 맞게끔 조절하면 됩니다. 별 기능이 아닌데 스크립트가 길다고 여겨지면 적용 안 하면 됩니다.(딴지 걸기 없기 ^.^) 위의 코드는 앨런 A.I가 만들어 줬습니다. 앨런 - 한국어 AI 검색엔진 | ChatGPT, Claude, Gemini, EXAONE 4개 모델 비교 최신 AI 모델 비교부터 논문·유튜브 요약까지. 한국어에 가장 강한 AI 검색엔진 앨런으로 검색, 글쓰기, 문서 작성에서 압도적인 생산성을 경험하세요. 모든 기능을 무료로 체험해보세요! from ALAN.ESTSOFT.AI
게시글 랭킹을 위젯에 추가하는 방법입니다. • 일단 위젯의 정렬대상 선택이 아래와 같이 존재 랭킹을 정할 대상을 선택 - 주천수 or 조회수 or 댓글순 • 정렬 방법을 올림차순으로 • 수정하는 위젯의 info.xml 파일에 랭킹 출력 선택 <var id="rank_no"> <name xml:lang="ko">랭킹 표시</name> <type>select</type> <description /> <options> <name xml:lang="ko">표시하지 않음</name> <value></value> </options> <options> <name xml:lang="ko">표시</name> <value>Y</value> </options> </var> • 위젯의 html 에서 아래 부분을 찾아서 위에 {@$i=1} 추가 {@$i=1} <!--@foreach($widget_info->content_items as $key => $item)--> 위젯 내용 {@$i++} <!--@end--> • 보통 제목앞에 랭킹을 붙이니 아래 부분을 찾아서 상단에 빨간색 부분 추가 <!--@if(!$wi->rank_no == '')--> <!--@if($i==1)--> <span class="rank-number rank-1">{$i}.</span> <!--@elseif($i==2)--> <span class="rank-number rank-2">{$i}.</span> <!--@elseif($i==3)--> <span class="rank-number rank-3">{$i}.</span> <!--@else--> <span class="rank-number">{$i}.</span> <!--@end--> <!--@end--> <a href="{$oDocument->getPermanentUrl()}" class="mtitle"> {$oDocument->getTitleText($wi->subject_cut_size)} </a> <style> .rank-number { font-weight:bold; margin-right:8px; font-size:1.05em;} .rank-1 { color:#FFD700; /* 금색 */ font-size:1.2em;} .rank-2 { color:#C0C0C0; /* 은색 */ font-size:1.1em;} .rank-3 { color:#CD7F32; /* 동색 */ } </style> 스타일은 자신이 원하는 모양이나 색상으로 꾸미면 됩니다.

Xe Total

게시판 Category 제목 글쓴이 날짜 조회수
모듈 첨부파일 외부 저장소 이동 file 팔공산 2026.07.27 0
애드온 자동 스크롤 애드온 수정판 updatefile 팔공산 2026.07.18 105
에디터 컴포넌트 탭 형태의 콘텐츠 박스 MH Tab file 팔공산 2026.07.16 159
에디터 컴포넌트 이미지 슬라이드 MH Slide file 팔공산 2026.07.09 232
에디터 컴포넌트 비교 슬라이더 MH Before/After file 팔공산 2026.07.07 186
에디터 컴포넌트 이미지 확대 MH Zoom file 팔공산 2026.07.03 221
에디터 컴포넌트 접기/펼치기 박스 MH Folding Box file 팔공산 2026.06.24 229
템플릿 템플릿 컴포넌트 폴딩 MH 템플릿 file 팔공산 2026.06.24 186
모듈 쇼핑몰 모듈 file 팔공산 2026.06.13 26
게시판스킨 23 SketchBook5 게시판스킨 - 문하우스 수정버전 V3 file
팔공산 2024.05.13 3,680