diff --git a/src/css/common.css b/src/css/common.css new file mode 100644 index 0000000..2010d9d --- /dev/null +++ b/src/css/common.css @@ -0,0 +1,1781 @@ +@charset "UTF-8"; +:root { + /* text - 텍스트 색상 */ + --text-intro-base: #1b1810; + --text-base: #131313; + --text-revers: #fff; + --text-primary: #3c3321; + --text-secondary: #747474; + --text-accent: #ff5c00; + --text-myclass: #ffdf60; + --text-main-primary: #4a4040; + --text-main-secondary: #3a200d; + --text-title-accent: #f5651d; + --text-pick: #2b5045; + --text-keyword-base: #c4c2c0; + --text-keyword-primary: #fff; + --text-keyword-secondary: #999999; + --text-video-primary: #b6d5a7; + --text-video-secondary: #ddd; + --text-cate-primary: #edd8c2; + --text-cate-secondary: #b6d5a7; + --text-cate-tertiary: #e8cfe4; + --text-comment-primary: #fff; + --text-comment-secondary: #8d8d8d; + --text-learning-base: #8d8888; + --text-learning-primary: #ff5c00; + --text-learning-secondary: #008c49; + --text-card-category: #252525; + --text-card-title-active: #ff7d33; + --text-card-title-complete: #66ba92; + --text-card-author: #d1cfcf; + /* 배경 */ + --bg-base: #e4ddcf; + --bg-primary: #ece3d2; + --bg-secondary: + radial-gradient( + 93.89% 93.89% at 49.32% 6.11%, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.11) 86.06%, + rgba(134, 114, 77, 0.2) 88.94% + ), + linear-gradient(180deg, #f9f6f0 0%, #e6ddcc 100%); + --bg-main-card: rgba(255, 255, 255, 0.6); + --bg-intro-mask: #1b1810; + --bg-intro: linear-gradient( + 180deg, + #f9f5f2 0%, + #fff 18.77%, + #fff 41.8%, + #ece8e4 100% + ); + --bg-main: + linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ) + top / 100% 114px no-repeat, + #ece3d2; + --bg-video: #1b1b1b; + --bg-comment: #2a2a2a; + --bg-nav: linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ); + --bg-nav-depth: #fff; + --bg-nav-alerts: #ff2200; + --bg-nav-alerts-hover: #188f6b; + --bg-cate-primary: #ded7cf; + --bg-cate-secondary: #d4decf; + --bg-cate-tertiary: #e8cfe4; + --bg-pick: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #d5ede6 100%); + --bg-gauge-base: #b4b4b4; + --bg-gauge-primary: #ff5c00; + --bg-search: #e4dbc9; + --bg-keyword-base: rgba(230, 205, 177, 0.3); + --bg-keyword-primary: #e47703; + --bg-keyword-primary-hover: #e6cdb1; + --bg-keyword-secondary: #786c60; + --bg-keyword-secondary-hover: #dcd3c9; + --bg-lecture-state: linear-gradient(270deg, #00ab66 0%, #058bb0 100%); + --bg-lecture-progress-primary: rgba(3, 9, 7, 0.3); + --bg-lecture-progress-secondary: #07855c; + --bg-btn-base: #000; + --bg-btn-primary: #ff5c00; + --bg-btn-secondary: #1f1f1f; + --bg-card: rgba(255, 255, 255, 0.6); + --bg-card-hover-start: #ff6600; + --bg-card-hover-end: rgba(255, 132, 0, 0.7); + --bg-card-thumb-overlay: rgba(255, 255, 255, 0.3); + --bg-card-shadow: rgba(217, 202, 190, 0.5); + --bg-card-hover-shadow: rgba(198, 187, 177, 0.5); + --bg-card-shadow-inner: rgba(0, 0, 0, 1); + --bg-pick-shadow: rgba(166, 154, 97, 0.25); + --bg-modal: rgba(0, 0, 0, 0.8); + --bg-modal-content: #f6f1e9; + --bg-modal-close-hover: #e00400; + --bg-scrollbar-thumb: #D0D0D0; + --bg-scrollbar-thumb-dark: #383838; + --bg-scrollbar-track: #F3F3F3; + --bg-scrollbar-thumb-light: rgba(255, 255, 255, 0.2); + --bg-scrollbar-thumb-light-hover: rgba(255, 255, 255, 0.3); + --bg-scrollbar-track-light: rgba(255, 255, 255, 0.05); + --bg-video-gauge: #171717; + --bg-video-gauge-fill: #ff6c19; + --bg-video-gauge-border: rgba(0, 0, 0, 0.4); + --bg-learning-line: #c6c3c3; + --bg-learning-dot: #8d8888; + --bg-learning-active: rgba(255, 92, 0, 0.2); + --bg-learning-active-line: #ffad7f; + --bg-learning-active-dot: #ff5c00; + --bg-learning-complete: rgba(0, 140, 73, 0.2); + --bg-learning-complete-line: #7fc5a4; + --bg-learning-complete-dot: #008c49; + --bg-chapter-current: #146b51; + --bg-chapter-completed: #ba9a85; + --bg-card-base: #7e7e7e; + --bg-card-current: #1d9b75; + --bg-card-current-border: #1f9b76; + --bg-card-current-bg: #dbefe9; + --bg-card-completed: #ab3d00; + --bg-gauge-fill: #e25e00; + --bg-shadow: #8a7d64; + --bg-puzzle-family: #7ed29b; + --bg-puzzle-hanmac: #ffccca; + --bg-puzzle-value: #87a7d6; + --bg-puzzle-company: #b49a65; + --bg-piece-1: #1d375d; + --bg-piece-2: #662a0d; + --bg-piece-3: #5b4822; + --bg-piece-4: #2a5338; + --bg-circle-border: #a7790f; + --bg-circle-start: rgba(127, 47, 0, 0.1); + --bg-circle-end: rgba(167, 121, 15, 0.5); + --bg-circle-dot: rgba(221, 144, 0, 0.6); + --bg-circle-dot-hover: rgba(221, 144, 0, 0.8); + --bg-circle-dot-stroke: rgba(221, 144, 0, 0.2); + --bg-gradient-start: #ece3d2; + --bg-gradient-end: #fff; + --bg-learning-line-color: #edba8a; + --bg-modal-header: #f04400; + --bg-textarea: #2a2a2a; + --bg-textarea-placeholder: rgba(255, 255, 255, 0.5); + /* border */ + --border-base: rgba(0, 0, 0, 0.05); + --border-primary: #fff; + --border-keyword-base: rgba(0, 0, 0, 0.05); + --border-keyword-primary: rgba(255, 255, 255, 0.4); + --border-keyword-primary-hover: rgba(0, 0, 0, 0.05); + --border-keyword-secondary: rgba(255, 255, 255, 0.4); + --border-keyword-secondary-hover: rgba(0, 0, 0, 0.1); + --border-pick: rgba(255, 255, 255, 0.5); + --border-video: rgba(255, 255, 255, 0.1); + --border-btn: rgba(255, 255, 255, 0.2); + --border-card: rgba(255, 255, 255, 0.8); + --border-modal: #888; + --border-comment-resizer: rgba(230, 227, 225, 0.1); + /* drop */ + --text-shadow: + -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; + --text-stroke: drop-shadow(0 0 0.5px #000); + /* shadow */ + --shadow-pagination: rgba(0, 0, 0, 0.4); + --shadow-modal: rgba(0, 0, 0, 0.25); + --shadow-card-drop: 0 4px 8px rgba(0, 0, 0, 0.2); + --shadow-card-drop-small: 0 4px 2px rgba(0, 0, 0, 0.05); + --shadow-gauge-inset: rgba(0, 0, 0, 0.3); + --shadow-comment: 0 8px 22px 22px rgba(0, 0, 0, 0.8); +} + +/* 페이드 전환 */ +.fade-out { + opacity: 0; + transition: opacity 0.4s ease; +} + +.fade-in { + opacity: 1; + transition: opacity 0.4s ease; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes arrow-next { + 0%, 100% { + right: 72px; + } + 50% { + right: 50px; + } +} +@keyframes bounce { + 0%, 100% { + transform: translateX(-50%) translateY(0); + } + 50% { + transform: translateX(-50%) translateY(-12px); + } +} +@keyframes slideUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes scroll-down { + 0% { + transform-origin: 50% 100%; + transform: scaleY(1); + } + 50% { + transform-origin: 50% 100%; + transform: scaleY(0); + } + 50.1% { + transform-origin: 50% 0; + transform: scaleY(0); + } + to { + transform-origin: 50% 0; + transform: scaleY(1); + } +} +@keyframes pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.4; + } +} +@keyframes borderFadeIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes borderPulse { + 0%, 100% { + border-color: #fff; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.4); + } + 50% { + border-color: #fff; + box-shadow: 0 0 30px rgba(255, 255, 255, 0.6); + } +} +html { + font-size: 10px; +} +html[lang=ko-KR], html[lang=ko] { + font-family: "Noto Sans KR", sans-serif; +} +html[lang=en], +html *[lang=en] { + font-family: "Noto Sans KR", Arial, sans-serif, serif; +} +html[lang=en] body, +html *[lang=en] body { + font-size: 18px; + font-weight: 400; +} +html body { + font-size: 16px; + font-weight: 400; + line-height: 1.4; + color: #000; + letter-spacing: -0.04em; +} + +/* ====================================================================== */ +/* [Web Font] +/* ====================================================================== */ +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local("NotoKR-Thin"), url(../fonts/NotoKR-Thin/notokr-thin.ttf) format("truetype"); +} +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local("NotoKR-Light"), url(../fonts/NotoKR-Light/notokr-light.ttf) format("truetype"); +} +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 350; + font-display: swap; + src: local("NotoKR-DemiLight"), url(../fonts/NotoKR-DemiLight/notokr-demilight.ttf) format("truetype"); +} +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local("NotoKR-Regular"), url(../fonts/NotoKR-Regular/notokr-regular.ttf) format("truetype"); +} +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local("NotoKR-Medium"), url(../fonts/NotoKR-Medium/notokr-medium.ttf) format("truetype"); +} +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local("NotoKR-Bold"), url(../fonts/NotoKR-Bold/notokr-bold.ttf) format("truetype"); +} +@font-face { + font-family: "Noto Sans KR"; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local("NotoKR-Black"), url(../fonts/NotoKR-Black/notokr-black.ttf) format("truetype"); +} +/* Caveat - 가변 폰트(400~700), cursive는 사용처에서 지정 */ +@font-face { + font-family: "Caveat"; + font-style: normal; + font-weight: 400 700; + font-display: swap; + src: url(../fonts/Caveat/Caveat-VariableFont_wght.ttf) format("truetype"); +} +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html { + -webkit-text-size-adjust: 100%; +} + +main { + display: block; +} + +pre { + font-family: monospace, monospace; + font-size: 1em; +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +b, +em, +strong { + font-weight: bold; +} + +code, +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +img { + border-style: none; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +[type=button], +[type=reset], +[type=submit] { + -webkit-appearance: button; +} + +button::-moz-focus-inner, +[type=button]::-moz-focus-inner, +[type=reset]::-moz-focus-inner, +[type=submit]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +button:-moz-focusring, +[type=button]:-moz-focusring, +[type=reset]:-moz-focusring, +[type=submit]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +textarea { + overflow: auto; +} + +[type=checkbox], +[type=radio] { + box-sizing: border-box; + padding: 0; +} + +[type=number]::-webkit-inner-spin-button, +[type=number]::-webkit-outer-spin-button { + height: auto; + -webkit-appearance: none; + appearance: none; + margin: 0; +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +details { + display: block; +} + +summary { + display: list-item; +} + +template { + display: none; +} + +[hidden] { + display: none; +} + +/* reset */ +html * { + box-sizing: border-box; + word-wrap: break-word; +} + +body, +div, +dl, +dt, +dd, +ul, +ol, +li, +h1, +h2, +h3, +h4, +h5, +h6, +pre, +code, +form, +figure, +legend, +input, +textarea, +button, +p, +blockquote, +th, +td, +form, +fieldset, +blockquote, +iframe { + margin: 0; + padding: 0; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + text-size-adjust: 100%; +} + +article, +aside, +canvas, +details, +embed, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section, +summary { + display: block; +} + +command, +datalist, +keygen, +mark, +meter, +progress, +rp, +rt, +ruby, +time, +wbr { + display: inline; +} + +img { + display: inline-block; + vertical-align: top; + max-width: 100%; + border: 0; + image-rendering: -moz-crisp-edges; /* firefox */ + image-rendering: -o-crisp-edges; /* opera */ + image-rendering: -webkit-optimize-contrast; /* chrome(비표준) */ + image-rendering: crisp-edges; + transform: translateZ(0); +} + +fieldset { + border: 0; +} + +ul, +ol, +li { + list-style: none; +} + +pre { + white-space: pre-wrap; +} + +legend, +caption { + position: relative; + clip: rect(0 0 0 0); + clip-path: inset(50%); + width: 1px; + height: 1px; + margin: -1px; + overflow: hidden; + border: 0; + padding: 0; + white-space: nowrap; + clear: both; +} + +a { + color: inherit; + cursor: pointer; + background-color: transparent; +} +a:link { + text-decoration: none; +} +a:hover, a:focus, a:active, a:visited { + text-decoration: none; +} + +em, +i, +address, +cite { + font-style: normal; + font-weight: normal; +} + +input, +textarea, +select, +button, +table { + font-size: inherit; + font-family: inherit; + border: 0; + background-color: transparent; +} + +button, +select { + cursor: pointer; +} + +textarea, +input, +select { + border-radius: 0; + border: 0; + outline-color: -moz-use-text-color; + outline-width: medium; +} + +textarea { + resize: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +label { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +table { + table-layout: fixed; + border-collapse: collapse; + border-spacing: 0; +} + +th, +td { + border-collapse: collapse; +} + +select::-ms-expand { + display: none; +} + +html::-webkit-scrollbar { + height: 10px; + width: 7px; +} +html::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +html::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track); +} + +.clearfix:after { + content: ""; + display: block; + clear: both; +} + +.blind { + position: absolute; + clip: rect(0 0 0 0); + clip-path: inset(50%); + width: 1px; + height: 1px; + margin: -1px; + overflow: hidden; + border: 0; + padding: 0; + white-space: nowrap; +} + +.hidden, +.d-none { + display: none !important; +} + +.btn-full { + width: 100%; +} + +.btn-primary { + height: 36px; + border-radius: 3px; + font-size: 13px; + font-weight: 700; + border: 1px solid rgba(0, 0, 0, 0.1); + background: radial-gradient(117.51% 77.94% at 48.87% 100%, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.3) 24.58%, rgba(255, 255, 255, 0) 100%), #FF8200; + background-blend-mode: screen, normal; + color: #fff; +} +.btn-primary:disabled { + background: #C4C4C4; + border-color: #B1B1B1; + color: #E8E8E8; +} + +.ico-play { + display: inline-block; + width: 18px; + height: 18px; + background-image: url("../img/ico/ico_watch.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} + +.ico-bookmark { + display: inline-block; + width: 18px; + height: 18px; + background-image: url("../img/ico/ico_bookmark.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} + +.ico-pin { + display: inline-block; + width: 24px; + height: 32px; + background-image: url("../img/ico/ico_pin.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} + +.ico-comment { + display: inline-block; + width: 22px; + height: 22px; + background-image: url("../img/ico/ico_comment.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} + +.input-group { + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.input-group .btn-clear { + position: absolute; + top: 50%; + right: 16px; + translate: 0 -50%; + width: 12px; + height: 12px; + border: none; + background: transparent; + cursor: pointer; + display: none; + background-image: url("../img/ico/ico_clear.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.input-group .btn-clear:hover { + opacity: 0.7; +} +.input-group:has(input:not(:-moz-placeholder)) .input-group .btn-clear { + display: block; +} +.input-group:has(input[value]:not([value=""])) .input-group .btn-clear, .input-group:has(input:not(:placeholder-shown)) .input-group .btn-clear { + display: block; +} + +.checkbox { + position: relative; +} +.checkbox input[type=checkbox] { + position: absolute; + width: 0; + height: 0; + opacity: 0; +} + +[class^=keyword] { + flex-wrap: wrap; + gap: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +[class^=keyword][class*=list] { + max-width: 470px; +} +[class^=keyword][class*=tag] label:has(:checked) { + color: var(--text-keyword-primary); + background: var(--bg-keyword-primary); + border: 1px solid var(--border-keyword-primary); +} +[class^=keyword] label { + position: relative; + display: block; + min-width: 52px; + padding: 12px 9px; + border-radius: 24px; + color: var(--text-keyword-base); + background: var(--bg-keyword-base); + border: 1px solid var(--border-keyword-base); + font-weight: 500; + line-height: 1; +} +[class^=keyword] label:has(+ [type=checkbox]:checked) { + color: var(--text-keyword-secondary); + background: var(--bg-keyword-primary-hover); + border: 1px solid var(--border-keyword-primary-hover); +} +[class^=keyword] label:has(+ [type=checkbox]:checked):has(:checked) { + color: var(--text-keyword-primary); + background: var(--bg-keyword-primary); + border: 1px solid var(--border-keyword-primary); +} +[class^=keyword] label input[type=checkbox] { + position: absolute; + width: 0; + height: 0; + opacity: 0; +} +[class^=keyword] .kw-box { + position: relative; +} +[class^=keyword] .kw-box label::after { + content: " "; + display: block; + position: absolute; + top: -2px; + right: 0; + width: 14px; + height: 14px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 50%; + background-image: url("../img/ico/ico_add.svg"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} +[class^=keyword] .kw-box label:has(:checked)::after { + background-image: url("../img/ico/ico_remove.svg"); +} +[class^=keyword] .kw-allow { + color: var(--text-keyword-secondary); + background: var(--bg-keyword-primary-hover); + border: 1px solid var(--border-keyword-primary-hover); +} +[class^=keyword] .kw-allow:has(:checked) { + color: var(--text-keyword-primary); + background: var(--bg-keyword-primary); + border: 1px solid var(--border-keyword-primary); +} +[class^=keyword] .kw-deny { + color: var(--text-keyword-secondary); + background: var(--bg-keyword-secondary-hover); + border: 1px solid var(--border-keyword-secondary-hover); +} +[class^=keyword] .kw-deny:has(:checked) { + color: var(--text-keyword-primary); + background: var(--bg-keyword-secondary); + border: 1px solid var(--border-keyword-secondary); +} + +.item-info .leader { + background: var(--bg-cate-primary); +} +.item-info .insight { + background: var(--bg-cate-secondary); +} +.item-info .biz { + background: var(--bg-cate-tertiary); +} + +.page-title { + padding-top: 74px; + text-align: center; +} +.page-title p { + font-size: 40px; + font-weight: 400; +} +.page-title p.fw-b { + font-weight: 700; +} +.page-title p em { + font-weight: 700; + color: var(--text-title-accent); +} +.page-title p small { + font-size: 32px; + font-weight: 400; +} +.page-title h3 { + font-size: 32px; + font-weight: 300; +} +.page-title h3 span { + font-weight: 400; +} +.page-title h3 em { + font-weight: 500; +} + +h5 { + font-size: 16px; + font-weight: 700; + text-align: left; +} + +.badge-danger { + border-radius: 4px; + color: var(--text-keyword-primary); + background: var(--bg-nav-alerts); + text-align: center; +} + +.bookmark { + padding: 8px; + border-radius: 4px; + border: 1px solid var(--border-btn); + gap: 6px; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.bookmark input[type=checkbox] { + height: 10px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + cursor: pointer; + background-image: url("../img/ico/ico_bookmark_off.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + width: 12px; + aspect-ratio: 1/1; +} +.bookmark input[type=checkbox]:checked { + background-image: url("../img/ico/ico_bookmark_on.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + width: 12px; + aspect-ratio: 1/1; +} + +.m-br { + display: none; +} +@media only screen and (max-width: 1023px) { + .m-br { + display: block; + } +} + +.modal { + display: none; + position: fixed; + z-index: 30; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: var(--bg-modal); + overflow: auto; +} +.modal .modal-content { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + background-color: var(--bg-modal-content); + padding: 40px 20px 20px; + border: 1px solid var(--border-modal); + width: 470px; + min-height: 500px; + border-radius: 8px; + text-align: center; +} +.modal .close { + position: absolute; + right: 12px; + top: 12px; + display: block; + width: 26px; + height: 26px; + color: #000; + font-size: 26px; + font-weight: bold; + line-height: 1; + background: none; + filter: var(--text-shadow); + transition: all 0.1s ease-in-out; +} +.modal .close:hover, .modal .close:focus { + color: var(--bg-modal-close-hover); + text-decoration: none; + cursor: pointer; + text-shadow: var(--text-stroke); +} +.modal.video::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.modal.video::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb-dark); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.modal.video::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track-light); +} +.modal.video .close { + color: var(--text-revers); + font-size: 22px; + font-weight: 500; + width: 22px; + height: 22px; + top: 14px; + right: 16px; +} +.modal.video .modal-content { + width: 90%; + max-width: 1720px; + height: 900px; + max-height: 900px; + padding: 0; + background: var(--bg-video); + border: none; + border-radius: 20px; + overflow: hidden; +} +.modal.video .modal-body { + display: grid; + grid-template-columns: 1fr 437px; + height: 100%; +} +.modal.video .video-contents { + display: flex; + flex-direction: column; + height: 900px; + border-right: 1px solid var(--border-video); +} +.modal.video .video-area { + flex: 1; + border-bottom: 1px solid var(--border-video); + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.modal.video .video-box { + position: relative; + flex-shrink: 0; + width: 100%; + padding-top: 56.0405%; + overflow: hidden; +} +.modal.video .video-box iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.modal.video .video-info { + height: 188px; + padding: 0 36px; + text-align: left; + color: var(--text-revers); + overflow-y: auto; +} +.modal.video .video-info::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.modal.video .video-info::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb-dark); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.modal.video .video-info::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track-light); +} +.modal.video .video-info .tit-box { + position: sticky; + top: 0; + width: 100%; + min-height: 80px; + padding: 16px 0; + border-bottom: 1px solid var(--border-video); + background: var(--bg-video); + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: column; +} +.modal.video .video-info .tit-box h3 { + font-size: 20px; + font-weight: 700; + padding-right: 80px; +} +.modal.video .video-info .tit-box .bookmark { + position: absolute; + bottom: 18px; + right: 0; + color: rgba(255, 255, 255, 0.5); +} +.modal.video .video-info .meta { + font-size: 14px; + color: var(--text-video-primary); +} +.modal.video .video-info .meta span { + position: relative; + display: inline-block; + color: var(--text-video-primary); +} +.modal.video .video-info .meta span::after { + content: " "; + display: inline-block; + width: 12px; + height: 10px; + background: url("../img/ico/ico_arrow_meta.svg") no-repeat center center; +} +.modal.video .video-info .meta em { + font-weight: 700; +} +.modal.video .video-info .desc { + padding: 22px 0; + font-size: 14px; + font-weight: 500; + color: var(--text-video-secondary); +} +.modal.video .video-side { + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + grid-auto-rows: auto; + max-height: 900px; + overflow: hidden; +} +.modal.video .video-side > * { + min-height: 0; +} +.modal.video .video-side .comment-wrap { + display: flex; + flex-direction: column; + min-height: 0; + overflow: visible; + align-self: end; +} +.modal.video .video-side .comment-wrap:has(.comment-list) { + border-top: 1px solid var(--border-video); + box-shadow: var(--shadow-comment); +} +.modal.video .video-side.step:has(:not(.comment-wrap)) .video-list { + padding-bottom: 20px; +} +.modal.video .video-side.step .video-header { + flex-direction: column; + background: linear-gradient(271deg, rgba(255, 244, 233, 0.04) 1.54%, rgba(255, 244, 233, 0.09) 99.11%), #1b1b1b; + height: -moz-max-content; + height: max-content; +} +.modal.video .video-side.step .video-header .tit-box { + gap: 12px; + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; +} +.modal.video .video-side.step .video-header .sub-txt { + font-size: 20px; + font-weight: 400; + word-break: keep-all; + text-align: left; + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.modal.video .video-side.step .video-header .gauge-container { + position: relative; + width: 100%; +} +.modal.video .video-side.step .video-header .gauge-bar { + position: relative; + width: 100%; + height: 15px; + background: var(--bg-video-gauge); + border-radius: 4px; + overflow: hidden; + border: 0.5px solid var(--bg-video-gauge-border); + margin-bottom: 8px; +} +.modal.video .video-side.step .video-header .gauge-fill { + position: absolute; + top: 0; + left: 0; + height: 100%; + background: var(--bg-video-gauge-fill); + border-radius: 4px; + transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1); +} +.modal.video .video-side.step .video-header .gauge-fill::after { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(90deg, rgba(255, 255, 255, 0) 50%, rgba(255, 191, 0, 0.4) 100%), var(--bg-video-gauge-fill); + mix-blend-mode: color-dodge; + opacity: 0.4; +} +.modal.video .video-side.step .video-header .gauge-ticks { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + mix-blend-mode: plus-lighter; + opacity: 0.2; +} +.modal.video .video-side.step .video-header .gauge-labels { + font-size: 14px; + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; +} +.modal.video .video-side.step .video-header .gauge-labels em { + font-weight: 700; + margin: 0 2px; +} +.modal.video .video-side.step .video-list { + padding-top: 20px; +} +.modal.video .video-header { + padding: 38px 16px 16px; + min-height: 80px; + color: var(--text-revers); + gap: 16px; + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: row; +} +.modal.video .video-header .tit { + line-height: 26px; +} +.modal.video .video-header .badge { + width: 42px; + height: 26px; + background: var(--bg-keyword-primary); + font-size: 14px; + color: var(--text-keyword-primary); + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + border-radius: 25px; +} +.modal.video .video-list { + overflow-y: auto; + align-self: start; +} +.modal.video .video-list::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.modal.video .video-list::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb-dark); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.modal.video .video-list::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track-light); +} +.modal.video .video-list .tit { + color: #fff; + margin-bottom: 8px; +} +.modal.video .learning-list { + position: relative; + color: var(--text-learning-base); +} +.modal.video .learning-list::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.modal.video .learning-list::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb-dark); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.modal.video .learning-list::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track-light); +} +.modal.video .learning-list::before { + content: ""; + display: block; + position: absolute; + left: 56px; + width: 1px; + height: var(--scroll-height, calc(100% - 5px)); + opacity: 0.2; + background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #fff 10%, #fff 90%, rgba(255, 255, 255, 0) 100%); +} +.modal.video .learning-list > li { + border: none; +} +.modal.video .learning-list .list { + gap: 12px; + padding: 0; + margin-bottom: 5px; + grid-template-columns: 44px 1fr; +} +.modal.video .learning-list .list .learning-box { + position: relative; + gap: 12px; + padding: 8px 20px; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.modal.video .learning-list .list .learning-box::before { + content: ""; + display: block; + position: absolute; + left: 0; + width: 20px; + height: 1px; + background-color: var(--bg-learning-line); +} +.modal.video .learning-list .list .learning-box::after { + content: ""; + display: block; + position: absolute; + left: -6px; + width: 12px; + height: 12px; + background-color: var(--bg-learning-dot); + border-radius: 50%; + outline: 2px solid var(--bg-learning-line); +} +.modal.video .learning-list .list .learning-box .title { + color: var(--text-card-author); +} +.modal.video .learning-list .list .thumb { + min-width: 105px; + max-width: 105px; +} +.modal.video .learning-list .list .sep, +.modal.video .learning-list .list .state { + font-size: 14px; + white-space: nowrap; +} +.modal.video .learning-list .active { + color: var(--text-learning-primary); +} +.modal.video .learning-list .active .seq, +.modal.video .learning-list .active .state { + font-weight: 600; +} +.modal.video .learning-list .active .learning-box { + background: linear-gradient(90deg, var(--bg-learning-active) 0%, rgba(255, 92, 0, 0) 100%); +} +.modal.video .learning-list .active .learning-box::before { + background-color: var(--bg-learning-active-line); +} +.modal.video .learning-list .active .learning-box::after { + background-color: var(--bg-learning-active-dot); + outline-color: var(--bg-learning-active-line); +} +.modal.video .learning-list .active .learning-box .title { + color: var(--text-card-title-active); + font-weight: 600; +} +.modal.video .learning-list .complet { + color: var(--text-learning-secondary); +} +.modal.video .learning-list .complet .learning-box { + background: linear-gradient(90deg, var(--bg-learning-complete) 0%, rgba(0, 140, 73, 0) 100%); +} +.modal.video .learning-list .complet .learning-box::before { + background-color: var(--bg-learning-complete-line); +} +.modal.video .learning-list .complet .learning-box::after { + background-color: var(--bg-learning-complete-dot); + outline-color: var(--bg-learning-complete-line); +} +.modal.video .learning-list .complet .learning-box .title { + color: var(--text-card-title-complete); +} +.modal.video .comment-box { + width: 100%; + padding: 8px; + gap: 8px; + border-top: 1px solid var(--border-video); + display: flex; + align-items: flex-end; + justify-content: flex-start; + flex-direction: row; +} +.modal.video textarea { + flex-grow: 1; + min-height: 32px; + max-height: 120px; + color: var(--text-revers); + background: var(--bg-textarea); + height: 32px; + border-radius: 4px; + padding: 6px 8px; + font-size: 14px; + line-height: 20px; + resize: none; + overflow-y: auto; + transition: height 0.3s linear; +} +.modal.video textarea::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.modal.video textarea::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb-dark); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.modal.video textarea::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track-light); +} +.modal.video textarea::-moz-placeholder { + color: var(--bg-textarea-placeholder); +} +.modal.video textarea::placeholder { + color: var(--bg-textarea-placeholder); +} +.modal.video .btn-area { + gap: 4px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.modal.video .btn-active { + background: var(--bg-btn-primary); +} +.modal.video [class^=btn] { + min-width: 40px; + min-height: 32px; + border-radius: 4px; + color: #fff; + font-size: 12px; +} +.modal.video [class^=btn]:not([class*=area]) { + background: var(--bg-btn-base); +} +.modal.video [class^=btn]:disabled { + color: #8f8f8f; + background: var(--bg-btn-secondary); + border: 1px solid var(--border-btn); +} +.modal.video [class^=btn][class*=save]:not(:disabled) { + background: var(--text-accent); + border: none; +} + +.video-list { + padding: 0 16px; +} +.video-list li { + border-bottom: 1px solid var(--border-video); +} +.video-list .list { + width: 100%; + gap: 20px; + padding: 8px; + display: grid; + grid-template-columns: 120px 1fr; + align-items: center; +} +.video-list .thumb { + position: relative; + min-height: 68px; + max-height: 94px; + border-radius: 4px; + background: rgba(255, 255, 255, 0.1); + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.video-list .thumb img { + width: 100%; + height: auto; + max-width: auto; +} +.video-list .txt-box { + gap: 4px; + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: column; +} +.video-list .txt-box .checkbox { + position: absolute; + top: 10px; + right: 42px; + width: 24px; + height: 24px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='18' viewBox='0 0 22 18' fill='none'%3E%3Cpath d='M15.95 0C14.036 0 12.199 0.79455 11 2.04033C9.801 0.79455 7.964 0 6.05 0C2.662 0 0 2.36403 0 5.39509C0 9.09319 3.74 12.1243 9.405 16.7052L11 18L12.595 16.7052C18.26 12.1243 22 9.09319 22 5.39509C22 2.36403 19.338 0 15.95 0Z' fill='white'/%3E%3Cpath d='M15.9502 0.75C19.0079 0.750089 21.25 2.85803 21.25 5.39551C21.2499 6.96829 20.4613 8.47481 18.8691 10.2119C17.2723 11.9541 14.9715 13.8187 12.123 16.1221L12.1221 16.123L11 17.0332L9.87793 16.123L9.87695 16.1221L7.8457 14.4717C5.92217 12.894 4.3285 11.5185 3.13086 10.2119C1.5387 8.47481 0.750138 6.96829 0.75 5.39551C0.75 2.85803 2.99208 0.75009 6.0498 0.75C7.76714 0.75 9.40728 1.4668 10.46 2.56055L11 3.12207L11.54 2.56055C12.5927 1.4668 14.2329 0.75 15.9502 0.75Z' stroke='%23060505' stroke-opacity='0.2' stroke-width='1.5'/%3E%3C/svg%3E"); + background-position: center; + background-repeat: no-repeat; + background-size: contain; +} +.video-list .txt-box .checkbox:has(:checked) { + background-image: url("data:image/svg+xml,%3Csvg width='22' height='18' viewBox='0 0 22 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.95 0C14.036 0 12.199 0.79455 11 2.04033C9.801 0.79455 7.964 0 6.05 0C2.662 0 0 2.36403 0 5.39509C0 9.09319 3.74 12.1243 9.405 16.7052L11 18L12.595 16.7052C18.26 12.1243 22 9.09319 22 5.39509C22 2.36403 19.338 0 15.95 0Z' fill='%23ff5c00'/%3E%3Cpath d='M15.9502 0.75C19.0079 0.750089 21.25 2.85803 21.25 5.39551C21.2499 6.96829 20.4613 8.47481 18.8691 10.2119C17.2723 11.9541 14.9715 13.8187 12.123 16.1221L12.1221 16.123L11 17.0332L9.87793 16.123L9.87695 16.1221L7.8457 14.4717C5.92217 12.894 4.3285 11.5185 3.13086 10.2119C1.5387 8.47481 0.750138 6.96829 0.75 5.39551C0.75 2.85803 2.99208 0.75009 6.0498 0.75C7.76714 0.75 9.40728 1.4668 10.46 2.56055L11 3.12207L11.54 2.56055C12.5927 1.4668 14.2329 0.75 15.9502 0.75Z' stroke='white' stroke-opacity='0.2' stroke-width='1.5'/%3E%3C/svg%3E%0A"); +} +.video-list .txt-box .category { + font-size: 12px; + font-weight: 700; + color: #fff; +} +.video-list .txt-box .category.leader { + color: var(--text-cate-primary); +} +.video-list .txt-box .category.insight { + color: var(--text-cate-secondary); +} +.video-list .txt-box .category.biz { + color: var(--text-cate-tertiary); +} +.video-list .txt-box .title { + font-size: 16px; + font-weight: 500; + line-height: 1.2; + color: var(--text-revers); + text-align: left; + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.comment-wrap { + position: relative; + background: var(--bg-comment); +} +.comment-box { + flex-shrink: 0; +} +.comment-resizer { + position: absolute; + left: calc(50% - 21px); + top: -24px; + width: 42px; + height: 24px; + flex-shrink: 0; + background: var(--bg-comment); + border-radius: 8px 8px 0 0; + border-top: 1px solid var(--border-comment-resizer); + border-right: 1px solid var(--border-comment-resizer); + border-left: 1px solid var(--border-comment-resizer); + cursor: ns-resize; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.comment-resizer:hover .resizer-handle, .comment-resizer.resizing .resizer-handle { + background-color: var(--text-myclass); +} +.comment-resizer .resizer-handle { + width: 100%; + height: 100%; + margin: auto; + transition: background 0.2s ease; + background-color: var(--text-revers); + mask-image: url("../img/ico/ico_drag.svg"); + mask-size: contain; + mask-position: center; + mask-repeat: no-repeat; + -webkit-mask-image: url("../img/ico/ico_drag.svg"); + -webkit-mask-size: contain; + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + width: 18px; + aspect-ratio: 1/1; +} +.comment-list-wrap { + flex: 1; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + max-height: 340px; +} +.comment-list-wrap::-webkit-scrollbar { + width: 6px; +} +.comment-list-wrap::-webkit-scrollbar-track { + background: var(--bg-scrollbar-track-light); +} +.comment-list-wrap::-webkit-scrollbar-thumb { + background: var(--bg-scrollbar-thumb-light); + border-radius: 3px; +} +.comment-list-wrap::-webkit-scrollbar-thumb:hover { + background: var(--bg-scrollbar-thumb-light-hover); +} +.comment-list { + padding: 16px 0; +} +.comment-info { + padding: 0 20px; + gap: 12px; + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: row; +} +.comment-info .photo { + width: 20px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.comment-info .photo img { + width: 20px; + aspect-ratio: 1/1; + height: 20px; + border-radius: 50%; + background: var(--text-comment-secondary); + overflow: hidden; +} +.comment-info .user-comment { + font-size: 14px; + font-weight: 500; + flex-grow: 1; + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: row; +} +.comment-info .user-name { + font-weight: 700; + color: var(--text-comment-secondary); + line-height: 32px; +} +.comment-info .user-text { + color: var(--text-comment-primary); + height: 32px; + word-break: keep-all; + overflow-y: hidden; +} +.comment-info .user-text:disabled { + background: none; +} + +@media only screen and (min-width: 1200px) { + html { + font-size: 10px; + } +} +@media only screen and (max-width: 991px) { + html { + font-size: 14px; + } +} +@media only screen and (max-width: 767px) { + html { + font-size: 13px; + } +} +@media only screen and (max-width: 639px) { + html { + font-size: 12px; + } +} +@media only screen and (max-width: 479px) { + html { + font-size: 10px; + } +} +@media only screen and (max-width: 359px) { + html { + font-size: 9px; + } +} +@media only screen and (max-width: 319px) { + html { + font-size: 8px; + } +} \ No newline at end of file diff --git a/src/css/intro.css b/src/css/intro.css new file mode 100644 index 0000000..06db20d --- /dev/null +++ b/src/css/intro.css @@ -0,0 +1,775 @@ +@charset "UTF-8"; +:root { + /* text - 텍스트 색상 */ + --text-intro-base: #1b1810; + --text-base: #131313; + --text-revers: #fff; + --text-primary: #3c3321; + --text-secondary: #747474; + --text-accent: #ff5c00; + --text-myclass: #ffdf60; + --text-main-primary: #4a4040; + --text-main-secondary: #3a200d; + --text-title-accent: #f5651d; + --text-pick: #2b5045; + --text-keyword-base: #c4c2c0; + --text-keyword-primary: #fff; + --text-keyword-secondary: #999999; + --text-video-primary: #b6d5a7; + --text-video-secondary: #ddd; + --text-cate-primary: #edd8c2; + --text-cate-secondary: #b6d5a7; + --text-cate-tertiary: #e8cfe4; + --text-comment-primary: #fff; + --text-comment-secondary: #8d8d8d; + --text-learning-base: #8d8888; + --text-learning-primary: #ff5c00; + --text-learning-secondary: #008c49; + --text-card-category: #252525; + --text-card-title-active: #ff7d33; + --text-card-title-complete: #66ba92; + --text-card-author: #d1cfcf; + /* 배경 */ + --bg-base: #e4ddcf; + --bg-primary: #ece3d2; + --bg-secondary: + radial-gradient( + 93.89% 93.89% at 49.32% 6.11%, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.11) 86.06%, + rgba(134, 114, 77, 0.2) 88.94% + ), + linear-gradient(180deg, #f9f6f0 0%, #e6ddcc 100%); + --bg-main-card: rgba(255, 255, 255, 0.6); + --bg-intro-mask: #1b1810; + --bg-intro: linear-gradient( + 180deg, + #f9f5f2 0%, + #fff 18.77%, + #fff 41.8%, + #ece8e4 100% + ); + --bg-main: + linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ) + top / 100% 114px no-repeat, + #ece3d2; + --bg-video: #1b1b1b; + --bg-comment: #2a2a2a; + --bg-nav: linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ); + --bg-nav-depth: #fff; + --bg-nav-alerts: #ff2200; + --bg-nav-alerts-hover: #188f6b; + --bg-cate-primary: #ded7cf; + --bg-cate-secondary: #d4decf; + --bg-cate-tertiary: #e8cfe4; + --bg-pick: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #d5ede6 100%); + --bg-gauge-base: #b4b4b4; + --bg-gauge-primary: #ff5c00; + --bg-search: #e4dbc9; + --bg-keyword-base: rgba(230, 205, 177, 0.3); + --bg-keyword-primary: #e47703; + --bg-keyword-primary-hover: #e6cdb1; + --bg-keyword-secondary: #786c60; + --bg-keyword-secondary-hover: #dcd3c9; + --bg-lecture-state: linear-gradient(270deg, #00ab66 0%, #058bb0 100%); + --bg-lecture-progress-primary: rgba(3, 9, 7, 0.3); + --bg-lecture-progress-secondary: #07855c; + --bg-btn-base: #000; + --bg-btn-primary: #ff5c00; + --bg-btn-secondary: #1f1f1f; + --bg-card: rgba(255, 255, 255, 0.6); + --bg-card-hover-start: #ff6600; + --bg-card-hover-end: rgba(255, 132, 0, 0.7); + --bg-card-thumb-overlay: rgba(255, 255, 255, 0.3); + --bg-card-shadow: rgba(217, 202, 190, 0.5); + --bg-card-hover-shadow: rgba(198, 187, 177, 0.5); + --bg-card-shadow-inner: rgba(0, 0, 0, 1); + --bg-pick-shadow: rgba(166, 154, 97, 0.25); + --bg-modal: rgba(0, 0, 0, 0.8); + --bg-modal-content: #f6f1e9; + --bg-modal-close-hover: #e00400; + --bg-scrollbar-thumb: #D0D0D0; + --bg-scrollbar-thumb-dark: #383838; + --bg-scrollbar-track: #F3F3F3; + --bg-scrollbar-thumb-light: rgba(255, 255, 255, 0.2); + --bg-scrollbar-thumb-light-hover: rgba(255, 255, 255, 0.3); + --bg-scrollbar-track-light: rgba(255, 255, 255, 0.05); + --bg-video-gauge: #171717; + --bg-video-gauge-fill: #ff6c19; + --bg-video-gauge-border: rgba(0, 0, 0, 0.4); + --bg-learning-line: #c6c3c3; + --bg-learning-dot: #8d8888; + --bg-learning-active: rgba(255, 92, 0, 0.2); + --bg-learning-active-line: #ffad7f; + --bg-learning-active-dot: #ff5c00; + --bg-learning-complete: rgba(0, 140, 73, 0.2); + --bg-learning-complete-line: #7fc5a4; + --bg-learning-complete-dot: #008c49; + --bg-chapter-current: #146b51; + --bg-chapter-completed: #ba9a85; + --bg-card-base: #7e7e7e; + --bg-card-current: #1d9b75; + --bg-card-current-border: #1f9b76; + --bg-card-current-bg: #dbefe9; + --bg-card-completed: #ab3d00; + --bg-gauge-fill: #e25e00; + --bg-shadow: #8a7d64; + --bg-puzzle-family: #7ed29b; + --bg-puzzle-hanmac: #ffccca; + --bg-puzzle-value: #87a7d6; + --bg-puzzle-company: #b49a65; + --bg-piece-1: #1d375d; + --bg-piece-2: #662a0d; + --bg-piece-3: #5b4822; + --bg-piece-4: #2a5338; + --bg-circle-border: #a7790f; + --bg-circle-start: rgba(127, 47, 0, 0.1); + --bg-circle-end: rgba(167, 121, 15, 0.5); + --bg-circle-dot: rgba(221, 144, 0, 0.6); + --bg-circle-dot-hover: rgba(221, 144, 0, 0.8); + --bg-circle-dot-stroke: rgba(221, 144, 0, 0.2); + --bg-gradient-start: #ece3d2; + --bg-gradient-end: #fff; + --bg-learning-line-color: #edba8a; + --bg-modal-header: #f04400; + --bg-textarea: #2a2a2a; + --bg-textarea-placeholder: rgba(255, 255, 255, 0.5); + /* border */ + --border-base: rgba(0, 0, 0, 0.05); + --border-primary: #fff; + --border-keyword-base: rgba(0, 0, 0, 0.05); + --border-keyword-primary: rgba(255, 255, 255, 0.4); + --border-keyword-primary-hover: rgba(0, 0, 0, 0.05); + --border-keyword-secondary: rgba(255, 255, 255, 0.4); + --border-keyword-secondary-hover: rgba(0, 0, 0, 0.1); + --border-pick: rgba(255, 255, 255, 0.5); + --border-video: rgba(255, 255, 255, 0.1); + --border-btn: rgba(255, 255, 255, 0.2); + --border-card: rgba(255, 255, 255, 0.8); + --border-modal: #888; + --border-comment-resizer: rgba(230, 227, 225, 0.1); + /* drop */ + --text-shadow: + -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; + --text-stroke: drop-shadow(0 0 0.5px #000); + /* shadow */ + --shadow-pagination: rgba(0, 0, 0, 0.4); + --shadow-modal: rgba(0, 0, 0, 0.25); + --shadow-card-drop: 0 4px 8px rgba(0, 0, 0, 0.2); + --shadow-card-drop-small: 0 4px 2px rgba(0, 0, 0, 0.05); + --shadow-gauge-inset: rgba(0, 0, 0, 0.3); + --shadow-comment: 0 8px 22px 22px rgba(0, 0, 0, 0.8); +} + +/* 페이드 전환 */ +.fade-out { + opacity: 0; + transition: opacity 0.4s ease; +} + +.fade-in { + opacity: 1; + transition: opacity 0.4s ease; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes arrow-next { + 0%, 100% { + right: 72px; + } + 50% { + right: 50px; + } +} +@keyframes bounce { + 0%, 100% { + transform: translateX(-50%) translateY(0); + } + 50% { + transform: translateX(-50%) translateY(-12px); + } +} +@keyframes slideUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes scroll-down { + 0% { + transform-origin: 50% 100%; + transform: scaleY(1); + } + 50% { + transform-origin: 50% 100%; + transform: scaleY(0); + } + 50.1% { + transform-origin: 50% 0; + transform: scaleY(0); + } + to { + transform-origin: 50% 0; + transform: scaleY(1); + } +} +@keyframes pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.4; + } +} +@keyframes borderFadeIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes borderPulse { + 0%, 100% { + border-color: #fff; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.4); + } + 50% { + border-color: #fff; + box-shadow: 0 0 30px rgba(255, 255, 255, 0.6); + } +} +@font-face { + font-family: "YeogiOttaeJalnan"; + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_four@1.2/JalnanOTF00.woff") format("woff"); + font-weight: normal; + font-display: swap; +} +.intro { + background: var(--bg-intro); + background-attachment: fixed; + overflow: auto; +} +.intro .container { + position: relative; + top: 0; + height: var(--window-inner-height); + margin-top: 0; + padding: 0; + overflow: hidden; + border: none; + border-radius: 0; + clip-path: none; + background: var(--bg-intro); + color: var(--text-intro-base); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.intro .text-ani { + display: none; +} +.intro .text-ani:has(.welcome span.show), .intro .text-ani:has(.update-line span.show), .intro .text-ani:has(.card.show), .intro .text-ani.animating { + display: block; +} +@media only screen and (max-width: 991px) { + .intro .text-ani:has(.card.show) { + display: flex; + flex-direction: column; + flex: 1 1 0; + min-height: 0; + width: 100%; + } +} +.intro .text-area { + min-height: 160px; + margin-bottom: 34px; + text-align: center; +} +.intro .text-area:has(.greeting.hidden, .update-text.hidden) { + min-height: 0; + margin-bottom: 0; +} +.intro .greeting { + font-size: 36px; + font-weight: 300; + line-height: 1.2; +} +@media only screen and (min-width: 1024px) { + .intro .greeting { + height: 656px; + } +} +@media only screen and (max-width: 1023px) { + .intro .greeting { + font-size: 2.8rem; + min-height: 15.8rem; + } + .intro .greeting p.welcome > *:nth-child(5) { + display: block; + line-height: 0; + } +} +.intro .name { + font-weight: 500; +} +.intro .welcome span { + display: inline-block; + opacity: 0; + transform: translateY(20px); +} +.intro .welcome span.show { + animation: fadeInUp 0.4s forwards; +} +.intro .welcome em { + font-weight: 500; +} +@media only screen and (max-width: 1023px) { + .intro .welcome { + margin-top: 2rem; + } +} +.intro .update-text { + text-align: center; +} +.intro .update-line { + overflow: hidden; + font-size: 36px; +} +@media only screen and (max-width: 1023px) { + .intro .update-line { + font-size: 2.8rem; + } +} +@media only screen and (max-width: 1023px) { + .intro .update-line:last-child { + margin-top: 2rem; + } +} +.intro .update-line.bold, +.intro .update-line em { + font-weight: 500; +} +.intro .update-line > span { + display: inline-block; + opacity: 0; + transform: translateY(100%); +} +.intro .update-line > span.show { + animation: slideUp 0.6s forwards; +} +.intro .cta-text { + position: relative; + width: 100vw; + height: var(--window-inner-height); + font-size: 42px; + font-weight: 300; + line-height: 1.2; + text-align: center; +} +@media only screen and (max-width: 1023px) { + .intro .cta-text { + font-size: 2.8rem; + } +} +.intro .cta-text > div { + width: 100vw; + height: var(--window-inner-height); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.intro .cta-text > div:not(.mask) { + position: relative; + z-index: 1; + cursor: default; +} +.intro .cta-text > div:not(.mask) .text-section { + opacity: 0; + transform: translateY(20px); +} +.intro .cta-text > div:not(.mask) .text-section:has(.show) { + background-image: url(../images/intro/promise.svg); + background-position: bottom; + background-size: 586px auto; + background-repeat: no-repeat; + animation: fadeInUp 0.5s forwards; +} +@media only screen and (max-width: 1023px) { + .intro .cta-text > div:not(.mask) .text-section:has(.show) { + background-size: 120% auto; + } +} +.intro .cta-text > div:not(.mask) p { + opacity: 0; + transform: translateY(20px); +} +.intro .cta-text > div:not(.mask) p.show { + animation: fadeInUp 0.5s forwards; +} +.intro .cta-text .text-section { + height: 260px; + translate: 0 -35%; + opacity: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +@media only screen and (max-width: 1023px) { + .intro .cta-text .text-section { + width: calc(100% - 32px); + max-width: 452px; + height: 18rem; + translate: 0 -40%; + } +} +.intro .cta-text .text-section.show { + animation: opacity 0.5s forwards; +} +.intro .cta-text em { + font-weight: 500; +} +.intro .cta-btn { + position: absolute; + top: 50%; + left: 50%; + z-index: 99; + width: calc(100% - 32px); + max-width: 483px; + height: 116px; + padding: 22px 0; + border: none; + border-radius: 24px; + background: radial-gradient(117.51% 77.94% at 48.87% 100%, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.3) 24.58%, rgba(255, 255, 255, 0) 100%), #ff8200; + color: white; + font-size: 36px; + font-weight: bold; + text-align: center; + text-shadow: 0 2px 2px rgba(201, 121, 38, 0.5); + opacity: 0; + transform: translate(-50%, 25%) translateY(50px); + transition: all 0.3s; +} +@media only screen and (max-width: 1023px) { + .intro .cta-btn { + width: calc(100% - 32px); + height: 68px; + padding: 0; + font-size: 28px; + } +} +@media only screen and (min-width: 992px) { + .intro .cta-btn { + box-shadow: 0 -4px 4px 0 #e18d36 inset, 4px 4px 12px -4px rgba(220, 196, 172, 0.85); + } +} +.intro .cta-btn.show { + opacity: 1; + transform: translate(-50%, 25%) translateY(0); + transition: all 0.3s linear; +} +.intro .cta-btn::after { + content: " "; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 2px; + box-sizing: border-box; + background: linear-gradient(65deg, rgba(255, 255, 255, 0.6) 0%, transparent 100%) #ff8200; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; +} +.intro .cta-btn .ico-arrow { + position: absolute; + top: 50%; + right: 72px; + display: inline-block; + width: 24px; + height: 32px; + background-image: url(../images/ico/ico_arrow_next_dashed.svg); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + filter: drop-shadow(0 2px 2px rgba(201, 121, 38, 0.5)); + transform: translateY(-50%); + animation: arrow-next 3s infinite ease-in-out; +} +.intro .mask { + position: absolute; + top: 0; + left: 0; + z-index: 10; + width: 100%; + height: 100%; + min-height: 100%; + background-color: var(--bg-intro-mask); + color: rgba(0, 0, 0, 0.2); + pointer-events: none; + --size: 0px; + --x: 50vw; + --y: 50vh; + -webkit-mask-image: url(../images/cursor.svg); + -webkit-mask-origin: content-box; + -webkit-mask-position: calc(var(--x) - var(--size) / 2) calc(var(--y) - var(--size) / 2), center; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: var(--size), contain; + mask-image: url(../images/cursor.svg); + mask-origin: content-box; + mask-position: calc(var(--x) - var(--size) / 2) calc(var(--y) - var(--size) / 2), center; + mask-repeat: no-repeat; + mask-size: var(--size), contain; + transition: -webkit-mask-size 0.2s linear; + transition: mask-size 0.2s linear; + transition: mask-size 0.2s linear, -webkit-mask-size 0.2s linear; +} +@media only screen and (max-width: 991px) { + .intro .mask { + -webkit-mask-image: none; + mask-image: none; + clip-path: circle(0% at 50% 50%); + transition: clip-path 3.5s ease-out; + } + .intro .mask.expand { + clip-path: circle(300% at 50% 50%); + } +} +.intro .mask .text-section { + background-image: url(../images/intro/promise_b.svg); + background-position: bottom; + background-size: auto; + background-repeat: no-repeat; +} +@media only screen and (max-width: 1023px) { + .intro .mask .text-section { + background-size: 95% auto; + } +} +.intro .mask .mask-text { + color: #fff; +} +.intro .mask .mask-text em { + font-style: normal; +} +.intro .content-area { + margin-top: 34px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +@media only screen and (max-width: 991px) { + .intro .content-area { + flex: 1 1 0; + min-height: 0; + margin-top: 0; + align-items: stretch; + } +} +.intro .card { + opacity: 0; + transform: translateY(60px); + transition: all 0.6s ease; + background: white; + border: 2px solid rgba(255, 255, 255, 0.8); + border-radius: 0 0 12px 12px; + box-shadow: 0 24px 24px 0 rgba(215, 209, 204, 0.5); + text-align: center; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +@media only screen and (min-width: 992px) { + .intro .card { + flex: 0 0 calc(50% - 12px); + padding: 60px 32px 48px; + } +} +@media only screen and (min-width: 1200px) { + .intro .card { + flex: 1 1 0; + width: 340px; + padding: 86px 22px 68px; + } +} +@media only screen and (max-width: 991px) { + .intro .card { + flex: 1 1 0; + min-height: 0; + width: calc(100vw - 32px); + padding: 0 24px; + z-index: 9; + transition-duration: 0.9s; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + box-shadow: 0 4px 12px rgba(215, 209, 204, 0.5); + border-radius: 12px; + } +} +.intro .card.show { + opacity: 1; + transform: translateY(0); +} +.intro .card-list { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 20px; +} +@media only screen and (min-width: 1200px) { + .intro .card-list { + flex-wrap: nowrap; + } +} +@media only screen and (max-width: 1023px) { + .intro .card-list { + gap: 2.4rem; + } +} +@media only screen and (max-width: 991px) { + .intro .card-list { + flex-direction: column; + flex-wrap: nowrap; + flex: 1 1 0; + min-height: var(--window-inner-height); + gap: 8px; + padding: 8px 16px; + align-items: stretch; + } +} +.intro .card-num { + font-family: YeogiOttaeJalnan; + font-size: 120px; + font-weight: 400; + line-height: 1; + color: transparent; + opacity: 0.1; + background: linear-gradient(180deg, transparent 30%, #ffffff 80%) #1b1810; + -webkit-background-clip: text; + background-clip: text; +} +@media only screen and (max-width: 1023px) { + .intro .card-num { + font-size: 6rem; + } +} +.intro .card-title { + margin-top: -46px; + margin-bottom: 42px; + font-size: 32px; + font-weight: 300; + line-height: 1.2; +} +@media only screen and (max-width: 1023px) { + .intro .card-title { + font-size: 2.4rem; + } +} +@media only screen and (max-width: 991px) { + .intro .card-title { + margin-top: -32px; + margin-bottom: 0; + } +} +.intro .card-desc { + font-size: 24px; +} +@media only screen and (max-width: 1023px) { + .intro .card-desc { + font-size: 1.6rem; + } +} +@media only screen and (max-width: 991px) { + .intro .card-desc { + display: none; + } +} +.intro .card em { + font-weight: 700; +} + +.typing { + opacity: 0; +} + +.cursor { + display: inline-block; + width: 2px; + height: 1.2em; + margin-left: 2px; + vertical-align: text-bottom; + animation: blink 0.7s infinite; +} + +.scroll-indicator { + position: absolute; + bottom: 0; + left: 50%; + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; + transform: translateX(-50%); +} +.scroll-indicator.hidden { + opacity: 0; + pointer-events: none; +} +.scroll-indicator.sec2 .bar { + height: 60px; +} +.scroll-indicator span { + font-size: 16px; + color: var(--text-intro-base); + opacity: 0.8; +} +.scroll-indicator .bar { + width: 2px; + height: 110px; + overflow: hidden; + background-color: rgba(0, 0, 0, 0.3); + transition: height 0.5s linear; +} +.scroll-indicator .bar::before { + content: " "; + display: block; + width: 100%; + height: 100%; + border-radius: 0 0 4px 4px; + background-color: #111; + animation: scroll-down 2s infinite ease-in-out; +} \ No newline at end of file diff --git a/src/css/learning_objectives.css b/src/css/learning_objectives.css new file mode 100644 index 0000000..3b4273b --- /dev/null +++ b/src/css/learning_objectives.css @@ -0,0 +1,698 @@ +@charset "UTF-8"; +:root { + /* text - 텍스트 색상 */ + --text-intro-base: #1b1810; + --text-base: #131313; + --text-revers: #fff; + --text-primary: #3c3321; + --text-secondary: #747474; + --text-accent: #ff5c00; + --text-myclass: #ffdf60; + --text-main-primary: #4a4040; + --text-main-secondary: #3a200d; + --text-title-accent: #f5651d; + --text-pick: #2b5045; + --text-keyword-base: #c4c2c0; + --text-keyword-primary: #fff; + --text-keyword-secondary: #999999; + --text-video-primary: #b6d5a7; + --text-video-secondary: #ddd; + --text-cate-primary: #edd8c2; + --text-cate-secondary: #b6d5a7; + --text-cate-tertiary: #e8cfe4; + --text-comment-primary: #fff; + --text-comment-secondary: #8d8d8d; + --text-learning-base: #8d8888; + --text-learning-primary: #ff5c00; + --text-learning-secondary: #008c49; + --text-card-category: #252525; + --text-card-title-active: #ff7d33; + --text-card-title-complete: #66ba92; + --text-card-author: #d1cfcf; + /* 배경 */ + --bg-base: #e4ddcf; + --bg-primary: #ece3d2; + --bg-secondary: + radial-gradient( + 93.89% 93.89% at 49.32% 6.11%, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.11) 86.06%, + rgba(134, 114, 77, 0.2) 88.94% + ), + linear-gradient(180deg, #f9f6f0 0%, #e6ddcc 100%); + --bg-main-card: rgba(255, 255, 255, 0.6); + --bg-intro-mask: #1b1810; + --bg-intro: linear-gradient( + 180deg, + #f9f5f2 0%, + #fff 18.77%, + #fff 41.8%, + #ece8e4 100% + ); + --bg-main: + linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ) + top / 100% 114px no-repeat, + #ece3d2; + --bg-video: #1b1b1b; + --bg-comment: #2a2a2a; + --bg-nav: linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ); + --bg-nav-depth: #fff; + --bg-nav-alerts: #ff2200; + --bg-nav-alerts-hover: #188f6b; + --bg-cate-primary: #ded7cf; + --bg-cate-secondary: #d4decf; + --bg-cate-tertiary: #e8cfe4; + --bg-pick: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #d5ede6 100%); + --bg-gauge-base: #b4b4b4; + --bg-gauge-primary: #ff5c00; + --bg-search: #e4dbc9; + --bg-keyword-base: rgba(230, 205, 177, 0.3); + --bg-keyword-primary: #e47703; + --bg-keyword-primary-hover: #e6cdb1; + --bg-keyword-secondary: #786c60; + --bg-keyword-secondary-hover: #dcd3c9; + --bg-lecture-state: linear-gradient(270deg, #00ab66 0%, #058bb0 100%); + --bg-lecture-progress-primary: rgba(3, 9, 7, 0.3); + --bg-lecture-progress-secondary: #07855c; + --bg-btn-base: #000; + --bg-btn-primary: #ff5c00; + --bg-btn-secondary: #1f1f1f; + --bg-card: rgba(255, 255, 255, 0.6); + --bg-card-hover-start: #ff6600; + --bg-card-hover-end: rgba(255, 132, 0, 0.7); + --bg-card-thumb-overlay: rgba(255, 255, 255, 0.3); + --bg-card-shadow: rgba(217, 202, 190, 0.5); + --bg-card-hover-shadow: rgba(198, 187, 177, 0.5); + --bg-card-shadow-inner: rgba(0, 0, 0, 1); + --bg-pick-shadow: rgba(166, 154, 97, 0.25); + --bg-modal: rgba(0, 0, 0, 0.8); + --bg-modal-content: #f6f1e9; + --bg-modal-close-hover: #e00400; + --bg-scrollbar-thumb: #D0D0D0; + --bg-scrollbar-thumb-dark: #383838; + --bg-scrollbar-track: #F3F3F3; + --bg-scrollbar-thumb-light: rgba(255, 255, 255, 0.2); + --bg-scrollbar-thumb-light-hover: rgba(255, 255, 255, 0.3); + --bg-scrollbar-track-light: rgba(255, 255, 255, 0.05); + --bg-video-gauge: #171717; + --bg-video-gauge-fill: #ff6c19; + --bg-video-gauge-border: rgba(0, 0, 0, 0.4); + --bg-learning-line: #c6c3c3; + --bg-learning-dot: #8d8888; + --bg-learning-active: rgba(255, 92, 0, 0.2); + --bg-learning-active-line: #ffad7f; + --bg-learning-active-dot: #ff5c00; + --bg-learning-complete: rgba(0, 140, 73, 0.2); + --bg-learning-complete-line: #7fc5a4; + --bg-learning-complete-dot: #008c49; + --bg-chapter-current: #146b51; + --bg-chapter-completed: #ba9a85; + --bg-card-base: #7e7e7e; + --bg-card-current: #1d9b75; + --bg-card-current-border: #1f9b76; + --bg-card-current-bg: #dbefe9; + --bg-card-completed: #ab3d00; + --bg-gauge-fill: #e25e00; + --bg-shadow: #8a7d64; + --bg-puzzle-family: #7ed29b; + --bg-puzzle-hanmac: #ffccca; + --bg-puzzle-value: #87a7d6; + --bg-puzzle-company: #b49a65; + --bg-piece-1: #1d375d; + --bg-piece-2: #662a0d; + --bg-piece-3: #5b4822; + --bg-piece-4: #2a5338; + --bg-circle-border: #a7790f; + --bg-circle-start: rgba(127, 47, 0, 0.1); + --bg-circle-end: rgba(167, 121, 15, 0.5); + --bg-circle-dot: rgba(221, 144, 0, 0.6); + --bg-circle-dot-hover: rgba(221, 144, 0, 0.8); + --bg-circle-dot-stroke: rgba(221, 144, 0, 0.2); + --bg-gradient-start: #ece3d2; + --bg-gradient-end: #fff; + --bg-learning-line-color: #edba8a; + --bg-modal-header: #f04400; + --bg-textarea: #2a2a2a; + --bg-textarea-placeholder: rgba(255, 255, 255, 0.5); + /* border */ + --border-base: rgba(0, 0, 0, 0.05); + --border-primary: #fff; + --border-keyword-base: rgba(0, 0, 0, 0.05); + --border-keyword-primary: rgba(255, 255, 255, 0.4); + --border-keyword-primary-hover: rgba(0, 0, 0, 0.05); + --border-keyword-secondary: rgba(255, 255, 255, 0.4); + --border-keyword-secondary-hover: rgba(0, 0, 0, 0.1); + --border-pick: rgba(255, 255, 255, 0.5); + --border-video: rgba(255, 255, 255, 0.1); + --border-btn: rgba(255, 255, 255, 0.2); + --border-card: rgba(255, 255, 255, 0.8); + --border-modal: #888; + --border-comment-resizer: rgba(230, 227, 225, 0.1); + /* drop */ + --text-shadow: + -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; + --text-stroke: drop-shadow(0 0 0.5px #000); + /* shadow */ + --shadow-pagination: rgba(0, 0, 0, 0.4); + --shadow-modal: rgba(0, 0, 0, 0.25); + --shadow-card-drop: 0 4px 8px rgba(0, 0, 0, 0.2); + --shadow-card-drop-small: 0 4px 2px rgba(0, 0, 0, 0.05); + --shadow-gauge-inset: rgba(0, 0, 0, 0.3); + --shadow-comment: 0 8px 22px 22px rgba(0, 0, 0, 0.8); +} + +/* 페이드 전환 */ +.fade-out { + opacity: 0; + transition: opacity 0.4s ease; +} + +.fade-in { + opacity: 1; + transition: opacity 0.4s ease; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes arrow-next { + 0%, 100% { + right: 72px; + } + 50% { + right: 50px; + } +} +@keyframes bounce { + 0%, 100% { + transform: translateX(-50%) translateY(0); + } + 50% { + transform: translateX(-50%) translateY(-12px); + } +} +@keyframes slideUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes scroll-down { + 0% { + transform-origin: 50% 100%; + transform: scaleY(1); + } + 50% { + transform-origin: 50% 100%; + transform: scaleY(0); + } + 50.1% { + transform-origin: 50% 0; + transform: scaleY(0); + } + to { + transform-origin: 50% 0; + transform: scaleY(1); + } +} +@keyframes pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.4; + } +} +@keyframes borderFadeIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes borderPulse { + 0%, 100% { + border-color: #fff; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.4); + } + 50% { + border-color: #fff; + box-shadow: 0 0 30px rgba(255, 255, 255, 0.6); + } +} +.study .study-container { + display: flex; + gap: 40px; + margin: 0 auto; + padding: 40px 60px; + position: relative; + background: linear-gradient(180deg, #F9F6F0 0%, #E6DDCC 100%); +} +.study .study-container::before { + content: " "; + display: block; + position: absolute; + top: 0; + right: 0; + width: 30.8333%; + padding-top: 90.54054%; + background-image: url("../images/myclass/bg_lo.png"); + background-size: contain; + background-position: 0 0; + background-repeat: no-repeat; + pointer-events: none; + z-index: 0; + mix-blend-mode: multiply; +} +.study .page-title { + margin-bottom: 72px; +} +.study .study-sidebar { + flex-shrink: 0; + width: 380px; + position: sticky; + top: 40px; + height: -moz-fit-content; + height: fit-content; + z-index: 1; +} +.study .study-sidebar .sidebar-content { + background: rgba(255, 255, 255, 0.8); + border-radius: 20px; + padding: 32px; + backdrop-filter: blur(10px); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); +} +.study .study-sidebar .user-rank-info { + margin-bottom: 32px; + padding-bottom: 24px; + border-bottom: 1px solid rgba(0, 0, 0, 0.08); +} +.study .study-sidebar .user-rank-info .rank-header { + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: row; + gap: 12px; +} +.study .study-sidebar .user-rank-info .rank-header .ico-trophy { + background-image: url("../images/ico/ico_trophy.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + width: 32px; + aspect-ratio: 1/1; + flex-shrink: 0; +} +.study .study-sidebar .user-rank-info .rank-header .rank-text { + font-size: 16px; + line-height: 1.5; + color: var(--text-base); +} +.study .study-sidebar .user-rank-info .rank-header .rank-text strong { + font-weight: 700; + color: var(--text-primary); +} +.study .study-sidebar .user-rank-info .rank-header .rank-text em { + font-style: normal; + font-weight: 700; + color: var(--text-accent); + margin: 0 4px; +} +.study .study-sidebar .user-rank-info .rank-header .rank-text small { + font-size: 14px; + color: var(--text-secondary); + font-weight: 400; +} +.study .study-sidebar .quarter-selector { + margin-bottom: 32px; +} +.study .study-sidebar .quarter-selector .year-select { + margin-bottom: 12px; +} +.study .study-sidebar .quarter-selector .year-select .btn-year { + width: 100%; + padding: 12px 16px; + background: #fff; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 12px; + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + cursor: pointer; + display: flex; + align-items: space-between; + justify-content: center; + flex-direction: row; + transition: all 0.2s ease; +} +.study .study-sidebar .quarter-selector .year-select .btn-year:hover { + background: #f5f5f5; + border-color: rgba(0, 0, 0, 0.2); +} +.study .study-sidebar .quarter-selector .year-select .btn-year .ico-arrow-down { + background-image: url("../images/ico/ico_arrow_down.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + width: 16px; + aspect-ratio: 1/1; +} +.study .study-sidebar .quarter-selector .quarter-select .btn-quarter { + width: 100%; + padding: 12px 16px; + background: #fff; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 12px; + font-size: 16px; + font-weight: 600; + color: var(--text-secondary); + cursor: pointer; + transition: all 0.2s ease; +} +.study .study-sidebar .quarter-selector .quarter-select .btn-quarter.active { + background: var(--text-accent); + color: #fff; + border-color: var(--text-accent); +} +.study .study-sidebar .quarter-selector .quarter-select .btn-quarter:hover:not(.active) { + background: #f5f5f5; + border-color: rgba(0, 0, 0, 0.2); +} +.study .study-sidebar .learning-goal .goal-title { + font-size: 22px; + font-weight: 700; + color: var(--text-primary); + margin-bottom: 12px; + line-height: 1.4; +} +.study .study-sidebar .learning-goal .goal-title .emoji { + font-style: normal; + margin-left: 4px; +} +.study .study-sidebar .learning-goal .goal-description { + font-size: 14px; + line-height: 1.6; + color: var(--text-secondary); + margin-bottom: 20px; +} +.study .study-sidebar .learning-goal .btn-change-goal { + width: 100%; + padding: 12px 20px; + background: var(--text-accent); + color: #fff; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} +.study .study-sidebar .learning-goal .btn-change-goal:hover { + background: #e55000; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 92, 0, 0.3); +} +.study .study-main { + flex: 1; + min-width: 0; + position: relative; + z-index: 1; +} +.study .study-main .study-header { + margin-bottom: 40px; +} +.study .study-main .study-header .study-title { + font-size: 32px; + font-weight: 700; + line-height: 1.4; + color: var(--text-primary); + margin-bottom: 16px; +} +.study .study-main .study-header .study-title em { + font-style: normal; + color: var(--text-accent); +} +.study .study-main .study-header .study-countdown { + font-size: 18px; + color: var(--text-secondary); + line-height: 1.5; +} +.study .study-main .study-header .study-countdown small { + font-size: 16px; + color: var(--text-base); +} +.study .study-main .study-modules { + margin-bottom: 40px; +} +.study .study-main .study-modules .modules-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 32px 24px; +} +.study .study-main .module-card { + position: relative; + background: #fff; + border-radius: 16px; + overflow: hidden; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); + transition: all 0.3s ease; + cursor: pointer; + display: flex; + flex-direction: column; + height: 100%; +} +.study .study-main .module-card:hover { + transform: translateY(-8px); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15); +} +.study .study-main .module-card:hover .card-thumbnail img { + transform: scale(1.05); +} +.study .study-main .module-card.card-color-teal { + border-top: 4px solid #4db6ac; +} +.study .study-main .module-card.card-color-pink { + border-top: 4px solid #f48fb1; +} +.study .study-main .module-card.card-color-blue { + border-top: 4px solid #81d4fa; +} +.study .study-main .module-card.card-color-yellow { + border-top: 4px solid #ffeb3b; +} +.study .study-main .module-card.card-color-green { + border-top: 4px solid #a5d6a7; +} +.study .study-main .module-card.card-color-beige { + border-top: 4px solid #d7ccc8; +} +.study .study-main .module-card .card-thumbnail { + width: 100%; + aspect-ratio: 16/9; + overflow: hidden; + background: #f5f5f5; +} +.study .study-main .module-card .card-thumbnail img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; + transition: transform 0.3s ease; +} +.study .study-main .module-card .card-content { + padding: 20px 24px; + flex: 1; + display: flex; + flex-direction: column; +} +.study .study-main .module-card .card-content .card-category { + display: inline-block; + font-size: 12px; + font-weight: 600; + color: var(--text-secondary); + margin-bottom: 8px; + padding: 4px 12px; + background: rgba(0, 0, 0, 0.04); + border-radius: 12px; +} +.study .study-main .module-card .card-content .card-title { + font-size: 20px; + font-weight: 700; + line-height: 1.4; + color: var(--text-primary); + margin-bottom: 8px; +} +.study .study-main .module-card .card-content .card-subtitle { + font-size: 14px; + line-height: 1.5; + color: var(--text-base); + margin-bottom: 8px; + font-weight: 500; +} +.study .study-main .module-card .card-content .card-description { + font-size: 13px; + line-height: 1.5; + color: var(--text-secondary); + margin-bottom: 16px; +} +.study .study-main .module-card .card-content .card-check { + margin-top: auto; + padding-top: 16px; + border-top: 1px solid rgba(0, 0, 0, 0.08); +} +.study .study-main .module-card .card-content .card-check .check-title { + display: block; + font-size: 12px; + font-weight: 700; + color: var(--text-accent); + margin-bottom: 8px; + text-transform: uppercase; + letter-spacing: 0.5px; +} +.study .study-main .module-card .card-content .card-check .check-list { + list-style: none; + padding: 0; + margin: 0; +} +.study .study-main .module-card .card-content .card-check .check-list li { + position: relative; + padding-left: 20px; + font-size: 12px; + line-height: 1.6; + color: var(--text-secondary); + margin-bottom: 6px; +} +.study .study-main .module-card .card-content .card-check .check-list li:last-child { + margin-bottom: 0; +} +.study .study-main .module-card .card-content .card-check .check-list li::before { + content: "•"; + position: absolute; + left: 0; + color: var(--text-accent); + font-weight: 700; + font-size: 16px; + line-height: 1.2; +} +.study .study-main .study-tags { + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: row; + gap: 16px; + padding: 20px 0; + border-top: 1px solid rgba(0, 0, 0, 0.08); +} +.study .study-main .study-tags .tag-quarter { + font-size: 18px; + font-weight: 700; + color: var(--text-primary); + padding: 8px 16px; + background: rgba(255, 92, 0, 0.1); + border-radius: 12px; +} +.study .study-main .study-tags .tag-list { + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: row; + gap: 12px; + flex-wrap: wrap; +} +.study .study-main .study-tags .tag-list .tag-item { + padding: 8px 20px; + background: #fff; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 20px; + font-size: 14px; + font-weight: 500; + color: var(--text-secondary); + cursor: pointer; + transition: all 0.2s ease; +} +.study .study-main .study-tags .tag-list .tag-item:hover { + background: #f5f5f5; + border-color: rgba(0, 0, 0, 0.2); +} +.study .study-main .study-tags .tag-list .tag-item.active { + background: var(--text-accent); + color: #fff; + border-color: var(--text-accent); +} + +@media (max-width: 1400px) { + .study .study-container { + padding: 32px 40px; + gap: 32px; + } + .study .study-sidebar { + width: 320px; + } + .study .study-main .modules-grid { + gap: 24px 20px; + } +} +@media (max-width: 1024px) { + .study .study-container { + flex-direction: column; + padding: 24px 32px; + } + .study .study-sidebar { + width: 100%; + position: relative; + top: 0; + } + .study .study-sidebar .sidebar-content { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + } + .study .study-sidebar .user-rank-info, + .study .study-sidebar .quarter-selector, + .study .study-sidebar .learning-goal { + border-bottom: none; + margin-bottom: 0; + padding-bottom: 0; + } + .study .study-main .modules-grid { + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 768px) { + .study .study-container { + padding: 20px 24px; + } + .study .study-sidebar .sidebar-content { + grid-template-columns: 1fr; + gap: 20px; + } + .study .study-main .study-header .study-title { + font-size: 24px; + } + .study .study-main .study-header .study-countdown { + font-size: 16px; + } + .study .study-main .modules-grid { + grid-template-columns: 1fr; + gap: 20px; + } +} \ No newline at end of file diff --git a/src/css/lib/aos.min.css b/src/css/lib/aos.min.css new file mode 100644 index 0000000..66923fe --- /dev/null +++ b/src/css/lib/aos.min.css @@ -0,0 +1 @@ +[data-aos][data-aos][data-aos-duration="50"],body[data-aos-duration="50"] [data-aos]{transition-duration:50ms}[data-aos][data-aos][data-aos-delay="50"],body[data-aos-delay="50"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="50"].aos-animate,body[data-aos-delay="50"] [data-aos].aos-animate{transition-delay:50ms}[data-aos][data-aos][data-aos-duration="100"],body[data-aos-duration="100"] [data-aos]{transition-duration:.1s}[data-aos][data-aos][data-aos-delay="100"],body[data-aos-delay="100"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="100"].aos-animate,body[data-aos-delay="100"] [data-aos].aos-animate{transition-delay:.1s}[data-aos][data-aos][data-aos-duration="150"],body[data-aos-duration="150"] [data-aos]{transition-duration:.15s}[data-aos][data-aos][data-aos-delay="150"],body[data-aos-delay="150"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="150"].aos-animate,body[data-aos-delay="150"] [data-aos].aos-animate{transition-delay:.15s}[data-aos][data-aos][data-aos-duration="200"],body[data-aos-duration="200"] [data-aos]{transition-duration:.2s}[data-aos][data-aos][data-aos-delay="200"],body[data-aos-delay="200"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="200"].aos-animate,body[data-aos-delay="200"] [data-aos].aos-animate{transition-delay:.2s}[data-aos][data-aos][data-aos-duration="250"],body[data-aos-duration="250"] [data-aos]{transition-duration:.25s}[data-aos][data-aos][data-aos-delay="250"],body[data-aos-delay="250"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="250"].aos-animate,body[data-aos-delay="250"] [data-aos].aos-animate{transition-delay:.25s}[data-aos][data-aos][data-aos-duration="300"],body[data-aos-duration="300"] [data-aos]{transition-duration:.3s}[data-aos][data-aos][data-aos-delay="300"],body[data-aos-delay="300"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="300"].aos-animate,body[data-aos-delay="300"] [data-aos].aos-animate{transition-delay:.3s}[data-aos][data-aos][data-aos-duration="350"],body[data-aos-duration="350"] [data-aos]{transition-duration:.35s}[data-aos][data-aos][data-aos-delay="350"],body[data-aos-delay="350"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="350"].aos-animate,body[data-aos-delay="350"] [data-aos].aos-animate{transition-delay:.35s}[data-aos][data-aos][data-aos-duration="400"],body[data-aos-duration="400"] [data-aos]{transition-duration:.4s}[data-aos][data-aos][data-aos-delay="400"],body[data-aos-delay="400"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="400"].aos-animate,body[data-aos-delay="400"] [data-aos].aos-animate{transition-delay:.4s}[data-aos][data-aos][data-aos-duration="450"],body[data-aos-duration="450"] [data-aos]{transition-duration:.45s}[data-aos][data-aos][data-aos-delay="450"],body[data-aos-delay="450"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="450"].aos-animate,body[data-aos-delay="450"] [data-aos].aos-animate{transition-delay:.45s}[data-aos][data-aos][data-aos-duration="500"],body[data-aos-duration="500"] [data-aos]{transition-duration:.5s}[data-aos][data-aos][data-aos-delay="500"],body[data-aos-delay="500"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="500"].aos-animate,body[data-aos-delay="500"] [data-aos].aos-animate{transition-delay:.5s}[data-aos][data-aos][data-aos-duration="550"],body[data-aos-duration="550"] [data-aos]{transition-duration:.55s}[data-aos][data-aos][data-aos-delay="550"],body[data-aos-delay="550"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="550"].aos-animate,body[data-aos-delay="550"] [data-aos].aos-animate{transition-delay:.55s}[data-aos][data-aos][data-aos-duration="600"],body[data-aos-duration="600"] [data-aos]{transition-duration:.6s}[data-aos][data-aos][data-aos-delay="600"],body[data-aos-delay="600"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="600"].aos-animate,body[data-aos-delay="600"] [data-aos].aos-animate{transition-delay:.6s}[data-aos][data-aos][data-aos-duration="650"],body[data-aos-duration="650"] [data-aos]{transition-duration:.65s}[data-aos][data-aos][data-aos-delay="650"],body[data-aos-delay="650"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="650"].aos-animate,body[data-aos-delay="650"] [data-aos].aos-animate{transition-delay:.65s}[data-aos][data-aos][data-aos-duration="700"],body[data-aos-duration="700"] [data-aos]{transition-duration:.7s}[data-aos][data-aos][data-aos-delay="700"],body[data-aos-delay="700"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="700"].aos-animate,body[data-aos-delay="700"] [data-aos].aos-animate{transition-delay:.7s}[data-aos][data-aos][data-aos-duration="750"],body[data-aos-duration="750"] [data-aos]{transition-duration:.75s}[data-aos][data-aos][data-aos-delay="750"],body[data-aos-delay="750"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="750"].aos-animate,body[data-aos-delay="750"] [data-aos].aos-animate{transition-delay:.75s}[data-aos][data-aos][data-aos-duration="800"],body[data-aos-duration="800"] [data-aos]{transition-duration:.8s}[data-aos][data-aos][data-aos-delay="800"],body[data-aos-delay="800"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="800"].aos-animate,body[data-aos-delay="800"] [data-aos].aos-animate{transition-delay:.8s}[data-aos][data-aos][data-aos-duration="850"],body[data-aos-duration="850"] [data-aos]{transition-duration:.85s}[data-aos][data-aos][data-aos-delay="850"],body[data-aos-delay="850"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="850"].aos-animate,body[data-aos-delay="850"] [data-aos].aos-animate{transition-delay:.85s}[data-aos][data-aos][data-aos-duration="900"],body[data-aos-duration="900"] [data-aos]{transition-duration:.9s}[data-aos][data-aos][data-aos-delay="900"],body[data-aos-delay="900"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="900"].aos-animate,body[data-aos-delay="900"] [data-aos].aos-animate{transition-delay:.9s}[data-aos][data-aos][data-aos-duration="950"],body[data-aos-duration="950"] [data-aos]{transition-duration:.95s}[data-aos][data-aos][data-aos-delay="950"],body[data-aos-delay="950"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="950"].aos-animate,body[data-aos-delay="950"] [data-aos].aos-animate{transition-delay:.95s}[data-aos][data-aos][data-aos-duration="1000"],body[data-aos-duration="1000"] [data-aos]{transition-duration:1s}[data-aos][data-aos][data-aos-delay="1000"],body[data-aos-delay="1000"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1000"].aos-animate,body[data-aos-delay="1000"] [data-aos].aos-animate{transition-delay:1s}[data-aos][data-aos][data-aos-duration="1050"],body[data-aos-duration="1050"] [data-aos]{transition-duration:1.05s}[data-aos][data-aos][data-aos-delay="1050"],body[data-aos-delay="1050"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1050"].aos-animate,body[data-aos-delay="1050"] [data-aos].aos-animate{transition-delay:1.05s}[data-aos][data-aos][data-aos-duration="1100"],body[data-aos-duration="1100"] [data-aos]{transition-duration:1.1s}[data-aos][data-aos][data-aos-delay="1100"],body[data-aos-delay="1100"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1100"].aos-animate,body[data-aos-delay="1100"] [data-aos].aos-animate{transition-delay:1.1s}[data-aos][data-aos][data-aos-duration="1150"],body[data-aos-duration="1150"] [data-aos]{transition-duration:1.15s}[data-aos][data-aos][data-aos-delay="1150"],body[data-aos-delay="1150"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1150"].aos-animate,body[data-aos-delay="1150"] [data-aos].aos-animate{transition-delay:1.15s}[data-aos][data-aos][data-aos-duration="1200"],body[data-aos-duration="1200"] [data-aos]{transition-duration:1.2s}[data-aos][data-aos][data-aos-delay="1200"],body[data-aos-delay="1200"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1200"].aos-animate,body[data-aos-delay="1200"] [data-aos].aos-animate{transition-delay:1.2s}[data-aos][data-aos][data-aos-duration="1250"],body[data-aos-duration="1250"] [data-aos]{transition-duration:1.25s}[data-aos][data-aos][data-aos-delay="1250"],body[data-aos-delay="1250"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1250"].aos-animate,body[data-aos-delay="1250"] [data-aos].aos-animate{transition-delay:1.25s}[data-aos][data-aos][data-aos-duration="1300"],body[data-aos-duration="1300"] [data-aos]{transition-duration:1.3s}[data-aos][data-aos][data-aos-delay="1300"],body[data-aos-delay="1300"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1300"].aos-animate,body[data-aos-delay="1300"] [data-aos].aos-animate{transition-delay:1.3s}[data-aos][data-aos][data-aos-duration="1350"],body[data-aos-duration="1350"] [data-aos]{transition-duration:1.35s}[data-aos][data-aos][data-aos-delay="1350"],body[data-aos-delay="1350"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1350"].aos-animate,body[data-aos-delay="1350"] [data-aos].aos-animate{transition-delay:1.35s}[data-aos][data-aos][data-aos-duration="1400"],body[data-aos-duration="1400"] [data-aos]{transition-duration:1.4s}[data-aos][data-aos][data-aos-delay="1400"],body[data-aos-delay="1400"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1400"].aos-animate,body[data-aos-delay="1400"] [data-aos].aos-animate{transition-delay:1.4s}[data-aos][data-aos][data-aos-duration="1450"],body[data-aos-duration="1450"] [data-aos]{transition-duration:1.45s}[data-aos][data-aos][data-aos-delay="1450"],body[data-aos-delay="1450"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1450"].aos-animate,body[data-aos-delay="1450"] [data-aos].aos-animate{transition-delay:1.45s}[data-aos][data-aos][data-aos-duration="1500"],body[data-aos-duration="1500"] [data-aos]{transition-duration:1.5s}[data-aos][data-aos][data-aos-delay="1500"],body[data-aos-delay="1500"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1500"].aos-animate,body[data-aos-delay="1500"] [data-aos].aos-animate{transition-delay:1.5s}[data-aos][data-aos][data-aos-duration="1550"],body[data-aos-duration="1550"] [data-aos]{transition-duration:1.55s}[data-aos][data-aos][data-aos-delay="1550"],body[data-aos-delay="1550"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1550"].aos-animate,body[data-aos-delay="1550"] [data-aos].aos-animate{transition-delay:1.55s}[data-aos][data-aos][data-aos-duration="1600"],body[data-aos-duration="1600"] [data-aos]{transition-duration:1.6s}[data-aos][data-aos][data-aos-delay="1600"],body[data-aos-delay="1600"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1600"].aos-animate,body[data-aos-delay="1600"] [data-aos].aos-animate{transition-delay:1.6s}[data-aos][data-aos][data-aos-duration="1650"],body[data-aos-duration="1650"] [data-aos]{transition-duration:1.65s}[data-aos][data-aos][data-aos-delay="1650"],body[data-aos-delay="1650"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1650"].aos-animate,body[data-aos-delay="1650"] [data-aos].aos-animate{transition-delay:1.65s}[data-aos][data-aos][data-aos-duration="1700"],body[data-aos-duration="1700"] [data-aos]{transition-duration:1.7s}[data-aos][data-aos][data-aos-delay="1700"],body[data-aos-delay="1700"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1700"].aos-animate,body[data-aos-delay="1700"] [data-aos].aos-animate{transition-delay:1.7s}[data-aos][data-aos][data-aos-duration="1750"],body[data-aos-duration="1750"] [data-aos]{transition-duration:1.75s}[data-aos][data-aos][data-aos-delay="1750"],body[data-aos-delay="1750"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1750"].aos-animate,body[data-aos-delay="1750"] [data-aos].aos-animate{transition-delay:1.75s}[data-aos][data-aos][data-aos-duration="1800"],body[data-aos-duration="1800"] [data-aos]{transition-duration:1.8s}[data-aos][data-aos][data-aos-delay="1800"],body[data-aos-delay="1800"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1800"].aos-animate,body[data-aos-delay="1800"] [data-aos].aos-animate{transition-delay:1.8s}[data-aos][data-aos][data-aos-duration="1850"],body[data-aos-duration="1850"] [data-aos]{transition-duration:1.85s}[data-aos][data-aos][data-aos-delay="1850"],body[data-aos-delay="1850"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1850"].aos-animate,body[data-aos-delay="1850"] [data-aos].aos-animate{transition-delay:1.85s}[data-aos][data-aos][data-aos-duration="1900"],body[data-aos-duration="1900"] [data-aos]{transition-duration:1.9s}[data-aos][data-aos][data-aos-delay="1900"],body[data-aos-delay="1900"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1900"].aos-animate,body[data-aos-delay="1900"] [data-aos].aos-animate{transition-delay:1.9s}[data-aos][data-aos][data-aos-duration="1950"],body[data-aos-duration="1950"] [data-aos]{transition-duration:1.95s}[data-aos][data-aos][data-aos-delay="1950"],body[data-aos-delay="1950"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="1950"].aos-animate,body[data-aos-delay="1950"] [data-aos].aos-animate{transition-delay:1.95s}[data-aos][data-aos][data-aos-duration="2000"],body[data-aos-duration="2000"] [data-aos]{transition-duration:2s}[data-aos][data-aos][data-aos-delay="2000"],body[data-aos-delay="2000"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2000"].aos-animate,body[data-aos-delay="2000"] [data-aos].aos-animate{transition-delay:2s}[data-aos][data-aos][data-aos-duration="2050"],body[data-aos-duration="2050"] [data-aos]{transition-duration:2.05s}[data-aos][data-aos][data-aos-delay="2050"],body[data-aos-delay="2050"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2050"].aos-animate,body[data-aos-delay="2050"] [data-aos].aos-animate{transition-delay:2.05s}[data-aos][data-aos][data-aos-duration="2100"],body[data-aos-duration="2100"] [data-aos]{transition-duration:2.1s}[data-aos][data-aos][data-aos-delay="2100"],body[data-aos-delay="2100"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2100"].aos-animate,body[data-aos-delay="2100"] [data-aos].aos-animate{transition-delay:2.1s}[data-aos][data-aos][data-aos-duration="2150"],body[data-aos-duration="2150"] [data-aos]{transition-duration:2.15s}[data-aos][data-aos][data-aos-delay="2150"],body[data-aos-delay="2150"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2150"].aos-animate,body[data-aos-delay="2150"] [data-aos].aos-animate{transition-delay:2.15s}[data-aos][data-aos][data-aos-duration="2200"],body[data-aos-duration="2200"] [data-aos]{transition-duration:2.2s}[data-aos][data-aos][data-aos-delay="2200"],body[data-aos-delay="2200"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2200"].aos-animate,body[data-aos-delay="2200"] [data-aos].aos-animate{transition-delay:2.2s}[data-aos][data-aos][data-aos-duration="2250"],body[data-aos-duration="2250"] [data-aos]{transition-duration:2.25s}[data-aos][data-aos][data-aos-delay="2250"],body[data-aos-delay="2250"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2250"].aos-animate,body[data-aos-delay="2250"] [data-aos].aos-animate{transition-delay:2.25s}[data-aos][data-aos][data-aos-duration="2300"],body[data-aos-duration="2300"] [data-aos]{transition-duration:2.3s}[data-aos][data-aos][data-aos-delay="2300"],body[data-aos-delay="2300"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2300"].aos-animate,body[data-aos-delay="2300"] [data-aos].aos-animate{transition-delay:2.3s}[data-aos][data-aos][data-aos-duration="2350"],body[data-aos-duration="2350"] [data-aos]{transition-duration:2.35s}[data-aos][data-aos][data-aos-delay="2350"],body[data-aos-delay="2350"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2350"].aos-animate,body[data-aos-delay="2350"] [data-aos].aos-animate{transition-delay:2.35s}[data-aos][data-aos][data-aos-duration="2400"],body[data-aos-duration="2400"] [data-aos]{transition-duration:2.4s}[data-aos][data-aos][data-aos-delay="2400"],body[data-aos-delay="2400"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2400"].aos-animate,body[data-aos-delay="2400"] [data-aos].aos-animate{transition-delay:2.4s}[data-aos][data-aos][data-aos-duration="2450"],body[data-aos-duration="2450"] [data-aos]{transition-duration:2.45s}[data-aos][data-aos][data-aos-delay="2450"],body[data-aos-delay="2450"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2450"].aos-animate,body[data-aos-delay="2450"] [data-aos].aos-animate{transition-delay:2.45s}[data-aos][data-aos][data-aos-duration="2500"],body[data-aos-duration="2500"] [data-aos]{transition-duration:2.5s}[data-aos][data-aos][data-aos-delay="2500"],body[data-aos-delay="2500"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2500"].aos-animate,body[data-aos-delay="2500"] [data-aos].aos-animate{transition-delay:2.5s}[data-aos][data-aos][data-aos-duration="2550"],body[data-aos-duration="2550"] [data-aos]{transition-duration:2.55s}[data-aos][data-aos][data-aos-delay="2550"],body[data-aos-delay="2550"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2550"].aos-animate,body[data-aos-delay="2550"] [data-aos].aos-animate{transition-delay:2.55s}[data-aos][data-aos][data-aos-duration="2600"],body[data-aos-duration="2600"] [data-aos]{transition-duration:2.6s}[data-aos][data-aos][data-aos-delay="2600"],body[data-aos-delay="2600"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2600"].aos-animate,body[data-aos-delay="2600"] [data-aos].aos-animate{transition-delay:2.6s}[data-aos][data-aos][data-aos-duration="2650"],body[data-aos-duration="2650"] [data-aos]{transition-duration:2.65s}[data-aos][data-aos][data-aos-delay="2650"],body[data-aos-delay="2650"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2650"].aos-animate,body[data-aos-delay="2650"] [data-aos].aos-animate{transition-delay:2.65s}[data-aos][data-aos][data-aos-duration="2700"],body[data-aos-duration="2700"] [data-aos]{transition-duration:2.7s}[data-aos][data-aos][data-aos-delay="2700"],body[data-aos-delay="2700"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2700"].aos-animate,body[data-aos-delay="2700"] [data-aos].aos-animate{transition-delay:2.7s}[data-aos][data-aos][data-aos-duration="2750"],body[data-aos-duration="2750"] [data-aos]{transition-duration:2.75s}[data-aos][data-aos][data-aos-delay="2750"],body[data-aos-delay="2750"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2750"].aos-animate,body[data-aos-delay="2750"] [data-aos].aos-animate{transition-delay:2.75s}[data-aos][data-aos][data-aos-duration="2800"],body[data-aos-duration="2800"] [data-aos]{transition-duration:2.8s}[data-aos][data-aos][data-aos-delay="2800"],body[data-aos-delay="2800"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2800"].aos-animate,body[data-aos-delay="2800"] [data-aos].aos-animate{transition-delay:2.8s}[data-aos][data-aos][data-aos-duration="2850"],body[data-aos-duration="2850"] [data-aos]{transition-duration:2.85s}[data-aos][data-aos][data-aos-delay="2850"],body[data-aos-delay="2850"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2850"].aos-animate,body[data-aos-delay="2850"] [data-aos].aos-animate{transition-delay:2.85s}[data-aos][data-aos][data-aos-duration="2900"],body[data-aos-duration="2900"] [data-aos]{transition-duration:2.9s}[data-aos][data-aos][data-aos-delay="2900"],body[data-aos-delay="2900"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2900"].aos-animate,body[data-aos-delay="2900"] [data-aos].aos-animate{transition-delay:2.9s}[data-aos][data-aos][data-aos-duration="2950"],body[data-aos-duration="2950"] [data-aos]{transition-duration:2.95s}[data-aos][data-aos][data-aos-delay="2950"],body[data-aos-delay="2950"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="2950"].aos-animate,body[data-aos-delay="2950"] [data-aos].aos-animate{transition-delay:2.95s}[data-aos][data-aos][data-aos-duration="3000"],body[data-aos-duration="3000"] [data-aos]{transition-duration:3s}[data-aos][data-aos][data-aos-delay="3000"],body[data-aos-delay="3000"] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay="3000"].aos-animate,body[data-aos-delay="3000"] [data-aos].aos-animate{transition-delay:3s}[data-aos][data-aos][data-aos-easing=linear],body[data-aos-easing=linear] [data-aos]{transition-timing-function:cubic-bezier(.25,.25,.75,.75)}[data-aos][data-aos][data-aos-easing=ease],body[data-aos-easing=ease] [data-aos]{transition-timing-function:ease}[data-aos][data-aos][data-aos-easing=ease-in],body[data-aos-easing=ease-in] [data-aos]{transition-timing-function:ease-in}[data-aos][data-aos][data-aos-easing=ease-out],body[data-aos-easing=ease-out] [data-aos]{transition-timing-function:ease-out}[data-aos][data-aos][data-aos-easing=ease-in-out],body[data-aos-easing=ease-in-out] [data-aos]{transition-timing-function:ease-in-out}[data-aos][data-aos][data-aos-easing=ease-in-back],body[data-aos-easing=ease-in-back] [data-aos]{transition-timing-function:cubic-bezier(.6,-.28,.735,.045)}[data-aos][data-aos][data-aos-easing=ease-out-back],body[data-aos-easing=ease-out-back] [data-aos]{transition-timing-function:cubic-bezier(.175,.885,.32,1.275)}[data-aos][data-aos][data-aos-easing=ease-in-out-back],body[data-aos-easing=ease-in-out-back] [data-aos]{transition-timing-function:cubic-bezier(.68,-.55,.265,1.55)}[data-aos][data-aos][data-aos-easing=ease-in-sine],body[data-aos-easing=ease-in-sine] [data-aos]{transition-timing-function:cubic-bezier(.47,0,.745,.715)}[data-aos][data-aos][data-aos-easing=ease-out-sine],body[data-aos-easing=ease-out-sine] [data-aos]{transition-timing-function:cubic-bezier(.39,.575,.565,1)}[data-aos][data-aos][data-aos-easing=ease-in-out-sine],body[data-aos-easing=ease-in-out-sine] [data-aos]{transition-timing-function:cubic-bezier(.445,.05,.55,.95)}[data-aos][data-aos][data-aos-easing=ease-in-quad],body[data-aos-easing=ease-in-quad] [data-aos]{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[data-aos][data-aos][data-aos-easing=ease-out-quad],body[data-aos-easing=ease-out-quad] [data-aos]{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[data-aos][data-aos][data-aos-easing=ease-in-out-quad],body[data-aos-easing=ease-in-out-quad] [data-aos]{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[data-aos][data-aos][data-aos-easing=ease-in-cubic],body[data-aos-easing=ease-in-cubic] [data-aos]{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[data-aos][data-aos][data-aos-easing=ease-out-cubic],body[data-aos-easing=ease-out-cubic] [data-aos]{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[data-aos][data-aos][data-aos-easing=ease-in-out-cubic],body[data-aos-easing=ease-in-out-cubic] [data-aos]{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[data-aos][data-aos][data-aos-easing=ease-in-quart],body[data-aos-easing=ease-in-quart] [data-aos]{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[data-aos][data-aos][data-aos-easing=ease-out-quart],body[data-aos-easing=ease-out-quart] [data-aos]{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[data-aos][data-aos][data-aos-easing=ease-in-out-quart],body[data-aos-easing=ease-in-out-quart] [data-aos]{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[data-aos^=fade][data-aos^=fade]{opacity:0;transition-property:opacity,transform}[data-aos^=fade][data-aos^=fade].aos-animate{opacity:1;transform:translateZ(0)}[data-aos=fade-up]{transform:translate3d(0,100px,0)}[data-aos=fade-down]{transform:translate3d(0,-100px,0)}[data-aos=fade-right]{transform:translate3d(-100px,0,0)}[data-aos=fade-left]{transform:translate3d(100px,0,0)}[data-aos=fade-up-right]{transform:translate3d(-100px,100px,0)}[data-aos=fade-up-left]{transform:translate3d(100px,100px,0)}[data-aos=fade-down-right]{transform:translate3d(-100px,-100px,0)}[data-aos=fade-down-left]{transform:translate3d(100px,-100px,0)}[data-aos^=zoom][data-aos^=zoom]{opacity:0;transition-property:opacity,transform}[data-aos^=zoom][data-aos^=zoom].aos-animate{opacity:1;transform:translateZ(0) scale(1)}[data-aos=zoom-in]{transform:scale(.6)}[data-aos=zoom-in-up]{transform:translate3d(0,100px,0) scale(.6)}[data-aos=zoom-in-down]{transform:translate3d(0,-100px,0) scale(.6)}[data-aos=zoom-in-right]{transform:translate3d(-100px,0,0) scale(.6)}[data-aos=zoom-in-left]{transform:translate3d(100px,0,0) scale(.6)}[data-aos=zoom-out]{transform:scale(1.2)}[data-aos=zoom-out-up]{transform:translate3d(0,100px,0) scale(1.2)}[data-aos=zoom-out-down]{transform:translate3d(0,-100px,0) scale(1.2)}[data-aos=zoom-out-right]{transform:translate3d(-100px,0,0) scale(1.2)}[data-aos=zoom-out-left]{transform:translate3d(100px,0,0) scale(1.2)}[data-aos^=slide][data-aos^=slide]{transition-property:transform}[data-aos^=slide][data-aos^=slide].aos-animate{transform:translateZ(0)}[data-aos=slide-up]{transform:translate3d(0,100%,0)}[data-aos=slide-down]{transform:translate3d(0,-100%,0)}[data-aos=slide-right]{transform:translate3d(-100%,0,0)}[data-aos=slide-left]{transform:translate3d(100%,0,0)}[data-aos^=flip][data-aos^=flip]{backface-visibility:hidden;transition-property:transform}[data-aos=flip-left]{transform:perspective(2500px) rotateY(-100deg)}[data-aos=flip-left].aos-animate{transform:perspective(2500px) rotateY(0)}[data-aos=flip-right]{transform:perspective(2500px) rotateY(100deg)}[data-aos=flip-right].aos-animate{transform:perspective(2500px) rotateY(0)}[data-aos=flip-up]{transform:perspective(2500px) rotateX(-100deg)}[data-aos=flip-up].aos-animate{transform:perspective(2500px) rotateX(0)}[data-aos=flip-down]{transform:perspective(2500px) rotateX(100deg)}[data-aos=flip-down].aos-animate{transform:perspective(2500px) rotateX(0)} \ No newline at end of file diff --git a/src/css/lib/lenis.min.css b/src/css/lib/lenis.min.css new file mode 100644 index 0000000..f8b4ffd --- /dev/null +++ b/src/css/lib/lenis.min.css @@ -0,0 +1 @@ +html.lenis,html.lenis body{height:auto}.lenis.lenis-smooth{scroll-behavior:auto !important}.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}.lenis.lenis-stopped{overflow:hidden}.lenis.lenis-smooth iframe{pointer-events:none} \ No newline at end of file diff --git a/src/css/lib/swiper11.min.css b/src/css/lib/swiper11.min.css new file mode 100644 index 0000000..7a87bbf --- /dev/null +++ b/src/css/lib/swiper11.min.css @@ -0,0 +1,13 @@ +/** + * Swiper 11.2.5 + * Most modern mobile touch slider and framework with hardware accelerated transitions + * https://swiperjs.com + * + * Copyright 2014-2025 Vladimir Kharlampidi + * + * Released under the MIT License + * + * Released on: March 3, 2025 + */ + + @font-face{font-family:swiper-icons;src:url('data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA');font-weight:400;font-style:normal}:root{--swiper-theme-color:#007aff}:host{position:relative;display:block;margin-left:auto;margin-right:auto;z-index:1}.swiper{margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1;display:block}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;transition-timing-function:var(--swiper-wrapper-transition-timing-function,initial);box-sizing:content-box}.swiper-android .swiper-slide,.swiper-ios .swiper-slide,.swiper-wrapper{transform:translate3d(0px,0,0)}.swiper-horizontal{touch-action:pan-y}.swiper-vertical{touch-action:pan-x}.swiper-slide{flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform;display:block}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden .swiper-slide{transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d .swiper-slide{transform-style:preserve-3d}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}.swiper-css-mode.swiper-horizontal>.swiper-wrapper{scroll-snap-type:x mandatory}.swiper-css-mode.swiper-vertical>.swiper-wrapper{scroll-snap-type:y mandatory}.swiper-css-mode.swiper-free-mode>.swiper-wrapper{scroll-snap-type:none}.swiper-css-mode.swiper-free-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:none}.swiper-css-mode.swiper-centered>.swiper-wrapper::before{content:'';flex-shrink:0;order:9999}.swiper-css-mode.swiper-centered>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center;scroll-snap-stop:always}.swiper-css-mode.swiper-centered.swiper-horizontal>.swiper-wrapper>.swiper-slide:first-child{margin-inline-start:var(--swiper-centered-offset-before)}.swiper-css-mode.swiper-centered.swiper-horizontal>.swiper-wrapper::before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-css-mode.swiper-centered.swiper-vertical>.swiper-wrapper>.swiper-slide:first-child{margin-block-start:var(--swiper-centered-offset-before)}.swiper-css-mode.swiper-centered.swiper-vertical>.swiper-wrapper::before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-3d .swiper-slide-shadow{background:rgba(0,0,0,.15)}.swiper-3d .swiper-slide-shadow-left{background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-right{background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-top{background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-bottom{background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-lazy-preloader{width:42px;height:42px;position:absolute;left:50%;top:50%;margin-left:-21px;margin-top:-21px;z-index:10;transform-origin:50%;box-sizing:border-box;border:4px solid var(--swiper-preloader-color,var(--swiper-theme-color));border-radius:50%;border-top-color:transparent}.swiper-watch-progress .swiper-slide-visible .swiper-lazy-preloader,.swiper:not(.swiper-watch-progress) .swiper-lazy-preloader{animation:swiper-preloader-spin 1s infinite linear}.swiper-lazy-preloader-white{--swiper-preloader-color:#fff}.swiper-lazy-preloader-black{--swiper-preloader-color:#000}@keyframes swiper-preloader-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.swiper-virtual .swiper-slide{-webkit-backface-visibility:hidden;transform:translateZ(0)}.swiper-virtual.swiper-css-mode .swiper-wrapper::after{content:'';position:absolute;left:0;top:0;pointer-events:none}.swiper-virtual.swiper-css-mode.swiper-horizontal .swiper-wrapper::after{height:1px;width:var(--swiper-virtual-size)}.swiper-virtual.swiper-css-mode.swiper-vertical .swiper-wrapper::after{width:1px;height:var(--swiper-virtual-size)}:root{--swiper-navigation-size:44px}.swiper-button-next,.swiper-button-prev{position:absolute;top:var(--swiper-navigation-top-offset,50%);width:calc(var(--swiper-navigation-size)/ 44 * 27);height:var(--swiper-navigation-size);margin-top:calc(0px - (var(--swiper-navigation-size)/ 2));z-index:10;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--swiper-navigation-color,var(--swiper-theme-color))}.swiper-button-next.swiper-button-disabled,.swiper-button-prev.swiper-button-disabled{opacity:.35;cursor:auto;pointer-events:none}.swiper-button-next.swiper-button-hidden,.swiper-button-prev.swiper-button-hidden{opacity:0;cursor:auto;pointer-events:none}.swiper-navigation-disabled .swiper-button-next,.swiper-navigation-disabled .swiper-button-prev{display:none!important}.swiper-button-next svg,.swiper-button-prev svg{width:100%;height:100%;object-fit:contain;transform-origin:center}.swiper-rtl .swiper-button-next svg,.swiper-rtl .swiper-button-prev svg{transform:rotate(180deg)}.swiper-button-prev,.swiper-rtl .swiper-button-next{left:var(--swiper-navigation-sides-offset,10px);right:auto}.swiper-button-next,.swiper-rtl .swiper-button-prev{right:var(--swiper-navigation-sides-offset,10px);left:auto}.swiper-button-lock{display:none}.swiper-button-next:after,.swiper-button-prev:after{font-family:swiper-icons;font-size:var(--swiper-navigation-size);text-transform:none!important;letter-spacing:0;font-variant:initial;line-height:1}.swiper-button-prev:after,.swiper-rtl .swiper-button-next:after{content:'prev'}.swiper-button-next,.swiper-rtl .swiper-button-prev{right:var(--swiper-navigation-sides-offset,10px);left:auto}.swiper-button-next:after,.swiper-rtl .swiper-button-prev:after{content:'next'}.swiper-pagination{position:absolute;text-align:center;transition:.3s opacity;transform:translate3d(0,0,0);z-index:10}.swiper-pagination.swiper-pagination-hidden{opacity:0}.swiper-pagination-disabled>.swiper-pagination,.swiper-pagination.swiper-pagination-disabled{display:none!important}.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-bullets.swiper-pagination-horizontal,.swiper-pagination-custom,.swiper-pagination-fraction{bottom:var(--swiper-pagination-bottom,8px);top:var(--swiper-pagination-top,auto);left:0;width:100%}.swiper-pagination-bullets-dynamic{overflow:hidden;font-size:0}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transform:scale(.33);position:relative}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev{transform:scale(.33)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next{transform:scale(.33)}.swiper-pagination-bullet{width:var(--swiper-pagination-bullet-width,var(--swiper-pagination-bullet-size,8px));height:var(--swiper-pagination-bullet-height,var(--swiper-pagination-bullet-size,8px));display:inline-block;border-radius:var(--swiper-pagination-bullet-border-radius,50%);background:var(--swiper-pagination-bullet-inactive-color,#000);opacity:var(--swiper-pagination-bullet-inactive-opacity, .2)}button.swiper-pagination-bullet{border:none;margin:0;padding:0;box-shadow:none;-webkit-appearance:none;appearance:none}.swiper-pagination-clickable .swiper-pagination-bullet{cursor:pointer}.swiper-pagination-bullet:only-child{display:none!important}.swiper-pagination-bullet-active{opacity:var(--swiper-pagination-bullet-opacity, 1);background:var(--swiper-pagination-color,var(--swiper-theme-color))}.swiper-pagination-vertical.swiper-pagination-bullets,.swiper-vertical>.swiper-pagination-bullets{right:var(--swiper-pagination-right,8px);left:var(--swiper-pagination-left,auto);top:50%;transform:translate3d(0px,-50%,0)}.swiper-pagination-vertical.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets .swiper-pagination-bullet{margin:var(--swiper-pagination-bullet-vertical-gap,6px) 0;display:block}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{top:50%;transform:translateY(-50%);width:8px}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{display:inline-block;transition:.2s transform,.2s top}.swiper-horizontal>.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet{margin:0 var(--swiper-pagination-bullet-horizontal-gap,4px)}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{left:50%;transform:translateX(-50%);white-space:nowrap}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s left}.swiper-horizontal.swiper-rtl>.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s right}.swiper-pagination-fraction{color:var(--swiper-pagination-fraction-color,inherit)}.swiper-pagination-progressbar{background:var(--swiper-pagination-progressbar-bg-color,rgba(0,0,0,.25));position:absolute}.swiper-pagination-progressbar .swiper-pagination-progressbar-fill{background:var(--swiper-pagination-color,var(--swiper-theme-color));position:absolute;left:0;top:0;width:100%;height:100%;transform:scale(0);transform-origin:left top}.swiper-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill{transform-origin:right top}.swiper-horizontal>.swiper-pagination-progressbar,.swiper-pagination-progressbar.swiper-pagination-horizontal,.swiper-pagination-progressbar.swiper-pagination-vertical.swiper-pagination-progressbar-opposite,.swiper-vertical>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite{width:100%;height:var(--swiper-pagination-progressbar-size,4px);left:0;top:0}.swiper-horizontal>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-vertical,.swiper-vertical>.swiper-pagination-progressbar{width:var(--swiper-pagination-progressbar-size,4px);height:100%;left:0;top:0}.swiper-pagination-lock{display:none}.swiper-scrollbar{border-radius:var(--swiper-scrollbar-border-radius,10px);position:relative;touch-action:none;background:var(--swiper-scrollbar-bg-color,rgba(0,0,0,.1))}.swiper-scrollbar-disabled>.swiper-scrollbar,.swiper-scrollbar.swiper-scrollbar-disabled{display:none!important}.swiper-horizontal>.swiper-scrollbar,.swiper-scrollbar.swiper-scrollbar-horizontal{position:absolute;left:var(--swiper-scrollbar-sides-offset,1%);bottom:var(--swiper-scrollbar-bottom,4px);top:var(--swiper-scrollbar-top,auto);z-index:50;height:var(--swiper-scrollbar-size,4px);width:calc(100% - 2 * var(--swiper-scrollbar-sides-offset,1%))}.swiper-scrollbar.swiper-scrollbar-vertical,.swiper-vertical>.swiper-scrollbar{position:absolute;left:var(--swiper-scrollbar-left,auto);right:var(--swiper-scrollbar-right,4px);top:var(--swiper-scrollbar-sides-offset,1%);z-index:50;width:var(--swiper-scrollbar-size,4px);height:calc(100% - 2 * var(--swiper-scrollbar-sides-offset,1%))}.swiper-scrollbar-drag{height:100%;width:100%;position:relative;background:var(--swiper-scrollbar-drag-bg-color,rgba(0,0,0,.5));border-radius:var(--swiper-scrollbar-border-radius,10px);left:0;top:0}.swiper-scrollbar-cursor-drag{cursor:move}.swiper-scrollbar-lock{display:none}.swiper-zoom-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center;text-align:center}.swiper-zoom-container>canvas,.swiper-zoom-container>img,.swiper-zoom-container>svg{max-width:100%;max-height:100%;object-fit:contain}.swiper-slide-zoomed{cursor:move;touch-action:none}.swiper .swiper-notification{position:absolute;left:0;top:0;pointer-events:none;opacity:0;z-index:-1000}.swiper-free-mode>.swiper-wrapper{transition-timing-function:ease-out;margin:0 auto}.swiper-grid>.swiper-wrapper{flex-wrap:wrap}.swiper-grid-column>.swiper-wrapper{flex-wrap:wrap;flex-direction:column}.swiper-fade.swiper-free-mode .swiper-slide{transition-timing-function:ease-out}.swiper-fade .swiper-slide{pointer-events:none;transition-property:opacity}.swiper-fade .swiper-slide .swiper-slide{pointer-events:none}.swiper-fade .swiper-slide-active{pointer-events:auto}.swiper-fade .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper.swiper-cube{overflow:visible}.swiper-cube .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1;visibility:hidden;transform-origin:0 0;width:100%;height:100%}.swiper-cube .swiper-slide .swiper-slide{pointer-events:none}.swiper-cube.swiper-rtl .swiper-slide{transform-origin:100% 0}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-next,.swiper-cube .swiper-slide-prev{pointer-events:auto;visibility:visible}.swiper-cube .swiper-cube-shadow{position:absolute;left:0;bottom:0px;width:100%;height:100%;opacity:.6;z-index:0}.swiper-cube .swiper-cube-shadow:before{content:'';background:#000;position:absolute;left:0;top:0;bottom:0;right:0;filter:blur(50px)}.swiper-cube .swiper-slide-next+.swiper-slide{pointer-events:auto;visibility:visible}.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-bottom,.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-left,.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-right,.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper.swiper-flip{overflow:visible}.swiper-flip .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1}.swiper-flip .swiper-slide .swiper-slide{pointer-events:none}.swiper-flip .swiper-slide-active,.swiper-flip .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-bottom,.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-left,.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-right,.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-creative .swiper-slide{-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden;transition-property:transform,opacity,height}.swiper.swiper-cards{overflow:visible}.swiper-cards .swiper-slide{transform-origin:center bottom;-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden} \ No newline at end of file diff --git a/src/css/main.css b/src/css/main.css new file mode 100644 index 0000000..99327ca --- /dev/null +++ b/src/css/main.css @@ -0,0 +1,758 @@ +@charset "UTF-8"; +:root { + /* text - 텍스트 색상 */ + --text-intro-base: #1b1810; + --text-base: #131313; + --text-revers: #fff; + --text-primary: #3c3321; + --text-secondary: #747474; + --text-accent: #ff5c00; + --text-myclass: #ffdf60; + --text-main-primary: #4a4040; + --text-main-secondary: #3a200d; + --text-title-accent: #f5651d; + --text-pick: #2b5045; + --text-keyword-base: #c4c2c0; + --text-keyword-primary: #fff; + --text-keyword-secondary: #999999; + --text-video-primary: #b6d5a7; + --text-video-secondary: #ddd; + --text-cate-primary: #edd8c2; + --text-cate-secondary: #b6d5a7; + --text-cate-tertiary: #e8cfe4; + --text-comment-primary: #fff; + --text-comment-secondary: #8d8d8d; + --text-learning-base: #8d8888; + --text-learning-primary: #ff5c00; + --text-learning-secondary: #008c49; + --text-card-category: #252525; + --text-card-title-active: #ff7d33; + --text-card-title-complete: #66ba92; + --text-card-author: #d1cfcf; + /* 배경 */ + --bg-base: #e4ddcf; + --bg-primary: #ece3d2; + --bg-secondary: + radial-gradient( + 93.89% 93.89% at 49.32% 6.11%, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.11) 86.06%, + rgba(134, 114, 77, 0.2) 88.94% + ), + linear-gradient(180deg, #f9f6f0 0%, #e6ddcc 100%); + --bg-main-card: rgba(255, 255, 255, 0.6); + --bg-intro-mask: #1b1810; + --bg-intro: linear-gradient( + 180deg, + #f9f5f2 0%, + #fff 18.77%, + #fff 41.8%, + #ece8e4 100% + ); + --bg-main: + linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ) + top / 100% 114px no-repeat, + #ece3d2; + --bg-video: #1b1b1b; + --bg-comment: #2a2a2a; + --bg-nav: linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ); + --bg-nav-depth: #fff; + --bg-nav-alerts: #ff2200; + --bg-nav-alerts-hover: #188f6b; + --bg-cate-primary: #ded7cf; + --bg-cate-secondary: #d4decf; + --bg-cate-tertiary: #e8cfe4; + --bg-pick: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #d5ede6 100%); + --bg-gauge-base: #b4b4b4; + --bg-gauge-primary: #ff5c00; + --bg-search: #e4dbc9; + --bg-keyword-base: rgba(230, 205, 177, 0.3); + --bg-keyword-primary: #e47703; + --bg-keyword-primary-hover: #e6cdb1; + --bg-keyword-secondary: #786c60; + --bg-keyword-secondary-hover: #dcd3c9; + --bg-lecture-state: linear-gradient(270deg, #00ab66 0%, #058bb0 100%); + --bg-lecture-progress-primary: rgba(3, 9, 7, 0.3); + --bg-lecture-progress-secondary: #07855c; + --bg-btn-base: #000; + --bg-btn-primary: #ff5c00; + --bg-btn-secondary: #1f1f1f; + --bg-card: rgba(255, 255, 255, 0.6); + --bg-card-hover-start: #ff6600; + --bg-card-hover-end: rgba(255, 132, 0, 0.7); + --bg-card-thumb-overlay: rgba(255, 255, 255, 0.3); + --bg-card-shadow: rgba(217, 202, 190, 0.5); + --bg-card-hover-shadow: rgba(198, 187, 177, 0.5); + --bg-card-shadow-inner: rgba(0, 0, 0, 1); + --bg-pick-shadow: rgba(166, 154, 97, 0.25); + --bg-modal: rgba(0, 0, 0, 0.8); + --bg-modal-content: #f6f1e9; + --bg-modal-close-hover: #e00400; + --bg-scrollbar-thumb: #D0D0D0; + --bg-scrollbar-thumb-dark: #383838; + --bg-scrollbar-track: #F3F3F3; + --bg-scrollbar-thumb-light: rgba(255, 255, 255, 0.2); + --bg-scrollbar-thumb-light-hover: rgba(255, 255, 255, 0.3); + --bg-scrollbar-track-light: rgba(255, 255, 255, 0.05); + --bg-video-gauge: #171717; + --bg-video-gauge-fill: #ff6c19; + --bg-video-gauge-border: rgba(0, 0, 0, 0.4); + --bg-learning-line: #c6c3c3; + --bg-learning-dot: #8d8888; + --bg-learning-active: rgba(255, 92, 0, 0.2); + --bg-learning-active-line: #ffad7f; + --bg-learning-active-dot: #ff5c00; + --bg-learning-complete: rgba(0, 140, 73, 0.2); + --bg-learning-complete-line: #7fc5a4; + --bg-learning-complete-dot: #008c49; + --bg-chapter-current: #146b51; + --bg-chapter-completed: #ba9a85; + --bg-card-base: #7e7e7e; + --bg-card-current: #1d9b75; + --bg-card-current-border: #1f9b76; + --bg-card-current-bg: #dbefe9; + --bg-card-completed: #ab3d00; + --bg-gauge-fill: #e25e00; + --bg-shadow: #8a7d64; + --bg-puzzle-family: #7ed29b; + --bg-puzzle-hanmac: #ffccca; + --bg-puzzle-value: #87a7d6; + --bg-puzzle-company: #b49a65; + --bg-piece-1: #1d375d; + --bg-piece-2: #662a0d; + --bg-piece-3: #5b4822; + --bg-piece-4: #2a5338; + --bg-circle-border: #a7790f; + --bg-circle-start: rgba(127, 47, 0, 0.1); + --bg-circle-end: rgba(167, 121, 15, 0.5); + --bg-circle-dot: rgba(221, 144, 0, 0.6); + --bg-circle-dot-hover: rgba(221, 144, 0, 0.8); + --bg-circle-dot-stroke: rgba(221, 144, 0, 0.2); + --bg-gradient-start: #ece3d2; + --bg-gradient-end: #fff; + --bg-learning-line-color: #edba8a; + --bg-modal-header: #f04400; + --bg-textarea: #2a2a2a; + --bg-textarea-placeholder: rgba(255, 255, 255, 0.5); + /* border */ + --border-base: rgba(0, 0, 0, 0.05); + --border-primary: #fff; + --border-keyword-base: rgba(0, 0, 0, 0.05); + --border-keyword-primary: rgba(255, 255, 255, 0.4); + --border-keyword-primary-hover: rgba(0, 0, 0, 0.05); + --border-keyword-secondary: rgba(255, 255, 255, 0.4); + --border-keyword-secondary-hover: rgba(0, 0, 0, 0.1); + --border-pick: rgba(255, 255, 255, 0.5); + --border-video: rgba(255, 255, 255, 0.1); + --border-btn: rgba(255, 255, 255, 0.2); + --border-card: rgba(255, 255, 255, 0.8); + --border-modal: #888; + --border-comment-resizer: rgba(230, 227, 225, 0.1); + /* drop */ + --text-shadow: + -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; + --text-stroke: drop-shadow(0 0 0.5px #000); + /* shadow */ + --shadow-pagination: rgba(0, 0, 0, 0.4); + --shadow-modal: rgba(0, 0, 0, 0.25); + --shadow-card-drop: 0 4px 8px rgba(0, 0, 0, 0.2); + --shadow-card-drop-small: 0 4px 2px rgba(0, 0, 0, 0.05); + --shadow-gauge-inset: rgba(0, 0, 0, 0.3); + --shadow-comment: 0 8px 22px 22px rgba(0, 0, 0, 0.8); +} + +/* 페이드 전환 */ +.fade-out { + opacity: 0; + transition: opacity 0.4s ease; +} + +.fade-in { + opacity: 1; + transition: opacity 0.4s ease; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes arrow-next { + 0%, 100% { + right: 72px; + } + 50% { + right: 50px; + } +} +@keyframes bounce { + 0%, 100% { + transform: translateX(-50%) translateY(0); + } + 50% { + transform: translateX(-50%) translateY(-12px); + } +} +@keyframes slideUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes scroll-down { + 0% { + transform-origin: 50% 100%; + transform: scaleY(1); + } + 50% { + transform-origin: 50% 100%; + transform: scaleY(0); + } + 50.1% { + transform-origin: 50% 0; + transform: scaleY(0); + } + to { + transform-origin: 50% 0; + transform: scaleY(1); + } +} +@keyframes pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.4; + } +} +@keyframes borderFadeIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes borderPulse { + 0%, 100% { + border-color: #fff; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.4); + } + 50% { + border-color: #fff; + box-shadow: 0 0 30px rgba(255, 255, 255, 0.6); + } +} +.guide-wrap { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 99; + opacity: 0; + transition: opacity 0.5s ease; + display: none; + height: var(--window-inner-height, 100vh); +} +.guide-wrap.active { + opacity: 1; + pointer-events: auto; + display: block; +} +.guide-svg { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: auto; +} +.guide-borders { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10000; +} +.guide-border { + position: absolute; + border-radius: 4px; +} +.guide-mask.nav { + position: absolute; + left: 50%; + top: 14px; + width: 900px; + height: 32px; + translate: -61% 0; + opacity: 0.5; +} +.guide-tooltip-box { + position: fixed; + background: white; + border-radius: 8px; + padding: 12px; + word-break: keep-all; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 10001; + pointer-events: auto; + opacity: 0; +} +.guide-tooltip-box:has(.guide-tooltip-description) .guide-tooltip-title { + margin-bottom: 8px; +} +.guide-tooltip-box::before, .guide-tooltip-box::after { + content: ""; + display: black; + position: absolute; +} +.guide-tooltip-box::before { + top: -20px; + width: 1.5px; + height: 20px; + background-color: #fff; +} +.guide-tooltip-box::after { + top: -25px; + border-radius: 50px; + width: 12px; + height: 12px; + background-color: #FF602F; + border: 1.5px solid #fff; +} +.guide-tooltip-box.menu, .guide-tooltip-box.search, .guide-tooltip-box.notice, .guide-tooltip-box.mypage { + position: absolute; + top: 72px; +} +.guide-tooltip-box.menu { + left: calc(50% - 283px); + translate: -50% 0; +} +.guide-tooltip-box.menu::before, .guide-tooltip-box.menu::after { + content: ""; + display: black; + position: absolute; +} +.guide-tooltip-box.menu::before { + right: 58px; +} +.guide-tooltip-box.menu::after { + right: 51.5px; +} +.guide-tooltip-box.menu p { + position: relative; + padding-left: 12px; +} +.guide-tooltip-box.menu p::before { + content: " "; + position: absolute; + left: 0; + top: 10px; + display: block; + width: 3px; + height: 3px; + border-radius: 50%; + background-color: #333; +} +.guide-tooltip-box.search { + width: 180px; + translate: -35% 0; +} +.guide-tooltip-box.search::before { + right: 88px; +} +.guide-tooltip-box.search::after { + right: 81.5px; +} +.guide-tooltip-box.notice { + width: 122px; + translate: -106% 0; +} +.guide-tooltip-box.notice::before { + right: 32px; +} +.guide-tooltip-box.notice::after { + right: 25.5px; +} +.guide-tooltip-box.mypage { + width: 168px; + translate: 8% 0; +} +.guide-tooltip-box.mypage::before { + left: 22px; +} +.guide-tooltip-box.mypage::after { + left: 15.5px; +} +.guide-tooltip-box.mypage .guide-tooltip-description { + letter-spacing: -0.064em; +} +.guide-tooltip-box.change { + text-align: right; + white-space: nowrap; + width: -moz-max-content; + width: max-content; + translate: -100% 0; +} +.guide-tooltip-box.change::before { + right: 52px; +} +.guide-tooltip-box.change::after { + right: 45.5px; +} +.guide-tooltip-box.interest::before { + left: 52px; +} +.guide-tooltip-box.interest::after { + left: 45.5px; +} +.guide-tooltip-box.learning { + translate: -50% 142%; +} +.guide-tooltip-box.learning::before { + top: -70px; + left: 22px; + height: 70px; +} +.guide-tooltip-box.learning::after { + top: -72px; + left: 15.5px; +} +.guide-tooltip-title { + font-weight: 700; + font-size: 18px; + margin-bottom: 0; + color: #000; +} +.guide-tooltip-title:has(.guide-tooltip-description) { + margin-bottom: 8px; +} +.guide-tooltip-description { + font-size: 16px; + line-height: 1.5; + color: #666; + white-space: pre-line; +} + +.main .container { + position: relative; + height: calc(var(--window-inner-height) - 64px); + overflow: hidden; +} +.main .container .bg-circle { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 100%; + margin: auto; + height: calc(var(--window-inner-height) - 64px); + -webkit-mask: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%), linear-gradient(180deg, #d9d9d9 0%, rgba(166, 166, 166, 0.08) 40%, rgba(140, 140, 140, 0.08) 60%, #737373 100%); + mask: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%), linear-gradient(180deg, #d9d9d9 0%, rgba(166, 166, 166, 0.08) 40%, rgba(140, 140, 140, 0.08) 60%, #737373 100%); + -webkit-mask-composite: source-in, xor; + mask-composite: intersect; + -webkit-mask: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%), linear-gradient(180deg, #d9d9d9 0%, rgba(166, 166, 166, 0.08) 40%, rgba(140, 140, 140, 0.08) 60%, #737373 100%); + -webkit-mask-composite: source-in; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + overflow: hidden; +} +.main .container .bg-circle::before { + content: " "; + display: block; + position: absolute; + top: 50%; + width: 100vw; + height: 67.22222vh; + background: linear-gradient(180deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 23.56%, var(--bg-gradient-end) 66.83%, var(--bg-gradient-start) 100%); + translate: 0 -50%; + opacity: 0.67; + z-index: 1; + mix-blend-mode: multiply; +} +.main .container .bg-circle ul { + position: relative; + width: clamp(1372px, 71.45833vw, 71.45833vw); + aspect-ratio: 1/1; +} +.main .container .bg-circle ul li { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + aspect-ratio: 1/1; + border-radius: 50%; + border: 1px solid var(--bg-circle-border); + background-blend-mode: multiply, normal; + background: linear-gradient(0deg, var(--bg-circle-start) 0%, var(--bg-circle-start) 100%), radial-gradient(50% 50% at 50% 50%, rgba(167, 121, 15, 0) 94.72%, var(--bg-circle-end) 100%); +} +.main .container .bg-circle ul li::before, .main .container .bg-circle ul li::after { + content: " "; + display: block; + position: absolute; + border-radius: 50%; + aspect-ratio: 1/1; + top: 50%; +} +.main .container .bg-circle ul li:nth-child(1) { + width: clamp(1372px, 71.45833vw, 71.45833vw); + opacity: 1; + transform: rotate(30deg); +} +.main .container .bg-circle ul li:nth-child(1)::before { + width: 12px; + height: 12px; + background: radial-gradient(50% 50% at 50% 50%, var(--bg-circle-dot) 60%, var(--bg-circle-dot-hover) 100%); + stroke-width: 1px; + stroke: var(--bg-circle-dot-stroke); + left: -6px; + opacity: 0.6; +} +.main .container .bg-circle ul li:nth-child(1)::after { + width: 14px; + height: 14px; +} +.main .container .bg-circle ul li:nth-child(2) { + width: clamp(1066px, 55.5208vw, 55.5208vw); + opacity: 0.7; +} +.main .container .bg-circle ul li:nth-child(3) { + width: clamp(794px, 41.3541vw, 41.3541vw); + opacity: 0.6; +} +.main .container .bg-circle ul li:nth-child(4) { + width: clamp(470px, 24.4791vw, 24.4791vw); + opacity: 0.5; +} +.main-contents { + width: 100%; + height: calc(var(--window-inner-height) - 64px); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.main-contents .text-box { + text-align: center; + z-index: 1; +} +.main-contents .text-box span { + color: var(--text-main-primary); + font-size: 32px; +} +.main-contents .text-box span em { + font-weight: 700; +} +.main-contents .text-box p { + color: var(--text-main-primary); + font-size: 38px; +} +.main-contents .text-box p em { + color: var(--text-main-secondary); + font-weight: 700; +} +.main-contents .keyword-area { + padding: 8px 12px; + margin: 10px auto 8px auto; + text-align: center; + gap: 12px; + width: -moz-max-content; + width: max-content; + margin: auto; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.main-contents .keyword-area button { + font-size: 13px; + font-weight: 700; + color: var(--text-main-primary); + opacity: 0.7; + gap: 4px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.main-contents .keyword-area button .ico-setting { + display: inline-block; + width: 14px; + height: 14px; + background-image: url("../images/ico/ico_setting.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.main .video-wrap { + position: absolute; + width: 100%; + height: calc(var(--window-inner-height) - 64px); + pointer-events: none; +} +.main .video-wrap .btn-prev, .main .video-wrap .btn-next { + pointer-events: auto; + position: absolute; + width: 84px; + height: 180px; + top: 50%; + translate: 0 -50%; + text-indent: -9999px; + color: transparent; + font-size: 1px; + overflow: hidden; + background-repeat: no-repeat; + background-position: center; + background-size: contain; +} +.main .video-wrap .btn-prev { + left: 40px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M94.6499 3C88.3166 26.9504 62.8499 78.281 11.6499 92C39.3166 99.3333 94.6499 127.5 94.6499 181.5' stroke='url(%23paint0_linear_1337_60932)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_60932' x1='94.6499' y1='92.25' x2='11.6499' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23DD842F'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); + transition: left 0.2s linear; +} +.main .video-wrap .btn-prev:not(.disabled):hover { + left: 30px; +} +.main .video-wrap .btn-prev.disabled { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M94.6499 3C88.3166 26.9504 62.8499 78.281 11.6499 92C39.3166 99.3333 94.6499 127.5 94.6499 181.5' stroke='url(%23paint0_linear_1337_60932)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_60932' x1='94.6499' y1='92.25' x2='11.6499' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23EFE7D7'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.main .video-wrap .btn-prev:not(.disabled):hover { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M94.6499 3C88.3166 26.9504 62.8499 78.281 11.6499 92C39.3166 99.3333 94.6499 127.5 94.6499 181.5' stroke='url(%23paint0_linear_1337_60932)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_60932' x1='94.6499' y1='92.25' x2='11.6499' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23D34D00'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.main .video-wrap .btn-next { + right: 40px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M3.00098 3C9.33431 26.9504 34.801 78.281 86.001 92C58.3343 99.3333 3.00098 127.5 3.00098 181.5' stroke='url(%23paint0_linear_1337_61160)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_61160' x1='3.00098' y1='92.25' x2='86.001' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23DD842F'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); + transition: right 0.2s linear; +} +.main .video-wrap .btn-next:not(.disabled):hover { + right: 30px; +} +.main .video-wrap .btn-next.disabled { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M3.00098 3C9.33431 26.9504 34.801 78.281 86.001 92C58.3343 99.3333 3.00098 127.5 3.00098 181.5' stroke='url(%23paint0_linear_1337_61162)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_61162' x1='3.00098' y1='92.25' x2='86.001' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23EFE7D7'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.main .video-wrap .btn-next:not(.disabled):hover { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M3.00098 3C9.33431 26.9504 34.801 78.281 86.001 92C58.3343 99.3333 3.00098 127.5 3.00098 181.5' stroke='url(%23paint0_linear_1337_61162)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_61162' x1='3.00098' y1='92.25' x2='86.001' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23D34D00'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.main .video-wrap .pagination { + position: absolute; + left: 50%; + bottom: 20px; + transform: translateX(-50%); + font-size: 20px; + color: var(--shadow-pagination); + font-weight: 400; +} +.main .video-wrap .pagination .current { + font-size: 24px; + font-weight: 700; +} +.main .video-cards-container { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(3, 1fr); + align-items: center; + justify-items: center; + height: 100%; + -moz-column-gap: 10%; + column-gap: 10%; +} +.main .video-card:nth-child(3) { + margin-left: -30%; +} +.main .video-card:nth-child(4) { + margin-right: -30%; +} +.main .my-learning { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 796px; + translate: -50% -100%; +} +.main .learning-area { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + width: clamp(932px, 48.5416vw, 48.5416vw); + padding-top: 0; + background: none; +} +.main .learning-area::before, .main .learning-area::after { + content: " "; + display: block; + position: absolute; + width: 158px; + height: 1px; + bottom: 48.5%; +} +.main .learning-area::before { + left: 7.2%; + background: linear-gradient(90deg, var(--bg-learning-line-color) 0%, rgba(237, 186, 138, 0) 100%); +} +.main .learning-area::after { + right: 7.2%; + background: linear-gradient(90deg, rgba(237, 186, 138, 0) 11.59%, var(--bg-learning-line-color) 100%); +} + +.modal.keyword { + background-color: rgba(0, 0, 0, 0); +} +.modal.keyword .modal-header { + color: var(--bg-modal-header); + font-size: 14px; + font-weight: 700; +} +.modal.keyword .modal-header .btn-close { + color: inherit; + font: inherit; +} +.modal.keyword .modal-header .ico-check { + display: inline-block; + margin-left: 8px; + width: 8px; + height: 8px; + cursor: pointer; + background-image: url("../images/ico/ico_check.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.modal.keyword .modal-content { + width: 640px; + min-height: auto; + padding: 20px 26px; + gap: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: rgba(255, 255, 255, 0.98); + border: none; + box-shadow: 0 0 6px 0 var(--shadow-modal); + translate: 0 0; + transform: translate(-50%, -10px); +} +.modal.keyword .modal-content .keyword-list { + max-width: 100%; +} \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css new file mode 100644 index 0000000..ed04aad --- /dev/null +++ b/src/css/style.css @@ -0,0 +1,4793 @@ +@charset "UTF-8"; +:root { + /* text - 텍스트 색상 */ + --text-intro-base: #1b1810; + --text-base: #131313; + --text-revers: #fff; + --text-primary: #3c3321; + --text-secondary: #747474; + --text-accent: #ff5c00; + --text-myclass: #ffdf60; + --text-main-primary: #4a4040; + --text-main-secondary: #3a200d; + --text-title-accent: #f5651d; + --text-pick: #2b5045; + --text-keyword-base: #c4c2c0; + --text-keyword-primary: #fff; + --text-keyword-secondary: #999999; + --text-video-primary: #b6d5a7; + --text-video-secondary: #ddd; + --text-cate-primary: #edd8c2; + --text-cate-secondary: #b6d5a7; + --text-cate-tertiary: #e8cfe4; + --text-comment-primary: #fff; + --text-comment-secondary: #8d8d8d; + --text-learning-base: #8d8888; + --text-learning-primary: #ff5c00; + --text-learning-secondary: #008c49; + --text-card-category: #252525; + --text-card-title-active: #ff7d33; + --text-card-title-complete: #66ba92; + --text-card-author: #d1cfcf; + /* 배경 */ + --bg-base: #e4ddcf; + --bg-primary: #ece3d2; + --bg-secondary: + radial-gradient( + 93.89% 93.89% at 49.32% 6.11%, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.11) 86.06%, + rgba(134, 114, 77, 0.2) 88.94% + ), + linear-gradient(180deg, #f9f6f0 0%, #e6ddcc 100%); + --bg-main-card: rgba(255, 255, 255, 0.6); + --bg-intro-mask: #1b1810; + --bg-intro: linear-gradient( + 180deg, + #f9f5f2 0%, + #fff 18.77%, + #fff 41.8%, + #ece8e4 100% + ); + --bg-main: + linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ) + top / 100% 114px no-repeat, + #ece3d2; + --bg-video: #1b1b1b; + --bg-comment: #2a2a2a; + --bg-nav: linear-gradient( + 90deg, + #0f3025 0%, + #194335 38%, + #0b221b 87.51%, + #0d231c 100% + ); + --bg-nav-depth: #fff; + --bg-nav-alerts: #ff2200; + --bg-nav-alerts-hover: #188f6b; + --bg-cate-primary: #ded7cf; + --bg-cate-secondary: #d4decf; + --bg-cate-tertiary: #e8cfe4; + --bg-pick: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #d5ede6 100%); + --bg-gauge-base: #b4b4b4; + --bg-gauge-primary: #ff5c00; + --bg-search: #e4dbc9; + --bg-keyword-base: rgba(230, 205, 177, 0.3); + --bg-keyword-primary: #e47703; + --bg-keyword-primary-hover: #e6cdb1; + --bg-keyword-secondary: #786c60; + --bg-keyword-secondary-hover: #dcd3c9; + --bg-lecture-state: linear-gradient(270deg, #00ab66 0%, #058bb0 100%); + --bg-lecture-progress-primary: rgba(3, 9, 7, 0.3); + --bg-lecture-progress-secondary: #07855c; + --bg-btn-base: #000; + --bg-btn-primary: #ff5c00; + --bg-btn-secondary: #1f1f1f; + --bg-card: rgba(255, 255, 255, 0.6); + --bg-card-hover-start: #ff6600; + --bg-card-hover-end: rgba(255, 132, 0, 0.7); + --bg-card-thumb-overlay: rgba(255, 255, 255, 0.3); + --bg-card-shadow: rgba(217, 202, 190, 0.5); + --bg-card-hover-shadow: rgba(198, 187, 177, 0.5); + --bg-card-shadow-inner: rgba(0, 0, 0, 1); + --bg-pick-shadow: rgba(166, 154, 97, 0.25); + --bg-modal: rgba(0, 0, 0, 0.8); + --bg-modal-content: #f6f1e9; + --bg-modal-close-hover: #e00400; + --bg-scrollbar-thumb: #D0D0D0; + --bg-scrollbar-thumb-dark: #383838; + --bg-scrollbar-track: #F3F3F3; + --bg-scrollbar-thumb-light: rgba(255, 255, 255, 0.2); + --bg-scrollbar-thumb-light-hover: rgba(255, 255, 255, 0.3); + --bg-scrollbar-track-light: rgba(255, 255, 255, 0.05); + --bg-video-gauge: #171717; + --bg-video-gauge-fill: #ff6c19; + --bg-video-gauge-border: rgba(0, 0, 0, 0.4); + --bg-learning-line: #c6c3c3; + --bg-learning-dot: #8d8888; + --bg-learning-active: rgba(255, 92, 0, 0.2); + --bg-learning-active-line: #ffad7f; + --bg-learning-active-dot: #ff5c00; + --bg-learning-complete: rgba(0, 140, 73, 0.2); + --bg-learning-complete-line: #7fc5a4; + --bg-learning-complete-dot: #008c49; + --bg-chapter-current: #146b51; + --bg-chapter-completed: #ba9a85; + --bg-card-base: #7e7e7e; + --bg-card-current: #1d9b75; + --bg-card-current-border: #1f9b76; + --bg-card-current-bg: #dbefe9; + --bg-card-completed: #ab3d00; + --bg-gauge-fill: #e25e00; + --bg-shadow: #8a7d64; + --bg-puzzle-family: #7ed29b; + --bg-puzzle-hanmac: #ffccca; + --bg-puzzle-value: #87a7d6; + --bg-puzzle-company: #b49a65; + --bg-piece-1: #1d375d; + --bg-piece-2: #662a0d; + --bg-piece-3: #5b4822; + --bg-piece-4: #2a5338; + --bg-circle-border: #a7790f; + --bg-circle-start: rgba(127, 47, 0, 0.1); + --bg-circle-end: rgba(167, 121, 15, 0.5); + --bg-circle-dot: rgba(221, 144, 0, 0.6); + --bg-circle-dot-hover: rgba(221, 144, 0, 0.8); + --bg-circle-dot-stroke: rgba(221, 144, 0, 0.2); + --bg-gradient-start: #ece3d2; + --bg-gradient-end: #fff; + --bg-learning-line-color: #edba8a; + --bg-modal-header: #f04400; + --bg-textarea: #2a2a2a; + --bg-textarea-placeholder: rgba(255, 255, 255, 0.5); + /* border */ + --border-base: rgba(0, 0, 0, 0.05); + --border-primary: #fff; + --border-keyword-base: rgba(0, 0, 0, 0.05); + --border-keyword-primary: rgba(255, 255, 255, 0.4); + --border-keyword-primary-hover: rgba(0, 0, 0, 0.05); + --border-keyword-secondary: rgba(255, 255, 255, 0.4); + --border-keyword-secondary-hover: rgba(0, 0, 0, 0.1); + --border-pick: rgba(255, 255, 255, 0.5); + --border-video: rgba(255, 255, 255, 0.1); + --border-btn: rgba(255, 255, 255, 0.2); + --border-card: rgba(255, 255, 255, 0.8); + --border-modal: #888; + --border-comment-resizer: rgba(230, 227, 225, 0.1); + /* drop */ + --text-shadow: + -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; + --text-stroke: drop-shadow(0 0 0.5px #000); + /* shadow */ + --shadow-pagination: rgba(0, 0, 0, 0.4); + --shadow-modal: rgba(0, 0, 0, 0.25); + --shadow-card-drop: 0 4px 8px rgba(0, 0, 0, 0.2); + --shadow-card-drop-small: 0 4px 2px rgba(0, 0, 0, 0.05); + --shadow-gauge-inset: rgba(0, 0, 0, 0.3); + --shadow-comment: 0 8px 22px 22px rgba(0, 0, 0, 0.8); +} + +/* 페이드 전환 */ +.fade-out { + opacity: 0; + transition: opacity 0.4s ease; +} + +.fade-in { + opacity: 1; + transition: opacity 0.4s ease; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes arrow-next { + 0%, 100% { + right: 72px; + } + 50% { + right: 50px; + } +} +@keyframes bounce { + 0%, 100% { + transform: translateX(-50%) translateY(0); + } + 50% { + transform: translateX(-50%) translateY(-12px); + } +} +@keyframes slideUp { + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes scroll-down { + 0% { + transform-origin: 50% 100%; + transform: scaleY(1); + } + 50% { + transform-origin: 50% 100%; + transform: scaleY(0); + } + 50.1% { + transform-origin: 50% 0; + transform: scaleY(0); + } + to { + transform-origin: 50% 0; + transform: scaleY(1); + } +} +@keyframes pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.4; + } +} +@keyframes borderFadeIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes borderPulse { + 0%, 100% { + border-color: #fff; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.4); + } + 50% { + border-color: #fff; + box-shadow: 0 0 30px rgba(255, 255, 255, 0.6); + } +} +html { + scroll-behavior: smooth; +} +html.is-locked body { + height: calc(var(--window-inner-height) - 1px); + overflow: hidden; + box-sizing: border-box; +} +html.is-locked #wrap { + position: fixed; +} +html body { + margin: 0; + padding: 0; +} + +/* common */ +.wrap { + position: relative; + min-height: var(--window-inner-height); + background: var(--bg-main); + background-attachment: fixed; + overflow: hidden; + /* + &:not(.intro) { + &::after { + content: " "; + position: fixed; + left: 0; + top: 64px; + display: block; + width: 100%; + height: 46px; + border-top: 2px solid #f3dba8; + border-radius: 30px 30px 0 0; + background: var(--bg-primary); + box-shadow: 0 -9px 6.3px 0 rgba(0, 0, 0, 0.5); + } + }*/ +} + +.header h1 { + width: 95px; + height: 25px; + background-image: url("../img/logo.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + text-indent: -9999px; + color: transparent; + font-size: 1px; + overflow: hidden; +} + +.nav-wrap { + position: fixed; + top: 0; + left: 0; + width: 100%; + background: var(--bg-nav); + color: var(--text-revers); + height: 64px; + z-index: 9; +} +.nav-wrap .inner { + height: 64px; + padding: 0 140px; + margin: auto; + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; +} +@media only screen and (max-width: 1919px) { + .nav-wrap .inner { + padding: 0 7.2916%; + } +} +.nav-wrap .item-area { + gap: 6px; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-wrap .item-area .myclass { + font-size: 14px; + color: var(--text-myclass); + margin-right: 10px; + gap: 10px; + white-space: nowrap; +} +.nav-wrap .item-area .myclass span:has(.frequency) { + filter: var(--text-stroke); + text-shadow: var(--text-shadow); +} +.nav-wrap .item-area .myclass .frequency { + color: var(--text-base); +} +.nav-wrap .item-area .myclass .frequency .ico-frequency { + display: block; + width: 14px; + height: 14px; + margin: 0 auto 2px; + background-image: url("../img/ico/ico_frequency.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.nav-wrap .item-area .myclass .frequency span { + display: inline-block; + width: 22px; + height: 12px; + border-radius: 25px; + background-color: var(--text-myclass); + border: 1px solid rgba(0, 0, 0, 0.4); + font-size: 10px; + line-height: 11px; + font-weight: 900; + text-align: center; +} +.nav-wrap .item-box { + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-wrap .item-box:has(.mypage:hover), .nav-wrap .item-box:has(.mypage.active) { + position: relative; + height: 100%; + background-color: #fff; + border-radius: 8px 8px 0 0; +} +.nav-wrap .item-box:has(.mypage:hover)::before, .nav-wrap .item-box:has(.mypage.active)::before { + content: ""; + position: absolute; + left: 0; + bottom: -2px; + width: 100%; + height: 24px; + background: linear-gradient(180deg, #FFF 0%, #ECE9E4 100%); + pointer-events: none; + border-radius: 8px 8px 0 0; +} +.nav-wrap .item-box:has(.mypage:hover) .mypage, .nav-wrap .item-box:has(.mypage.active) .mypage { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='19' height='20' viewBox='0 0 19 20' fill='none'%3E%3Cpath d='M9.33691 0.251953C10.3269 0.224972 11.3078 0.450411 12.1836 0.90625C13.0594 1.36213 13.8007 2.03383 14.3359 2.85547C14.8711 3.6772 15.1821 4.62156 15.2373 5.59668C15.2925 6.57178 15.0906 7.54461 14.6514 8.41992C14.2812 9.15764 13.7508 9.80247 13.1035 10.3164C14.6572 10.9648 16.0048 12.0197 16.9961 13.3721C18.0772 14.8469 18.6859 16.6073 18.7422 18.4258V18.4365C18.739 18.6925 18.6351 18.9366 18.4541 19.1182C18.2729 19.2997 18.0274 19.4055 17.7705 19.4131C17.5138 19.4205 17.263 19.3295 17.0713 19.1592C16.8797 18.9888 16.762 18.7514 16.7432 18.4961L16.7422 18.4854C16.6847 16.6343 15.8973 14.8765 14.5449 13.5859C13.1925 12.2953 11.3808 11.5732 9.49512 11.5732C7.6096 11.5733 5.79863 12.2954 4.44629 13.5859C3.09396 14.8765 2.30652 16.6343 2.24902 18.4854V18.4902C2.23556 18.7494 2.11916 18.9923 1.92676 19.167C1.7344 19.3416 1.48077 19.4343 1.2207 19.4268C0.960695 19.4192 0.712987 19.3125 0.53125 19.127C0.349355 18.9412 0.247993 18.6912 0.25 18.4316V18.4268C0.306119 16.6081 0.913952 14.8471 1.99512 13.3721C2.98633 12.0197 4.33417 10.9649 5.8877 10.3164C5.28223 9.83555 4.77858 9.23977 4.41309 8.55957C3.97462 7.74356 3.7459 6.83365 3.74609 5.91016C3.74635 4.93354 4.00305 3.97374 4.49121 3.12402C4.9794 2.27426 5.6823 1.56356 6.53125 1.06055C7.3802 0.557543 8.34687 0.278976 9.33691 0.251953ZM9.49609 2.22461C8.50024 2.22461 7.54574 2.61399 6.84277 3.30566C6.14007 3.99711 5.74619 4.93411 5.74609 5.91016C5.74609 6.88636 6.13995 7.82409 6.84277 8.51562C7.54573 9.20725 8.50027 9.59668 9.49609 9.59668C10.4919 9.59666 11.4465 9.20728 12.1494 8.51562C12.8522 7.82409 13.2461 6.88636 13.2461 5.91016C13.246 4.93414 12.8521 3.9971 12.1494 3.30566C11.4465 2.61401 10.4919 2.22463 9.49609 2.22461Z' fill='%230D0D0D' stroke='%230D0D0D' stroke-width='0.5'/%3E%3C/svg%3E"); +} +.nav-wrap .item-box:hover .alerts { + background-color: var(--bg-nav-alerts-hover); +} +.nav-wrap .item-box:hover .alerts-area { + display: block; +} +.nav-wrap .input-group { + position: relative; +} +.nav-wrap input[type=text] { + padding-left: 38px; + padding-right: 14px; + width: 185px; + height: 38px; + border-radius: 4px; + border: 1px solid #138E65; + background: var(--bg-search); + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.3) inset; + filter: drop-shadow(0 0 0 #000); +} +.nav-wrap input[type=text]::-moz-placeholder { + color: rgba(0, 0, 0, 0.3); +} +.nav-wrap input[type=text]::placeholder { + color: rgba(0, 0, 0, 0.3); +} +.nav-wrap .input-group:focus-within input[type=text] { + border-radius: 4px 4px 0 0; + border-bottom-color: transparent; + outline: none; +} +.nav-wrap .input-group:focus-within .search-suggestions { + display: block; +} +.nav-wrap .search-suggestions { + display: none; + position: absolute; + top: 100%; + left: 0; + width: 185px; + padding: 4px 0; + border: 1px solid #138E65; + border-top: none; + border-radius: 0 0 4px 4px; + background: #F1ECE1; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15) inset, 0 2px 1.9px 0 rgba(0, 0, 0, 0.4); + z-index: 1; +} +.nav-wrap .search-suggestions-list { + list-style: none; + margin: 0; + padding: 0; +} +.nav-wrap .search-suggestions-list li { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 8px; + padding: 8px 12px; + font-size: 16px; + font-weight: 500; + color: var(--text-primary, #000); + cursor: pointer; + transition: background 0.15s; +} +.nav-wrap .search-suggestions-list li:hover { + background: rgba(0, 0, 0, 0.05); +} +.nav-wrap .search-suggestions-list li span { + flex: 1; + text-align: left; +} +.nav-wrap .ico-history { + flex-shrink: 0; + width: 16px; + height: 16px; + background: url("../img/ico/ico_history.svg") center/contain no-repeat; +} +.nav-wrap .btn-remove { + flex-shrink: 0; + width: 14px; + height: 14px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + opacity: 0.5; + background-image: url("../img/ico/ico_clear.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.nav-wrap .btn-remove:hover { + opacity: 0.8; +} +.nav-wrap .ico-search { + position: absolute; + left: 14px; + top: 12px; + width: 16px; + height: 16px; + background-image: url("../img/ico/ico_search.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.nav-wrap .alerts, +.nav-wrap .mypage { + position: relative; + width: 42px; + height: 38px; + border-radius: 4px; +} +.nav-wrap .alerts { + background-image: url(../img/ico/ico_alerts.svg); + background-repeat: no-repeat; + background-position: center; + background-size: 22px auto; + cursor: pointer; + z-index: 1; +} +.nav-wrap .alerts.on::after { + content: " "; + position: absolute; + top: 5px; + right: 7px; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--bg-nav-alerts); +} +.nav-wrap .alerts-area { + position: absolute; + top: 0px; + right: 0; + width: 238px; + display: none; + padding-top: 46px; +} +.nav-wrap .alerts-list { + position: relative; + padding: 20px 12px; + border-radius: 4px; + border: 1px solid #188f6b; + background: var(--bg-nav-depth); + gap: 24px; + filter: drop-shadow(4px 4px 8px rgba(0, 0, 0, 0.25)); + z-index: 1; + /* triangle dimension */ + --a: 55deg; /* angle */ + --h: 16px; /* height */ + --p: 91%; /* triangle position (0%:left 100%:right) */ + --r: 4px; /* the radius */ + --b: 0.05em; /* border width */ + --c1: #0d6759; + --c2: #fff; + padding: 12px; + border-radius: min(var(--r), var(--p) - var(--h) * tan(var(--a) / 2)) min(var(--r), 100% - var(--p) - var(--h) * tan(var(--a) / 2)) var(--r) var(--r)/var(--r); + clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 0, min(100%, var(--p) + var(--h) * tan(var(--a) / 2)) 0, var(--p) calc(-1 * var(--h)), max(0%, var(--p) - var(--h) * tan(var(--a) / 2)) 0); + background: var(--c1); + -o-border-image: conic-gradient(var(--c1) 0 0) 0/0 max(0%, 100% - var(--p) - var(--h) * tan(var(--a) / 2)) var(--r) max(0%, var(--p) - var(--h) * tan(var(--a) / 2))/var(--h) 0 0 0; + border-image: conic-gradient(var(--c1) 0 0) fill 0/0 max(0%, 100% - var(--p) - var(--h) * tan(var(--a) / 2)) var(--r) max(0%, var(--p) - var(--h) * tan(var(--a) / 2))/var(--h) 0 0 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.nav-wrap .alerts-list:before { + content: ""; + position: absolute; + z-index: -1; + inset: 0; + padding: var(--b); + border-radius: inherit; + clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 0, min(100% - var(--b), var(--p) + var(--h) * tan(var(--a) / 2) - var(--b) * tan(45deg - var(--a) / 4)) var(--b), var(--p) calc(var(--b) / sin(var(--a) / 2) - var(--h)), max(var(--b), var(--p) - var(--h) * tan(var(--a) / 2) + var(--b) * tan(45deg - var(--a) / 4)) var(--b)); + background: var(--c2) content-box; + -o-border-image: conic-gradient(var(--c2) 0 0) 0/0 max(var(--b), 100% - var(--p) - var(--h) * tan(var(--a) / 2)) var(--r) max(var(--b), var(--p) - var(--h) * tan(var(--a) / 2))/var(--h) 0 0 0; + border-image: conic-gradient(var(--c2) 0 0) fill 0/0 max(var(--b), 100% - var(--p) - var(--h) * tan(var(--a) / 2)) var(--r) max(var(--b), var(--p) - var(--h) * tan(var(--a) / 2))/var(--h) 0 0 0; +} +.nav-wrap .alerts-list dl { + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: column; +} +.nav-wrap .alerts-list dt { + margin-bottom: 4px; + font-size: 14px; + font-weight: 700; + color: var(--text-base); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.nav-wrap .alerts-list dd { + font-size: 13px; + color: var(--text-secondary); + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; +} +.nav-wrap .alerts-list .badge-danger { + display: inline-block; + width: 40px; + height: 17px; + border-radius: 2px; + font-size: 12px; + text-align: center; +} +.nav-wrap .mypage { + background: url(../img/ico/ico_user.svg) no-repeat center; + background-size: 22px auto; +} +.nav-wrap .myclass { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-group .depth01 { + gap: 24px; + font-size: 17px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-group .depth01 > li { + min-width: 130px; + height: 64px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-group .depth01 > li:hover::before, .nav-group .depth01 > li.active::before { + content: ""; + position: absolute; + left: 0; + bottom: -2px; + width: 100%; + height: 24px; + background: linear-gradient(180deg, #FFF 0%, #ECE9E4 100%); + pointer-events: none; +} +.nav-group .depth01 > li:hover > a, .nav-group .depth01 > li.active > a { + color: #000; + filter: none; + text-shadow: none; + z-index: 1; +} +.nav-group .depth01 > li:hover, .nav-group .depth01 > li.active { + border-radius: 8px 8px 0 0; + background: var(--bg-nav-depth); + filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.1)); +} +.nav-group .depth01 > li:hover:has(.depth02)::before, .nav-group .depth01 > li:hover:has(.depth02)::after { + content: " "; + position: absolute; + bottom: 0px; + width: 16px; + height: 16px; + background-color: #fff; +} +.nav-group .depth01 > li:hover:has(.depth02)::before { + left: -16px; + -webkit-mask: radial-gradient(16px at 0 0, rgba(0, 0, 0, 0) 98%, #000); + mask: radial-gradient(16px at 0 0, rgba(0, 0, 0, 0) 98%, #000); +} +.nav-group .depth01 > li:hover:has(.depth02)::after { + right: -16px; + -webkit-mask: radial-gradient(16px at 100% 0, rgba(0, 0, 0, 0) 98%, #000); + mask: radial-gradient(16px at 100% 0, rgba(0, 0, 0, 0) 98%, #000); +} +.nav-group .depth01 > li:hover .depth02 { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-group .depth01 > li > a { + filter: var(--text-stroke); + text-shadow: var(--text-shadow); +} +.nav-group .depth01 > li .state-box { + position: absolute; + left: 50%; + bottom: 4px; + height: 16px; + translate: -50% 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.nav-group .depth01 > li .state-box .unit { + position: absolute; + left: 7px; + padding: 0px 8px; + font-size: 14px; + height: 16px; + line-height: 14px; + font-weight: 700; + border-radius: 14px; + background: var(--bg-lecture-state); + border: 1px solid rgba(0, 0, 0, 0.2); +} +.nav-group .depth01 > li .state-box .unit small { + font-size: 12px; + font-weight: 400; +} +.nav-group .depth01 > li .state-box .progress { + display: inline-block; + width: 78px; + height: 4px; + border-radius: 4px; + background: var(--bg-lecture-progress-primary); +} +.nav-group .depth01 > li .state-box .progress-bar { + display: block; + min-width: 9px; + height: 100%; + border-radius: inherit; + background: var(--bg-lecture-progress-secondary); + transition: width 0.3s ease-out; +} +.nav-group .depth02 { + position: absolute; + top: 62px; + background: var(--bg-nav-depth); + color: var(--text-base); + font-size: 16px; + font-weight: 400; + border-radius: 8px; + padding: 8px 26px 18px; + gap: 48px; + display: none; +} +.nav-group .depth02 li { + position: relative; + white-space: nowrap; +} +.nav-group .depth02 li:not(:last-child)::after { + content: " "; + position: absolute; + right: -24px; + top: 0; + width: 1px; + height: 100%; + background: var(--border-base); +} +.nav-group .depth02 li a { + gap: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.nav-group .depth02 li .ico-box { + width: 60px; + aspect-ratio: 1/1; +} + +.container { + position: fixed; + top: 64px; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: calc(var(--window-inner-height, 100vh) - 64px); + margin-top: 0; + overflow: auto; + z-index: 1; + border-radius: 30px 30px 0 0; + border-top: 2px solid #f3dba8; + background: var(--bg-primary); + box-shadow: 0 -9px 6.3px 0 rgba(0, 0, 0, 0.5); +} +.container::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.container::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.container::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track); +} + +.video-card { + position: relative; + border-radius: 120px; + pointer-events: auto; +} +.video-card::before { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 100%; + height: 100%; + border-radius: inherit; + opacity: 0; + transition: all 0.1s linear; + padding: 0; +} +.video-card:hover::before { + opacity: 1; + background: linear-gradient(180deg, var(--bg-card-hover-start) 0%, var(--bg-card-hover-end) 100%); + box-shadow: 8px 16px 24px 0 var(--bg-card-hover-shadow); + backdrop-filter: blur(9.3500003815px); + padding: 15px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; + inset: auto; + top: 50%; + left: 50%; +} +.video-card:hover .card::before { + content: " "; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: inherit; + box-shadow: 0 0 7px 0 var(--bg-card-shadow-inner); + z-index: -1; +} +.video-card .pick { + position: absolute; + left: -110px; + top: -10px; + width: 144px; + height: 42px; + border-radius: 24px 24px 0 24px; + border: 1px solid var(--border-pick); + background: var(--bg-pick); + font-size: 16px; + color: var(--text-pick); + box-shadow: 0 4px 8px 0 var(--bg-pick-shadow); + backdrop-filter: blur(2px); + gap: 2px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.video-card .pick em { + margin-left: 2px; + font-weight: 700; +} +.video-card .pick .ico-pick { + z-index: 1; + background-image: url("../img/ico/ico_pick.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + width: 24px; + aspect-ratio: 1/1; +} +.video-card .card { + width: 360px; + height: 144px; + border-radius: inherit; + border: 1px solid var(--border-card); + background: var(--bg-card); + box-shadow: 0 8px 12px 0 var(--bg-card-shadow); + backdrop-filter: blur(9.3500003815px); + gap: 22px; + padding: 8px 32px 8px 12px; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.video-card .card:hover { + box-shadow: 0 8px 12px 0 var(--bg-card-shadow); +} +.video-card .thumb { + position: relative; + min-width: 128px; + max-width: 128px; + border-radius: 50%; + aspect-ratio: 1/1; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.video-card .thumb::after { + width: 100%; + height: 100%; + background: var(--bg-card-thumb-overlay); + box-sizing: border-box; + padding: 4px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; +} +.video-card .thumb img { + width: 100%; + height: 100%; + max-width: auto; + -o-object-fit: cover; + object-fit: cover; +} +.video-card .txt-box { + display: flex; + align-items: flex-start; + justify-content: center; + flex-direction: column; +} +.video-card .txt-box .bookmark { + position: absolute; + top: 10px; + right: 42px; + width: 24px; + height: 24px; + background-image: url(""); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.video-card .txt-box .bookmark input[type=checkbox] { + padding: 0; + width: 24px; + height: 24px; +} +.video-card .txt-box .category { + margin-bottom: 8px; + font-size: 12px; + font-weight: 700; + color: var(--text-card-category); + display: block; + padding: 4px 6px; + border-radius: 4px; + background: var(--bg-cate-primary); +} +.video-card .txt-box .category.leader { + background: var(--bg-cate-primary); +} +.video-card .txt-box .category.insight { + background: var(--bg-cate-secondary); +} +.video-card .txt-box .category.biz { + background: var(--bg-cate-tertiary); +} +.video-card .txt-box .title { + font-size: 20px; + font-weight: 700; + line-height: 1.2; + color: var(--text-primary); + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.video-card .txt-box .author { + margin-top: 16px; + font-size: 14px; + font-weight: 700; + color: var(--text-secondary); + gap: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.video-card .gauge-bar { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--bg-gauge-base); + padding-bottom: 4px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; +} +.video-card .gauge-bar.rect { + border-radius: 0; + background: #D9D9D9; +} +.video-card .gauge-bar.rect .gauge-fill { + border-radius: 0; +} +.video-card .gauge-fill { + position: absolute; + left: 0; + top: 50%; + height: 50%; + background: var(--bg-gauge-primary); + border-radius: 4px; +} + +.wrap.lessons .container { + position: relative; + overflow: hidden; +} +.wrap.lessons .container::before { + content: " "; + display: block; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 50%; + background-image: url("../img/learning/bg_cloud.png"); + background-size: 100% 100%; + background-position: 0px 0px; + background-repeat: no-repeat; + pointer-events: none; + -webkit-mask-image: linear-gradient(to bottom, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 100%); + mask-image: linear-gradient(to bottom, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 100%); +} + +.onboarding .container { + background: radial-gradient(93.89% 93.89% at 49.32% 6.11%, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.11) 86.06%, rgba(134, 114, 77, 0.2) 88.94%), linear-gradient(180deg, #f9f6f0 0%, #e6ddcc 100%); + box-shadow: 0 -2px 3px 0 rgba(0, 0, 0, 0.5); +} +.onboarding .puzzle-wrap { + position: relative; + width: 100%; + height: calc(var(--window-inner-height) - 64px); + padding: 0 32px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: column; +} +.onboarding .puzzle-area { + position: relative; + width: 100%; + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.onboarding .puzzle-board { + position: relative; + width: 90%; + margin-top: -4%; +} +.onboarding .puzzle-board svg { + width: 100%; + height: auto; + display: block; + transform: perspective(794px) rotateX(6deg); + transform-origin: bottom; +} +.onboarding .puzzle-board .img-box.family { + position: absolute; + z-index: 1; + pointer-events: none; + width: 14.07089%; + left: 0.75187%; + top: 5.03778%; +} +.onboarding .puzzle-board .img-box.hanmac { + position: absolute; + z-index: 1; + pointer-events: none; + width: 13.4264%; + top: 3.77833%; + right: 2.68528%; +} +.onboarding .puzzle-board .img-box.value { + position: absolute; + z-index: 1; + pointer-events: none; + width: 9.23737%; + left: -1.61117%; + bottom: -4.87804%; +} +.onboarding .puzzle-board .img-box.company { + position: absolute; + z-index: 1; + pointer-events: none; + width: 12.88936%; + right: -2.68528%; + bottom: -8.07926%; +} +.onboarding .puzzle-board .tit-box.family { + position: absolute; + font-size: 1.25vw; + font-weight: 700; + z-index: 1; + color: var(--bg-puzzle-family); + left: 8.2706%; + top: 31.1083%; +} +.onboarding .puzzle-board .tit-box.hanmac { + position: absolute; + font-size: 1.25vw; + font-weight: 700; + z-index: 1; + color: var(--bg-puzzle-hanmac); + top: 31.1083%; + right: 6.44468%; + opacity: 0.45; +} +.onboarding .puzzle-board .tit-box.value { + position: absolute; + font-size: 1.25vw; + font-weight: 700; + z-index: 1; + color: var(--bg-puzzle-value); + left: 8.05585%; + bottom: 10.51829%; +} +.onboarding .puzzle-board .tit-box.company { + position: absolute; + font-size: 1.25vw; + font-weight: 700; + z-index: 1; + color: var(--bg-puzzle-company); + right: 8.05585%; + bottom: 15.1133%; +} +.onboarding .puzzle-piece-group { + cursor: pointer; +} +.onboarding .puzzle-piece-group:hover .piece-overlay-hover { + fill-opacity: 0.3; +} +.onboarding .puzzle-piece-group.completed .piece-overlay-base { + fill: #D9D9D9; + fill-opacity: 0.5; +} +.onboarding .puzzle-piece-group.completed .gauge-fill-line { + stroke-dashoffset: 0 !important; +} +.onboarding .puzzle-piece-group:not(.completed) .piece-overlay-base { + fill-opacity: 0.2; +} +.onboarding .puzzle-piece-group:not(.completed)[data-piece="1"] .piece-overlay-base, .onboarding .puzzle-piece-group:not(.completed)[data-piece="6"] .piece-overlay-base { + fill: var(--bg-piece-1); +} +.onboarding .puzzle-piece-group:not(.completed)[data-piece="2"] .piece-overlay-base, .onboarding .puzzle-piece-group:not(.completed)[data-piece="3"] .piece-overlay-base, .onboarding .puzzle-piece-group:not(.completed)[data-piece="4"] .piece-overlay-base, .onboarding .puzzle-piece-group:not(.completed)[data-piece="5"] .piece-overlay-base { + fill: var(--bg-piece-2); +} +.onboarding .puzzle-piece-group:not(.completed)[data-piece="7"] .piece-overlay-base, .onboarding .puzzle-piece-group:not(.completed)[data-piece="8"] .piece-overlay-base { + fill: var(--bg-piece-3); +} +.onboarding .puzzle-piece-group:not(.completed)[data-piece="9"] .piece-overlay-base, .onboarding .puzzle-piece-group:not(.completed)[data-piece="10"] .piece-overlay-base { + fill: var(--bg-piece-4); +} +.onboarding .puzzle-piece-overlay-base { + fill: white; + fill-opacity: 0; + transition: all 0.3s ease; +} +.onboarding .puzzle-piece-overlay-hover { + fill: black; + fill-opacity: 0; + transition: all 0.3s ease; + pointer-events: none; +} + +.completion-animation-container svg { + transform: perspective(794px) rotateX(6deg); + transform-origin: bottom; +} +.completion { + /* 상단 왼쪽 영역(대략 0-600px x, 0-300px y) 마스킹 - 배경색으로 덮기 */ +} +.completion-background::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 70%; + background: linear-gradient(180deg, #f9f5f2 0%, #ece3d2 100%); + z-index: 0; + pointer-events: none; +} + +.lessons-wrap { + position: relative; + width: 100%; + height: calc(var(--window-inner-height) - 64px); + padding: 0 32px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: column; + padding: 0; + z-index: 2; +} +.lessons-area { + position: relative; + width: 100%; + flex-grow: 1; + background-image: url("../img/learning/bg_grid.svg"); + background-size: auto; + background-position: center; + background-repeat: no-repeat; + padding-top: 3%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.lessons-gauge { + text-align: center; + width: 84.427%; + margin: auto; + translate: 4% 0; +} +.lessons-gauge svg { + width: 100%; + height: auto; +} +.lessons .start-line { + position: absolute; + bottom: 0; + left: 0; + width: 3.91304%; + translate: -8% -32%; +} +.lessons #markers-container { + translate: none; +} +.lessons .marker { + position: absolute; + cursor: pointer; +} +.lessons .marker[data-type=chapter] { + width: 2.4676%; + transform: scale(1.4); +} +.lessons .marker[data-type=chapter]::before, .lessons .marker[data-type=chapter]::after { + content: ""; + display: block; + position: absolute; + aspect-ratio: 1/1; + top: 50%; + left: 50%; + translate: -50% -100%; + mix-blend-mode: multiply; + z-index: 9; +} +.lessons .marker[data-type=chapter]::before { + width: 25%; + filter: blur(2px); + opacity: 0.4; +} +.lessons .marker[data-type=chapter]::after { + width: 15%; + filter: blur(0.7px); + opacity: 0.2; +} +.lessons .marker[data-type=chapter].current::before, .lessons .marker[data-type=chapter].current::after { + background-color: var(--bg-chapter-current); +} +.lessons .marker[data-type=chapter].completed::before, .lessons .marker[data-type=chapter].completed::after { + background-color: var(--bg-chapter-completed); +} +.lessons .marker[data-type=normal] { + width: 2.3442%; + translate: 0 -40%; +} +.lessons .marker[data-type=normal].current { + transform: scale(1.6) !important; +} +.lessons .marker:first-child { + translate: 0 -80%; +} +.lessons .marker:nth-child(n+2):nth-child(-n+9) { + translate: 0 -90%; + transform: scale(1.2); +} +.lessons .marker:nth-child(n+11):nth-child(-n+12), .lessons .marker:nth-child(n+14):nth-child(-n+15) { + transform: scale(1.2); +} +.lessons .marker:nth-child(10), .lessons .marker:nth-child(13) { + translate: 0 -50%; +} +.lessons .marker:nth-child(16), .lessons .marker:nth-child(19) { + translate: 0 -60%; + transform: scale(1); +} +.lessons .marker:nth-child(n+17):nth-child(-n+18) { + translate: 0 -60%; + transform: scale(1.1); +} +.lessons .marker:nth-child(n+20):nth-child(-n+25) { + translate: 0 -60%; +} +.lessons .marker:nth-child(n+22):nth-child(-n+24) { + transform: scale(0.9); +} +.lessons .marker:nth-child(25) { + transform: scale(0.8); +} +.lessons .chapter-list { + pointer-events: none; +} +.lessons .chapter-list li { + position: absolute; + transition: all 0.3s ease; + pointer-events: auto; +} +.lessons .chapter-list li:not(.completed, .current) { + filter: grayscale(100%); +} +.lessons .chapter-list li:not(.completed, .current):nth-child(1) { + width: 9.9378881988%; +} +.lessons .chapter-list li:not(.completed, .current):nth-child(2) { + width: 9.9378881988%; +} +.lessons .chapter-list li:not(.completed, .current):nth-child(3) { + width: 9.9378881988%; +} +.lessons .chapter-list li:not(.completed, .current):nth-child(4) { + width: 9.3167701863%; +} +.lessons .chapter-list li:not(.completed, .current):nth-child(5) { + width: 8.6956521739%; +} +.lessons .chapter-list li.completed { + width: 12.4223602484%; +} +.lessons .chapter-list li.completed svg { + animation: bounce 0.5s ease; +} +.lessons .chapter-list li.current { + width: 20.3726708075%; +} +.lessons .chapter-list li.current svg { + animation: bounce 0.5s ease; +} +.lessons .chapter-list li svg { + width: 100%; + height: auto; + display: block; + filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2)); +} +.lessons .chapter-card { + position: relative; + transition: all 0.3s ease; +} +.lessons .chapter-card:nth-child(1) { + transform: scale(1); +} +.lessons .chapter-card:nth-child(2) { + transform: scale(1.1); +} +.lessons .chapter-card:nth-child(3) { + transform: scale(1.2); +} +.lessons .chapter-card:nth-child(4) { + transform: scale(1.3); +} +.lessons .chapter-card:nth-child(5) { + transform: scale(1.4); +} +.lessons .chapter-card-inner { + position: relative; + background: white; + border-radius: 8px; + padding: 0; + transition: all 0.3s ease; +} +.lessons .chapter-card-inner::before, .lessons .chapter-card-inner::after { + content: ""; + display: block; + position: absolute; + bottom: -14px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 15px solid white; + filter: drop-shadow(0 4px 2px rgba(0, 0, 0, 0.05)); +} +.lessons .chapter-card.base .chapter-card-inner { + border-radius: 8px; + background: linear-gradient(180deg, rgba(74, 74, 74, 0) 50%, rgba(74, 74, 74, 0.2) 100%) #fff; +} +.lessons .chapter-card.base .chapter-card-inner::after { + border-top-color: #d9d9d9; +} +.lessons .chapter-card.base .card-title { + font-size: 14px; + color: var(--bg-card-base); +} +.lessons .chapter-card.current .chapter-card-inner { + border-radius: 14px; + border: 3px solid var(--bg-card-current-border); + background: linear-gradient(180deg, rgba(31, 155, 118, 0) 50%, rgba(31, 155, 118, 0.2) 100%) #fff; +} +.lessons .chapter-card.current .chapter-card-inner::after { + border-top-color: var(--bg-card-current-bg); + border-top-width: 18px; + border-left-width: 14px; + border-right-width: 14px; + bottom: -16px; +} +.lessons .chapter-card.current .chapter-card-inner::before { + content: ""; + display: block; + position: absolute; + border-top-color: var(--bg-card-current-border); + border-top-width: 18px; + border-left-width: 16px; + border-right-width: 16px; + bottom: -18px; + width: 0; + height: 0; + left: 50%; + transform: translateX(-50%); + border-left-color: transparent; + border-right-color: transparent; +} +.lessons .chapter-card.current .card-title { + font-size: 24px; + padding: 8px 0; + color: var(--bg-card-current); +} +.lessons .chapter-card.current .card-play-button { + width: 16.4634146341%; +} +.lessons .chapter-card.completed:nth-child(2) .card-stamp { + transform: scale(1.1); +} +.lessons .chapter-card.completed:nth-child(3) .card-stamp { + transform: scale(1.2); +} +.lessons .chapter-card.completed:nth-child(4) .card-stamp { + transform: scale(1.3); +} +.lessons .chapter-card.completed:nth-child(5) .card-stamp { + transform: scale(1.4); +} +.lessons .chapter-card.completed .card-stamp { + content: " "; + display: block; + position: absolute; + top: 0; + right: 0; + translate: 50% -30%; + width: 60px; + aspect-ratio: 1/1; + background-image: url("../img/learning/img_stamp.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + z-index: 1; +} +.lessons .chapter-card.completed .chapter-card-inner { + border-radius: 10px; + background: linear-gradient(270deg, rgba(255, 255, 255, 0.9) 31%, rgba(234, 215, 204, 0.9) 100%) #fff; +} +.lessons .chapter-card.completed .chapter-card-inner::after { + border-top-color: #f9f4f1; +} +.lessons .chapter-card.completed .card-thumbnail-container { + position: relative; +} +.lessons .chapter-card.completed .card-thumbnail-container::after { + content: " "; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(0deg, rgba(138, 141, 115, 0.3) 0%, rgba(138, 141, 115, 0.3) 100%); +} +.lessons .chapter-card.completed .card-title { + font-size: 20px; + color: var(--bg-card-completed); +} +.lessons .chapter-card.completed .card-play-button { + width: 19%; +} +.lessons .chapter-card .card-thumbnail { + width: 100%; + height: auto; + display: block; +} +.lessons .chapter-card .card-thumbnail-container { + position: relative; + width: 100%; + border-radius: inherit; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + overflow: hidden; +} +.lessons .chapter-card .card-title { + text-align: center; + font-weight: 700; + padding: 4px 0; + transition: color 0.3s ease; +} +.lessons .chapter-card .card-gauge-bar { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background-color: #fff; + box-shadow: 1.234px 1.234px 1.234px 0 rgba(0, 0, 0, 0.3) inset; +} +.lessons .chapter-card .card-gauge-fill { + position: absolute; + bottom: 0; + left: 0; + height: 3px; + background-color: var(--bg-gauge-fill); + box-shadow: 0.922px 0.922px 0.922px 0 var(--shadow-gauge-inset) inset; +} +.lessons .chapter-card .shadow-effect { + position: absolute; + width: 80%; + height: 10px; + left: 50%; + bottom: -20%; + translate: -50% -50%; +} +.lessons .chapter-card .shadow-effect::before, .lessons .chapter-card .shadow-effect::after { + content: ""; + display: block; + position: absolute; + left: 50%; + transform: translateX(-50%); + background: var(--bg-shadow); + border-radius: 50%; + mix-blend-mode: multiply; + opacity: 0.2; +} +.lessons .chapter-card .shadow-effect::before { + width: 100%; + height: 10px; + top: 4px; + filter: blur(4px); + background: var(--bg-shadow); +} +.lessons .chapter-card .shadow-effect::after { + width: 80%; + height: 4px; + top: 8px; + filter: blur(2px); + background: var(--bg-shadow); +} +.lessons .chapter-card .card-play-button { + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 24.375%; + height: auto; + transition: transform 0.2s ease; + z-index: 2; +} +.lessons .progress-indicator { + width: 8.1366459627%; + height: auto; +} +.lessons .progress-indicator.completed { + width: 8.6956521739%; +} +.lessons .progress-indicator svg { + overflow: visible; +} + +.search-result { + overflow: visible !important; + background-attachment: scroll !important; +} +.search-result .container { + background: linear-gradient(180deg, #F4F1EB 0%, #F7F7F7 100%); +} +.search-result .search-result-wrap { + max-width: 1720px; + margin: 0 auto; + padding: 0px 140px 80px; +} +@media only screen and (max-width: 1919px) { + .search-result .search-result-wrap { + padding: 0px 7.2916% 80px; + } +} +.search-result .page-header { + display: flex; + flex-direction: column; + max-width: 1600px; + margin: auto; + padding-top: 24px; + padding-bottom: 56px; +} +.search-result .page-header .page-title { + padding-top: 64px; +} +.search-result .page-header h3 { + font-size: 26px; + font-weight: 700; + color: #000; +} +.search-result .page-header { + padding-bottom: 15px; +} +.search-result .page-title h3 { + margin: 0 0 12px; +} +.search-result .page-title .search-summary { + font-size: 27px; + line-height: 2; + margin: 0; +} +.search-result .page-title .search-summary em { + font-weight: 700; +} +.search-result .page-title .search-summary .search-keyword { + color: #FF7C33; + font-weight: 600; +} +.search-result .page-title .search-summary .search-count { + color: #000; +} +.search-result .filter-tabs { + display: flex; + gap: 16px; + width: 100%; + margin-bottom: 30px; + flex-wrap: wrap; + align-items: center; + justify-content: center; +} +.search-result .filter-tabs .filter-tab { + position: relative; + padding: 8px 16px; + border-radius: 8px; + font-size: 18px; + font-weight: 400; + cursor: pointer; + transition: all 0.2s ease; + color: #333333; + display: flex; + align-items: flex-end; + justify-content: center; + flex-direction: row; + gap: 8px; +} +.search-result .filter-tabs .filter-tab .ico-check, +.search-result .filter-tabs .filter-tab .ico-heart { + display: inline-block; + width: 14px; + height: 14px; +} +.search-result .filter-tabs .filter-tab .ico-check { + background-image: url("../img/ico/ico_check.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.search-result .filter-tabs .filter-tab .ico-heart { + background-image: url("../img/ico/ico_like.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.search-result .filter-tabs .filter-tab .count { + font-weight: 700; +} +.search-result .filter-tabs .filter-tab:hover { + background: var(--bg-keyword-primary-hover); + border-color: var(--border-keyword-primary-hover); + color: var(--text-keyword-primary); +} +.search-result .filter-tabs .filter-tab.active { + background: var(--bg-keyword-primary); + border-color: var(--border-keyword-primary); + color: var(--text-keyword-primary); + font-weight: 700; +} +.search-result .search-sections { + display: flex; + flex-direction: column; + gap: 140px; +} +.search-result .content-section .section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; +} +.search-result .content-section .section-header .section-title { + font-size: 18px; + font-weight: 500; + color: var(--text-primary); + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + gap: 8px; +} +.search-result .content-section .section-header .section-title .ico-check, +.search-result .content-section .section-header .section-title .ico-heart { + display: inline-block; + width: 16px; + height: 16px; +} +.search-result .content-section .section-header .section-title .ico-check { + background-image: url("../img/ico/ico_check.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.search-result .content-section .section-header .section-title .ico-heart { + background-image: url("../img/ico/ico_like.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.search-result .content-section .section-header .section-title .section-count { + font-size: 16px; + font-weight: 700; +} +.search-result .content-section .section-header .list-options { + display: flex; + align-items: center; + justify-content: flex-end; + flex-direction: row; + gap: 12px; + flex-wrap: wrap; +} +.search-result .content-section .section-header .select-wrap { + position: relative; +} +.search-result .content-section .section-header .select-sort { + padding: 8px 32px 8px 12px; + font-size: 16px; + color: var(--text-secondary); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23303030' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + cursor: pointer; +} +.search-result .content-section .video-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + -moz-column-gap: 24px; + column-gap: 24px; + row-gap: 56px; + list-style: none; + margin: 0; + padding: 0; +} +.search-result .content-section .video-item .card-link { + display: block; + position: relative; + overflow: hidden; + transition: box-shadow 0.2s, transform 0.2s; + text-decoration: none; + color: inherit; +} +.search-result .content-section .video-item .bookmark { + position: absolute; + top: 16px; + right: 14px; + z-index: 2; + width: 22px; + height: 22px; + cursor: pointer; +} +.search-result .content-section .video-item .bookmark input[type=checkbox] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + cursor: pointer; + border: none; + border-radius: 50%; + background-image: url("../img/ico/ico_bookmark_off.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.search-result .content-section .video-item .bookmark input[type=checkbox]:checked { + background-image: url("../img/ico/ico_bookmark_on.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.search-result .content-section .video-item .item-thumb { + position: relative; + width: 100%; + aspect-ratio: 16/9; + overflow: hidden; + border-radius: 8px 8px 0 0; + background: var(--bg-video); +} +.search-result .content-section .video-item .item-thumb img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} +.search-result .content-section .video-item .item-info { + padding: 14px 0; + display: flex; + align-items: flex-start; + justify-content: stretch; + flex-direction: column; + gap: 16px; +} +.search-result .content-section .video-item .item-title { + display: block; + font-size: 18px; + font-weight: 500; + line-height: 1.4; + height: calc(18px * 1.4 * 2); + color: var(--text-primary); + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.search-result .content-section .video-item .item-meta { + display: inline-block; + font-size: 14px; + font-weight: 500; + color: var(--text-keyword-secondary); +} +.search-result .content-section .video-item .tag-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.search-result .content-section .video-item .tag-list .tag { + display: inline-block; + font-size: 14px; + font-weight: 500; + color: var(--text-keyword-secondary); +} +@media only screen and (max-width: 1023px) { + .search-result .content-section .video-grid { + grid-template-columns: repeat(2, 1fr); + } +} +@media only screen and (max-width: 991px) { + .search-result .content-section .video-grid { + grid-template-columns: 1fr; + } +} +.search-result .content-section .video-item .item-title span { + color: #FF7C33; + font-weight: 700; +} +.search-result .gauge-bar { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--bg-gauge-base); + padding-bottom: 4px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; +} +.search-result .gauge-bar.rect { + border-radius: 0; + background: #D9D9D9; +} +.search-result .gauge-bar.rect .gauge-fill { + border-radius: 0; +} +.search-result .gauge-fill { + position: absolute; + left: 0; + top: 50%; + height: 50%; + background: var(--bg-gauge-primary); + border-radius: 4px; +} + +.insight .container { + background-color: #fff; +} +.insight .page-header { + display: flex; + flex-direction: column; + max-width: 1600px; + margin: auto; + padding-top: 24px; + padding-bottom: 56px; +} +.insight .page-header .page-title { + padding-top: 64px; +} +.insight .page-header h3 { + font-size: 26px; + font-weight: 700; + color: #000; +} +.insight .breadcrumb { + gap: 32px; + font-size: 14px; + align-self: flex-end; + color: var(--text-secondary); + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.insight .breadcrumb li { + position: relative; +} +.insight .breadcrumb li:not(:last-child)::after { + content: ">"; + position: absolute; + right: -14px; + top: 0; +} +.insight .breadcrumb a { + color: var(--text-secondary); + text-decoration: none; +} +.insight .breadcrumb a:hover { + color: var(--text-primary); +} +.insight .breadcrumb .sep { + margin: 0 2px; + color: var(--text-keyword-base); +} +.insight .breadcrumb .current { + color: var(--text-base); + font-weight: 700; +} +.insight .insight-inner, +.insight .editor-pick-inner { + position: relative; + display: flex; + flex-direction: column; + gap: 24px; + max-width: 1600px; + margin: 0 auto; +} +.insight .insight-inner { + padding: 24px 40px 48px; +} +.insight .editor-pick { + position: relative; + margin-bottom: 48px; + overflow: hidden; +} +.insight .editor-pick::before { + content: " "; + position: absolute; + left: 50%; + top: 50%; + width: 100%; + max-width: 1920px; + aspect-ratio: 1920/544; + background: linear-gradient(to bottom right, rgba(251, 215, 139, 0.3) 0%, #FFE5AC 27%, rgba(251, 215, 139, 0) 50%) bottom right/50% 50% no-repeat, linear-gradient(to bottom left, rgba(251, 215, 139, 0.3) 0%, #FFE5AC 27%, rgba(251, 215, 139, 0) 50%) bottom left/50% 50% no-repeat, linear-gradient(to top left, rgba(251, 215, 139, 0.3) 0%, #FFE5AC 27%, rgba(251, 215, 139, 0) 50%) top left/50% 50% no-repeat, linear-gradient(to top right, rgba(251, 215, 139, 0.3) 0%, #FFE5AC 27%, rgba(251, 215, 139, 0) 50%) top right/50% 50% no-repeat; + opacity: 0.56; + translate: -50% -50%; +} +.insight .editor-pick-swiper { + overflow: hidden; +} +.insight .swiper-slide { + padding: 84px 32px 84px; +} +.insight .pick-head { + position: absolute; + top: 0; + left: 50%; + margin-bottom: 20px; + flex-wrap: wrap; + gap: 12px; + translate: -50% 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.insight .pick-label { + font-size: 60px; + font-weight: 700; + color: #FF8922; + display: flex; + flex-direction: column; + font-family: "Caveat", cursive; + font-optical-sizing: auto; + font-weight: 700; + line-height: 1.1; + font-style: normal; +} +.insight .pick-label em { + background-color: #FF8922; + color: #fff; + padding: 0px 8px; + border-radius: 4px; + font-size: 36px; + height: -moz-max-content; + height: max-content; + width: -moz-max-content; + width: max-content; + display: block; + align-self: flex-end; + line-height: 1; + margin-right: -56px; +} +.insight .pick-category { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + gap: 8px; + font-size: 16px; + font-weight: 700; + color: var(--text-primary); +} +.insight .pick-category .ico-box { + display: inline-flex; + width: 28px; + height: 28px; +} +.insight .pick-category .ico-box img { + width: 100%; + height: 100%; + -o-object-fit: contain; + object-fit: contain; +} +.insight .pick-body { + display: flex; + align-items: stretch; + justify-content: flex-start; + flex-direction: row; + gap: 112px; + margin-bottom: 16px; +} +.insight .pick-left { + gap: 3 6px; + flex: 1; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.insight .pick-profile { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 20px; + padding: 20px 0; +} +.insight .pick-profile .pick-profile-photo { + position: relative; + width: 116px; + height: 116px; +} +.insight .pick-profile .pick-profile-photo .photo-frame { + width: 100%; + height: 100%; + border-radius: 50%; + overflow: hidden; + background: #fff; + outline: 2px solid rgba(0, 0, 0, 0.4); + box-shadow: 0 2.127px 5.316px 0 rgba(0, 0, 0, 0.25); +} +.insight .pick-profile .pick-profile-photo img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} +.insight .pick-profile .pick-profile-crown { + position: absolute; + top: -36px; + left: 50%; + transform: translateX(-50%); + line-height: 0; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15)); +} +.insight .pick-profile .pick-profile-name { + margin: 0; + font-size: 16px; + font-weight: 400; + color: var(--text-base, #333); + text-align: center; + line-height: 1.4; +} +.insight .pick-profile .pick-profile-name em { + font-weight: 700; +} +.insight .pick-profile .pick-profile-badge { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + gap: 6px; + font-size: 15px; + font-weight: 600; + color: #000; +} +.insight .pick-profile .pick-profile-badge svg { + flex-shrink: 0; +} +.insight .pick-text { + flex: 1; + padding: 20px; + gap: 40px; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.insight .pick-text .pick-title { + font-size: 32px; + font-weight: 400; +} +.insight .pick-text .pick-title em { + font-weight: 700; +} +.insight .pick-text .pick-desc { + margin: 0; + font-size: 24px; + line-height: 1.2; + color: var(--text-secondary); +} +.insight .pick-dday { + position: absolute; + bottom: 42px; + left: calc(50% - 42px); + font-size: 150px; + font-weight: 900; + color: #00CE85; + line-height: 1; + padding-bottom: 12px; + border-radius: 4px; + border: 4px solid #00CE85; + transform: rotate(-12deg); +} +.insight .pick-video { + flex: 1; + min-width: 0; +} +.insight .video-placeholder { + width: 100%; + max-width: 640px; + aspect-ratio: 16/9; + border-radius: 12px; + background: var(--bg-video); + box-shadow: 0 0 13.4px 0 rgba(0, 0, 0, 0.5); + overflow: hidden; +} +.insight .swiper-pagination { + position: relative; + bottom: 0px; +} +.insight .swiper-pagination .swiper-pagination-bullet { + background: #7E5A0F; +} +.insight .swiper-pagination .swiper-pagination-bullet-active { + width: 62px; + height: 6px; + border-radius: 3px; + background: rgba(126, 90, 15, 0.3); + position: relative; + overflow: hidden; + transition: all 0.3s; +} +.insight .swiper-pagination .swiper-pagination-bullet-active::after { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 0; + height: 100%; + background: #FF8922; + transition: width 0.3s ease; + width: 100%; + animation: fillGauge 2.9s linear forwards; +} +.insight .swiper-button-next, +.insight .swiper-button-prev { + width: 84px; + height: 180px; + background-size: 100% 100%; + background-repeat: no-repeat; + background-position: center; + top: 50%; + transform: translateY(-50%); + margin-top: 0; +} +.insight .swiper-button-next::after, +.insight .swiper-button-prev::after { + content: ""; + text-indent: -9999px; + color: transparent; + font-size: 1px; + overflow: hidden; +} +.insight .swiper-button-prev { + left: -120px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M94.6499 3C88.3166 26.9504 62.8499 78.281 11.6499 92C39.3166 99.3333 94.6499 127.5 94.6499 181.5' stroke='url(%23paint0_linear_1337_60932)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_60932' x1='94.6499' y1='92.25' x2='11.6499' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23DD842F'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); + transition: left 0.2s linear; +} +.insight .swiper-button-prev.swiper-button-disabled { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M94.6499 3C88.3166 26.9504 62.8499 78.281 11.6499 92C39.3166 99.3333 94.6499 127.5 94.6499 181.5' stroke='url(%23paint0_linear_1337_60932)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_60932' x1='94.6499' y1='92.25' x2='11.6499' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23EFE7D7'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.insight .swiper-button-prev:not(.swiper-button-disabled):hover { + left: -130px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M94.6499 3C88.3166 26.9504 62.8499 78.281 11.6499 92C39.3166 99.3333 94.6499 127.5 94.6499 181.5' stroke='url(%23paint0_linear_1337_60932)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_60932' x1='94.6499' y1='92.25' x2='11.6499' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23D34D00'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.insight .swiper-button-next { + right: -120px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M3.00098 3C9.33431 26.9504 34.801 78.281 86.001 92C58.3343 99.3333 3.00098 127.5 3.00098 181.5' stroke='url(%23paint0_linear_1337_61160)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_61160' x1='3.00098' y1='92.25' x2='86.001' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23DD842F'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); + transition: right 0.2s linear; +} +.insight .swiper-button-next.swiper-button-disabled { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M3.00098 3C9.33431 26.9504 34.801 78.281 86.001 92C58.3343 99.3333 3.00098 127.5 3.00098 181.5' stroke='url(%23paint0_linear_1337_61162)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_61162' x1='3.00098' y1='92.25' x2='86.001' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23EFE7D7'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +.insight .swiper-button-next:not(.swiper-button-disabled):hover { + right: -130px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98' height='185' viewBox='0 0 98 185' fill='none'%3E%3Cpath d='M3.00098 3C9.33431 26.9504 34.801 78.281 86.001 92C58.3343 99.3333 3.00098 127.5 3.00098 181.5' stroke='url(%23paint0_linear_1337_61162)' stroke-width='6' stroke-linecap='round'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1337_61162' x1='3.00098' y1='92.25' x2='86.001' y2='92.25' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23CBAF94' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23D34D00'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E"); +} +@keyframes fillGauge { + from { + width: 0; + } + to { + width: 100%; + } +} +.insight .insight-video-list .list-head { + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; + margin-bottom: 20px; + flex-wrap: wrap; + gap: 16px; +} +.insight .insight-video-list .total { + font-size: 16px; + font-weight: 400; + color: var(--text-secondary); +} +.insight .insight-video-list .total em { + font-style: normal; + font-weight: 700; + font-size: 20px; +} +.insight .insight-video-list .list-options { + display: flex; + align-items: center; + justify-content: flex-end; + flex-direction: row; + gap: 12px; + flex-wrap: wrap; +} +.insight .insight-video-list .select-wrap { + position: relative; +} +.insight .insight-video-list .select-sort { + padding: 8px 32px 8px 12px; + font-size: 16px; + color: var(--text-secondary); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23303030' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + cursor: pointer; +} +.insight .insight-video-list .filter-tabs { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + gap: 4px; +} +.insight .insight-video-list .filter-tabs button { + padding: 8px 14px; + font-size: 13px; + color: var(--text-secondary); + background: transparent; + border: 1px solid var(--border-keyword-base); + border-radius: 8px; + cursor: pointer; + transition: color 0.2s, background 0.2s, border-color 0.2s; +} +.insight .insight-video-list .filter-tabs button:hover { + color: var(--text-primary); + border-color: var(--border-keyword-primary-hover); +} +.insight .insight-video-list .filter-tabs button.on { + color: var(--text-primary); + font-weight: 600; + background: var(--bg-keyword-base); + border-color: transparent; +} +.insight .video-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + -moz-column-gap: 24px; + column-gap: 24px; + row-gap: 56px; + list-style: none; + margin: 0; + padding: 0; +} +.insight .video-item .card-link { + display: block; + position: relative; + overflow: hidden; + transition: box-shadow 0.2s, transform 0.2s; + text-decoration: none; + color: inherit; +} +.insight .video-item .bookmark { + position: absolute; + top: 16px; + right: 14px; + z-index: 2; + width: 22px; + height: 22px; + cursor: pointer; +} +.insight .video-item .bookmark input[type=checkbox] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + cursor: pointer; + border: none; + border-radius: 50%; + background-image: url("../img/ico/ico_bookmark_off.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.insight .video-item .bookmark input[type=checkbox]:checked { + background-image: url("../img/ico/ico_bookmark_on.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.insight .video-item .item-thumb { + position: relative; + width: 100%; + aspect-ratio: 16/9; + overflow: hidden; + border-radius: 8px 8px 0 0; + background: var(--bg-video); +} +.insight .video-item .item-thumb img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} +.insight .video-item .item-info { + padding: 14px 0; + display: flex; + align-items: flex-start; + justify-content: stretch; + flex-direction: column; + gap: 16px; +} +.insight .video-item .item-title { + display: block; + font-size: 18px; + font-weight: 500; + line-height: 1.4; + height: calc(18px * 1.4 * 2); + color: var(--text-primary); + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.insight .video-item .item-meta { + display: inline-block; + font-size: 14px; + font-weight: 500; + color: var(--text-keyword-secondary); +} +.insight .video-item .tag-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.insight .video-item .tag-list .tag { + display: inline-block; + font-size: 14px; + font-weight: 500; + color: var(--text-keyword-secondary); +} +.insight .video-item .item-desc { + display: block; + font-size: 12px; + color: var(--text-secondary); + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +@media (max-width: 1200px) { + .insight .video-grid { + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 768px) { + .insight .insight-inner { + padding: 16px 20px 32px; + } + .insight .pick-body { + flex-direction: column-reverse; + } + .insight .pick-profile { + order: -1; + } + .insight .pick-desc { + flex: none; + width: 100%; + } + .insight .video-grid { + grid-template-columns: 1fr; + } +} +.biztrend .container { + background: linear-gradient(180deg, rgba(236, 232, 228, 0.6) -5.19%, #FEFDFD 21.11%, #FFF 73.7%, #FFF 100%), #FFF; +} +.biztrend .page-header { + display: flex; + flex-direction: column; + max-width: 1600px; + margin: auto; + padding-top: 24px; + padding-bottom: 56px; +} +.biztrend .page-header .page-title { + padding-top: 64px; +} +.biztrend .page-header h3 { + font-size: 26px; + font-weight: 700; + color: #000; +} +.biztrend .breadcrumb { + gap: 32px; + font-size: 14px; + align-self: flex-end; + color: var(--text-secondary); + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.biztrend .breadcrumb li { + position: relative; +} +.biztrend .breadcrumb li:not(:last-child)::after { + content: ">"; + position: absolute; + right: -14px; + top: 0; +} +.biztrend .breadcrumb a { + color: var(--text-secondary); + text-decoration: none; +} +.biztrend .breadcrumb a:hover { + color: var(--text-primary); +} +.biztrend .breadcrumb .sep { + margin: 0 2px; + color: var(--text-keyword-base); +} +.biztrend .breadcrumb .current { + color: var(--text-base); + font-weight: 700; +} +.biztrend .biztrend-wrap { + margin-top: -48px; + margin-top: -72px; +} +.biztrend .biztrend-inner { + max-width: 1600px; + margin: 0 auto; +} +.biztrend .biztrend-header { + margin-bottom: 10px; + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; + gap: 24px; +} +.biztrend .biztrend-header .biztrend-sub-title { + gap: 16px; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.biztrend .biztrend-header .biztrend-photo { + width: 48px; + height: 48px; + border-radius: 50%; + overflow: hidden; +} +.biztrend .biztrend-header .biztrend-intro-desc { + margin: 0; + font-size: 15px; + line-height: 1.28; + color: var(--text-base, #333); +} +.biztrend .biztrend-header .biztrend-intro-desc em { + font-weight: 700; +} +.biztrend .biztrend-intro-inner { + position: relative; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 24px; + padding: 24px 0; + display: none; +} +.biztrend .biztrend-intro-photo { + flex-shrink: 0; + width: 120px; + height: 120px; + border-radius: 50%; + overflow: hidden; + background: var(--bg-video, #eee); + border: 2px solid rgba(0, 0, 0, 0.08); +} +.biztrend .biztrend-intro-photo img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} +.biztrend .article-grid { + max-width: 1600px; + margin: 0 auto; + padding: 0 10px; + max-height: calc(var(--window-inner-height) - 234px - 64px); + overflow-y: auto; +} +.biztrend .article-grid::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.biztrend .article-grid::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.biztrend .article-grid::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track); +} +.biztrend .article-grid-inner { + display: flex; + flex-direction: column; + gap: 16px; +} +.biztrend .article-grid-head { + position: sticky; + top: 0; + width: 100%; + z-index: 3; + background: #fff; +} +.biztrend .article-grid-days { + display: grid; + grid-template-columns: repeat(5, calc((100% - 68px) / 6)) calc((100% - 68px) / 6 + 28px); + gap: 8px; + list-style: none; + margin: 0; + padding: 0; + font-size: 14px; + font-weight: 600; + color: var(--text-secondary); + width: 100%; +} +.biztrend .article-grid-days::after { + content: "&"; + position: absolute; + top: 0; + right: calc(16.6666% - 12px); + width: 28px; + height: 100%; + font-size: 21px; + font-weight: 300; + color: #C3C3C3; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.biztrend .article-grid-days li { + position: relative; + padding: 8px 0; + text-align: center; + font-size: 17px; + color: #C3C3C3; +} +.biztrend .article-grid-days li:last-child::before, .biztrend .article-grid-days li:last-child::after { + left: auto; + right: 0; + width: calc(100% - 28px); +} +.biztrend .article-grid-days li::before { + content: " "; + position: absolute; + top: 0; + left: 0; + width: calc(100% + 8px); + height: 2px; + background: #E5E4E3; +} +.biztrend .article-grid-days li::after { + content: " "; + position: absolute; + bottom: 0; + left: 0; + width: calc(100% + 8px); + height: 1px; + background: #E5E4E3; +} +.biztrend .article-grid-date { + position: relative; +} +.biztrend .article-grid-date .date-select-trigger { + padding: 8px 32px 8px 12px; + font-size: 14px; + color: var(--text-primary); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='11' viewBox='0 0 13 11' fill='none'%3E%3Cpath d='M6.06226 10.5L7.81656e-05 -1.88258e-07L12.1244 8.71687e-07L6.06226 10.5Z' fill='%230C0C0C'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + cursor: pointer; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.biztrend .article-grid-date .date-select-trigger .date-select-label { + gap: 8px; + display: flex; + align-items: flex-end; + justify-content: flex-start; + flex-direction: row; +} +.biztrend .article-grid-date .date-select-trigger .date-year { + color: #979797; + font-size: 20px; +} +.biztrend .article-grid-date .date-select-trigger .date-month { + font-size: 25px; + color: #020202; +} +.biztrend .article-grid-date .date-select-trigger .date-select-month { + font-weight: bold; +} +.biztrend .article-grid-date .date-select-trigger .date-select-icon { + display: none; +} +.biztrend .article-grid-date .datepicker-dropdown { + display: none; + position: absolute; + top: 100%; + right: 0; + margin-top: 4px; + z-index: 50; + min-width: 280px; + background: #fff; + border: 1px solid var(--border-keyword-base, #ddd); + border-radius: 8px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); +} +.biztrend .article-grid-date .datepicker-dropdown.is-open { + display: block; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-dropdown-inner { + padding: 16px; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheels { + display: flex; + align-items: stretch; + justify-content: center; + flex-direction: row; + gap: 16px; + margin-bottom: 16px; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-wrap { + flex: 1; + display: flex; + flex-direction: column; + gap: 6px; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-label { + font-size: 14px; + font-weight: 600; + color: var(--text-secondary); +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel { + height: 180px; + overflow: hidden; + border: 1px solid var(--border-keyword-base, #ddd); + border-radius: 6px; + position: relative; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel::before, .biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel::after { + content: ""; + position: absolute; + left: 0; + right: 0; + height: 72px; + pointer-events: none; + z-index: 1; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel::before { + top: 0; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel::after { + bottom: 0; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-track { + height: 100%; + overflow-y: auto; + overflow-x: hidden; + scroll-behavior: smooth; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-track::-webkit-scrollbar { + height: 6px; + width: 6px; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-track::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-track::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track); +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-item { + height: 36px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + font-size: 16px; + color: var(--text-secondary); + cursor: pointer; + transition: color 0.15s, font-weight 0.15s; +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-item.is-selected { + font-weight: 700; + color: var(--text-primary); +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-wheel-item:hover { + color: var(--text-primary); +} +.biztrend .article-grid-date .datepicker-dropdown .datepicker-actions { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + gap: 10px; +} +.biztrend .article-grid-date .datepicker-dropdown .btn-datepicker-cancel, +.biztrend .article-grid-date .datepicker-dropdown .btn-datepicker-confirm { + padding: 8px 20px; + font-size: 13px; + font-weight: 600; + border-radius: 6px; + cursor: pointer; + transition: background 0.2s, color 0.2s; +} +.biztrend .article-grid-date .datepicker-dropdown .btn-datepicker-cancel { + background: #f5f5f5; + color: var(--text-secondary); + border: none; +} +.biztrend .article-grid-date .datepicker-dropdown .btn-datepicker-cancel:hover { + background: #eee; +} +.biztrend .article-grid-date .datepicker-dropdown .btn-datepicker-confirm { + background: var(--bg-keyword-primary, #333); + color: #fff; + border: none; +} +.biztrend .article-grid-date .datepicker-dropdown .btn-datepicker-confirm:hover { + background: var(--bg-keyword-hover, #555); +} +.biztrend .article-grid-body { + position: relative; + display: grid; + grid-template-columns: repeat(5, calc((100% - 68px) / 6)) calc((100% - 68px) / 6 + 28px); + -moz-column-gap: 8px; + column-gap: 8px; + row-gap: 54px; +} +.biztrend .article-grid-body::before { + content: ""; + position: absolute; + bottom: 0; + right: calc(16.6666% + 4px); + width: 1px; + height: 100%; + border-right: 1px dashed #CBCACA; +} +.biztrend .article-card { + position: relative; + display: grid; + grid-template-columns: 1fr; + align-items: flex-start; + min-height: 160px; + min-width: 0; + padding: 0 8px; + background: linear-gradient(180deg, #EFF4F3 0%, #F9F9F9 71.15%, rgba(249, 249, 249, 0) 96.63%); + border-radius: 6px; +} +.biztrend .article-card:nth-child(6n) { + margin-left: 28px; +} +.biztrend .article-card:not(.weekend) .article-card-content { + height: 144px; +} +.biztrend .article-card.weekend { + display: flex; + background: linear-gradient(180deg, #F8F3EE 0%, rgba(248, 243, 238, 0) 100%); +} +.biztrend .article-card.weekend .article-card-num::before { + background-color: #8C7B6A; +} +.biztrend .article-card.weekend .article-card-content .article-card-title-box { + background: #EDDFD1; +} +.biztrend .article-card.is-future { + opacity: 0.3; +} +.biztrend .article-card.today::before { + content: ""; + position: absolute; + top: 0; + left: 6px; + height: 2px; + width: calc(100% - 12px); + z-index: 1; + background: linear-gradient(90deg, #FF5905 75%, rgba(255, 89, 5, 0) 100%); +} +.biztrend .article-card.today .article-card-num { + filter: drop-shadow(3px 0 2px rgba(0, 0, 0, 0.25)); +} +.biztrend .article-card.today .article-card-num::before { + background-color: #FF5905; +} +.biztrend .article-card-num { + position: absolute; + top: 0; + left: 0; + font-size: 15px; + font-weight: 700; + color: #fff; + width: 39px; + height: 33px; + z-index: 2; + padding-left: 6px; + padding-right: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.biztrend .article-card-num::before { + content: " "; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #6A7B77; + mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 39 33'%3E%3Cpath fill='white' d='M0 33C32 33 29.5 28.5 38.5 0H6C2.68629 0 0 2.68629 0 6V33Z'/%3E%3C/svg%3E") center/contain no-repeat; + -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 39 33'%3E%3Cpath fill='white' d='M0 33C32 33 29.5 28.5 38.5 0H6C2.68629 0 0 2.68629 0 6V33Z'/%3E%3C/svg%3E") center/contain no-repeat; + z-index: -1; +} +.biztrend .article-card-content { + position: relative; + width: 100%; + height: 100%; + min-width: 0; + gap: 8px; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: column; +} +.biztrend .article-card-content:first-of-type .article-card-title-box { + padding-left: 32px; +} +.biztrend .article-card-content:not(:first-of-type):last-of-type .article-card-title-box { + background: #EBE3EB; +} +.biztrend .article-card-content:hover { + box-shadow: 0 0 5.4px 0 rgba(0, 0, 0, 0.3); + z-index: 1; +} +.biztrend .article-card-content:hover::before { + background: linear-gradient(180deg, var(--bg-card-hover-start) 0%, var(--bg-card-hover-end) 100%); +} +.biztrend .article-card-content::before { + content: " "; + display: none; + position: absolute; + width: 100%; + height: 100%; + padding: 8px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; + top: -8px; + left: -8px; + border-radius: 10px; +} +.biztrend .article-card-content { + position: relative; +} +.biztrend .article-card-content .article-card-link { + display: block; + width: 100%; + text-decoration: none; + color: inherit; +} +.biztrend .article-card-content > .article-card-like { + position: absolute; + right: 10px; + top: 8px; + z-index: 1; + flex-shrink: 0; + width: 20px; + height: 20px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + background-image: url("../img/ico/ico_bookmark_off.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.biztrend .article-card-content > .article-card-like.is-active { + background-image: url("../img/ico/ico_bookmark_on.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.biztrend .article-card-title-box { + position: relative; + padding-left: 16px; + padding-right: 32px; + margin-bottom: 8px; + width: 100%; + min-width: 0; + height: 36px; + min-height: 36px; + background: #DBE8E5; + gap: 8px; + border-radius: 2px 2px 20px 20px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.biztrend .article-card-title { + position: relative; + flex: 1; + min-width: 0; + font-size: 16px; + font-weight: 700; + line-height: 1.35; + color: var(--text-primary); + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.biztrend .article-card-list { + padding: 0 16px; + width: 100%; + min-width: 0; + font-size: 14px; + line-height: 1.6; + color: #2D2D2D; +} +.biztrend .article-card-list li { + position: relative; + padding-left: 14px; + text-align: left; + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.biztrend .article-card-list li::before { + content: " "; + position: absolute; + left: 0; + top: 10px; + width: 4px; + height: 1px; + background: #000; +} +.biztrend .article-card-author { + margin: 0; + font-size: 11px; + color: var(--text-keyword-secondary, #999); +} +.biztrend .article-card-badge { + position: absolute; + top: -14px; + right: 2px; + padding: 2px 6px; + font-size: 11px; + font-weight: 700; + color: #fff; + background: #e53935; + border-radius: 4px; +} +.biztrend .article-card-hover-label { + position: absolute; + top: 12px; + right: 12px; + padding: 2px 8px; + font-size: 11px; + font-weight: 700; + color: #fff; + background: #ff9800; + border-radius: 4px; +} +.biztrend .article-card-image { + display: grid; + grid-template-columns: 68fr 149fr; + gap: 12px; + max-width: 100%; +} +.biztrend .article-card-image-placeholder { + width: 100%; + height: 100%; + aspect-ratio: 2/3; + display: flex; + align-items: flex-start; + justify-content: center; + font-size: 12px; + color: var(--text-keyword-secondary, #999); + text-align: center; + padding-left: 12px; + padding-top: 4px; +} +.biztrend .article-card-image-placeholder + .article-card-list { + padding-left: 0; +} +.biztrend .article-card--with-image .article-card-content { + display: flex; + flex-direction: column; + gap: 8px; +} +.biztrend .article-card--with-image .article-card-title { + padding-top: 0; +} +.biztrend .gauge-bar { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--bg-gauge-base); + padding-bottom: 4px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; +} +.biztrend .gauge-bar.rect { + border-radius: 0; + background: #D9D9D9; +} +.biztrend .gauge-bar.rect .gauge-fill { + border-radius: 0; +} +.biztrend .gauge-fill { + position: absolute; + left: 0; + top: 50%; + height: 50%; + background: var(--bg-gauge-primary); + border-radius: 4px; +} + +.mypage .container { + background: linear-gradient(180deg, transparent 0%, rgba(255, 255, 255, 0.67) 22%, rgba(255, 255, 255, 0.67) 66%, rgba(236, 227, 210, 0.67) 100%) #ECE3D2; + overflow: hidden; +} +.mypage .mypage-inner { + display: grid; + grid-template-columns: 295px 1305fr; + max-width: 1600px; + height: 100%; + min-height: 0; + margin: 0 auto; +} +.mypage .mypage-sidebar { + position: relative; + flex-shrink: 0; + width: 100%; + min-height: 0; + padding-top: 32px; + overflow-y: auto; +} +.mypage .mypage-sidebar::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.mypage .mypage-sidebar::-webkit-scrollbar-thumb { + border-radius: 4px; + background: #E5DECD; + background-blend-mode: multiply; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.mypage .mypage-sidebar::-webkit-scrollbar-track { + border-radius: 4px; + background: #F2ECDF; + background-blend-mode: multiply; +} +.mypage .mypage-sidebar::after { + content: ""; + position: absolute; + width: 1px; + height: 100%; + border-left: 1px solid #C7C5C0; + right: 0; + top: 0; + box-shadow: 1px 0 0 0 #fff, -1px 0 0 0 #fff inset; +} +@media only screen and (max-width: 1023px) { + .mypage .mypage-sidebar { + width: 100%; + max-width: 400px; + margin: 0 auto; + } +} +.mypage .mypage-title { + margin: 0 0 24px; + font-size: 28px; + font-weight: 700; + color: var(--text-primary); +} +.mypage .profile-area { + margin-top: 64px; + max-width: 202px; + margin: 64px auto 0; +} +.mypage .profile-card { + margin-bottom: 24px; + text-align: center; +} +.mypage .profile-photo { + width: 144px; + height: 144px; + margin: 0 auto 18px; + border-radius: 50%; + overflow: hidden; + background: var(--bg-video); + box-shadow: 3px 1px 11.6px 10px rgba(210, 205, 194, 0.2), 0 0 0 2px #FFF, 0 4px 6.4px 0 rgba(0, 0, 0, 0.25) inset; +} +.mypage .profile-photo img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} +.mypage .profile-name { + margin: 0 0 4px; + font-size: 18px; + font-weight: 400; + color: var(--text-primary); +} +.mypage .profile-name em { + font-weight: 700; +} +.mypage .profile-role { + margin: 0 0 22px; + font-size: 15px; + color: #7D7B78; +} +.mypage .badge-level { + display: inline-flex; + align-items: center; + justify-content: space-between; + gap: 6px; + width: 100%; + padding: 8px 14px; + font-size: 12px; + font-weight: 500; + color: #625C53; + background: #F6E7D0; + border-radius: 4px; +} +.mypage .badge-level .ico-level { + width: 16px; + height: 16px; +} +.mypage .badge-level .ico-info { + width: 14px; + height: 14px; + opacity: 0.6; + background: url("../img/ico/ico_info.svg") center/contain no-repeat; + cursor: help; +} +.mypage .badge-level .badge-level-value { + font-size: 13px; + font-weight: 700; + color: #000; + gap: 4px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.mypage .suggest-section { + display: flex; + flex-direction: column; + gap: 8px; +} +.mypage .suggest-title { + font-size: 13px; + font-weight: 500; + color: #5E4311; +} +.mypage .suggest-input { + width: 100%; + min-height: 140px; + padding: 8px 12px; + font-size: 14px; + line-height: 1.5; + color: var(--text-primary); + background: #FAFAFA; + border: 1px solid #ccc; + border-radius: 4px; + resize: none; + box-sizing: border-box; +} +.mypage .suggest-input::-moz-placeholder { + color: #C8C8C8; +} +.mypage .suggest-input::placeholder { + color: #C8C8C8; +} +.mypage .suggest-status { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 8px; +} +.mypage .accordion-trigger { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + width: 100%; + font-size: 12px; + font-weight: 500; + color: #A9770B; + background: transparent; + border: none; + cursor: pointer; + text-align: left; + transition: color 0.2s; +} +.mypage .accordion-trigger:hover { + color: #4a3510; +} +.mypage .accordion-trigger:hover .ico-chevron { + background-color: #4a3510; +} +.mypage .accordion-title { + flex-shrink: 0; +} +.mypage .ico-chevron { + flex-shrink: 0; + width: 10px; + height: 10px; + background-color: #A9770B; + mask-image: url("../img/ico/ico_chevron.svg"); + mask-size: contain; + mask-position: center; + mask-repeat: no-repeat; + -webkit-mask-image: url("../img/ico/ico_chevron.svg"); + -webkit-mask-size: contain; + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + width: 10px; + aspect-ratio: 1/1; + transition: transform 0.2s, background-color 0.2s; +} +.mypage .accordion.is-open .ico-chevron { + transform: rotate(180deg); +} +.mypage .accordion-content { + padding-bottom: 12px; + overflow: hidden; +} +.mypage .suggest-status-list { + list-style: none; + margin: 0; + padding: 0; + font-size: 13px; + color: var(--text-secondary); +} +.mypage .suggest-status-list li { + display: flex; + align-items: center; + justify-content: space-between; + gap: 4px; + padding: 6px 0; +} +.mypage .consider .suggest-date, +.mypage .consider .suggest-state { + color: #000; + font-weight: 500; + font-size: 13px; +} +.mypage .suggest-date { + flex-shrink: 0; +} +.mypage .suggest-dots { + flex: 1; + min-width: 8px; + height: 1px; + border-bottom: 1px dotted #999; + margin: 0 4px; +} +.mypage .suggest-state { + flex-shrink: 0; +} +.mypage .btn-suggest { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; + margin-top: 12px; + padding: 12px 16px; + font-size: 13px; + font-weight: 500; + color: var(--text-primary); + background: #e8e4dc; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 8px; + cursor: pointer; + transition: background 0.2s; +} +.mypage .btn-suggest:hover { + background: #ddd9d0; +} +.mypage .btn-suggest .ico-arrow { + width: 14px; + height: 14px; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23333'%3E%3Cpath d='M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z'/%3E%3C/svg%3E") center/contain no-repeat; +} +.mypage .mypage-main { + flex: 1; + min-width: 0; + min-height: 0; + padding-top: 32px; + display: flex; + flex-direction: column; + overflow: hidden; +} +.mypage .section-title { + margin: 0 0 16px; + font-size: 18px; + font-weight: 500; + color: var(--text-primary); + gap: 10px; + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.mypage .section-title .year-badge { + font-size: 14px; + font-weight: 300; +} +.mypage .section-title .count { + font-weight: 800; + font-size: 14px; +} +.mypage .activity-section { + flex-shrink: 0; + position: relative; + margin-bottom: 48px; + padding: 0 18px 0 32px; +} +.mypage .activity-section .section-title { + margin-bottom: 8px; +} +.mypage .total-time-area { + position: absolute; + left: 50%; + top: 0; + transform: translateX(-50%); + display: flex; + align-items: center; + justify-content: flex-end; + flex-direction: row; + gap: 12px; + z-index: 2; +} +.mypage .total-time-inner { + position: relative; + height: 40px; + overflow: hidden; +} +.mypage .total-time { + position: relative; + width: 298px; + height: 90px; + padding-bottom: 50px; + font-size: 14px; + color: var(--text-primary); + border-radius: 160px 160px 0 0; + background: rgb(247, 232, 215); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + color: #CF6800; + z-index: 1; +} +.mypage .total-time .total-time-text { + margin: 0; +} +.mypage .total-time strong { + font-size: 20px; + font-weight: 700; +} +.mypage .total-time .gauge-bar { + padding-top: 4px; + padding-bottom: 0; + background: rgba(230, 191, 145, 0.4); +} +.mypage .total-time .gauge-bar .gauge-fill { + top: 0; + background: #CF6800; +} +.mypage .total-average { + position: absolute; + right: -48px; + bottom: -50%; + flex-shrink: 0; + padding: 2px 8px 2px 8px; + font-size: 12px; + color: #D0B290; + background: #F9F6F0; + border: 1px solid #D0B290; + border-radius: 20px; + white-space: nowrap; + transform: translateY(-50%); + z-index: 1; +} +.mypage .total-average::before { + content: ""; + position: absolute; + left: -12px; + top: calc(50% + 1px); + width: 16px; + height: 8px; + transform: translateY(-50%); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='7' viewBox='0 0 12 7' fill='none'%3E%3Cpath d='M10.1699 0.516165C10.3393 0.471368 10.5206 0.518006 10.6464 0.639994C10.7723 0.762118 10.8249 0.941611 10.7852 1.11241L10.4455 2.57344C10.323 3.10031 10.3747 3.65271 10.5928 4.1477L11.198 5.52063C11.2742 5.69385 11.2459 5.89537 11.1251 6.04108C11.0043 6.18689 10.8114 6.25187 10.6269 6.20898L0.886712 3.94431C0.662962 3.89229 0.503447 3.69408 0.500001 3.46439C0.496751 3.23507 0.649987 3.03317 0.871623 2.97429L10.1699 0.516165Z' fill='%23D0B290' stroke='%23F9F6F0' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} +.mypage .activity-list { + position: relative; + list-style: none; + padding: 0; + display: grid; + grid-template-columns: repeat(6, 1fr); + width: 100%; + border-radius: 6px; + background: linear-gradient(180deg, rgba(240, 225, 207, 0.2) 0.05%, rgba(240, 225, 207, 0) 32.26%), linear-gradient(181deg, #FFEFDC 1.28%, rgba(251, 244, 235, 0.5) 94.84%); + padding: 16px 8px; + border-left: 1px solid #fff; + border-top: 1px solid #fff; + z-index: 1; +} +.mypage .activity-list::before { + content: ""; + position: absolute; + width: calc(100% + 1px); + height: calc(100% + 1px); + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; + top: -2px; + left: -2px; + padding: 1px; + background: linear-gradient(180deg, #FADEBD 0%, #DBD8D2 100%); +} +.mypage .activity-list::after { + content: ""; + position: absolute; + width: 2px; + height: 100%; + left: 50%; + bottom: 0; + transform: translateX(-50%); + background: linear-gradient(180deg, rgba(199, 197, 192, 0.2) 0%, rgba(199, 197, 192, 0.6) 50%, #DBD8D2 100%) left/1px 100% no-repeat, #fff right/1px 100% no-repeat; + -webkit-mask: linear-gradient(180deg, transparent 0%, #000 30% 100%); + mask: linear-gradient(180deg, transparent 0%, #000 30% 100%); +} +.mypage .activity-item { + position: relative; + padding: 0px 16px; +} +.mypage .activity-item.gauge:first-of-type::after { + content: ""; + position: absolute; + width: 1px; + height: 100%; + right: 0px; + top: 0; + border-right: 1px dashed #C7C5C0; +} +.mypage .activity-item.tag .activity-value { + color: #7C590D; +} +.mypage .activity-head { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 8px; + margin-bottom: 8px; +} +.mypage .activity-head .activity-label { + font-size: 14px; + font-weight: 500; + color: #000; +} +.mypage .activity-head .activity-value { + font-size: 14px; + color: #1B8B6F; +} +.mypage .activity-head .activity-value em { + font-size: 20px; + font-weight: 700; + margin-left: 2px; +} +.mypage .activity-note { + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + font-size: 11px; + color: #fff; + font-weight: 600; + text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.15), 1px -1px 0 rgba(0, 0, 0, 0.15), -1px 1px 0 rgba(0, 0, 0, 0.15), 1px 1px 0 rgba(0, 0, 0, 0.15); +} +.mypage .average-label { + margin: 0 0 4px; + font-size: 12px; + color: var(--text-secondary); +} +.mypage .progress-track { + position: relative; + overflow: hidden; +} +.mypage .progress-track .gauge-bar { + position: relative; + width: 100%; + padding-bottom: 24px; + background: #CBC5BA; + border-radius: 70px; + box-shadow: 0 0 2.828px 0 rgba(0, 0, 0, 0.25) inset; +} +.mypage .progress-track .gauge-bar .gauge-fill { + position: absolute; + left: 0; + top: 0; + height: 100%; + border-radius: 70.696px; + background: linear-gradient(91deg, #5693AF 20%, #0C895D 72.48%); + box-shadow: 0.707px 2.121px 2.828px 0 rgba(0, 0, 0, 0.25), 0.707px 1.414px 2.828px 0 rgba(255, 255, 255, 0.25) inset, -0.707px -2.121px 3.393px 0 rgba(0, 0, 0, 0.25) inset; +} +.mypage .progress-track .gauge-value { + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; + font-size: 10px; + color: #B3B0AA; +} +.mypage .progress-track .gauge-value .gauge-start { + font-weight: 500; +} +.mypage .progress-track .gauge-value .gauge-end { + font-weight: 700; +} +.mypage .progress-track .tag-list { + display: flex; + flex-wrap: nowrap; + align-items: stretch; + justify-content: space-between; + gap: 3px; + width: 100%; + height: 24px; + margin-top: 0; +} +.mypage .progress-track .tag-list .tag-wrap { + display: flex; + min-width: 0; + container-type: inline-size; +} +.mypage .progress-track .tag-list .tag { + padding: 0 8px; + width: 100%; + min-width: 0; + font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + color: rgba(255, 255, 255, 0.8); + white-space: nowrap; + border-radius: 4px; + background: linear-gradient(0deg, rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0.4) 100%), linear-gradient(91deg, #805D00 20%, #624B19 72.48%); + text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.15), 1px -1px 0 rgba(0, 0, 0, 0.15), -1px 1px 0 rgba(0, 0, 0, 0.15), 1px 1px 0 rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +@container (max-width: 44px) { + .mypage .progress-track .tag-list .tag { + font-size: 0; + line-height: 0; + padding: 0 4px; + } +} +.mypage .progress-track .tag-list .tag-wrap:first-child .tag { + border-radius: 16px 4px 4px 16px; + background: linear-gradient(91deg, #B08100 20%, #936F23 72.48%); + box-shadow: 0.707px 1.414px 2.828px 0 rgba(255, 255, 255, 0.25) inset, -0.707px -2.121px 3.393px 0 rgba(0, 0, 0, 0.25) inset; +} +.mypage .progress-track .tag-list .tag-wrap:last-child .tag { + border-radius: 4px 16px 16px 4px; +} +.mypage .tag-list { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 8px; +} +.mypage .tag-list .tag { + font-size: 12px; + color: var(--text-secondary); +} +.mypage .mylist-area { + flex: 1; + min-height: 0; + padding-left: 46px; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 18px; +} +.mypage .mylist-area .content-section, +.mypage .mylist-area .review-section { + min-width: 0; + min-height: 0; +} +.mypage .content-section { + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} +.mypage .content-section .section-title { + flex-shrink: 0; +} +.mypage .content-grid { + flex: 1; + min-height: 0; + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-auto-rows: min-content; + gap: 12px; + list-style: none; + margin: 0; + padding: 0 4px 48px 0; + overflow-y: auto; + align-content: start; +} +.mypage .content-grid::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.mypage .content-grid::-webkit-scrollbar-thumb { + border-radius: 4px; + background: #E5DECD; + background-blend-mode: multiply; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.mypage .content-grid::-webkit-scrollbar-track { + border-radius: 4px; + background: #F2ECDF; + background-blend-mode: multiply; +} +@media only screen and (max-width: 991px) { + .mypage .content-grid { + grid-template-columns: repeat(2, 1fr); + gap: 16px; + } +} +.mypage .content-card { + position: relative; + background: rgba(255, 255, 255, 0.8); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: box-shadow 0.2s; +} +.mypage .content-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} +.mypage .content-card .card-link { + display: block; + text-decoration: none; + color: inherit; +} +.mypage .content-card .bookmark { + position: absolute; + top: 12px; + right: 12px; + padding: 0; + z-index: 2; + width: 24px; + height: 24px; + cursor: pointer; +} +.mypage .content-card .bookmark input[type=checkbox] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + cursor: pointer; + border: none; + background: transparent; + background-image: url("../img/ico/ico_bookmark_off.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.mypage .content-card .bookmark input[type=checkbox]:checked { + background-image: url("../img/ico/ico_bookmark_on.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.mypage .content-card .btn-remove-card { + position: absolute; + bottom: 10px; + right: 12px; + z-index: 2; + width: 16px; + height: 16px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + opacity: 0.7; + background-image: url("../img/ico/ico_trash.svg"); + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.mypage .content-card .btn-remove-card:hover { + opacity: 1; +} +.mypage .item-thumb { + position: relative; + width: 100%; + aspect-ratio: 16/9; + overflow: hidden; + background: var(--bg-video); + border-radius: 12px 12px 0 0; +} +.mypage .item-thumb img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} +.mypage .item-info { + padding: 8px 12px; +} +.mypage .category-tag { + display: inline-block; + padding: 2px 4px; + margin-bottom: 8px; + font-size: 10px; + font-weight: 600; + color: #000; + opacity: 0.5; + border-radius: 4px; +} +.mypage .item-title { + display: block; + margin: 0 0 8px; + font-size: 12px; + font-weight: 500; + line-height: 1.4; + height: 33.6px; + color: var(--text-primary); + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.mypage .item-info .tag-list { + margin-top: 0; +} +.mypage .gauge-bar { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--bg-gauge-base); + padding-bottom: 4px; + content: ""; + display: block; + position: absolute; + inset: 0; + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + border-radius: inherit; +} +.mypage .gauge-bar.rect { + border-radius: 0; + background: #D9D9D9; +} +.mypage .gauge-bar.rect .gauge-fill { + border-radius: 0; +} +.mypage .gauge-fill { + position: absolute; + left: 0; + top: 50%; + height: 50%; + background: var(--bg-gauge-primary); + border-radius: 4px; +} +.mypage .review-section { + display: flex; + flex-direction: column; + min-height: 0; + margin-bottom: 48px; + overflow: hidden; +} +.mypage .review-section .section-title { + flex-shrink: 0; +} +.mypage .review-list { + flex: 1; + min-height: 0; + list-style: none; + margin: 0; + padding: 0 4px 4px 0; + display: flex; + flex-direction: column; + gap: 12px; + overflow-y: auto; +} +.mypage .review-list::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.mypage .review-list::-webkit-scrollbar-thumb { + border-radius: 4px; + background: #E5DECD; + background-blend-mode: multiply; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.mypage .review-list::-webkit-scrollbar-track { + border-radius: 4px; + background: #F2ECDF; + background-blend-mode: multiply; +} +.mypage .review-item { + flex-shrink: 0; + padding: 10px 14px; + background: rgba(255, 255, 255, 0.8); + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} +.mypage .review-item .review-head { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 10px; + margin-bottom: 8px; + min-width: 0; + overflow: hidden; +} +.mypage .review-item .category-tag { + flex-shrink: 0; + padding: 2px 8px 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 500; + white-space: nowrap; + color: rgba(0, 0, 0, 0.5); + margin-bottom: 0; + background: rgba(0, 0, 0, 0.06); +} +.mypage .review-item .category-tag.insight { + background: #DAE6E5; + opacity: 1; +} +.mypage .review-item .category-tag.leader { + background: #DED7CF; + opacity: 1; +} +.mypage .review-item .category-tag.biz { + background: #DED7CF; + opacity: 1; +} +.mypage .review-text { + flex: 1 1 0; + min-width: 0; + margin: 0; + font-size: 12px; + line-height: 1.5; + color: rgba(0, 0, 0, 0.3); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.mypage .review-comment { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + font-weight: 500; + color: var(--text-secondary); +} +.mypage .review-comment .review-comment-text { + min-width: 0; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.mypage .review-comment .review-user { + flex-shrink: 0; + width: 20px; + height: 20px; + border-radius: 50%; + overflow: hidden; +} +.mypage .review-comment strong { + color: var(--text-primary); + white-space: nowrap; +} + +.myclass .container { + background: linear-gradient(180deg, #F4F1EB 0%, #EDE5D8 100%); +} +.myclass .myclass-wrap { + position: relative; + min-height: 100%; + padding: 24px 60px 60px; +} +.myclass .myclass-wrap::after { + content: ""; + position: absolute; + top: 0; + right: 0; + width: 30%; + padding-top: 56%; + background-image: url("../img/myclass/bg_lo.png"); + background-repeat: no-repeat; + background-size: contain; + background-position: top right; + pointer-events: none; + mix-blend-mode: multiply; + opacity: 0.6; + z-index: 0; +} +.myclass .myclass-wrap > * { + position: relative; + z-index: 1; +} +.myclass .myclass-top-bar { + display: flex; + align-items: flex-start; + justify-content: space-between; + flex-direction: row; + gap: 24px; + padding-bottom: 16px; +} +.myclass .top-bar-left { + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; + gap: 8px; + flex-shrink: 0; +} +.myclass .user-rank-area { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 6px; + font-size: 14px; + color: var(--text-primary); +} +.myclass .user-rank-area .ico-trophy { + font-size: 20px; + line-height: 1; +} +.myclass .user-rank-area .rank-text em { + font-style: normal; + font-weight: 700; + color: #B06A00; +} +.myclass .user-rank-area .btn-rank-toggle { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + width: 20px; + height: 20px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; +} +.myclass .user-rank-area .btn-rank-toggle .ico-chevron-down { + display: block; + width: 10px; + height: 6px; + border-left: 2px solid currentColor; + border-bottom: 2px solid currentColor; + transform: rotate(-45deg); + color: var(--text-secondary); + margin-top: -4px; +} +.myclass .btn-year { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 6px; + padding: 6px 14px; + background: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 8px; + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + cursor: pointer; + transition: background 0.2s; +} +.myclass .btn-year:hover { + background: rgba(255, 255, 255, 0.9); +} +.myclass .btn-year .ico-year-arrow { + display: block; + width: 10px; + height: 6px; + border-left: 2px solid currentColor; + border-bottom: 2px solid currentColor; + transform: rotate(-45deg) translateY(-2px); + color: var(--text-secondary); +} +.myclass .page-intro { + text-align: right; + flex: 1; + min-width: 0; + padding: 0; +} +.myclass .intro-state { + display: flex; + align-items: flex-end; + justify-content: flex-end; + flex-direction: column; + gap: 4px; +} +.myclass .intro-greeting { + margin: 0; + font-size: 16px; + font-weight: 400; + color: var(--text-primary); +} +.myclass .intro-greeting strong { + font-weight: 700; +} +.myclass .intro-desc { + margin: 0; + font-size: 32px; + font-weight: 400; + color: var(--text-primary); + line-height: 1.4; +} +.myclass .intro-desc .intro-dday { + font-style: normal; + color: var(--text-accent); +} +.myclass .intro-desc strong { + font-weight: 700; + color: var(--text-accent); +} +.myclass .selected-goal-section { + margin-bottom: 40px; +} +.myclass .selected-goal-header { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 12px; + margin-bottom: 16px; +} +.myclass .selected-goal-meta { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 8px; +} +.myclass .quarter-badge { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 4px 12px; + font-size: 13px; + font-weight: 700; + color: #4A3200; + background: linear-gradient(135deg, #FFD54F 0%, #FFC107 100%); + border-radius: 20px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); +} +.myclass .selected-goal-title { + margin: 0; + font-size: 22px; + font-weight: 700; + color: var(--text-primary); +} +.myclass .selected-goal-bookshelf { + position: relative; +} +.myclass .selected-books-wrap { + padding: 0 40px; + text-align: center; +} +.myclass .selected-books-img { + max-width: 100%; + max-height: 200px; + -o-object-fit: contain; + object-fit: contain; + vertical-align: bottom; +} +.myclass .selected-books-placeholder { + display: none; + align-items: center; + justify-content: center; + height: 120px; + color: var(--text-secondary); + font-size: 14px; +} +.myclass .goal-list { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; + list-style: none; + margin: 0; + padding: 0; + padding: 0 42px; +} +.myclass .goal-item { + position: relative; +} +.myclass .goal-card { + position: relative; + width: 100%; + min-height: 220px; + height: 100%; + padding: 20px 20px 24px; + background: linear-gradient(145deg, #46B8A5 0%, #2D9180 100%); + border: none; + border-radius: 10px 10px 0 0; + cursor: pointer; + text-align: left; + overflow: hidden; + transition: transform 0.2s ease, box-shadow 0.2s ease; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} +.myclass .goal-card::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50%; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 100%); + border-radius: 10px 10px 0 0; + pointer-events: none; +} +.myclass .goal-card:hover { + transform: translateY(-4px); + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25); +} +.myclass .goal-card:focus-visible { + outline: 3px solid rgba(255, 255, 255, 0.8); + outline-offset: 2px; +} +.myclass .goal-item:nth-child(1) .goal-card { + background: linear-gradient(145deg, #46B8A5 0%, #2D9180 100%); +} +.myclass .goal-item:nth-child(2) .goal-card { + background: linear-gradient(145deg, #3EAEBC 0%, #2089A0 100%); +} +.myclass .goal-item:nth-child(3) .goal-card { + background: linear-gradient(145deg, #46B8A5 0%, #2D9180 100%); +} +.myclass .goal-item:nth-child(4) .goal-card { + background: linear-gradient(145deg, #3B9EAC 0%, #217A8C 100%); +} +.myclass .goal-item:nth-child(5) .goal-card { + background: linear-gradient(145deg, #3EB09C 0%, #268A78 100%); +} +.myclass .goal-item:nth-child(6) .goal-card { + background: linear-gradient(145deg, #42ACBA 0%, #258298 100%); +} +.myclass .goal-item:nth-child(7) .goal-card { + background: linear-gradient(145deg, #3DAE9E 0%, #228880 100%); +} +.myclass .goal-item:nth-child(8) .goal-card { + background: linear-gradient(145deg, #40B2A2 0%, #268C84 100%); +} +.myclass .card-default-area { + height: 100%; + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; + gap: 12px; +} +.myclass .card-icon-wrap { + flex-shrink: 0; +} +.myclass .card-icon { + display: block; + width: 56px; + height: 56px; + mix-blend-mode: plus-darker; +} +.myclass .card-icon svg { + width: 100%; + height: 100%; +} +.myclass .card-body { + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; + gap: 8px; +} +.myclass .card-title { + display: block; + font-size: 18px; + font-weight: 700; + line-height: 1.4; + color: #fff; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} +.myclass .card-desc { + margin: 0; + font-size: 13px; + line-height: 1.6; + color: rgba(255, 255, 255, 0.85); +} +.myclass .card-dot { + position: absolute; + top: 12px; + right: 12px; + display: none; + width: 12px; + height: 12px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.4); + border: 1.5px solid rgba(255, 255, 255, 0.7); +} +.myclass .bookshelf.has-selected .goal-item:not(.is-selected) .card-dot { + display: block; +} +.myclass .bookshelf.has-selected .goal-item:not(.is-selected) .goal-card { + filter: brightness(0.9); +} +.myclass .goal-item.is-selected .goal-card { + background: linear-gradient(145deg, #4A9B8A 0%, #2D7568 100%); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25), inset 0 0 0 2px rgba(255, 255, 255, 0.2); +} +.myclass .shelf-board { + position: relative; + width: 100%; + height: 20px; + background: #6C3D20; + border-radius: 1px; +} +.myclass .shelf-leg--left { + left: 18px; + border-radius: 0 0 3px 3px; +} +.myclass .shelf-leg--right { + right: 18px; + border-radius: 0 0 3px 3px; +} +.myclass .myclass-cta { + padding: 32px 0 0; + text-align: center; +} +.myclass .btn-select-goal { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 200px; + padding: 16px 40px; + font-size: 18px; + font-weight: 700; + color: #fff; + background: rgba(100, 90, 80, 0.85); + border: none; + border-radius: 8px; + cursor: pointer; + transition: background 0.2s, transform 0.15s; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} +.myclass .btn-select-goal:hover { + background: rgba(80, 72, 64, 0.9); + transform: translateY(-2px); +} +.myclass .btn-select-goal:active { + transform: translateY(0); +} + +.goal-layer { + display: none; + position: fixed; + inset: 0; + z-index: 200; + background: rgba(0, 0, 0, 0.72); + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + gap: 24px; + padding: 16px 24px; +} +.goal-layer.is-open { + display: flex; +} +.goal-layer .btn-goal-prev, +.goal-layer .btn-goal-next { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + width: 90px; + min-height: 200px; + background: transparent; + border: none; + cursor: pointer; + color: rgba(255, 255, 255, 0.6); + font-size: 14px; + font-weight: 500; + line-height: 1.4; + text-align: center; + padding: 16px 8px; + transition: color 0.2s; +} +.goal-layer .btn-goal-prev:disabled, +.goal-layer .btn-goal-next:disabled { + opacity: 0; + pointer-events: none; +} +.goal-layer .btn-goal-prev:not(:disabled):hover, +.goal-layer .btn-goal-next:not(:disabled):hover { + color: rgba(255, 255, 255, 0.9); +} +.goal-layer .btn-goal-prev .prev-goal-name, +.goal-layer .btn-goal-prev .next-goal-name, +.goal-layer .btn-goal-next .prev-goal-name, +.goal-layer .btn-goal-next .next-goal-name { + word-break: keep-all; +} +.goal-layer .btn-goal-prev::before { + content: ""; + display: block; + width: 40px; + height: 80px; + margin: 0 auto 8px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='80' viewBox='0 0 40 80' fill='none'%3E%3Cpath d='M36 4C30 20 8 36 4 40C8 44 30 60 36 76' stroke='rgba(255,255,255,0.5)' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: contain; +} +.goal-layer .btn-goal-next::before { + content: ""; + display: block; + width: 40px; + height: 80px; + margin: 0 auto 8px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='80' viewBox='0 0 40 80' fill='none'%3E%3Cpath d='M4 4C10 20 32 36 36 40C32 44 10 60 4 76' stroke='rgba(255,255,255,0.5)' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: contain; +} + +.goal-popup { + position: relative; + flex: 1; + max-width: 1300px; + max-height: calc(100vh - 60px); + display: flex; + flex-direction: column; + background: var(--bg-primary, #F4F1EB); + border: 3px solid #FF7020; + border-radius: 12px; + overflow: hidden; +} +.goal-popup .popup-side-deco { + position: absolute; + top: 50%; + font-size: 14px; + font-weight: 500; + color: rgba(255, 255, 255, 0.5); + white-space: nowrap; + letter-spacing: 0.1em; + pointer-events: none; + z-index: 0; +} +.goal-popup .popup-side-deco--left { + left: -110px; + transform: translateY(-50%) rotate(-90deg); +} +.goal-popup .popup-side-deco--right { + right: -110px; + transform: translateY(-50%) rotate(90deg); +} +.goal-popup .btn-close-layer { + position: absolute; + top: 20px; + right: 20px; + z-index: 10; + width: 36px; + height: 36px; + padding: 0; + background: rgba(0, 0, 0, 0.08); + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + transition: background 0.2s; +} +.goal-popup .btn-close-layer::before, .goal-popup .btn-close-layer::after { + content: ""; + position: absolute; + width: 16px; + height: 2px; + background: #333; + border-radius: 2px; +} +.goal-popup .btn-close-layer::before { + transform: rotate(45deg); +} +.goal-popup .btn-close-layer::after { + transform: rotate(-45deg); +} +.goal-popup .btn-close-layer:hover { + background: rgba(0, 0, 0, 0.14); +} +.goal-popup .popup-scroll { + position: relative; + z-index: 1; + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 48px 60px 32px; +} +.goal-popup .popup-scroll::-webkit-scrollbar { + height: 10px; + width: 7px; +} +.goal-popup .popup-scroll::-webkit-scrollbar-thumb { + background-color: var(--bg-scrollbar-thumb); + border-radius: 8px; + background-clip: padding-box; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.goal-popup .popup-scroll::-webkit-scrollbar-track { + background-color: var(--bg-scrollbar-track); +} +.goal-popup .popup-header { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + gap: 20px; + margin-bottom: 40px; +} +.goal-popup .popup-icon { + flex-shrink: 0; + width: 64px; + height: 64px; +} +.goal-popup .popup-icon svg { + width: 100%; + height: 100%; +} +.goal-popup .popup-title-area { + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; + gap: 6px; +} +.goal-popup .popup-title { + font-size: 28px; + font-weight: 700; + color: var(--text-primary); + text-decoration-line: none; +} +.goal-popup .popup-desc { + margin: 0; + font-size: 15px; + color: var(--text-secondary); + line-height: 1.6; +} +.goal-popup .popup-recs { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + margin-bottom: 32px; + padding-bottom: 32px; + border-bottom: 1px solid rgba(0, 0, 0, 0.08); +} +.goal-popup .rec-item { + display: flex; + align-items: flex-start; + justify-content: flex-start; + flex-direction: column; + gap: 10px; +} +.goal-popup .rec-label { + display: inline-block; + padding: 3px 10px; + font-size: 13px; + font-weight: 700; + color: var(--text-accent); + background: rgba(255, 92, 0, 0.08); + border-radius: 4px; +} +.goal-popup .rec-target { + font-size: 16px; + font-weight: 700; + line-height: 1.5; + color: var(--text-primary); +} +.goal-popup .rec-text { + margin: 0; + font-size: 14px; + line-height: 1.6; + color: var(--text-secondary); +} +.goal-popup .popup-bookshelf { + position: relative; +} +.goal-popup .popup-books-wrap { + padding: 0 40px; + text-align: center; +} +.goal-popup .popup-books-img { + max-width: 100%; + max-height: 240px; + -o-object-fit: contain; + object-fit: contain; + vertical-align: bottom; +} +.goal-popup .popup-shelf-plank { + width: 100%; + height: 22px; + background: linear-gradient(180deg, #8B5E3C 0%, #6A4628 100%); + border-radius: 2px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3), inset 0 2px 0 rgba(255, 255, 255, 0.12); + margin-bottom: 16px; +} +.goal-popup .popup-book-tags { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + flex-wrap: wrap; + gap: 8px; + padding: 0 4px; +} +.goal-popup .pop-tag { + display: inline-block; + padding: 4px 12px; + font-size: 13px; + font-weight: 500; + color: var(--text-secondary); + background: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 20px; +} +.goal-popup .popup-foot { + flex-shrink: 0; + padding: 20px 60px 24px; + border-top: 1px solid rgba(0, 0, 0, 0.08); + text-align: center; +} +.goal-popup .btn-set-goal { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 240px; + padding: 18px 48px; + font-size: 18px; + font-weight: 700; + color: #fff; + background: radial-gradient(117.51% 77.94% at 48.87% 100%, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.3) 24.58%, rgba(255, 255, 255, 0) 100%), #FF7020; + background-blend-mode: screen, normal; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 10px; + cursor: pointer; + transition: background-color 0.2s, transform 0.15s; + box-shadow: 0 4px 16px rgba(255, 112, 32, 0.35); +} +.goal-popup .btn-set-goal:hover { + background-color: #E05A10; + transform: translateY(-2px); +} +.goal-popup .btn-set-goal:active { + transform: translateY(0); +} + +body.layer-open { + overflow: hidden; +} \ No newline at end of file diff --git a/src/fonts/Caveat/Caveat-VariableFont_wght.ttf b/src/fonts/Caveat/Caveat-VariableFont_wght.ttf new file mode 100644 index 0000000..5908b6c Binary files /dev/null and b/src/fonts/Caveat/Caveat-VariableFont_wght.ttf differ diff --git a/src/fonts/Caveat/Caveat-VariableFont_wght.woff2 b/src/fonts/Caveat/Caveat-VariableFont_wght.woff2 new file mode 100644 index 0000000..5dd44f5 Binary files /dev/null and b/src/fonts/Caveat/Caveat-VariableFont_wght.woff2 differ diff --git a/src/fonts/NotoKR-Black/generator_config.txt b/src/fonts/NotoKR-Black/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-Black/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-Black/notokr-black-demo.html b/src/fonts/NotoKR-Black/notokr-black-demo.html new file mode 100644 index 0000000..5d0b61d --- /dev/null +++ b/src/fonts/NotoKR-Black/notokr-black-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕-Black Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-Black
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-Black +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-Black in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-Black in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

 

+

!

!
+

"

"
+

#

#
+

$

$
+

%

%
+

&

&
+

'

'
+

(

(
+

)

)
+

*

*
+

+

+
+

,

,
+

-

-
+

.

.
+

/

/
+

0

0
+

1

1
+

2

2
+

3

3
+

4

4
+

5

5
+

6

6
+

7

7
+

8

8
+

9

9
+

:

:
+

&#59;

;
+

<

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-Black/notokr-black.eot b/src/fonts/NotoKR-Black/notokr-black.eot new file mode 100644 index 0000000..64b77a0 Binary files /dev/null and b/src/fonts/NotoKR-Black/notokr-black.eot differ diff --git a/src/fonts/NotoKR-Black/notokr-black.svg b/src/fonts/NotoKR-Black/notokr-black.svg new file mode 100644 index 0000000..aea22df --- /dev/null +++ b/src/fonts/NotoKR-Black/notokr-black.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-Black/notokr-black.ttf b/src/fonts/NotoKR-Black/notokr-black.ttf new file mode 100644 index 0000000..4912c75 Binary files /dev/null and b/src/fonts/NotoKR-Black/notokr-black.ttf differ diff --git a/src/fonts/NotoKR-Black/notokr-black.woff b/src/fonts/NotoKR-Black/notokr-black.woff new file mode 100644 index 0000000..4195bc9 Binary files /dev/null and b/src/fonts/NotoKR-Black/notokr-black.woff differ diff --git a/src/fonts/NotoKR-Black/notokr-black.woff2 b/src/fonts/NotoKR-Black/notokr-black.woff2 new file mode 100644 index 0000000..a99d007 Binary files /dev/null and b/src/fonts/NotoKR-Black/notokr-black.woff2 differ diff --git a/src/fonts/NotoKR-Black/specimen_files/easytabs.js b/src/fonts/NotoKR-Black/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-Black/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-Black/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-Black/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-Black/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-Black/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-Black/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-Black/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-Black/stylesheet.css b/src/fonts/NotoKR-Black/stylesheet.css new file mode 100644 index 0000000..96d4ab0 --- /dev/null +++ b/src/fonts/NotoKR-Black/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-black'; + src: url('notokr-black.eot'); + src: url('notokr-black.eot?#iefix') format('embedded-opentype'), + url('notokr-black.woff2') format('woff2'), + url('notokr-black.woff') format('woff'), + url('notokr-black.ttf') format('truetype'), + url('notokr-black.svg#notokr-black') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/fonts/NotoKR-Bold/generator_config.txt b/src/fonts/NotoKR-Bold/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-Bold/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-Bold/notokr-bold-demo.html b/src/fonts/NotoKR-Bold/notokr-bold-demo.html new file mode 100644 index 0000000..80a24b2 --- /dev/null +++ b/src/fonts/NotoKR-Bold/notokr-bold-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕-Bold Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-Bold
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-Bold +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-Bold in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-Bold in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

&#32;

+

&#33;

!
+

&#34;

"
+

&#35;

#
+

&#36;

$
+

&#37;

%
+

&#38;

&
+

&#39;

'
+

&#40;

(
+

&#41;

)
+

&#42;

*
+

&#43;

+
+

&#44;

,
+

&#45;

-
+

&#46;

.
+

&#47;

/
+

&#48;

0
+

&#49;

1
+

&#50;

2
+

&#51;

3
+

&#52;

4
+

&#53;

5
+

&#54;

6
+

&#55;

7
+

&#56;

8
+

&#57;

9
+

&#58;

:
+

&#59;

;
+

&#60;

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-Bold/notokr-bold.eot b/src/fonts/NotoKR-Bold/notokr-bold.eot new file mode 100644 index 0000000..4537996 Binary files /dev/null and b/src/fonts/NotoKR-Bold/notokr-bold.eot differ diff --git a/src/fonts/NotoKR-Bold/notokr-bold.svg b/src/fonts/NotoKR-Bold/notokr-bold.svg new file mode 100644 index 0000000..c018a29 --- /dev/null +++ b/src/fonts/NotoKR-Bold/notokr-bold.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-Bold/notokr-bold.ttf b/src/fonts/NotoKR-Bold/notokr-bold.ttf new file mode 100644 index 0000000..7915a91 Binary files /dev/null and b/src/fonts/NotoKR-Bold/notokr-bold.ttf differ diff --git a/src/fonts/NotoKR-Bold/notokr-bold.woff b/src/fonts/NotoKR-Bold/notokr-bold.woff new file mode 100644 index 0000000..fc151f8 Binary files /dev/null and b/src/fonts/NotoKR-Bold/notokr-bold.woff differ diff --git a/src/fonts/NotoKR-Bold/notokr-bold.woff2 b/src/fonts/NotoKR-Bold/notokr-bold.woff2 new file mode 100644 index 0000000..dccc40d Binary files /dev/null and b/src/fonts/NotoKR-Bold/notokr-bold.woff2 differ diff --git a/src/fonts/NotoKR-Bold/specimen_files/easytabs.js b/src/fonts/NotoKR-Bold/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-Bold/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-Bold/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-Bold/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-Bold/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-Bold/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-Bold/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-Bold/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-Bold/stylesheet.css b/src/fonts/NotoKR-Bold/stylesheet.css new file mode 100644 index 0000000..63dcd8d --- /dev/null +++ b/src/fonts/NotoKR-Bold/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-bold'; + src: url('notokr-bold.eot'); + src: url('notokr-bold.eot?#iefix') format('embedded-opentype'), + url('notokr-bold.woff2') format('woff2'), + url('notokr-bold.woff') format('woff'), + url('notokr-bold.ttf') format('truetype'), + url('notokr-bold.svg#notokr-bold') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/fonts/NotoKR-DemiLight/generator_config.txt b/src/fonts/NotoKR-DemiLight/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-DemiLight/notokr-demilight-demo.html b/src/fonts/NotoKR-DemiLight/notokr-demilight-demo.html new file mode 100644 index 0000000..3e38c76 --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/notokr-demilight-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕-DemiLight Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-DemiLight
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-DemiLight +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-DemiLight in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-DemiLight in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

&#32;

+

&#33;

!
+

&#34;

"
+

&#35;

#
+

&#36;

$
+

&#37;

%
+

&#38;

&
+

&#39;

'
+

&#40;

(
+

&#41;

)
+

&#42;

*
+

&#43;

+
+

&#44;

,
+

&#45;

-
+

&#46;

.
+

&#47;

/
+

&#48;

0
+

&#49;

1
+

&#50;

2
+

&#51;

3
+

&#52;

4
+

&#53;

5
+

&#54;

6
+

&#55;

7
+

&#56;

8
+

&#57;

9
+

&#58;

:
+

&#59;

;
+

&#60;

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-DemiLight/notokr-demilight.eot b/src/fonts/NotoKR-DemiLight/notokr-demilight.eot new file mode 100644 index 0000000..e3bf3f3 Binary files /dev/null and b/src/fonts/NotoKR-DemiLight/notokr-demilight.eot differ diff --git a/src/fonts/NotoKR-DemiLight/notokr-demilight.svg b/src/fonts/NotoKR-DemiLight/notokr-demilight.svg new file mode 100644 index 0000000..7faa5d1 --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/notokr-demilight.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-DemiLight/notokr-demilight.ttf b/src/fonts/NotoKR-DemiLight/notokr-demilight.ttf new file mode 100644 index 0000000..5734bd9 Binary files /dev/null and b/src/fonts/NotoKR-DemiLight/notokr-demilight.ttf differ diff --git a/src/fonts/NotoKR-DemiLight/notokr-demilight.woff b/src/fonts/NotoKR-DemiLight/notokr-demilight.woff new file mode 100644 index 0000000..c6469b4 Binary files /dev/null and b/src/fonts/NotoKR-DemiLight/notokr-demilight.woff differ diff --git a/src/fonts/NotoKR-DemiLight/notokr-demilight.woff2 b/src/fonts/NotoKR-DemiLight/notokr-demilight.woff2 new file mode 100644 index 0000000..6d09542 Binary files /dev/null and b/src/fonts/NotoKR-DemiLight/notokr-demilight.woff2 differ diff --git a/src/fonts/NotoKR-DemiLight/specimen_files/easytabs.js b/src/fonts/NotoKR-DemiLight/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-DemiLight/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-DemiLight/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-DemiLight/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-DemiLight/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-DemiLight/stylesheet.css b/src/fonts/NotoKR-DemiLight/stylesheet.css new file mode 100644 index 0000000..23d5fc0 --- /dev/null +++ b/src/fonts/NotoKR-DemiLight/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-demilight'; + src: url('notokr-demilight.eot'); + src: url('notokr-demilight.eot?#iefix') format('embedded-opentype'), + url('notokr-demilight.woff2') format('woff2'), + url('notokr-demilight.woff') format('woff'), + url('notokr-demilight.ttf') format('truetype'), + url('notokr-demilight.svg#notokr-demilight') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/fonts/NotoKR-Light/generator_config.txt b/src/fonts/NotoKR-Light/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-Light/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-Light/notokr-light-demo.html b/src/fonts/NotoKR-Light/notokr-light-demo.html new file mode 100644 index 0000000..76544ef --- /dev/null +++ b/src/fonts/NotoKR-Light/notokr-light-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕 Light Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-Light
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-Light +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-Light in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-Light in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

&#32;

+

&#33;

!
+

&#34;

"
+

&#35;

#
+

&#36;

$
+

&#37;

%
+

&#38;

&
+

&#39;

'
+

&#40;

(
+

&#41;

)
+

&#42;

*
+

&#43;

+
+

&#44;

,
+

&#45;

-
+

&#46;

.
+

&#47;

/
+

&#48;

0
+

&#49;

1
+

&#50;

2
+

&#51;

3
+

&#52;

4
+

&#53;

5
+

&#54;

6
+

&#55;

7
+

&#56;

8
+

&#57;

9
+

&#58;

:
+

&#59;

;
+

&#60;

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-Light/notokr-light.eot b/src/fonts/NotoKR-Light/notokr-light.eot new file mode 100644 index 0000000..b100da0 Binary files /dev/null and b/src/fonts/NotoKR-Light/notokr-light.eot differ diff --git a/src/fonts/NotoKR-Light/notokr-light.svg b/src/fonts/NotoKR-Light/notokr-light.svg new file mode 100644 index 0000000..855f055 --- /dev/null +++ b/src/fonts/NotoKR-Light/notokr-light.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-Light/notokr-light.ttf b/src/fonts/NotoKR-Light/notokr-light.ttf new file mode 100644 index 0000000..c014882 Binary files /dev/null and b/src/fonts/NotoKR-Light/notokr-light.ttf differ diff --git a/src/fonts/NotoKR-Light/notokr-light.woff b/src/fonts/NotoKR-Light/notokr-light.woff new file mode 100644 index 0000000..132ebbf Binary files /dev/null and b/src/fonts/NotoKR-Light/notokr-light.woff differ diff --git a/src/fonts/NotoKR-Light/notokr-light.woff2 b/src/fonts/NotoKR-Light/notokr-light.woff2 new file mode 100644 index 0000000..b751735 Binary files /dev/null and b/src/fonts/NotoKR-Light/notokr-light.woff2 differ diff --git a/src/fonts/NotoKR-Light/specimen_files/easytabs.js b/src/fonts/NotoKR-Light/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-Light/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-Light/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-Light/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-Light/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-Light/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-Light/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-Light/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-Light/stylesheet.css b/src/fonts/NotoKR-Light/stylesheet.css new file mode 100644 index 0000000..1ca6735 --- /dev/null +++ b/src/fonts/NotoKR-Light/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-light'; + src: url('notokr-light.eot'); + src: url('notokr-light.eot?#iefix') format('embedded-opentype'), + url('notokr-light.woff2') format('woff2'), + url('notokr-light.woff') format('woff'), + url('notokr-light.ttf') format('truetype'), + url('notokr-light.svg#notokr-light') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/fonts/NotoKR-Medium/generator_config.txt b/src/fonts/NotoKR-Medium/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-Medium/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-Medium/notokr-medium-demo.html b/src/fonts/NotoKR-Medium/notokr-medium-demo.html new file mode 100644 index 0000000..2c3da5d --- /dev/null +++ b/src/fonts/NotoKR-Medium/notokr-medium-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕-Medium Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-Medium
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-Medium +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-Medium in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-Medium in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

&#32;

+

&#33;

!
+

&#34;

"
+

&#35;

#
+

&#36;

$
+

&#37;

%
+

&#38;

&
+

&#39;

'
+

&#40;

(
+

&#41;

)
+

&#42;

*
+

&#43;

+
+

&#44;

,
+

&#45;

-
+

&#46;

.
+

&#47;

/
+

&#48;

0
+

&#49;

1
+

&#50;

2
+

&#51;

3
+

&#52;

4
+

&#53;

5
+

&#54;

6
+

&#55;

7
+

&#56;

8
+

&#57;

9
+

&#58;

:
+

&#59;

;
+

&#60;

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-Medium/notokr-medium.eot b/src/fonts/NotoKR-Medium/notokr-medium.eot new file mode 100644 index 0000000..fdfa4ce Binary files /dev/null and b/src/fonts/NotoKR-Medium/notokr-medium.eot differ diff --git a/src/fonts/NotoKR-Medium/notokr-medium.svg b/src/fonts/NotoKR-Medium/notokr-medium.svg new file mode 100644 index 0000000..b9f6cc0 --- /dev/null +++ b/src/fonts/NotoKR-Medium/notokr-medium.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-Medium/notokr-medium.ttf b/src/fonts/NotoKR-Medium/notokr-medium.ttf new file mode 100644 index 0000000..5e94df0 Binary files /dev/null and b/src/fonts/NotoKR-Medium/notokr-medium.ttf differ diff --git a/src/fonts/NotoKR-Medium/notokr-medium.woff b/src/fonts/NotoKR-Medium/notokr-medium.woff new file mode 100644 index 0000000..fbacb0c Binary files /dev/null and b/src/fonts/NotoKR-Medium/notokr-medium.woff differ diff --git a/src/fonts/NotoKR-Medium/notokr-medium.woff2 b/src/fonts/NotoKR-Medium/notokr-medium.woff2 new file mode 100644 index 0000000..54c7ded Binary files /dev/null and b/src/fonts/NotoKR-Medium/notokr-medium.woff2 differ diff --git a/src/fonts/NotoKR-Medium/specimen_files/easytabs.js b/src/fonts/NotoKR-Medium/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-Medium/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-Medium/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-Medium/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-Medium/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-Medium/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-Medium/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-Medium/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-Medium/stylesheet.css b/src/fonts/NotoKR-Medium/stylesheet.css new file mode 100644 index 0000000..9ab12f4 --- /dev/null +++ b/src/fonts/NotoKR-Medium/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-medium'; + src: url('notokr-medium.eot'); + src: url('notokr-medium.eot?#iefix') format('embedded-opentype'), + url('notokr-medium.woff2') format('woff2'), + url('notokr-medium.woff') format('woff'), + url('notokr-medium.ttf') format('truetype'), + url('notokr-medium.svg#notokr-medium') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/fonts/NotoKR-Regular/generator_config.txt b/src/fonts/NotoKR-Regular/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-Regular/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-Regular/notokr-regular-demo.html b/src/fonts/NotoKR-Regular/notokr-regular-demo.html new file mode 100644 index 0000000..06cc9d0 --- /dev/null +++ b/src/fonts/NotoKR-Regular/notokr-regular-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕-Regular Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-Regular
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-Regular Regular +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-Regular Regular in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-Regular Regular in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

&#32;

+

&#33;

!
+

&#34;

"
+

&#35;

#
+

&#36;

$
+

&#37;

%
+

&#38;

&
+

&#39;

'
+

&#40;

(
+

&#41;

)
+

&#42;

*
+

&#43;

+
+

&#44;

,
+

&#45;

-
+

&#46;

.
+

&#47;

/
+

&#48;

0
+

&#49;

1
+

&#50;

2
+

&#51;

3
+

&#52;

4
+

&#53;

5
+

&#54;

6
+

&#55;

7
+

&#56;

8
+

&#57;

9
+

&#58;

:
+

&#59;

;
+

&#60;

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-Regular/notokr-regular.eot b/src/fonts/NotoKR-Regular/notokr-regular.eot new file mode 100644 index 0000000..47f6cec Binary files /dev/null and b/src/fonts/NotoKR-Regular/notokr-regular.eot differ diff --git a/src/fonts/NotoKR-Regular/notokr-regular.svg b/src/fonts/NotoKR-Regular/notokr-regular.svg new file mode 100644 index 0000000..14fc7ce --- /dev/null +++ b/src/fonts/NotoKR-Regular/notokr-regular.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-Regular/notokr-regular.ttf b/src/fonts/NotoKR-Regular/notokr-regular.ttf new file mode 100644 index 0000000..9e3297d Binary files /dev/null and b/src/fonts/NotoKR-Regular/notokr-regular.ttf differ diff --git a/src/fonts/NotoKR-Regular/notokr-regular.woff b/src/fonts/NotoKR-Regular/notokr-regular.woff new file mode 100644 index 0000000..a5a4551 Binary files /dev/null and b/src/fonts/NotoKR-Regular/notokr-regular.woff differ diff --git a/src/fonts/NotoKR-Regular/notokr-regular.woff2 b/src/fonts/NotoKR-Regular/notokr-regular.woff2 new file mode 100644 index 0000000..2ced143 Binary files /dev/null and b/src/fonts/NotoKR-Regular/notokr-regular.woff2 differ diff --git a/src/fonts/NotoKR-Regular/specimen_files/easytabs.js b/src/fonts/NotoKR-Regular/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-Regular/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-Regular/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-Regular/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-Regular/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-Regular/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-Regular/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-Regular/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-Regular/stylesheet.css b/src/fonts/NotoKR-Regular/stylesheet.css new file mode 100644 index 0000000..6f4e02d --- /dev/null +++ b/src/fonts/NotoKR-Regular/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-regular'; + src: url('notokr-regular.eot'); + src: url('notokr-regular.eot?#iefix') format('embedded-opentype'), + url('notokr-regular.woff2') format('woff2'), + url('notokr-regular.woff') format('woff'), + url('notokr-regular.ttf') format('truetype'), + url('notokr-regular.svg#notokr-regular') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/fonts/NotoKR-Thin/generator_config.txt b/src/fonts/NotoKR-Thin/generator_config.txt new file mode 100644 index 0000000..fa60335 --- /dev/null +++ b/src/fonts/NotoKR-Thin/generator_config.txt @@ -0,0 +1,5 @@ +# Font Squirrel Font-face Generator Configuration File +# Upload this file to the generator to recreate the settings +# you used to create these fonts. + +{"mode":"basic","formats":["ttf","woff","woff2","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} \ No newline at end of file diff --git a/src/fonts/NotoKR-Thin/notokr-thin-demo.html b/src/fonts/NotoKR-Thin/notokr-thin-demo.html new file mode 100644 index 0000000..13246ab --- /dev/null +++ b/src/fonts/NotoKR-Thin/notokr-thin-demo.html @@ -0,0 +1,2827 @@ + + + + + + + + + + + + + 본고딕-Thin Specimen + + + + + + +
+ + + +
+ + +
+ +
+
+
본고딕-Thin
+
+
+ +
+
A​B​C​D​E​F​G​H​I​J​K​L​M​N​O​P​Q​R​S​T​U​V​W​X​Y​Z​a​b​c​d​e​f​g​h​i​j​k​l​m​n​o​p​q​r​s​t​u​v​w​x​y​z​1​2​3​4​5​6​7​8​9​0​&​.​,​?​!​@​(​)​#​$​%​*​+​-​=​:​;
+
+
+
+ + + + + + + + + + + + + + + + +
10다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
11다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
12다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
13다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
14다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
16다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
18다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
20다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
24다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
30다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
36다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
48다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
60다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
72다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
90다람쥐헌쳇바퀴에타고파ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ +
+ +
+ + + +
+ + +
+
◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼body
body
body
body
+
+ bodyNotoKR-Thin +
+
+ bodyArial +
+
+ bodyVerdana +
+
+ bodyGeorgia +
+ + + +
+ + +
+ +
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+ +
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + +
+
+

10.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

11.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

12.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

13.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

14.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

16.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+

18.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+ +
+
+ +
+ +
+
+

20.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+

24.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+ +
+ +
+ +
+
+

30.Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.

+
+
+ +
+ + + + +
+ +
+ +
+ +
+

Lorem Ipsum Dolor

+

Etiam porta sem malesuada magna mollis euismod

+ + +
+
+
+
+

Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ + +

Pellentesque ornare sem

+ +

Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

+ +

Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur.

+ +

Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla.

+ +

Cras mattis consectetur

+ +

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.

+ +

Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.

+
+ + +
+ +
+ + + + + + +
+
+
+ +

Language Support

+

The subset of NotoKR-Thin in this kit supports the following languages:
+ + English

+

Glyph Chart

+

The subset of NotoKR-Thin in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.

+
+ +

&#32;

+

&#33;

!
+

&#34;

"
+

&#35;

#
+

&#36;

$
+

&#37;

%
+

&#38;

&
+

&#39;

'
+

&#40;

(
+

&#41;

)
+

&#42;

*
+

&#43;

+
+

&#44;

,
+

&#45;

-
+

&#46;

.
+

&#47;

/
+

&#48;

0
+

&#49;

1
+

&#50;

2
+

&#51;

3
+

&#52;

4
+

&#53;

5
+

&#54;

6
+

&#55;

7
+

&#56;

8
+

&#57;

9
+

&#58;

:
+

&#59;

;
+

&#60;

<
+

&#61;

=
+

&#62;

>
+

&#63;

?
+

&#64;

@
+

&#65;

A
+

&#66;

B
+

&#67;

C
+

&#68;

D
+

&#69;

E
+

&#70;

F
+

&#71;

G
+

&#72;

H
+

&#73;

I
+

&#74;

J
+

&#75;

K
+

&#76;

L
+

&#77;

M
+

&#78;

N
+

&#79;

O
+

&#80;

P
+

&#81;

Q
+

&#82;

R
+

&#83;

S
+

&#84;

T
+

&#85;

U
+

&#86;

V
+

&#87;

W
+

&#88;

X
+

&#89;

Y
+

&#90;

Z
+

&#91;

[
+

&#92;

\
+

&#93;

]
+

&#94;

^
+

&#95;

_
+

&#96;

`
+

&#97;

a
+

&#98;

b
+

&#99;

c
+

&#100;

d
+

&#101;

e
+

&#102;

f
+

&#103;

g
+

&#104;

h
+

&#105;

i
+

&#106;

j
+

&#107;

k
+

&#108;

l
+

&#109;

m
+

&#110;

n
+

&#111;

o
+

&#112;

p
+

&#113;

q
+

&#114;

r
+

&#115;

s
+

&#116;

t
+

&#117;

u
+

&#118;

v
+

&#119;

w
+

&#120;

x
+

&#121;

y
+

&#122;

z
+

&#123;

{
+

&#124;

|
+

&#125;

}
+

&#126;

~
+

&#44032;

+

&#44033;

+

&#44036;

+

&#44039;

+

&#44040;

+

&#44041;

+

&#44042;

+

&#44048;

+

&#44049;

+

&#44050;

+

&#44051;

+

&#44052;

+

&#44053;

+

&#44054;

+

&#44055;

+

&#44057;

+

&#44058;

+

&#44059;

+

&#44060;

+

&#44061;

+

&#44064;

+

&#44068;

+

&#44076;

+

&#44077;

+

&#44079;

+

&#44080;

+

&#44081;

+

&#44088;

+

&#44089;

+

&#44092;

+

&#44096;

+

&#44107;

+

&#44109;

+

&#44116;

+

&#44120;

+

&#44124;

+

&#44144;

+

&#44145;

+

&#44148;

+

&#44151;

+

&#44152;

+

&#44154;

+

&#44160;

+

&#44161;

+

&#44163;

+

&#44164;

+

&#44165;

+

&#44166;

+

&#44169;

+

&#44170;

+

&#44171;

+

&#44172;

+

&#44176;

+

&#44180;

+

&#44188;

+

&#44189;

+

&#44191;

+

&#44192;

+

&#44193;

+

&#44200;

+

&#44201;

+

&#44202;

+

&#44204;

+

&#44207;

+

&#44208;

+

&#44216;

+

&#44217;

+

&#44219;

+

&#44220;

+

&#44221;

+

&#44225;

+

&#44228;

+

&#44232;

+

&#44236;

+

&#44245;

+

&#44247;

+

&#44256;

+

&#44257;

+

&#44260;

+

&#44263;

+

&#44264;

+

&#44266;

+

&#44268;

+

&#44271;

+

&#44272;

+

&#44273;

+

&#44275;

+

&#44277;

+

&#44278;

+

&#44284;

+

&#44285;

+

&#44288;

+

&#44292;

+

&#44294;

+

&#44300;

+

&#44301;

+

&#44303;

+

&#44305;

+

&#44312;

+

&#44316;

+

&#44320;

+

&#44329;

+

&#44332;

+

&#44333;

+

&#44340;

+

&#44341;

+

&#44344;

+

&#44348;

+

&#44356;

+

&#44357;

+

&#44359;

+

&#44361;

+

&#44368;

+

&#44372;

+

&#44376;

+

&#44385;

+

&#44387;

+

&#44396;

+

&#44397;

+

&#44400;

+

&#44403;

+

&#44404;

+

&#44405;

+

&#44406;

+

&#44411;

+

&#44412;

+

&#44413;

+

&#44415;

굿
+

&#44417;

+

&#44418;

+

&#44424;

+

&#44425;

+

&#44428;

+

&#44432;

+

&#44444;

+

&#44445;

+

&#44452;

+

&#44471;

+

&#44480;

+

&#44481;

+

&#44484;

+

&#44488;

+

&#44496;

+

&#44497;

+

&#44499;

+

&#44508;

+

&#44512;

+

&#44516;

+

&#44536;

+

&#44537;

+

&#44540;

+

&#44543;

귿
+

&#44544;

+

&#44545;

+

&#44552;

+

&#44553;

+

&#44555;

+

&#44557;

+

&#44564;

+

&#44592;

+

&#44593;

+

&#44596;

+

&#44599;

+

&#44600;

+

&#44602;

+

&#44608;

+

&#44609;

+

&#44611;

+

&#44613;

+

&#44614;

+

&#44618;

+

&#44620;

+

&#44621;

+

&#44622;

+

&#44624;

+

&#44628;

+

&#44630;

+

&#44636;

+

&#44637;

+

&#44639;

+

&#44640;

+

&#44641;

+

&#44645;

+

&#44648;

+

&#44649;

+

&#44652;

+

&#44656;

+

&#44664;

+

&#44665;

+

&#44667;

+

&#44668;

+

&#44669;

+

&#44676;

+

&#44677;

+

&#44684;

+

&#44732;

+

&#44733;

+

&#44734;

+

&#44736;

+

&#44740;

+

&#44748;

+

&#44749;

+

&#44751;

+

&#44752;

+

&#44753;

+

&#44760;

+

&#44761;

+

&#44764;

+

&#44776;

+

&#44779;

+

&#44781;

+

&#44788;

+

&#44792;

+

&#44796;

+

&#44807;

+

&#44808;

+

&#44813;

+

&#44816;

+

&#44844;

+

&#44845;

+

&#44848;

+

&#44850;

+

&#44852;

+

&#44860;

+

&#44861;

+

&#44863;

꼿
+

&#44865;

+

&#44866;

+

&#44867;

+

&#44872;

+

&#44873;

+

&#44880;

+

&#44892;

+

&#44893;

+

&#44900;

+

&#44901;

+

&#44921;

+

&#44928;

+

&#44932;

+

&#44936;

+

&#44944;

+

&#44945;

+

&#44949;

+

&#44956;

+

&#44984;

+

&#44985;

+

&#44988;

+

&#44992;

+

&#44999;

+

&#45000;

+

&#45001;

+

&#45003;

+

&#45005;

+

&#45006;

+

&#45012;

+

&#45020;

+

&#45032;

+

&#45033;

+

&#45040;

+

&#45041;

+

&#45044;

+

&#45048;

+

&#45056;

뀀
+

&#45057;

+

&#45060;

+

&#45068;

+

&#45072;

+

&#45076;

+

&#45084;

+

&#45085;

+

&#45096;

+

&#45124;

+

&#45125;

+

&#45128;

+

&#45130;

+

&#45132;

+

&#45134;

+

&#45139;

+

&#45140;

+

&#45141;

+

&#45143;

+

&#45145;

+

&#45149;

+

&#45180;

+

&#45181;

+

&#45184;

+

&#45188;

+

&#45196;

+

&#45197;

+

&#45199;

+

&#45201;

+

&#45208;

+

&#45209;

+

&#45210;

+

&#45212;

+

&#45215;

+

&#45216;

+

&#45217;

+

&#45218;

+

&#45224;

+

&#45225;

+

&#45227;

+

&#45228;

+

&#45229;

+

&#45230;

+

&#45231;

+

&#45233;

+

&#45235;

+

&#45236;

+

&#45237;

+

&#45240;

+

&#45244;

+

&#45252;

+

&#45253;

+

&#45255;

+

&#45256;

+

&#45257;

+

&#45264;

+

&#45265;

+

&#45268;

+

&#45272;

+

&#45280;

+

&#45285;

+

&#45320;

+

&#45321;

+

&#45323;

+

&#45324;

+

&#45328;

+

&#45330;

+

&#45331;

+

&#45336;

+

&#45337;

+

&#45339;

+

&#45340;

+

&#45341;

+

&#45347;

+

&#45348;

+

&#45349;

+

&#45352;

+

&#45356;

+

&#45364;

+

&#45365;

+

&#45367;

+

&#45368;

+

&#45369;

+

&#45376;

+

&#45377;

+

&#45380;

+

&#45384;

+

&#45392;

+

&#45393;

+

&#45396;

+

&#45397;

+

&#45400;

+

&#45404;

+

&#45408;

+

&#45432;

+

&#45433;

+

&#45436;

+

&#45440;

+

&#45442;

+

&#45448;

+

&#45449;

+

&#45451;

+

&#45453;

+

&#45458;

+

&#45459;

+

&#45460;

+

&#45464;

+

&#45468;

+

&#45480;

+

&#45516;

+

&#45520;

+

&#45524;

+

&#45532;

+

&#45533;

+

&#45535;

+

&#45544;

+

&#45545;

+

&#45548;

+

&#45552;

+

&#45561;

+

&#45563;

+

&#45565;

+

&#45572;

+

&#45573;

+

&#45576;

+

&#45579;

+

&#45580;

+

&#45588;

+

&#45589;

+

&#45591;

+

&#45593;

+

&#45600;

+

&#45620;

+

&#45628;

+

&#45656;

+

&#45660;

+

&#45664;

+

&#45672;

+

&#45673;

+

&#45684;

+

&#45685;

+

&#45692;

+

&#45700;

+

&#45701;

+

&#45705;

+

&#45712;

+

&#45713;

+

&#45716;

+

&#45720;

+

&#45721;

+

&#45722;

+

&#45728;

+

&#45729;

+

&#45731;

+

&#45733;

+

&#45734;

+

&#45738;

+

&#45740;

+

&#45744;

+

&#45748;

+

&#45768;

+

&#45769;

+

&#45772;

+

&#45776;

+

&#45778;

+

&#45784;

+

&#45785;

+

&#45787;

+

&#45789;

+

&#45794;

+

&#45796;

+

&#45797;

+

&#45798;

+

&#45800;

+

&#45803;

+

&#45804;

+

&#45805;

+

&#45806;

+

&#45807;

+

&#45811;

+

&#45812;

+

&#45813;

+

&#45815;

+

&#45816;

+

&#45817;

+

&#45818;

+

&#45819;

+

&#45823;

+

&#45824;

+

&#45825;

+

&#45828;

+

&#45832;

+

&#45840;

+

&#45841;

+

&#45843;

+

&#45844;

+

&#45845;

+

&#45852;

+

&#45908;

+

&#45909;

+

&#45910;

+

&#45912;

+

&#45915;

+

&#45916;

+

&#45918;

+

&#45919;

+

&#45924;

+

&#45925;

+

&#45927;

+

&#45929;

+

&#45931;

+

&#45934;

+

&#45936;

+

&#45937;

+

&#45940;

+

&#45944;

+

&#45952;

+

&#45953;

+

&#45955;

+

&#45956;

+

&#45957;

+

&#45964;

+

&#45968;

+

&#45972;

+

&#45984;

+

&#45985;

+

&#45992;

+

&#45996;

+

&#46020;

+

&#46021;

+

&#46024;

+

&#46027;

+

&#46028;

+

&#46030;

+

&#46032;

+

&#46036;

+

&#46037;

+

&#46039;

+

&#46041;

+

&#46043;

+

&#46045;

+

&#46048;

+

&#46052;

+

&#46056;

+

&#46076;

+

&#46096;

+

&#46104;

+

&#46108;

+

&#46112;

+

&#46120;

+

&#46121;

+

&#46123;

+

&#46132;

+

&#46160;

+

&#46161;

+

&#46164;

+

&#46168;

+

&#46176;

+

&#46177;

+

&#46179;

+

&#46181;

+

&#46188;

+

&#46208;

+

&#46216;

+

&#46237;

+

&#46244;

+

&#46248;

+

&#46252;

+

&#46261;

+

&#46263;

+

&#46265;

+

&#46272;

+

&#46276;

+

&#46280;

+

&#46288;

+

&#46293;

+

&#46300;

+

&#46301;

+

&#46304;

+

&#46307;

+

&#46308;

+

&#46310;

+

&#46316;

+

&#46317;

+

&#46319;

+

&#46321;

+

&#46328;

+

&#46356;

+

&#46357;

+

&#46360;

+

&#46363;

+

&#46364;

+

&#46372;

+

&#46373;

+

&#46375;

+

&#46376;

+

&#46377;

+

&#46378;

+

&#46384;

+

&#46385;

+

&#46388;

+

&#46392;

+

&#46400;

+

&#46401;

+

&#46403;

+

&#46404;

+

&#46405;

+

&#46411;

+

&#46412;

+

&#46413;

+

&#46416;

+

&#46420;

+

&#46428;

+

&#46429;

+

&#46431;

+

&#46432;

+

&#46433;

+

&#46496;

+

&#46497;

+

&#46500;

+

&#46504;

+

&#46506;

+

&#46507;

+

&#46512;

+

&#46513;

+

&#46515;

+

&#46516;

+

&#46517;

+

&#46523;

+

&#46524;

+

&#46525;

+

&#46528;

+

&#46532;

+

&#46540;

+

&#46541;

+

&#46543;

+

&#46544;

+

&#46545;

+

&#46552;

+

&#46572;

+

&#46608;

+

&#46609;

+

&#46612;

+

&#46616;

+

&#46629;

+

&#46636;

+

&#46644;

+

&#46664;

+

&#46692;

+

&#46696;

+

&#46748;

+

&#46749;

+

&#46752;

+

&#46756;

+

&#46763;

+

&#46764;

+

&#46769;

+

&#46804;

+

&#46832;

+

&#46836;

+

&#46840;

+

&#46848;

+

&#46849;

+

&#46853;

+

&#46888;

+

&#46889;

+

&#46892;

+

&#46895;

+

&#46896;

+

&#46904;

+

&#46905;

+

&#46907;

+

&#46916;

+

&#46920;

+

&#46924;

+

&#46932;

+

&#46933;

+

&#46944;

+

&#46948;

+

&#46952;

+

&#46960;

+

&#46961;

+

&#46963;

+

&#46965;

+

&#46972;

+

&#46973;

+

&#46976;

+

&#46980;

+

&#46988;

+

&#46989;

+

&#46991;

+

&#46992;

+

&#46993;

+

&#46994;

+

&#46998;

+

&#46999;

+

&#47000;

+

&#47001;

+

&#47004;

+

&#47008;

+

&#47016;

+

&#47017;

+

&#47019;

+

&#47020;

+

&#47021;

+

&#47028;

+

&#47029;

+

&#47032;

+

&#47047;

+

&#47049;

+

&#47084;

+

&#47085;

+

&#47088;

+

&#47092;

+

&#47100;

+

&#47101;

+

&#47103;

+

&#47104;

+

&#47105;

+

&#47111;

+

&#47112;

+

&#47113;

+

&#47116;

+

&#47120;

+

&#47128;

+

&#47129;

+

&#47131;

+

&#47133;

+

&#47140;

+

&#47141;

+

&#47144;

+

&#47148;

+

&#47156;

+

&#47157;

+

&#47159;

+

&#47160;

+

&#47161;

+

&#47168;

+

&#47172;

+

&#47185;

+

&#47187;

+

&#47196;

+

&#47197;

+

&#47200;

+

&#47204;

+

&#47212;

+

&#47213;

+

&#47215;

+

&#47217;

+

&#47224;

+

&#47228;

+

&#47245;

+

&#47272;

+

&#47280;

+

&#47284;

+

&#47288;

+

&#47296;

+

&#47297;

+

&#47299;

+

&#47301;

+

&#47308;

+

&#47312;

+

&#47316;

+

&#47325;

+

&#47327;

+

&#47329;

+

&#47336;

+

&#47337;

+

&#47340;

+

&#47344;

+

&#47352;

+

&#47353;

+

&#47355;

+

&#47357;

+

&#47364;

+

&#47384;

+

&#47392;

+

&#47420;

+

&#47421;

+

&#47424;

+

&#47428;

+

&#47436;

+

&#47439;

+

&#47441;

+

&#47448;

+

&#47449;

+

&#47452;

+

&#47456;

+

&#47464;

+

&#47465;

+

&#47467;

+

&#47469;

+

&#47476;

+

&#47477;

+

&#47480;

+

&#47484;

+

&#47492;

+

&#47493;

+

&#47495;

+

&#47497;

+

&#47498;

+

&#47501;

+

&#47502;

+

&#47532;

+

&#47533;

+

&#47536;

+

&#47540;

+

&#47548;

+

&#47549;

+

&#47551;

릿
+

&#47553;

+

&#47560;

+

&#47561;

+

&#47564;

+

&#47566;

+

&#47567;

+

&#47568;

+

&#47569;

+

&#47570;

+

&#47576;

+

&#47577;

+

&#47579;

+

&#47581;

+

&#47582;

+

&#47585;

+

&#47587;

+

&#47588;

+

&#47589;

+

&#47592;

+

&#47596;

+

&#47604;

+

&#47605;

+

&#47607;

+

&#47608;

+

&#47609;

+

&#47610;

+

&#47616;

+

&#47617;

+

&#47624;

+

&#47637;

+

&#47672;

+

&#47673;

+

&#47676;

+

&#47680;

+

&#47682;

+

&#47688;

+

&#47689;

+

&#47691;

+

&#47693;

+

&#47694;

+

&#47699;

+

&#47700;

+

&#47701;

+

&#47704;

+

&#47708;

+

&#47716;

+

&#47717;

+

&#47719;

+

&#47720;

+

&#47721;

+

&#47728;

+

&#47729;

+

&#47732;

+

&#47736;

+

&#47747;

+

&#47748;

+

&#47749;

+

&#47751;

+

&#47756;

+

&#47784;

+

&#47785;

+

&#47787;

+

&#47788;

+

&#47792;

+

&#47794;

+

&#47800;

+

&#47801;

+

&#47803;

+

&#47805;

+

&#47812;

+

&#47816;

+

&#47832;

+

&#47833;

+

&#47868;

+

&#47872;

+

&#47876;

+

&#47885;

+

&#47887;

+

&#47889;

+

&#47896;

+

&#47900;

+

&#47904;

+

&#47913;

+

&#47915;

+

&#47924;

+

&#47925;

+

&#47926;

+

&#47928;

+

&#47931;

+

&#47932;

+

&#47933;

+

&#47934;

+

&#47940;

+

&#47941;

+

&#47943;

+

&#47945;

+

&#47949;

+

&#47951;

+

&#47952;

+

&#47956;

+

&#47960;

+

&#47969;

+

&#47971;

+

&#47980;

+

&#48008;

+

&#48012;

+

&#48016;

+

&#48036;

+

&#48040;

+

&#48044;

+

&#48052;

+

&#48055;

+

&#48064;

+

&#48068;

+

&#48072;

+

&#48080;

+

&#48083;

+

&#48120;

+

&#48121;

+

&#48124;

+

&#48127;

믿
+

&#48128;

+

&#48130;

+

&#48136;

+

&#48137;

+

&#48139;

+

&#48140;

+

&#48141;

+

&#48143;

+

&#48145;

+

&#48148;

+

&#48149;

+

&#48150;

+

&#48151;

+

&#48152;

+

&#48155;

+

&#48156;

+

&#48157;

+

&#48158;

+

&#48159;

+

&#48164;

+

&#48165;

+

&#48167;

+

&#48169;

+

&#48173;

+

&#48176;

+

&#48177;

+

&#48180;

+

&#48184;

+

&#48192;

+

&#48193;

+

&#48195;

+

&#48196;

+

&#48197;

+

&#48201;

+

&#48204;

+

&#48205;

+

&#48208;

+

&#48221;

+

&#48260;

+

&#48261;

+

&#48264;

+

&#48267;

+

&#48268;

+

&#48270;

+

&#48276;

+

&#48277;

+

&#48279;

+

&#48281;

+

&#48282;

+

&#48288;

+

&#48289;

+

&#48292;

+

&#48295;

+

&#48296;

+

&#48304;

+

&#48305;

+

&#48307;

+

&#48308;

+

&#48309;

+

&#48316;

+

&#48317;

+

&#48320;

+

&#48324;

+

&#48333;

+

&#48335;

+

&#48336;

+

&#48337;

+

&#48341;

+

&#48344;

+

&#48348;

+

&#48372;

+

&#48373;

+

&#48374;

+

&#48376;

+

&#48380;

+

&#48388;

+

&#48389;

+

&#48391;

+

&#48393;

+

&#48400;

+

&#48404;

+

&#48420;

+

&#48428;

+

&#48448;

+

&#48456;

+

&#48457;

+

&#48460;

+

&#48464;

+

&#48472;

+

&#48473;

+

&#48484;

+

&#48488;

+

&#48512;

+

&#48513;

+

&#48516;

+

&#48519;

+

&#48520;

+

&#48521;

+

&#48522;

+

&#48528;

+

&#48529;

+

&#48531;

+

&#48533;

+

&#48537;

+

&#48538;

+

&#48540;

+

&#48548;

+

&#48560;

+

&#48568;

+

&#48596;

+

&#48597;

+

&#48600;

+

&#48604;

+

&#48617;

+

&#48624;

+

&#48628;

+

&#48632;

+

&#48640;

+

&#48643;

+

&#48645;

+

&#48652;

+

&#48653;

+

&#48656;

+

&#48660;

+

&#48668;

+

&#48669;

+

&#48671;

+

&#48708;

+

&#48709;

+

&#48712;

+

&#48716;

+

&#48718;

+

&#48724;

+

&#48725;

+

&#48727;

+

&#48729;

+

&#48730;

+

&#48731;

+

&#48736;

+

&#48737;

+

&#48740;

+

&#48744;

+

&#48746;

+

&#48752;

+

&#48753;

+

&#48755;

+

&#48756;

+

&#48757;

+

&#48763;

+

&#48764;

+

&#48765;

+

&#48768;

+

&#48772;

+

&#48780;

+

&#48781;

+

&#48783;

+

&#48784;

+

&#48785;

+

&#48792;

+

&#48793;

+

&#48808;

+

&#48848;

+

&#48849;

+

&#48852;

+

&#48855;

+

&#48856;

+

&#48864;

+

&#48867;

+

&#48868;

+

&#48869;

+

&#48876;

+

&#48897;

+

&#48904;

+

&#48905;

+

&#48920;

+

&#48921;

+

&#48923;

+

&#48924;

+

&#48925;

+

&#48960;

+

&#48961;

+

&#48964;

+

&#48968;

+

&#48976;

+

&#48977;

+

&#48981;

+

&#49044;

+

&#49072;

+

&#49093;

+

&#49100;

+

&#49101;

+

&#49104;

+

&#49108;

+

&#49116;

+

&#49119;

+

&#49121;

+

&#49212;

+

&#49233;

+

&#49240;

+

&#49244;

+

&#49248;

+

&#49256;

+

&#49257;

+

&#49296;

+

&#49297;

+

&#49300;

+

&#49304;

+

&#49312;

+

&#49313;

+

&#49315;

+

&#49317;

+

&#49324;

+

&#49325;

+

&#49327;

+

&#49328;

+

&#49331;

+

&#49332;

+

&#49333;

+

&#49334;

+

&#49340;

+

&#49341;

+

&#49343;

+

&#49344;

+

&#49345;

+

&#49349;

+

&#49352;

+

&#49353;

+

&#49356;

+

&#49360;

+

&#49368;

+

&#49369;

+

&#49371;

+

&#49372;

+

&#49373;

+

&#49380;

+

&#49381;

+

&#49384;

+

&#49388;

+

&#49396;

+

&#49397;

+

&#49399;

+

&#49401;

+

&#49408;

+

&#49412;

+

&#49416;

+

&#49424;

+

&#49429;

+

&#49436;

+

&#49437;

+

&#49438;

+

&#49439;

+

&#49440;

+

&#49443;

+

&#49444;

+

&#49446;

+

&#49447;

+

&#49452;

+

&#49453;

+

&#49455;

+

&#49456;

+

&#49457;

+

&#49462;

+

&#49464;

+

&#49465;

+

&#49468;

+

&#49472;

+

&#49480;

+

&#49481;

+

&#49483;

+

&#49484;

+

&#49485;

+

&#49492;

+

&#49493;

+

&#49496;

+

&#49500;

+

&#49508;

+

&#49509;

+

&#49511;

+

&#49512;

+

&#49513;

+

&#49520;

+

&#49524;

+

&#49528;

+

&#49541;

+

&#49548;

+

&#49549;

+

&#49550;

+

&#49552;

+

&#49556;

+

&#49558;

+

&#49564;

+

&#49565;

+

&#49567;

+

&#49569;

+

&#49573;

+

&#49576;

+

&#49577;

+

&#49580;

+

&#49584;

+

&#49597;

+

&#49604;

+

&#49608;

+

&#49612;

+

&#49620;

+

&#49623;

+

&#49624;

+

&#49632;

+

&#49636;

+

&#49640;

+

&#49648;

+

&#49649;

+

&#49651;

+

&#49660;

+

&#49661;

+

&#49664;

+

&#49668;

+

&#49676;

+

&#49677;

+

&#49679;

+

&#49681;

+

&#49688;

+

&#49689;

+

&#49692;

+

&#49695;

+

&#49696;

+

&#49704;

+

&#49705;

+

&#49707;

+

&#49709;

+

&#49711;

+

&#49713;

+

&#49714;

+

&#49716;

+

&#49736;

+

&#49744;

+

&#49745;

+

&#49748;

+

&#49752;

+

&#49760;

+

&#49765;

+

&#49772;

+

&#49773;

+

&#49776;

+

&#49780;

+

&#49788;

+

&#49789;

+

&#49791;

+

&#49793;

+

&#49800;

+

&#49801;

+

&#49808;

+

&#49816;

+

&#49819;

+

&#49821;

+

&#49828;

+

&#49829;

+

&#49832;

+

&#49836;

+

&#49837;

+

&#49844;

+

&#49845;

+

&#49847;

+

&#49849;

+

&#49884;

+

&#49885;

+

&#49888;

+

&#49891;

+

&#49892;

+

&#49899;

+

&#49900;

+

&#49901;

+

&#49903;

+

&#49905;

+

&#49910;

+

&#49912;

+

&#49913;

+

&#49915;

+

&#49916;

+

&#49920;

+

&#49928;

+

&#49929;

+

&#49932;

+

&#49933;

+

&#49939;

+

&#49940;

+

&#49941;

+

&#49944;

+

&#49948;

+

&#49956;

+

&#49957;

+

&#49960;

+

&#49961;

+

&#49989;

+

&#50024;

+

&#50025;

+

&#50028;

+

&#50032;

+

&#50034;

+

&#50040;

+

&#50041;

+

&#50044;

+

&#50045;

+

&#50052;

+

&#50056;

+

&#50060;

+

&#50112;

+

&#50136;

+

&#50137;

+

&#50140;

+

&#50143;

+

&#50144;

+

&#50146;

+

&#50152;

+

&#50153;

+

&#50157;

+

&#50164;

+

&#50165;

+

&#50168;

+

&#50184;

+

&#50192;

+

&#50212;

+

&#50220;

+

&#50224;

+

&#50228;

+

&#50236;

+

&#50237;

+

&#50248;

+

&#50276;

+

&#50277;

+

&#50280;

+

&#50284;

+

&#50292;

+

&#50293;

+

&#50297;

+

&#50304;

+

&#50324;

+

&#50332;

+

&#50360;

+

&#50364;

+

&#50409;

+

&#50416;

+

&#50417;

+

&#50420;

+

&#50424;

+

&#50426;

+

&#50431;

+

&#50432;

+

&#50433;

+

&#50444;

+

&#50448;

+

&#50452;

+

&#50460;

+

&#50472;

+

&#50473;

+

&#50476;

+

&#50480;

+

&#50488;

+

&#50489;

+

&#50491;

+

&#50493;

+

&#50500;

+

&#50501;

+

&#50504;

+

&#50505;

+

&#50506;

+

&#50508;

+

&#50509;

+

&#50510;

+

&#50515;

+

&#50516;

+

&#50517;

+

&#50519;

+

&#50520;

+

&#50521;

+

&#50525;

+

&#50526;

+

&#50528;

+

&#50529;

+

&#50532;

+

&#50536;

+

&#50544;

+

&#50545;

+

&#50547;

+

&#50548;

+

&#50549;

+

&#50556;

+

&#50557;

+

&#50560;

+

&#50564;

+

&#50567;

+

&#50572;

+

&#50573;

+

&#50575;

+

&#50577;

+

&#50581;

+

&#50583;

+

&#50584;

+

&#50588;

+

&#50592;

+

&#50601;

+

&#50612;

+

&#50613;

+

&#50616;

+

&#50617;

+

&#50619;

+

&#50620;

+

&#50621;

+

&#50622;

+

&#50628;

+

&#50629;

+

&#50630;

+

&#50631;

+

&#50632;

+

&#50633;

+

&#50634;

+

&#50636;

+

&#50638;

+

&#50640;

+

&#50641;

+

&#50644;

+

&#50648;

+

&#50656;

+

&#50657;

+

&#50659;

+

&#50661;

+

&#50668;

+

&#50669;

+

&#50670;

+

&#50672;

+

&#50676;

+

&#50678;

+

&#50679;

+

&#50684;

+

&#50685;

+

&#50686;

+

&#50687;

+

&#50688;

+

&#50689;

+

&#50693;

+

&#50694;

+

&#50695;

+

&#50696;

+

&#50700;

+

&#50704;

+

&#50712;

+

&#50713;

+

&#50715;

+

&#50716;

+

&#50724;

+

&#50725;

+

&#50728;

+

&#50732;

+

&#50733;

+

&#50734;

+

&#50736;

+

&#50739;

+

&#50740;

+

&#50741;

+

&#50743;

+

&#50745;

+

&#50747;

+

&#50752;

+

&#50753;

+

&#50756;

+

&#50760;

+

&#50768;

+

&#50769;

+

&#50771;

+

&#50772;

+

&#50773;

+

&#50780;

+

&#50781;

+

&#50784;

+

&#50796;

+

&#50799;

+

&#50801;

+

&#50808;

+

&#50809;

+

&#50812;

+

&#50816;

+

&#50824;

+

&#50825;

+

&#50827;

+

&#50829;

+

&#50836;

+

&#50837;

+

&#50840;

+

&#50844;

+

&#50852;

+

&#50853;

+

&#50855;

+

&#50857;

+

&#50864;

+

&#50865;

+

&#50868;

+

&#50872;

+

&#50873;

+

&#50874;

+

&#50880;

+

&#50881;

+

&#50883;

+

&#50885;

+

&#50892;

+

&#50893;

+

&#50896;

+

&#50900;

+

&#50908;

+

&#50909;

+

&#50912;

+

&#50913;

+

&#50920;

+

&#50921;

+

&#50924;

+

&#50928;

+

&#50936;

+

&#50937;

+

&#50941;

+

&#50948;

+

&#50949;

+

&#50952;

+

&#50956;

+

&#50964;

+

&#50965;

+

&#50967;

+

&#50969;

+

&#50976;

+

&#50977;

+

&#50980;

+

&#50984;

+

&#50992;

+

&#50993;

+

&#50995;

+

&#50997;

+

&#50999;

+

&#51004;

+

&#51005;

+

&#51008;

+

&#51012;

+

&#51018;

+

&#51020;

+

&#51021;

+

&#51023;

+

&#51025;

+

&#51026;

+

&#51027;

+

&#51028;

+

&#51029;

+

&#51030;

+

&#51031;

+

&#51032;

+

&#51036;

+

&#51040;

+

&#51048;

+

&#51051;

+

&#51060;

+

&#51061;

+

&#51064;

+

&#51068;

+

&#51069;

+

&#51070;

+

&#51075;

+

&#51076;

+

&#51077;

+

&#51079;

+

&#51080;

+

&#51081;

+

&#51082;

+

&#51086;

+

&#51088;

+

&#51089;

+

&#51092;

+

&#51094;

+

&#51095;

+

&#51096;

+

&#51098;

+

&#51104;

+

&#51105;

+

&#51107;

+

&#51108;

+

&#51109;

+

&#51110;

+

&#51116;

+

&#51117;

+

&#51120;

+

&#51124;

+

&#51132;

+

&#51133;

+

&#51135;

+

&#51136;

+

&#51137;

+

&#51144;

+

&#51145;

+

&#51148;

+

&#51150;

+

&#51152;

+

&#51160;

+

&#51165;

+

&#51172;

+

&#51176;

+

&#51180;

+

&#51200;

+

&#51201;

+

&#51204;

+

&#51208;

+

&#51210;

+

&#51216;

+

&#51217;

+

&#51219;

+

&#51221;

+

&#51222;

+

&#51228;

+

&#51229;

+

&#51232;

+

&#51236;

+

&#51244;

+

&#51245;

+

&#51247;

+

&#51249;

+

&#51256;

+

&#51260;

+

&#51264;

+

&#51272;

+

&#51273;

+

&#51276;

+

&#51277;

+

&#51284;

+

&#51312;

+

&#51313;

+

&#51316;

+

&#51320;

+

&#51322;

+

&#51328;

+

&#51329;

+

&#51331;

+

&#51333;

+

&#51334;

+

&#51335;

+

&#51339;

+

&#51340;

+

&#51341;

+

&#51348;

+

&#51357;

+

&#51359;

+

&#51361;

+

&#51368;

+

&#51388;

+

&#51389;

+

&#51396;

+

&#51400;

+

&#51404;

+

&#51412;

+

&#51413;

+

&#51415;

+

&#51417;

+

&#51424;

+

&#51425;

+

&#51428;

+

&#51445;

+

&#51452;

+

&#51453;

+

&#51456;

+

&#51460;

+

&#51461;

+

&#51462;

+

&#51468;

+

&#51469;

+

&#51471;

+

&#51473;

+

&#51480;

+

&#51500;

+

&#51508;

+

&#51536;

+

&#51537;

+

&#51540;

+

&#51544;

+

&#51552;

+

&#51553;

+

&#51555;

+

&#51564;

+

&#51568;

+

&#51572;

+

&#51580;

+

&#51592;

+

&#51593;

+

&#51596;

+

&#51600;

+

&#51608;

+

&#51609;

+

&#51611;

+

&#51613;

+

&#51648;

+

&#51649;

+

&#51652;

+

&#51655;

+

&#51656;

+

&#51658;

+

&#51664;

+

&#51665;

+

&#51667;

+

&#51669;

+

&#51670;

+

&#51673;

+

&#51674;

+

&#51676;

+

&#51677;

+

&#51680;

+

&#51682;

+

&#51684;

+

&#51687;

+

&#51692;

+

&#51693;

+

&#51695;

+

&#51696;

+

&#51697;

+

&#51704;

+

&#51705;

+

&#51708;

+

&#51712;

+

&#51720;

+

&#51721;

+

&#51723;

+

&#51724;

+

&#51725;

+

&#51732;

+

&#51736;

+

&#51753;

+

&#51788;

+

&#51789;

+

&#51792;

+

&#51796;

+

&#51804;

+

&#51805;

+

&#51807;

+

&#51808;

+

&#51809;

+

&#51816;

+

&#51837;

+

&#51844;

+

&#51864;

+

&#51900;

+

&#51901;

+

&#51904;

+

&#51908;

+

&#51916;

+

&#51917;

+

&#51919;

+

&#51921;

+

&#51923;

+

&#51928;

+

&#51929;

+

&#51936;

+

&#51948;

+

&#51956;

+

&#51976;

+

&#51984;

+

&#51988;

+

&#51992;

+

&#52000;

+

&#52001;

+

&#52033;

+

&#52040;

+

&#52041;

+

&#52044;

+

&#52048;

+

&#52056;

+

&#52057;

+

&#52061;

+

&#52068;

+

&#52088;

+

&#52089;

+

&#52124;

+

&#52152;

+

&#52180;

+

&#52196;

+

&#52199;

+

&#52201;

+

&#52236;

+

&#52237;

+

&#52240;

+

&#52244;

+

&#52252;

+

&#52253;

+

&#52257;

+

&#52258;

+

&#52263;

+

&#52264;

+

&#52265;

+

&#52268;

+

&#52270;

+

&#52272;

+

&#52280;

+

&#52281;

+

&#52283;

+

&#52284;

+

&#52285;

+

&#52286;

+

&#52292;

+

&#52293;

+

&#52296;

+

&#52300;

+

&#52308;

+

&#52309;

+

&#52311;

+

&#52312;

+

&#52313;

+

&#52320;

+

&#52324;

+

&#52326;

+

&#52328;

+

&#52336;

+

&#52341;

+

&#52376;

+

&#52377;

+

&#52380;

+

&#52384;

+

&#52392;

+

&#52393;

+

&#52395;

+

&#52396;

+

&#52397;

+

&#52404;

+

&#52405;

+

&#52408;

+

&#52412;

+

&#52420;

+

&#52421;

+

&#52423;

+

&#52425;

+

&#52432;

+

&#52436;

+

&#52452;

+

&#52460;

+

&#52464;

+

&#52481;

+

&#52488;

+

&#52489;

+

&#52492;

+

&#52496;

+

&#52504;

+

&#52505;

+

&#52507;

+

&#52509;

+

&#52516;

+

&#52520;

+

&#52524;

+

&#52537;

+

&#52572;

+

&#52576;

+

&#52580;

+

&#52588;

+

&#52589;

+

&#52591;

+

&#52593;

+

&#52600;

+

&#52616;

+

&#52628;

+

&#52629;

+

&#52632;

+

&#52636;

+

&#52644;

+

&#52645;

+

&#52647;

+

&#52649;

+

&#52656;

+

&#52676;

+

&#52684;

+

&#52688;

+

&#52712;

+

&#52716;

+

&#52720;

+

&#52728;

+

&#52729;

+

&#52731;

+

&#52733;

+

&#52740;

+

&#52744;

+

&#52748;

+

&#52756;

+

&#52761;

+

&#52768;

+

&#52769;

+

&#52772;

+

&#52776;

+

&#52784;

+

&#52785;

+

&#52787;

+

&#52789;

+

&#52824;

+

&#52825;

+

&#52828;

+

&#52831;

+

&#52832;

+

&#52833;

+

&#52840;

+

&#52841;

+

&#52843;

+

&#52845;

+

&#52852;

+

&#52853;

+

&#52856;

+

&#52860;

+

&#52868;

+

&#52869;

+

&#52871;

+

&#52873;

+

&#52880;

+

&#52881;

+

&#52884;

+

&#52888;

+

&#52896;

+

&#52897;

+

&#52899;

+

&#52900;

+

&#52901;

+

&#52908;

+

&#52909;

+

&#52929;

+

&#52964;

+

&#52965;

+

&#52968;

+

&#52971;

+

&#52972;

+

&#52980;

+

&#52981;

+

&#52983;

+

&#52984;

+

&#52985;

+

&#52992;

+

&#52993;

+

&#52996;

+

&#53000;

+

&#53008;

+

&#53009;

+

&#53011;

+

&#53013;

+

&#53020;

+

&#53024;

+

&#53028;

+

&#53036;

+

&#53037;

+

&#53039;

+

&#53040;

+

&#53041;

+

&#53048;

+

&#53076;

+

&#53077;

+

&#53080;

+

&#53084;

+

&#53092;

+

&#53093;

+

&#53095;

+

&#53097;

+

&#53104;

+

&#53105;

+

&#53108;

+

&#53112;

+

&#53120;

+

&#53125;

+

&#53132;

+

&#53153;

+

&#53160;

+

&#53168;

+

&#53188;

+

&#53216;

+

&#53217;

+

&#53220;

+

&#53224;

+

&#53232;

+

&#53233;

+

&#53235;

+

&#53237;

+

&#53244;

+

&#53248;

퀀
+

&#53252;

+

&#53265;

+

&#53272;

+

&#53293;

+

&#53300;

+

&#53301;

+

&#53304;

+

&#53308;

+

&#53316;

+

&#53317;

+

&#53319;

+

&#53321;

+

&#53328;

+

&#53332;

+

&#53336;

+

&#53344;

+

&#53356;

+

&#53357;

+

&#53360;

+

&#53364;

+

&#53372;

+

&#53373;

+

&#53377;

+

&#53412;

+

&#53413;

+

&#53416;

+

&#53420;

+

&#53428;

+

&#53429;

+

&#53431;

+

&#53433;

+

&#53440;

+

&#53441;

+

&#53444;

+

&#53448;

+

&#53449;

+

&#53456;

+

&#53457;

+

&#53459;

+

&#53460;

+

&#53461;

+

&#53468;

+

&#53469;

+

&#53472;

+

&#53476;

+

&#53484;

+

&#53485;

+

&#53487;

+

&#53488;

+

&#53489;

+

&#53496;

+

&#53517;

+

&#53552;

+

&#53553;

+

&#53556;

+

&#53560;

+

&#53562;

+

&#53568;

+

&#53569;

+

&#53571;

+

&#53572;

+

&#53573;

+

&#53580;

+

&#53581;

+

&#53584;

+

&#53588;

+

&#53596;

+

&#53597;

+

&#53599;

+

&#53601;

+

&#53608;

+

&#53612;

+

&#53628;

+

&#53636;

+

&#53640;

+

&#53664;

+

&#53665;

+

&#53668;

+

&#53672;

+

&#53680;

+

&#53681;

+

&#53683;

+

&#53685;

+

&#53690;

+

&#53692;

+

&#53696;

+

&#53720;

+

&#53748;

+

&#53752;

+

&#53767;

+

&#53769;

+

&#53776;

+

&#53804;

+

&#53805;

+

&#53808;

+

&#53812;

+

&#53820;

+

&#53821;

+

&#53823;

+

&#53825;

+

&#53832;

+

&#53852;

+

&#53860;

+

&#53888;

+

&#53889;

+

&#53892;

+

&#53896;

+

&#53904;

+

&#53905;

+

&#53909;

+

&#53916;

+

&#53920;

+

&#53924;

+

&#53932;

+

&#53937;

+

&#53944;

+

&#53945;

+

&#53948;

+

&#53951;

+

&#53952;

+

&#53954;

+

&#53960;

+

&#53961;

+

&#53963;

+

&#53972;

+

&#53976;

+

&#53980;

+

&#53988;

+

&#53989;

+

&#54000;

+

&#54001;

+

&#54004;

+

&#54008;

+

&#54016;

+

&#54017;

+

&#54019;

+

&#54021;

+

&#54028;

+

&#54029;

+

&#54030;

+

&#54032;

+

&#54036;

+

&#54038;

+

&#54044;

+

&#54045;

+

&#54047;

+

&#54048;

+

&#54049;

+

&#54053;

+

&#54056;

+

&#54057;

+

&#54060;

+

&#54064;

+

&#54072;

+

&#54073;

+

&#54075;

+

&#54076;

+

&#54077;

+

&#54084;

+

&#54085;

+

&#54140;

+

&#54141;

+

&#54144;

+

&#54148;

+

&#54156;

+

&#54157;

+

&#54159;

+

&#54160;

+

&#54161;

+

&#54168;

+

&#54169;

+

&#54172;

+

&#54176;

+

&#54184;

+

&#54185;

+

&#54187;

+

&#54189;

+

&#54196;

+

&#54200;

+

&#54204;

+

&#54212;

+

&#54213;

+

&#54216;

+

&#54217;

+

&#54224;

+

&#54232;

+

&#54241;

+

&#54243;

+

&#54252;

+

&#54253;

+

&#54256;

+

&#54260;

+

&#54268;

+

&#54269;

+

&#54271;

+

&#54273;

+

&#54280;

+

&#54301;

+

&#54336;

+

&#54340;

+

&#54364;

+

&#54368;

+

&#54372;

+

&#54381;

+

&#54383;

+

&#54392;

+

&#54393;

+

&#54396;

+

&#54399;

+

&#54400;

+

&#54402;

+

&#54408;

+

&#54409;

+

&#54411;

+

&#54413;

+

&#54420;

+

&#54441;

+

&#54476;

+

&#54480;

+

&#54484;

+

&#54492;

+

&#54495;

+

&#54504;

+

&#54508;

+

&#54512;

+

&#54520;

+

&#54523;

+

&#54525;

+

&#54532;

+

&#54536;

+

&#54540;

+

&#54548;

+

&#54549;

+

&#54551;

+

&#54588;

+

&#54589;

+

&#54592;

+

&#54596;

+

&#54604;

+

&#54605;

+

&#54607;

+

&#54609;

+

&#54616;

+

&#54617;

+

&#54620;

+

&#54624;

+

&#54629;

+

&#54632;

+

&#54633;

+

&#54635;

+

&#54637;

+

&#54644;

+

&#54645;

+

&#54648;

+

&#54652;

+

&#54660;

+

&#54661;

+

&#54663;

+

&#54664;

+

&#54665;

+

&#54672;

+

&#54693;

+

&#54728;

+

&#54729;

+

&#54732;

+

&#54736;

+

&#54738;

+

&#54744;

+

&#54745;

+

&#54747;

+

&#54749;

+

&#54756;

+

&#54757;

+

&#54760;

+

&#54764;

+

&#54772;

+

&#54773;

+

&#54775;

+

&#54777;

+

&#54784;

+

&#54785;

+

&#54788;

+

&#54792;

+

&#54800;

+

&#54801;

+

&#54803;

+

&#54804;

+

&#54805;

+

&#54812;

+

&#54816;

+

&#54820;

+

&#54829;

+

&#54840;

+

&#54841;

+

&#54844;

+

&#54848;

+

&#54853;

+

&#54856;

+

&#54857;

+

&#54859;

+

&#54861;

+

&#54865;

+

&#54868;

+

&#54869;

+

&#54872;

+

&#54876;

+

&#54887;

+

&#54889;

+

&#54896;

+

&#54897;

+

&#54900;

+

&#54915;

+

&#54917;

+

&#54924;

+

&#54925;

+

&#54928;

+

&#54932;

+

&#54941;

+

&#54943;

+

&#54945;

+

&#54952;

+

&#54956;

+

&#54960;

+

&#54969;

+

&#54971;

+

&#54980;

+

&#54981;

+

&#54984;

+

&#54988;

+

&#54993;

+

&#54996;

+

&#54999;

+

&#55001;

+

&#55008;

+

&#55012;

+

&#55016;

+

&#55024;

+

&#55029;

+

&#55036;

+

&#55037;

+

&#55040;

+

&#55044;

+

&#55057;

+

&#55064;

+

&#55065;

+

&#55068;

+

&#55072;

+

&#55080;

+

&#55081;

+

&#55083;

+

&#55085;

+

&#55092;

+

&#55093;

+

&#55096;

+

&#55100;

+

&#55108;

+

&#55111;

+

&#55113;

+

&#55120;

+

&#55121;

+

&#55124;

+

&#55126;

+

&#55127;

+

&#55128;

+

&#55129;

+

&#55136;

+

&#55137;

+

&#55139;

+

&#55141;

+

&#55145;

+

&#55148;

+

&#55152;

+

&#55156;

+

&#55164;

+

&#55165;

+

&#55169;

+

&#55176;

+

&#55177;

+

&#55180;

+

&#55184;

+

&#55192;

+

&#55193;

+

&#55195;

+

&#55197;

+
+
+ + +
+
+ + +
+ +
+ +
+
+
+

Installing Webfonts

+ +

Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.

+ +

1. Upload your webfonts

+

You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.

+ +

2. Include the webfont stylesheet

+

A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the Fontspring blog post about it. The code for it is as follows:

+ + + +@font-face{ + font-family: 'MyWebFont'; + src: url('WebFont.eot'); + src: url('WebFont.eot?#iefix') format('embedded-opentype'), + url('WebFont.woff') format('woff'), + url('WebFont.ttf') format('truetype'), + url('WebFont.svg#webfont') format('svg'); +} + + +

We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:

+ <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> + +

3. Modify your own stylesheet

+

To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:

+p { font-family: 'WebFont', Arial, sans-serif; } + +

4. Test

+

Getting webfonts to work cross-browser can be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.

+
+ + +
+ +
+ +
+ +
+ + diff --git a/src/fonts/NotoKR-Thin/notokr-thin.eot b/src/fonts/NotoKR-Thin/notokr-thin.eot new file mode 100644 index 0000000..4276bf4 Binary files /dev/null and b/src/fonts/NotoKR-Thin/notokr-thin.eot differ diff --git a/src/fonts/NotoKR-Thin/notokr-thin.svg b/src/fonts/NotoKR-Thin/notokr-thin.svg new file mode 100644 index 0000000..f545325 --- /dev/null +++ b/src/fonts/NotoKR-Thin/notokr-thin.svg @@ -0,0 +1,2457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fonts/NotoKR-Thin/notokr-thin.ttf b/src/fonts/NotoKR-Thin/notokr-thin.ttf new file mode 100644 index 0000000..9456462 Binary files /dev/null and b/src/fonts/NotoKR-Thin/notokr-thin.ttf differ diff --git a/src/fonts/NotoKR-Thin/notokr-thin.woff b/src/fonts/NotoKR-Thin/notokr-thin.woff new file mode 100644 index 0000000..57f52f2 Binary files /dev/null and b/src/fonts/NotoKR-Thin/notokr-thin.woff differ diff --git a/src/fonts/NotoKR-Thin/notokr-thin.woff2 b/src/fonts/NotoKR-Thin/notokr-thin.woff2 new file mode 100644 index 0000000..8fbb23a Binary files /dev/null and b/src/fonts/NotoKR-Thin/notokr-thin.woff2 differ diff --git a/src/fonts/NotoKR-Thin/specimen_files/easytabs.js b/src/fonts/NotoKR-Thin/specimen_files/easytabs.js new file mode 100644 index 0000000..167f53b --- /dev/null +++ b/src/fonts/NotoKR-Thin/specimen_files/easytabs.js @@ -0,0 +1,7 @@ +(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;} +if(typeof param.defaultContent=="number") +{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;} +$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();} +function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none") +{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}} +$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery); \ No newline at end of file diff --git a/src/fonts/NotoKR-Thin/specimen_files/grid_12-825-55-15.css b/src/fonts/NotoKR-Thin/specimen_files/grid_12-825-55-15.css new file mode 100644 index 0000000..3d6aef7 --- /dev/null +++ b/src/fonts/NotoKR-Thin/specimen_files/grid_12-825-55-15.css @@ -0,0 +1,129 @@ +/*Notes about grid: +Columns: 12 +Grid Width: 825px +Column Width: 55px +Gutter Width: 15px +-------------------------------*/ + + + +.section {margin-bottom: 18px; +} +.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;} +.section {*zoom: 1;} + +.section .firstcolumn, +.section .firstcol {margin-left: 0;} + + +/* Border on left hand side of a column. */ +.border { + padding-left: 7px; + margin-left: 7px; + border-left: 1px solid #eee; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-left: 42px; + margin-left: 42px; + border-left: 1px solid #eee; +} + + + +/* The Grid Classes */ +.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12 +{margin-left: 15px;float: left;display: inline; overflow: hidden;} + + +.width1, .grid1, .span-1 {width: 55px;} +.width1_2cols,.grid1_2cols {width: 20px;} +.width1_3cols,.grid1_3cols {width: 8px;} +.width1_4cols,.grid1_4cols {width: 2px;} +.input_width1 {width: 49px;} + +.width2, .grid2, .span-2 {width: 125px;} +.width2_3cols,.grid2_3cols {width: 31px;} +.width2_4cols,.grid2_4cols {width: 20px;} +.input_width2 {width: 119px;} + +.width3, .grid3, .span-3 {width: 195px;} +.width3_2cols,.grid3_2cols {width: 90px;} +.width3_4cols,.grid3_4cols {width: 37px;} +.input_width3 {width: 189px;} + +.width4, .grid4, .span-4 {width: 265px;} +.width4_3cols,.grid4_3cols {width: 78px;} +.input_width4 {width: 259px;} + +.width5, .grid5, .span-5 {width: 335px;} +.width5_2cols,.grid5_2cols {width: 160px;} +.width5_3cols,.grid5_3cols {width: 101px;} +.width5_4cols,.grid5_4cols {width: 72px;} +.input_width5 {width: 329px;} + +.width6, .grid6, .span-6 {width: 405px;} +.width6_4cols,.grid6_4cols {width: 90px;} +.input_width6 {width: 399px;} + +.width7, .grid7, .span-7 {width: 475px;} +.width7_2cols,.grid7_2cols {width: 230px;} +.width7_3cols,.grid7_3cols {width: 148px;} +.width7_4cols,.grid7_4cols {width: 107px;} +.input_width7 {width: 469px;} + +.width8, .grid8, .span-8 {width: 545px;} +.width8_3cols,.grid8_3cols {width: 171px;} +.input_width8 {width: 539px;} + +.width9, .grid9, .span-9 {width: 615px;} +.width9_2cols,.grid9_2cols {width: 300px;} +.width9_4cols,.grid9_4cols {width: 142px;} +.input_width9 {width: 609px;} + +.width10, .grid10, .span-10 {width: 685px;} +.width10_3cols,.grid10_3cols {width: 218px;} +.width10_4cols,.grid10_4cols {width: 160px;} +.input_width10 {width: 679px;} + +.width11, .grid11, .span-11 {width: 755px;} +.width11_2cols,.grid11_2cols {width: 370px;} +.width11_3cols,.grid11_3cols {width: 241px;} +.width11_4cols,.grid11_4cols {width: 177px;} +.input_width11 {width: 749px;} + +.width12, .grid12, .span-12 {width: 825px;} +.input_width12 {width: 819px;} + +/* Subdivided grid spaces */ +.emptycols_left1, .prepend-1 {padding-left: 70px;} +.emptycols_right1, .append-1 {padding-right: 70px;} +.emptycols_left2, .prepend-2 {padding-left: 140px;} +.emptycols_right2, .append-2 {padding-right: 140px;} +.emptycols_left3, .prepend-3 {padding-left: 210px;} +.emptycols_right3, .append-3 {padding-right: 210px;} +.emptycols_left4, .prepend-4 {padding-left: 280px;} +.emptycols_right4, .append-4 {padding-right: 280px;} +.emptycols_left5, .prepend-5 {padding-left: 350px;} +.emptycols_right5, .append-5 {padding-right: 350px;} +.emptycols_left6, .prepend-6 {padding-left: 420px;} +.emptycols_right6, .append-6 {padding-right: 420px;} +.emptycols_left7, .prepend-7 {padding-left: 490px;} +.emptycols_right7, .append-7 {padding-right: 490px;} +.emptycols_left8, .prepend-8 {padding-left: 560px;} +.emptycols_right8, .append-8 {padding-right: 560px;} +.emptycols_left9, .prepend-9 {padding-left: 630px;} +.emptycols_right9, .append-9 {padding-right: 630px;} +.emptycols_left10, .prepend-10 {padding-left: 700px;} +.emptycols_right10, .append-10 {padding-right: 700px;} +.emptycols_left11, .prepend-11 {padding-left: 770px;} +.emptycols_right11, .append-11 {padding-right: 770px;} +.pull-1 {margin-left: -70px;} +.push-1 {margin-right: -70px;margin-left: 18px;float: right;} +.pull-2 {margin-left: -140px;} +.push-2 {margin-right: -140px;margin-left: 18px;float: right;} +.pull-3 {margin-left: -210px;} +.push-3 {margin-right: -210px;margin-left: 18px;float: right;} +.pull-4 {margin-left: -280px;} +.push-4 {margin-right: -280px;margin-left: 18px;float: right;} \ No newline at end of file diff --git a/src/fonts/NotoKR-Thin/specimen_files/specimen_stylesheet.css b/src/fonts/NotoKR-Thin/specimen_files/specimen_stylesheet.css new file mode 100644 index 0000000..d4c8222 --- /dev/null +++ b/src/fonts/NotoKR-Thin/specimen_files/specimen_stylesheet.css @@ -0,0 +1,396 @@ +@import url('grid_12-825-55-15.css'); + +/* + CSS Reset by Eric Meyer - Released under Public Domain + http://meyerweb.com/eric/tools/css/reset/ +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, +caption, tbody, tfoot, thead, tr, th, td + {margin: 0;padding: 0;border: 0;outline: 0; + font-size: 100%;vertical-align: baseline; + background: transparent;} +body {line-height: 1;} +ol, ul {list-style: none;} +blockquote, q {quotes: none;} +blockquote:before, blockquote:after, +q:before, q:after {content: ''; content: none;} +:focus {outline: 0;} +ins {text-decoration: none;} +del {text-decoration: line-through;} +table {border-collapse: collapse;border-spacing: 0;} + + + + +body { + color: #000; + background-color: #dcdcdc; +} + +a { + text-decoration: none; + color: #1883ba; +} + +h1{ + font-size: 32px; + font-weight: normal; + font-style: normal; + margin-bottom: 18px; +} + +h2{ + font-size: 18px; +} + +#container { + width: 865px; + margin: 0px auto; +} + + +#header { + padding: 20px; + font-size: 36px; + background-color: #000; + color: #fff; +} + +#header span { + color: #666; +} +#main_content { + background-color: #fff; + padding: 60px 20px 20px; +} + + +#footer p { + margin: 0; + padding-top: 10px; + padding-bottom: 50px; + color: #333; + font: 10px Arial, sans-serif; +} + +.tabs { + width: 100%; + height: 31px; + background-color: #444; +} +.tabs li { + float: left; + margin: 0; + overflow: hidden; + background-color: #444; +} +.tabs li a { + display: block; + color: #fff; + text-decoration: none; + font: bold 11px/11px 'Arial'; + text-transform: uppercase; + padding: 10px 15px; + border-right: 1px solid #fff; +} + +.tabs li a:hover { + background-color: #00b3ff; + +} + +.tabs li.active a { + color: #000; + background-color: #fff; +} + + + +div.huge { + + font-size: 120px; + line-height: 1em; + padding: 0; + letter-spacing: -.02em; + overflow: hidden; +} +div.glyph_range { + font-size: 72px; + line-height: 1.1em; +} + +.size10{ font-size: 10px; } +.size11{ font-size: 11px; } +.size12{ font-size: 12px; } +.size13{ font-size: 13px; } +.size14{ font-size: 14px; } +.size16{ font-size: 16px; } +.size18{ font-size: 18px; } +.size20{ font-size: 20px; } +.size24{ font-size: 24px; } +.size30{ font-size: 30px; } +.size36{ font-size: 36px; } +.size48{ font-size: 48px; } +.size60{ font-size: 60px; } +.size72{ font-size: 72px; } +.size90{ font-size: 90px; } + + +.psample_row1 { height: 120px;} +.psample_row1 { height: 120px;} +.psample_row2 { height: 160px;} +.psample_row3 { height: 160px;} +.psample_row4 { height: 160px;} + +.psample { + overflow: hidden; + position: relative; +} +.psample p { + line-height: 1.3em; + display: block; + overflow: hidden; + margin: 0; +} + +.psample span { + margin-right: .5em; +} + +.white_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==); + position: absolute; + bottom: 0; +} +.black_blend { + width: 100%; + height: 61px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC); + position: absolute; + bottom: 0; +} +.fullreverse { + background: #000 !important; + color: #fff !important; + margin-left: -20px; + padding-left: 20px; + margin-right: -20px; + padding-right: 20px; + padding: 20px; + margin-bottom:0; +} + + +.sample_table td { + padding-top: 3px; + padding-bottom:5px; + padding-left: 5px; + vertical-align: middle; + line-height: 1.2em; +} + +.sample_table td:first-child { + background-color: #eee; + text-align: right; + padding-right: 5px; + padding-left: 0; + padding: 5px; + font: 11px/12px "Courier New", Courier, mono; +} + +code { + white-space: pre; + background-color: #eee; + display: block; + padding: 10px; + margin-bottom: 18px; + overflow: auto; +} + + +.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;} + +.box { + padding: 18px; + margin-bottom: 18px; + background: #eee; +} + +.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;} + +#bodycomparison { + position: relative; + overflow: hidden; + font-size: 72px; + height: 90px; + white-space: nowrap; +} + +#bodycomparison div{ + font-size: 72px; + line-height: 90px; + display: inline; + margin: 0 15px 0 0; + padding: 0; +} + +#bodycomparison div span{ + font: 10px Arial; + position: absolute; + left: 0; +} +#xheight { + float: none; + position: absolute; + color: #d9f3ff; + font-size: 72px; + line-height: 90px; +} + +.fontbody { + position: relative; +} +.arialbody{ + font-family: Arial; + position: relative; +} +.verdanabody{ + font-family: Verdana; + position: relative; +} +.georgiabody{ + font-family: Georgia; + position: relative; +} + +/* @group Layout page + */ + +#layout h1 { + font-size: 36px; + line-height: 42px; + font-weight: normal; + font-style: normal; +} + +#layout h2 { + font-size: 24px; + line-height: 23px; + font-weight: normal; + font-style: normal; +} + +#layout h3 { + font-size: 22px; + line-height: 1.4em; + margin-top: 1em; + font-weight: normal; + font-style: normal; +} + + +#layout p.byline { + font-size: 12px; + margin-top: 18px; + line-height: 12px; + margin-bottom: 0; +} +#layout p { + font-size: 14px; + line-height: 21px; + margin-bottom: .5em; +} + +#layout p.large{ + font-size: 18px; + line-height: 26px; +} + +#layout .sidebar p{ + font-size: 12px; + line-height: 1.4em; +} + +#layout p.caption { + font-size: 10px; + margin-top: -16px; + margin-bottom: 18px; +} + +/* @end */ + +/* @group Glyphs */ + +#glyph_chart div{ + background-color: #d9f3ff; + color: black; + float: left; + font-size: 36px; + height: 1.2em; + line-height: 1.2em; + margin-bottom: 1px; + margin-right: 1px; + text-align: center; + width: 1.2em; + position: relative; + padding: .6em .2em .2em; +} + +#glyph_chart div p { + position: absolute; + left: 0; + top: 0; + display: block; + text-align: center; + font: bold 9px Arial, sans-serif; + background-color: #3a768f; + width: 100%; + color: #fff; + padding: 2px 0; +} + + +#glyphs h1 { + font-family: Arial, sans-serif; +} +/* @end */ + +/* @group Installing */ + +#installing { + font: 13px Arial, sans-serif; +} + +#installing p, +#glyphs p{ + line-height: 1.2em; + margin-bottom: 18px; + font: 13px Arial, sans-serif; +} + + + +#installing h3{ + font-size: 15px; + margin-top: 18px; +} + +/* @end */ + +#rendering h1 { + font-family: Arial, sans-serif; +} +.render_table td { + font: 11px "Courier New", Courier, mono; + vertical-align: middle; +} + + diff --git a/src/fonts/NotoKR-Thin/stylesheet.css b/src/fonts/NotoKR-Thin/stylesheet.css new file mode 100644 index 0000000..9e60668 --- /dev/null +++ b/src/fonts/NotoKR-Thin/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 28, 2015 */ + + + +@font-face { + font-family: 'notokr-thin'; + src: url('notokr-thin.eot'); + src: url('notokr-thin.eot?#iefix') format('embedded-opentype'), + url('notokr-thin.woff2') format('woff2'), + url('notokr-thin.woff') format('woff'), + url('notokr-thin.ttf') format('truetype'), + url('notokr-thin.svg#notokr-thin') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file diff --git a/src/img/_favicon.svg b/src/img/_favicon.svg new file mode 100644 index 0000000..461176b --- /dev/null +++ b/src/img/_favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/bg_alerts.svg b/src/img/bg_alerts.svg new file mode 100644 index 0000000..7f5a516 --- /dev/null +++ b/src/img/bg_alerts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/bg_circle.svg b/src/img/bg_circle.svg new file mode 100644 index 0000000..fad3691 --- /dev/null +++ b/src/img/bg_circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/bg_puzzle.svg b/src/img/bg_puzzle.svg new file mode 100644 index 0000000..7b0f75e --- /dev/null +++ b/src/img/bg_puzzle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/bg_puzzle_1.svg b/src/img/bg_puzzle_1.svg new file mode 100644 index 0000000..2f6da01 --- /dev/null +++ b/src/img/bg_puzzle_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/bg_puzzle_2.svg b/src/img/bg_puzzle_2.svg new file mode 100644 index 0000000..cdac1a8 --- /dev/null +++ b/src/img/bg_puzzle_2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/biztrend/img_book_01.png b/src/img/biztrend/img_book_01.png new file mode 100644 index 0000000..2ccafba Binary files /dev/null and b/src/img/biztrend/img_book_01.png differ diff --git a/src/img/biztrend/img_profile.png b/src/img/biztrend/img_profile.png new file mode 100644 index 0000000..a0f7416 Binary files /dev/null and b/src/img/biztrend/img_profile.png differ diff --git a/src/img/btn_next.svg b/src/img/btn_next.svg new file mode 100644 index 0000000..3c2916b --- /dev/null +++ b/src/img/btn_next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/btn_prev.svg b/src/img/btn_prev.svg new file mode 100644 index 0000000..fe40381 --- /dev/null +++ b/src/img/btn_prev.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/cursor.svg b/src/img/cursor.svg new file mode 100644 index 0000000..d9ef500 --- /dev/null +++ b/src/img/cursor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/dim_guide.svg b/src/img/dim_guide.svg new file mode 100644 index 0000000..89ca60e --- /dev/null +++ b/src/img/dim_guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_add.svg b/src/img/ico/ico_add.svg new file mode 100644 index 0000000..c5616ad --- /dev/null +++ b/src/img/ico/ico_add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_alerts.svg b/src/img/ico/ico_alerts.svg new file mode 100644 index 0000000..11ec80d --- /dev/null +++ b/src/img/ico/ico_alerts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_arrow_meta.svg b/src/img/ico/ico_arrow_meta.svg new file mode 100644 index 0000000..444bcb5 --- /dev/null +++ b/src/img/ico/ico_arrow_meta.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_arrow_next_dashed.svg b/src/img/ico/ico_arrow_next_dashed.svg new file mode 100644 index 0000000..43d8e93 --- /dev/null +++ b/src/img/ico/ico_arrow_next_dashed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_arrow_suggest.svg b/src/img/ico/ico_arrow_suggest.svg new file mode 100644 index 0000000..294905f --- /dev/null +++ b/src/img/ico/ico_arrow_suggest.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_bookmark.svg b/src/img/ico/ico_bookmark.svg new file mode 100644 index 0000000..1b746ce --- /dev/null +++ b/src/img/ico/ico_bookmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_bookmark_off.svg b/src/img/ico/ico_bookmark_off.svg new file mode 100644 index 0000000..0d83ba5 --- /dev/null +++ b/src/img/ico/ico_bookmark_off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_bookmark_on.svg b/src/img/ico/ico_bookmark_on.svg new file mode 100644 index 0000000..2f40bd5 --- /dev/null +++ b/src/img/ico/ico_bookmark_on.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_check.svg b/src/img/ico/ico_check.svg new file mode 100644 index 0000000..8284ca6 --- /dev/null +++ b/src/img/ico/ico_check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_chevron.svg b/src/img/ico/ico_chevron.svg new file mode 100644 index 0000000..4d87f77 --- /dev/null +++ b/src/img/ico/ico_chevron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_clear.svg b/src/img/ico/ico_clear.svg new file mode 100644 index 0000000..401787b --- /dev/null +++ b/src/img/ico/ico_clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_comment.svg b/src/img/ico/ico_comment.svg new file mode 100644 index 0000000..b63214f --- /dev/null +++ b/src/img/ico/ico_comment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_drag.svg b/src/img/ico/ico_drag.svg new file mode 100644 index 0000000..aa3bc3e --- /dev/null +++ b/src/img/ico/ico_drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_economy.svg b/src/img/ico/ico_economy.svg new file mode 100644 index 0000000..0ce84b9 --- /dev/null +++ b/src/img/ico/ico_economy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_frequency.svg b/src/img/ico/ico_frequency.svg new file mode 100644 index 0000000..75da47a --- /dev/null +++ b/src/img/ico/ico_frequency.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_future.svg b/src/img/ico/ico_future.svg new file mode 100644 index 0000000..b8e5503 --- /dev/null +++ b/src/img/ico/ico_future.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_health.svg b/src/img/ico/ico_health.svg new file mode 100644 index 0000000..4491bc8 --- /dev/null +++ b/src/img/ico/ico_health.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_history.svg b/src/img/ico/ico_history.svg new file mode 100644 index 0000000..b42bb9d --- /dev/null +++ b/src/img/ico/ico_history.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_info.svg b/src/img/ico/ico_info.svg new file mode 100644 index 0000000..a3d5c75 --- /dev/null +++ b/src/img/ico/ico_info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_level_master.svg b/src/img/ico/ico_level_master.svg new file mode 100644 index 0000000..b0ec085 --- /dev/null +++ b/src/img/ico/ico_level_master.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_life.svg b/src/img/ico/ico_life.svg new file mode 100644 index 0000000..dfb6083 --- /dev/null +++ b/src/img/ico/ico_life.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_like.svg b/src/img/ico/ico_like.svg new file mode 100644 index 0000000..2f40bd5 --- /dev/null +++ b/src/img/ico/ico_like.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_people.svg b/src/img/ico/ico_people.svg new file mode 100644 index 0000000..9b9ea8b --- /dev/null +++ b/src/img/ico/ico_people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_pick.svg b/src/img/ico/ico_pick.svg new file mode 100644 index 0000000..03bdf4a --- /dev/null +++ b/src/img/ico/ico_pick.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_pin.svg b/src/img/ico/ico_pin.svg new file mode 100644 index 0000000..bd401d5 --- /dev/null +++ b/src/img/ico/ico_pin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_remove.svg b/src/img/ico/ico_remove.svg new file mode 100644 index 0000000..25f18f6 --- /dev/null +++ b/src/img/ico/ico_remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_search.svg b/src/img/ico/ico_search.svg new file mode 100644 index 0000000..e256800 --- /dev/null +++ b/src/img/ico/ico_search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_setting.svg b/src/img/ico/ico_setting.svg new file mode 100644 index 0000000..6a14165 --- /dev/null +++ b/src/img/ico/ico_setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_trash.svg b/src/img/ico/ico_trash.svg new file mode 100644 index 0000000..d87313d --- /dev/null +++ b/src/img/ico/ico_trash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_trend.svg b/src/img/ico/ico_trend.svg new file mode 100644 index 0000000..4ee61ab --- /dev/null +++ b/src/img/ico/ico_trend.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_user.svg b/src/img/ico/ico_user.svg new file mode 100644 index 0000000..f906c9c --- /dev/null +++ b/src/img/ico/ico_user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/ico_watch.svg b/src/img/ico/ico_watch.svg new file mode 100644 index 0000000..52a71e2 --- /dev/null +++ b/src/img/ico/ico_watch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/icon-rocket.svg b/src/img/ico/icon-rocket.svg new file mode 100644 index 0000000..070b802 --- /dev/null +++ b/src/img/ico/icon-rocket.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/ico/icon-snail.svg b/src/img/ico/icon-snail.svg new file mode 100644 index 0000000..f1e40fd --- /dev/null +++ b/src/img/ico/icon-snail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/insight/crown.svg b/src/img/insight/crown.svg new file mode 100644 index 0000000..7b67936 --- /dev/null +++ b/src/img/insight/crown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/insight/img_banner_02.png b/src/img/insight/img_banner_02.png new file mode 100644 index 0000000..ee7cae4 Binary files /dev/null and b/src/img/insight/img_banner_02.png differ diff --git a/src/img/insight/img_banner_03.png b/src/img/insight/img_banner_03.png new file mode 100644 index 0000000..c469f66 Binary files /dev/null and b/src/img/insight/img_banner_03.png differ diff --git a/src/img/insight/profile.png b/src/img/insight/profile.png new file mode 100644 index 0000000..72b2665 Binary files /dev/null and b/src/img/insight/profile.png differ diff --git a/src/img/intro/promise.svg b/src/img/intro/promise.svg new file mode 100644 index 0000000..4927873 --- /dev/null +++ b/src/img/intro/promise.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/intro/promise_b.svg b/src/img/intro/promise_b.svg new file mode 100644 index 0000000..1d6dd5c --- /dev/null +++ b/src/img/intro/promise_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/Chapter.svg b/src/img/learning/Chapter.svg new file mode 100644 index 0000000..55b0829 --- /dev/null +++ b/src/img/learning/Chapter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/bg _chapter_completed.svg b/src/img/learning/bg _chapter_completed.svg new file mode 100644 index 0000000..77a38f7 --- /dev/null +++ b/src/img/learning/bg _chapter_completed.svg @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/img/learning/bg_chapter_base.png b/src/img/learning/bg_chapter_base.png new file mode 100644 index 0000000..0aaa101 Binary files /dev/null and b/src/img/learning/bg_chapter_base.png differ diff --git a/src/img/learning/bg_chapter_base.svg b/src/img/learning/bg_chapter_base.svg new file mode 100644 index 0000000..e0728d8 --- /dev/null +++ b/src/img/learning/bg_chapter_base.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/bg_chapter_completed.png b/src/img/learning/bg_chapter_completed.png new file mode 100644 index 0000000..5bcd975 Binary files /dev/null and b/src/img/learning/bg_chapter_completed.png differ diff --git a/src/img/learning/bg_chapter_current.png b/src/img/learning/bg_chapter_current.png new file mode 100644 index 0000000..03b0721 Binary files /dev/null and b/src/img/learning/bg_chapter_current.png differ diff --git a/src/img/learning/bg_chapter_current.svg b/src/img/learning/bg_chapter_current.svg new file mode 100644 index 0000000..52c685d --- /dev/null +++ b/src/img/learning/bg_chapter_current.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/bg_cloud.png b/src/img/learning/bg_cloud.png new file mode 100644 index 0000000..675b09d Binary files /dev/null and b/src/img/learning/bg_cloud.png differ diff --git a/src/img/learning/bg_grid.png b/src/img/learning/bg_grid.png new file mode 100644 index 0000000..cfe3e34 Binary files /dev/null and b/src/img/learning/bg_grid.png differ diff --git a/src/img/learning/bg_grid.svg b/src/img/learning/bg_grid.svg new file mode 100644 index 0000000..d77dd4c --- /dev/null +++ b/src/img/learning/bg_grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/bg_learning-gauge.svg b/src/img/learning/bg_learning-gauge.svg new file mode 100644 index 0000000..1148997 --- /dev/null +++ b/src/img/learning/bg_learning-gauge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/bg_line.svg b/src/img/learning/bg_line.svg new file mode 100644 index 0000000..f43fe44 --- /dev/null +++ b/src/img/learning/bg_line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/bg_temp.svg b/src/img/learning/bg_temp.svg new file mode 100644 index 0000000..370186e --- /dev/null +++ b/src/img/learning/bg_temp.svg @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/img/learning/btn_play_base.png b/src/img/learning/btn_play_base.png new file mode 100644 index 0000000..b49c62a Binary files /dev/null and b/src/img/learning/btn_play_base.png differ diff --git a/src/img/learning/btn_play_completed.png b/src/img/learning/btn_play_completed.png new file mode 100644 index 0000000..082cf66 Binary files /dev/null and b/src/img/learning/btn_play_completed.png differ diff --git a/src/img/learning/btn_play_current.png b/src/img/learning/btn_play_current.png new file mode 100644 index 0000000..30b65fd Binary files /dev/null and b/src/img/learning/btn_play_current.png differ diff --git a/src/img/learning/completion-bg.svg b/src/img/learning/completion-bg.svg new file mode 100644 index 0000000..ea3e306 --- /dev/null +++ b/src/img/learning/completion-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/completion-bg_bak.svg b/src/img/learning/completion-bg_bak.svg new file mode 100644 index 0000000..ebd52d4 --- /dev/null +++ b/src/img/learning/completion-bg_bak.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_learning_01.jpg b/src/img/learning/img_learning_01.jpg new file mode 100644 index 0000000..7db42b8 Binary files /dev/null and b/src/img/learning/img_learning_01.jpg differ diff --git a/src/img/learning/img_learning_02.jpg b/src/img/learning/img_learning_02.jpg new file mode 100644 index 0000000..e10814c Binary files /dev/null and b/src/img/learning/img_learning_02.jpg differ diff --git a/src/img/learning/img_learning_03.jpg b/src/img/learning/img_learning_03.jpg new file mode 100644 index 0000000..8114764 Binary files /dev/null and b/src/img/learning/img_learning_03.jpg differ diff --git a/src/img/learning/img_learning_04.jpg b/src/img/learning/img_learning_04.jpg new file mode 100644 index 0000000..14f0423 Binary files /dev/null and b/src/img/learning/img_learning_04.jpg differ diff --git a/src/img/learning/img_learning_05.jpg b/src/img/learning/img_learning_05.jpg new file mode 100644 index 0000000..f2d0e21 Binary files /dev/null and b/src/img/learning/img_learning_05.jpg differ diff --git a/src/img/learning/img_stamp.svg b/src/img/learning/img_stamp.svg new file mode 100644 index 0000000..1a99966 --- /dev/null +++ b/src/img/learning/img_stamp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_start_off.svg b/src/img/learning/img_start_off.svg new file mode 100644 index 0000000..86090ec --- /dev/null +++ b/src/img/learning/img_start_off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_start_on.svg b/src/img/learning/img_start_on.svg new file mode 100644 index 0000000..e8350a5 --- /dev/null +++ b/src/img/learning/img_start_on.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_state_01.svg b/src/img/learning/img_state_01.svg new file mode 100644 index 0000000..512559a --- /dev/null +++ b/src/img/learning/img_state_01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_state_02.svg b/src/img/learning/img_state_02.svg new file mode 100644 index 0000000..c0fd8d0 --- /dev/null +++ b/src/img/learning/img_state_02.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_state_03.svg b/src/img/learning/img_state_03.svg new file mode 100644 index 0000000..b45ff13 --- /dev/null +++ b/src/img/learning/img_state_03.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/img_trophy_completed.svg b/src/img/learning/img_trophy_completed.svg new file mode 100644 index 0000000..df2f54b --- /dev/null +++ b/src/img/learning/img_trophy_completed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/learning/mark_base.png b/src/img/learning/mark_base.png new file mode 100644 index 0000000..4db56ff Binary files /dev/null and b/src/img/learning/mark_base.png differ diff --git a/src/img/learning/mark_chapter_base.png b/src/img/learning/mark_chapter_base.png new file mode 100644 index 0000000..e6e6054 Binary files /dev/null and b/src/img/learning/mark_chapter_base.png differ diff --git a/src/img/learning/mark_chapter_completed.png b/src/img/learning/mark_chapter_completed.png new file mode 100644 index 0000000..103673b Binary files /dev/null and b/src/img/learning/mark_chapter_completed.png differ diff --git a/src/img/learning/mark_chapter_current.png b/src/img/learning/mark_chapter_current.png new file mode 100644 index 0000000..8a66aec Binary files /dev/null and b/src/img/learning/mark_chapter_current.png differ diff --git a/src/img/learning/mark_completed.png b/src/img/learning/mark_completed.png new file mode 100644 index 0000000..f925db0 Binary files /dev/null and b/src/img/learning/mark_completed.png differ diff --git a/src/img/learning/mark_current.png b/src/img/learning/mark_current.png new file mode 100644 index 0000000..0ffed6d Binary files /dev/null and b/src/img/learning/mark_current.png differ diff --git a/src/img/learning/mark_start.png b/src/img/learning/mark_start.png new file mode 100644 index 0000000..2978713 Binary files /dev/null and b/src/img/learning/mark_start.png differ diff --git a/src/img/logo.svg b/src/img/logo.svg new file mode 100644 index 0000000..b274622 --- /dev/null +++ b/src/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/mask_page_tail.svg b/src/img/mask_page_tail.svg new file mode 100644 index 0000000..a2f4dd2 --- /dev/null +++ b/src/img/mask_page_tail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/myclass/Group 1171286226.png b/src/img/myclass/Group 1171286226.png new file mode 100644 index 0000000..25539bc Binary files /dev/null and b/src/img/myclass/Group 1171286226.png differ diff --git a/src/img/myclass/bg_lo.png b/src/img/myclass/bg_lo.png new file mode 100644 index 0000000..c7967ed Binary files /dev/null and b/src/img/myclass/bg_lo.png differ diff --git a/src/img/myclass/ico_01.png b/src/img/myclass/ico_01.png new file mode 100644 index 0000000..191e4d9 Binary files /dev/null and b/src/img/myclass/ico_01.png differ diff --git a/src/img/onboarding/bg_piece.jpg b/src/img/onboarding/bg_piece.jpg new file mode 100644 index 0000000..0200656 Binary files /dev/null and b/src/img/onboarding/bg_piece.jpg differ diff --git a/src/img/onboarding/bg_piece.png b/src/img/onboarding/bg_piece.png new file mode 100644 index 0000000..3b8afea Binary files /dev/null and b/src/img/onboarding/bg_piece.png differ diff --git a/src/img/onboarding/bg_piece_all_completed.png b/src/img/onboarding/bg_piece_all_completed.png new file mode 100644 index 0000000..e64a831 Binary files /dev/null and b/src/img/onboarding/bg_piece_all_completed.png differ diff --git a/src/img/onboarding/bg_piece_completed.png b/src/img/onboarding/bg_piece_completed.png new file mode 100644 index 0000000..6a27722 Binary files /dev/null and b/src/img/onboarding/bg_piece_completed.png differ diff --git a/src/img/onboarding/bg_piece_finish.png b/src/img/onboarding/bg_piece_finish.png new file mode 100644 index 0000000..5ba074d Binary files /dev/null and b/src/img/onboarding/bg_piece_finish.png differ diff --git a/src/img/onboarding/img_obj_01.png b/src/img/onboarding/img_obj_01.png new file mode 100644 index 0000000..2af3e02 Binary files /dev/null and b/src/img/onboarding/img_obj_01.png differ diff --git a/src/img/onboarding/img_obj_02.png b/src/img/onboarding/img_obj_02.png new file mode 100644 index 0000000..2bc69c3 Binary files /dev/null and b/src/img/onboarding/img_obj_02.png differ diff --git a/src/img/onboarding/img_obj_03.png b/src/img/onboarding/img_obj_03.png new file mode 100644 index 0000000..b4eefc0 Binary files /dev/null and b/src/img/onboarding/img_obj_03.png differ diff --git a/src/img/onboarding/img_obj_04.png b/src/img/onboarding/img_obj_04.png new file mode 100644 index 0000000..89f48a9 Binary files /dev/null and b/src/img/onboarding/img_obj_04.png differ diff --git a/src/img/onboarding/img_ribbon.svg b/src/img/onboarding/img_ribbon.svg new file mode 100644 index 0000000..cfb04a2 --- /dev/null +++ b/src/img/onboarding/img_ribbon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/video/img_essential_thumb_01.png b/src/img/video/img_essential_thumb_01.png new file mode 100644 index 0000000..247768a Binary files /dev/null and b/src/img/video/img_essential_thumb_01.png differ diff --git a/src/img/video/img_essential_thumb_02.png b/src/img/video/img_essential_thumb_02.png new file mode 100644 index 0000000..dd7ea3d Binary files /dev/null and b/src/img/video/img_essential_thumb_02.png differ diff --git a/src/img/video/img_essential_thumb_03.png b/src/img/video/img_essential_thumb_03.png new file mode 100644 index 0000000..be24825 Binary files /dev/null and b/src/img/video/img_essential_thumb_03.png differ diff --git a/src/img/video/img_essential_thumb_04.png b/src/img/video/img_essential_thumb_04.png new file mode 100644 index 0000000..37b5847 Binary files /dev/null and b/src/img/video/img_essential_thumb_04.png differ diff --git a/src/img/video/img_gauge_ticks.svg b/src/img/video/img_gauge_ticks.svg new file mode 100644 index 0000000..14e268a --- /dev/null +++ b/src/img/video/img_gauge_ticks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/video/img_learning_thumb_01.png b/src/img/video/img_learning_thumb_01.png new file mode 100644 index 0000000..fb5f7e8 Binary files /dev/null and b/src/img/video/img_learning_thumb_01.png differ diff --git a/src/img/video/img_learning_thumb_02.png b/src/img/video/img_learning_thumb_02.png new file mode 100644 index 0000000..c1dfd75 Binary files /dev/null and b/src/img/video/img_learning_thumb_02.png differ diff --git a/src/img/video/img_learning_thumb_03.png b/src/img/video/img_learning_thumb_03.png new file mode 100644 index 0000000..b068cc4 Binary files /dev/null and b/src/img/video/img_learning_thumb_03.png differ diff --git a/src/img/video/img_learning_thumb_04.png b/src/img/video/img_learning_thumb_04.png new file mode 100644 index 0000000..d441ca2 Binary files /dev/null and b/src/img/video/img_learning_thumb_04.png differ diff --git a/src/img/video/img_learning_thumb_05.png b/src/img/video/img_learning_thumb_05.png new file mode 100644 index 0000000..e2926f1 Binary files /dev/null and b/src/img/video/img_learning_thumb_05.png differ diff --git a/src/img/video/img_learning_thumb_06.png b/src/img/video/img_learning_thumb_06.png new file mode 100644 index 0000000..5bc3a17 Binary files /dev/null and b/src/img/video/img_learning_thumb_06.png differ diff --git a/src/img/video/img_thumb_01.png b/src/img/video/img_thumb_01.png new file mode 100644 index 0000000..0259e42 Binary files /dev/null and b/src/img/video/img_thumb_01.png differ diff --git a/src/img/video/img_thumb_02.png b/src/img/video/img_thumb_02.png new file mode 100644 index 0000000..85f1f64 Binary files /dev/null and b/src/img/video/img_thumb_02.png differ diff --git a/src/img/video/img_thumb_03.png b/src/img/video/img_thumb_03.png new file mode 100644 index 0000000..2ce35b1 Binary files /dev/null and b/src/img/video/img_thumb_03.png differ diff --git a/src/img/video/img_thumb_04.png b/src/img/video/img_thumb_04.png new file mode 100644 index 0000000..8ce9b0c Binary files /dev/null and b/src/img/video/img_thumb_04.png differ diff --git a/src/img/video/img_thumb_05.png b/src/img/video/img_thumb_05.png new file mode 100644 index 0000000..1030ed4 Binary files /dev/null and b/src/img/video/img_thumb_05.png differ diff --git a/src/img/video/img_thumb_06.png b/src/img/video/img_thumb_06.png new file mode 100644 index 0000000..35ef991 Binary files /dev/null and b/src/img/video/img_thumb_06.png differ diff --git a/src/img/video/img_thumb_07.png b/src/img/video/img_thumb_07.png new file mode 100644 index 0000000..1030ed4 Binary files /dev/null and b/src/img/video/img_thumb_07.png differ diff --git a/src/js/biztrend.js b/src/js/biztrend.js new file mode 100644 index 0000000..ae3062b --- /dev/null +++ b/src/js/biztrend.js @@ -0,0 +1,343 @@ +/** + * 비즈트렌드 달력 페이지 전용 스크립트 + * =================================== + * biztrend.html (성공예감&별책부록)에서 사용합니다. + * - 연·월 선택 날짜피커 (휠 UI) + * - 미래 날짜 카드 투명도 처리 + */ + +(function () { + "use strict"; + + // ------------------------------ + // 설정 상수 + // ------------------------------ + var YEAR_START = 2020; + var YEAR_END = 2030; + var ITEM_HEIGHT = 36; // 휠 한 항목 높이(px) + + // ------------------------------ + // 날짜 값 읽기/쓰기 + // ------------------------------ + + /** + * hidden input에서 현재 선택된 연·월 값을 읽습니다. + * @returns {{ year: number, month: number }} + */ + function getDateValue() { + var input = document.querySelector(".biztrend .date-select-value"); + if (!input) return { year: 2026, month: 2 }; + + var match = (input.value || "").match(/^(\d{4})-(\d{2})$/); + if (match) { + return { + year: parseInt(match[1], 10), + month: parseInt(match[2], 10), + }; + } + + var now = new Date(); + return { + year: now.getFullYear(), + month: now.getMonth() + 1, + }; + } + + /** + * hidden input과 화면 라벨에 연·월 값을 반영합니다. + */ + function setDateValue(year, month) { + var input = document.querySelector(".biztrend .date-select-value"); + var label = document.querySelector(".biztrend .date-select-label"); + + if (input) { + input.value = year + "-" + String(month).padStart(2, "0"); + } + if (label) { + var yearEl = label.querySelector(".date-select-year"); + var monthEl = label.querySelector(".date-select-month"); + if (yearEl) yearEl.textContent = year; + if (monthEl) monthEl.textContent = month; + } + } + + // ------------------------------ + // 미래 날짜 카드 스타일 처리 + // ------------------------------ + + /** + * 선택한 달 기준으로 아직 오지 않은 날짜의 카드에 is-future 클래스를 적용합니다. + */ + function applyFutureDateOpacity() { + var cards = document.querySelectorAll(".biztrend .article-card"); + if (!cards.length) return; + + var selected = getDateValue(); + var today = new Date(); + today.setHours(0, 0, 0, 0); + + cards.forEach(function (card) { + card.classList.remove("is-future"); + + var numEl = card.querySelector(".article-card-num"); + if (!numEl) return; + + var day = parseInt(numEl.textContent.trim(), 10); + if (isNaN(day) || day < 1 || day > 31) return; + + var cardDate = new Date(selected.year, selected.month - 1, day); + cardDate.setHours(0, 0, 0, 0); + + if (cardDate > today) { + card.classList.add("is-future"); + } + }); + } + + /** + * 선택한 달이 현재 달과 같을 때, 오늘 날짜 카드에 today 클래스를 적용합니다. + */ + function applyTodayClass() { + var cards = document.querySelectorAll(".biztrend .article-card"); + if (!cards.length) return; + + var selected = getDateValue(); + var now = new Date(); + var currentYear = now.getFullYear(); + var currentMonth = now.getMonth() + 1; + var currentDay = now.getDate(); + + cards.forEach(function (card) { + card.classList.remove("today"); + + if (selected.year !== currentYear || selected.month !== currentMonth) return; + + var numEl = card.querySelector(".article-card-num"); + if (!numEl) return; + + var day = parseInt(numEl.textContent.trim(), 10); + if (isNaN(day) || day < 1 || day > 31) return; + + if (day === currentDay) { + card.classList.add("today"); + } + }); + } + + // ------------------------------ + // 날짜피커 휠 (연·월) + // ------------------------------ + + /** + * 연도·월 선택용 휠 항목 데이터를 생성합니다. + */ + function buildWheelItems() { + var years = []; + for (var y = YEAR_START; y <= YEAR_END; y++) { + years.push({ value: y, label: y + "년" }); + } + var months = []; + for (var m = 1; m <= 12; m++) { + months.push({ value: m, label: m + "월" }); + } + return { years: years, months: months }; + } + + /** + * 휠에 항목을 렌더링하고 선택값 위치로 스크롤합니다. + */ + function renderWheel(trackEl, items, selectedValue) { + var idx = items.findIndex(function (i) { + return i.value === selectedValue; + }); + if (idx < 0) idx = 0; + + var html = items + .map(function (item, i) { + var isSelected = i === idx ? " is-selected" : ""; + return ( + '
' + + item.label + + "
" + ); + }) + .join(""); + + trackEl.innerHTML = html; + trackEl.scrollTop = idx * ITEM_HEIGHT; + } + + /** + * 휠 스크롤 위치에서 현재 선택된 값을 반환합니다. + */ + function getSelectedFromWheel(trackEl) { + var idx = Math.round(trackEl.scrollTop / ITEM_HEIGHT); + var items = trackEl.querySelectorAll(".datepicker-wheel-item"); + idx = Math.max(0, Math.min(idx, items.length - 1)); + var item = items[idx]; + return item ? parseInt(item.dataset.value, 10) : null; + } + + /** + * 특정 값으로 휠을 스크롤하고 is-selected 클래스를 갱신합니다. + */ + function scrollToSelected(trackEl, value) { + var item = trackEl.querySelector( + '.datepicker-wheel-item[data-value="' + value + '"]' + ); + if (!item) return; + + var allItems = trackEl.querySelectorAll(".datepicker-wheel-item"); + var idx = Array.from(allItems).indexOf(item); + if (idx < 0) return; + + trackEl.scrollTop = idx * ITEM_HEIGHT; + allItems.forEach(function (el) { + el.classList.toggle( + "is-selected", + parseInt(el.dataset.value, 10) === value + ); + }); + } + + // ------------------------------ + // 날짜피커 드롭다운 초기화 + // ------------------------------ + + /** + * 연·월 선택 버튼과 드롭다운을 연결하고 이벤트를 등록합니다. + */ + function initDatepickerDropdown() { + var trigger = document.getElementById("datepicker-trigger"); + var dropdown = document.getElementById("datepicker-dropdown"); + if (!trigger || !dropdown) return; + + var yearTrack = dropdown.querySelector('[data-wheel="year"]'); + var monthTrack = dropdown.querySelector('[data-wheel="month"]'); + var items = buildWheelItems(); + + function openDropdown() { + var v = getDateValue(); + renderWheel(yearTrack, items.years, v.year); + renderWheel(monthTrack, items.months, v.month); + + dropdown.classList.add("is-open"); + dropdown.setAttribute("aria-hidden", "false"); + trigger.setAttribute("aria-expanded", "true"); + + document.addEventListener("keydown", onEscape); + setTimeout(function () { + document.addEventListener("click", onDocumentClick); + }, 0); + } + + function closeDropdown() { + dropdown.classList.remove("is-open"); + dropdown.setAttribute("aria-hidden", "true"); + trigger.setAttribute("aria-expanded", "false"); + + document.removeEventListener("keydown", onEscape); + document.removeEventListener("click", onDocumentClick); + } + + function onEscape(e) { + if (e.key === "Escape") closeDropdown(); + } + + function onDocumentClick(e) { + if (!dropdown.contains(e.target) && e.target !== trigger) { + closeDropdown(); + } + } + + function onConfirm() { + var year = getSelectedFromWheel(yearTrack); + var month = getSelectedFromWheel(monthTrack); + if (year != null && month != null) { + setDateValue(year, month); + applyFutureDateOpacity(); + applyTodayClass(); + } + closeDropdown(); + } + + // 버튼 클릭: 열기/닫기 토글 + trigger.addEventListener("click", function (e) { + e.stopPropagation(); + if (dropdown.classList.contains("is-open")) { + closeDropdown(); + } else { + openDropdown(); + } + }); + + dropdown + .querySelector(".btn-datepicker-cancel") + .addEventListener("click", function (e) { + e.stopPropagation(); + closeDropdown(); + }); + + dropdown + .querySelector(".btn-datepicker-confirm") + .addEventListener("click", function (e) { + e.stopPropagation(); + onConfirm(); + }); + + // 휠 스크롤 시 선택 항목 표시 갱신 + function updateSelectedClass(trackEl) { + var val = getSelectedFromWheel(trackEl); + if (val != null) { + trackEl.querySelectorAll(".datepicker-wheel-item").forEach(function (el) { + el.classList.toggle( + "is-selected", + parseInt(el.dataset.value, 10) === val + ); + }); + } + } + yearTrack.addEventListener("scroll", function () { + updateSelectedClass(yearTrack); + }); + monthTrack.addEventListener("scroll", function () { + updateSelectedClass(monthTrack); + }); + + // 휠 항목 클릭 시 해당 위치로 스크롤 + yearTrack.addEventListener("click", function (e) { + var item = e.target.closest(".datepicker-wheel-item"); + if (item) { + scrollToSelected(yearTrack, parseInt(item.dataset.value, 10)); + } + }); + monthTrack.addEventListener("click", function (e) { + var item = e.target.closest(".datepicker-wheel-item"); + if (item) { + scrollToSelected(monthTrack, parseInt(item.dataset.value, 10)); + } + }); + } + + // ------------------------------ + // 페이지 초기화 + // ------------------------------ + + function initBiztrendCalendar() { + if (!document.querySelector(".wrap.biztrend")) return; + + applyFutureDateOpacity(); + applyTodayClass(); + initDatepickerDropdown(); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initBiztrendCalendar); + } else { + initBiztrendCalendar(); + } +})(); diff --git a/src/js/common.js b/src/js/common.js new file mode 100644 index 0000000..a7e67b6 --- /dev/null +++ b/src/js/common.js @@ -0,0 +1,757 @@ +/** + * 공통 스크립트 (common.js) + * ======================================== + * 모든 페이지에서 공통으로 사용하는 기능을 모아둔 진입점입니다. + * + * [주요 역할] + * - ScrollManager: 모바일 뷰포트 높이 동기화, 스크롤 잠금(모달 열 때) + * - ModalManager: 모달 열기/닫기 (애니메이션, 비디오 정지) + * - DeviceUtils: 모바일/태블릿/데스크톱 감지 + * - NavigationManager: 현재 페이지에 맞는 네비게이션 active 클래스 + * + * [기존 코드 호환 함수] - 레거시 코드에서 그대로 사용 가능 + * - popOpen(id): 모달 열기 (예: popOpen('modal-video')) + * - popClose(element): 모달 닫기 (닫기 버튼에 사용) + * - syncHeight(), bodyLock(), bodyUnlock(), isMobile() + * + * @module CommonUtils + */ + +/** + * 스크롤 및 레이아웃 관리 클래스 + * - syncHeight(): CSS 변수 --window-inner-height 설정 (모바일 주소창 대응) + * - lock()/unlock(): 모달 열 때 body 스크롤 잠금/해제 + */ +class ScrollManager { + constructor() { + this.scrollY = 0; + this.wrap = null; + this.isLocked = false; + } + + /** + * 스크린 높이 계산 및 CSS 변수 설정 + */ + syncHeight() { + try { + document.documentElement.style.setProperty( + "--window-inner-height", + `${window.innerHeight}px` + ); + } catch (error) { + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { context: 'ScrollManager.syncHeight' }); + } else { + console.error('[ScrollManager] syncHeight error:', error); + } + } + } + + /** + * body 스크롤 잠금 + */ + lock() { + if (this.isLocked) return; + + this.scrollY = window.scrollY; + document.documentElement.classList.add("is-locked"); + document.documentElement.style.scrollBehavior = "auto"; + + if (this.wrap) { + this.wrap.style.top = `-${this.scrollY}px`; + } + + this.isLocked = true; + } + + /** + * body 스크롤 잠금 해제 + */ + unlock() { + if (!this.isLocked) return; + + document.documentElement.classList.remove("is-locked"); + window.scrollTo(0, this.scrollY); + + if (this.wrap) { + this.wrap.style.top = ""; + } + + document.documentElement.style.scrollBehavior = ""; + this.isLocked = false; + } + + /** + * 초기화 + */ + init() { + this.wrap = typeof DOMUtils !== 'undefined' ? DOMUtils.$(".wrap") : document.querySelector(".wrap"); + + // 즉시 높이 설정 + this.syncHeight(); + + // 리사이즈 이벤트 (쓰로틀 적용) + const throttledSyncHeight = typeof Utils !== 'undefined' && Utils.throttle + ? Utils.throttle(() => this.syncHeight(), 100) + : (() => { + let resizeTimer; + return () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => this.syncHeight(), 100); + }; + })(); + + if (typeof eventManager !== 'undefined') { + eventManager.on(window, "resize", throttledSyncHeight); + eventManager.on(window, "orientationchange", () => { + setTimeout(() => this.syncHeight(), 100); + }); + } else { + window.addEventListener("resize", throttledSyncHeight); + window.addEventListener("orientationchange", () => { + setTimeout(() => this.syncHeight(), 100); + }); + } + } +} + +/** + * 모바일 감지 유틸리티 + */ +class DeviceUtils { + /** + * 모바일 기기 여부 확인 + * @param {number} breakpoint - 브레이크포인트 (기본값: 1025) + * @returns {boolean} + */ + static isMobile(breakpoint = 1025) { + return window.innerWidth < breakpoint; + } + + /** + * 태블릿 기기 여부 확인 + * @param {number} minWidth - 최소 너비 + * @param {number} maxWidth - 최대 너비 + * @returns {boolean} + */ + static isTablet(minWidth = 768, maxWidth = 1024) { + const width = window.innerWidth; + return width >= minWidth && width <= maxWidth; + } + + /** + * 데스크톱 기기 여부 확인 + * @param {number} breakpoint - 브레이크포인트 + * @returns {boolean} + */ + static isDesktop(breakpoint = 1025) { + return window.innerWidth >= breakpoint; + } +} + +/** + * 모달/팝업 관리 클래스 + */ +class ModalManager { + constructor(scrollManager) { + this.scrollManager = scrollManager; + this.openModals = new Set(); + } + + /** + * 모달 열기 + * @param {string|Element} target - 모달 ID 또는 요소 + * @param {Object} options - 옵션 + * @returns {Promise} + */ + async open(target, options = {}) { + const { + duration = 300, + lockScroll = true, + stopVideo = true, + } = options; + + try { + const element = typeof target === 'string' + ? (typeof DOMUtils !== 'undefined' ? DOMUtils.$(`#${target}`) : document.getElementById(target)) + : target; + + if (!element) { + console.warn('[ModalManager] Element not found:', target); + return; + } + + if (typeof DOMUtils !== 'undefined') { + await DOMUtils.fadeIn(element, duration); + } else if (typeof AnimationUtils !== 'undefined') { + await AnimationUtils.fade(element, 'in', duration); + } else { + element.style.display = 'block'; + element.style.opacity = '1'; + } + + if (lockScroll && this.scrollManager) { + this.scrollManager.lock(); + } + + this.openModals.add(element); + + return element; + } catch (error) { + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { context: 'ModalManager.open', target }); + } else { + console.error('[ModalManager] open error:', error); + } + } + } + + /** + * 모달 닫기 + * @param {string|Element} target - 모달 ID 또는 요소 + * @param {Object} options - 옵션 + * @returns {Promise} + */ + async close(target, options = {}) { + const { + duration = 300, + unlockScroll = true, + stopVideo = true, + } = options; + + try { + const element = typeof target === 'string' + ? (typeof DOMUtils !== 'undefined' ? DOMUtils.$(`#${target}`) : document.getElementById(target)) + : target; + + if (!element) return; + + if (typeof DOMUtils !== 'undefined') { + await DOMUtils.fadeOut(element, duration); + } else if (typeof AnimationUtils !== 'undefined') { + await AnimationUtils.fade(element, 'out', duration); + } else { + element.style.display = 'none'; + element.style.opacity = ''; + } + + // 비디오 정지 + if (stopVideo) { + const video = element.querySelector("video"); + if (video) video.pause(); + } + + if (unlockScroll && this.scrollManager && this.openModals.size <= 1) { + this.scrollManager.unlock(); + } + + this.openModals.delete(element); + + return element; + } catch (error) { + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { context: 'ModalManager.close', target }); + } else { + console.error('[ModalManager] close error:', error); + } + } + } + + /** + * 모든 모달 닫기 + */ + async closeAll() { + const promises = Array.from(this.openModals).map(modal => this.close(modal)); + await Promise.all(promises); + this.openModals.clear(); + } +} + +// 전역 인스턴스 생성 +const scrollManager = new ScrollManager(); +const modalManager = new ModalManager(scrollManager); + +// ---------------------------------------- +// 기존 함수 호환성 래퍼 (레거시 코드용) +// ---------------------------------------- +// 아래 함수들은 기존 코드에서 호출하는 이름입니다. +// 새로 작성 시에는 scrollManager, modalManager를 직접 사용하는 것을 권장합니다. +let scrollY = 0; +let wrap = null; + +/** 뷰포트 높이 동기화 (리사이즈 시 호출) */ +function syncHeight() { + scrollManager.syncHeight(); +} + +/** 모바일 기기 여부 (breakpoint 1025px) */ +function isMobile() { + return DeviceUtils.isMobile(); +} + +/** body 스크롤 잠금 (모달 열 때) */ +function bodyLock() { + scrollManager.lock(); +} + +/** body 스크롤 해제 (모달 닫을 때) */ +function bodyUnlock() { + scrollManager.unlock(); +} + +/** + * 모달 열기 - id가 "open-modal-video" 인 버튼 클릭 시 모달 "modal-video" 가 열림 + * @param {string} id - 모달 요소의 id (앞에 # 없이) + */ +async function popOpen(id) { + return await modalManager.open(id); +} + +/** + * 모달 닫기 - 닫기 버튼(.close) 또는 백드롭 클릭 시 호출 + * @param {Element} obj - 클릭된 요소 (보통 this 또는 event.target) + */ +async function popClose(obj) { + const popup = obj.closest ? obj.closest(".popup") : null; + if (popup) { + return await modalManager.close(popup); + } +} + +/** + * 공통 이벤트 초기화 + * - ScrollManager: 뷰포트 높이, 리사이즈 대응 + * - 모달: id가 "open-modal-XXX"인 요소 클릭 시 #modal-XXX 열기 + * - 모달 닫기: .close 클릭 또는 모달 바깥(.modal) 클릭 + */ +function initCommonEvents() { + const baseHref = window.location.href.split("#")[0]; + + // ScrollManager 초기화 + scrollManager.init(); + wrap = scrollManager.wrap; + + // 모달 열기 이벤트 (이벤트 위임) + const openModalHandler = function(e) { + const modalId = this.id.replace("open-", ""); + modalManager.open(modalId); + }; + + // 모달 닫기 이벤트 + const closeModalHandler = async function(e) { + const modal = this.closest(".modal"); + if (modal) { + await modalManager.close(modal); + } + }; + + // 모달 바깥 클릭 시 닫기 + const modalBackdropHandler = async function(e) { + const modalContent = e.target.closest(".modal-content"); + if (!modalContent && e.target === this) { + await modalManager.close(this); + } + }; + + // EventManager 사용 (있는 경우) + if (typeof eventManager !== 'undefined') { + eventManager.delegate(document, "click", "[id^=open-modal]", openModalHandler); + eventManager.delegate(document, "click", ".close", closeModalHandler); + eventManager.delegate(document, "click", ".modal", modalBackdropHandler); + } else if (typeof DOMUtils !== 'undefined' && DOMUtils.delegate) { + DOMUtils.delegate(document, "click", "[id^=open-modal]", openModalHandler); + DOMUtils.delegate(document, "click", ".close", closeModalHandler); + DOMUtils.delegate(document, "click", ".modal", modalBackdropHandler); + } else { + // 폴백: 직접 이벤트 리스너 등록 + document.addEventListener("click", (e) => { + const target = e.target.closest("[id^=open-modal]"); + if (target) { + openModalHandler.call(target, e); + } + + const closeBtn = e.target.closest(".close"); + if (closeBtn) { + closeModalHandler.call(closeBtn, e); + } + + const modal = e.target.closest(".modal"); + if (modal && e.target === modal) { + modalBackdropHandler.call(modal, e); + } + }); + } +} + +// DOMContentLoaded 시 초기화 +if (document.readyState === 'loading') { + document.addEventListener("DOMContentLoaded", initCommonEvents); +} else { + initCommonEvents(); +} + +// 리사이즈 이벤트는 ScrollManager.init()에서 처리됨 + +/** + * 컨테이너 스크롤 효과 클래스 + */ +class ContainerScrollEffect { + constructor(container, options = {}) { + this.container = container; + this.options = { + borderRadius: 30, + scrollThreshold: 100, + excludeClass: 'search-result', + ...options, + }; + this.isActive = false; + } + + /** + * 효과 초기화 + */ + init() { + if (!this.container) return; + + // 검색 결과 페이지에서는 이 효과를 적용하지 않음 + const wrap = this.container.closest(".wrap"); + if (wrap && wrap.classList.contains(this.options.excludeClass)) { + return; + } + + const throttledScroll = typeof Utils !== 'undefined' && Utils.throttle + ? Utils.throttle(() => this._handleScroll(), 16) + : (() => { + let lastTime = 0; + return () => { + const now = performance.now(); + if (now - lastTime >= 16) { + this._handleScroll(); + lastTime = now; + } + }; + })(); + + if (typeof eventManager !== 'undefined') { + eventManager.on(this.container, "scroll", throttledScroll); + } else { + this.container.addEventListener("scroll", throttledScroll); + } + + this.isActive = true; + } + + /** + * 스크롤 핸들러 + * @private + */ + _handleScroll() { + const scrollTop = this.container.scrollTop; + const progress = Math.min(scrollTop / this.options.scrollThreshold, 1); + const currentRadius = this.options.borderRadius * (1 - progress); + + this.container.style.clipPath = `inset(0 0 0 0 round ${currentRadius}px ${currentRadius}px 0 0)`; + } + + /** + * 효과 제거 + */ + destroy() { + if (this.container && this.isActive) { + this.container.style.clipPath = ''; + this.isActive = false; + } + } +} + +// 기존 함수 호환성 +function initContainerScrollEffect() { + const container = typeof DOMUtils !== 'undefined' + ? DOMUtils.$(".container") + : document.querySelector(".container"); + + if (container) { + const effect = new ContainerScrollEffect(container); + effect.init(); + } +} + +/** + * 컨테이너 상단 라운드 모서리 유지 + * - .container에 clip-path를 적용해 상단 30px 라운드를 고정 유지 + * - 스크롤, 스타일 변경 등으로 덮어써져도 복원 + */ +function initContainerRoundCorners() { + // 인트로 페이지에서는 clip-path 적용 안 함 + if (document.querySelector(".wrap.intro")) return; + + const container = document.querySelector(".container"); + if (!container) return; + + const targetClipPath = "inset(0 0 0 0 round 30px 30px 0 0)"; + container.style.clipPath = targetClipPath; + + function maintainRoundCorners() { + const currentClipPath = container.style.clipPath || ""; + if (currentClipPath !== targetClipPath && !currentClipPath.includes("30px")) { + container.style.clipPath = targetClipPath; + } + requestAnimationFrame(maintainRoundCorners); + } + + container.addEventListener( + "scroll", + function () { + this.style.clipPath = targetClipPath; + }, + { passive: true, capture: true } + ); + + const observer = new MutationObserver(function () { + if (container.style.clipPath !== targetClipPath) { + container.style.clipPath = targetClipPath; + } + }); + observer.observe(container, { + attributes: true, + attributeFilter: ["style"], + attributeOldValue: true, + }); + + maintainRoundCorners(); +} + +// DOMContentLoaded 시 초기화 +document.addEventListener("DOMContentLoaded", () => { + initContainerRoundCorners(); + + // 현재 페이지에 따라 네비게이션 active 클래스 추가 + setActiveNavigation(); + + // 마이페이지 링크 active 클래스 추가 + setActiveMypageLink(); +}); + +/** + * 네비게이션 관리 클래스 + */ +class NavigationManager { + constructor(options = {}) { + this.options = { + selector: '.nav-group .depth01 > li', + activeClass: 'active', + ...options, + }; + } + + /** + * 현재 페이지에 따라 네비게이션 active 클래스 설정 + */ + setActiveNavigation() { + try { + const currentPath = window.location.pathname; + const navItems = typeof DOMUtils !== 'undefined' + ? DOMUtils.$$(this.options.selector) + : document.querySelectorAll(this.options.selector); + + navItems.forEach((li) => { + const link = typeof DOMUtils !== 'undefined' + ? DOMUtils.$('a', li) + : li.querySelector('a'); + + if (!link) return; + + const href = link.getAttribute('href'); + if (!href) return; + + // 현재 경로와 링크의 href를 비교 + const isActive = this._isActiveLink(currentPath, href); + + if (typeof DOMUtils !== 'undefined') { + if (isActive) { + DOMUtils.addClasses(li, this.options.activeClass); + } else { + DOMUtils.removeClasses(li, this.options.activeClass); + } + } else { + if (isActive) { + li.classList.add(this.options.activeClass); + } else { + li.classList.remove(this.options.activeClass); + } + } + }); + } catch (error) { + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { context: 'NavigationManager.setActiveNavigation' }); + } else { + console.error('[NavigationManager] setActiveNavigation error:', error); + } + } + } + + /** + * 링크가 활성화되어야 하는지 확인 + * @private + */ + _isActiveLink(currentPath, href) { + // onboarding.html이 포함되어 있으면 active 클래스 추가 + if (currentPath.includes('onboarding') && href.includes('onboarding')) { + return true; + } + if (currentPath.includes('learning') && href.includes('learning')) { + return true; + } + if (currentPath.includes('insight') && href.includes('insight')) { + return true; + } + if (currentPath.includes('biztrend') && href.includes('biztrend')) { + return true; + } + if (currentPath.includes('mypage') && href.includes('mypage')) { + return true; + } + // 추가적인 매칭 로직을 여기에 구현할 수 있습니다 + return false; + } +} + +// 전역 인스턴스 +const navigationManager = new NavigationManager(); + +// 기존 함수 호환성 +function setActiveNavigation() { + navigationManager.setActiveNavigation(); +} + +/** + * 마이페이지 링크(.item-box .mypage)에 현재 페이지가 mypage일 때 active 클래스 추가 + */ +function setActiveMypageLink() { + try { + const mypageLink = document.querySelector('.item-box a.mypage'); + if (!mypageLink) return; + + const currentPath = window.location.pathname; + const href = mypageLink.getAttribute('href') || ''; + const isActive = currentPath.includes('mypage') && href.includes('mypage'); + + if (isActive) { + mypageLink.classList.add('active'); + } else { + mypageLink.classList.remove('active'); + } + } catch (error) { + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { context: 'setActiveMypageLink' }); + } else { + console.error('[setActiveMypageLink] error:', error); + } + } +} + +/** + * HTML Include 관리 클래스 + */ +class HTMLIncludeManager { + constructor(options = {}) { + this.options = { + selector: "[data-include-path]", + attribute: "data-include-path", + ...options, + }; + } + + /** + * HTML include 실행 + * @returns {Promise} + */ + async include() { + const allElements = typeof DOMUtils !== 'undefined' + ? DOMUtils.$$(this.options.selector) + : document.querySelectorAll(this.options.selector); + + const promises = Array.from(allElements).map(async (el) => { + const includePath = el.dataset.includePath || el.getAttribute(this.options.attribute); + if (!includePath) return; + + try { + const response = await fetch(includePath); + if (!response.ok) { + throw new Error(`Failed to load: ${includePath} (${response.status})`); + } + + const html = await response.text(); + el.innerHTML = html; + el.removeAttribute(this.options.attribute); + + // 포함된 HTML에 대한 이벤트 재초기화 (필요한 경우) + this._reinitializeEvents(el); + } catch (error) { + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { + context: 'HTMLIncludeManager.include', + includePath, + }); + } else { + console.error(`[HTMLIncludeManager] Error loading ${includePath}:`, error); + } + } + }); + + await Promise.all(promises); + } + + /** + * 포함된 HTML의 이벤트 재초기화 + * @private + */ + _reinitializeEvents(element) { + // 포함된 스크립트 실행 (보안 주의) + const scripts = element.querySelectorAll('script'); + scripts.forEach((script) => { + const newScript = document.createElement('script'); + if (script.src) { + newScript.src = script.src; + } else { + newScript.textContent = script.textContent; + } + script.parentNode.replaceChild(newScript, script); + }); + } +} + +// 전역 인스턴스 +const htmlIncludeManager = new HTMLIncludeManager(); + +// 기존 함수 호환성 +async function includehtml() { + return await htmlIncludeManager.include(); +} + +// 전역으로 내보내기 (선택사항) +if (typeof window !== 'undefined') { + window.CommonUtils = { + ScrollManager, + DeviceUtils, + ModalManager, + ContainerScrollEffect, + NavigationManager, + HTMLIncludeManager, + scrollManager, + modalManager, + navigationManager, + htmlIncludeManager, + // 기존 함수들 + syncHeight, + isMobile, + bodyLock, + bodyUnlock, + popOpen, + popClose, + initContainerScrollEffect, + initContainerRoundCorners, + setActiveNavigation, + includehtml, + }; +} diff --git a/src/js/common/AnimationUtils.js b/src/js/common/AnimationUtils.js new file mode 100644 index 0000000..7289358 --- /dev/null +++ b/src/js/common/AnimationUtils.js @@ -0,0 +1,505 @@ +/** + * 애니메이션 유틸리티 모듈 (AnimationUtils.js) + * ======================================== + * 페이드, 슬라이드, 카운트업 등 애니메이션을 제공합니다. + * + * [초보자용] AnimationUtils.fade(el, 'in', 300) / AnimationUtils.fade(el, 'out', 300) + * + * @module AnimationUtils + */ +class AnimationUtils { + /** + * 순차적 요소 애니메이션 + * @param {Array|NodeList} elements - 요소 배열 + * @param {string} className - 추가할 클래스 + * @param {number} delay - 각 요소 간 지연 시간 (ms) + * @param {Function} callback - 각 요소 애니메이션 후 콜백 + */ + static async sequentialAnimate(elements, className = "show", delay = 50, callback = null) { + const items = Array.isArray(elements) ? elements : Array.from(elements); + + for (let i = 0; i < items.length; i++) { + await new Promise((resolve) => { + setTimeout(() => { + items[i].classList.add(className); + if (callback) callback(items[i], i); + resolve(); + }, delay * i); + }); + } + } + + /** + * 요소 페이드 인/아웃 + * @param {Element} element - 대상 요소 + * @param {string} type - "in" 또는 "out" + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async fade(element, type = "in", duration = 300) { + if (!element) return; + + return new Promise((resolve) => { + // 기존 display 값 저장 (grid, flex 등 유지) + const originalDisplay = element.style.display || window.getComputedStyle(element).display; + const isGridOrFlex = originalDisplay === "grid" || originalDisplay === "flex" || + originalDisplay.includes("grid") || originalDisplay.includes("flex"); + + if (type === "in") { + // grid/flex인 경우 display를 설정하지 않음 + if (!isGridOrFlex) { + element.style.display = "block"; + } + element.style.opacity = "0"; + element.style.transition = `opacity ${duration}ms ease`; + + requestAnimationFrame(() => { + element.style.opacity = "1"; + setTimeout(() => { + element.style.transition = ""; + // grid/flex인 경우 display 스타일 제거 + if (isGridOrFlex) { + element.style.display = ""; + } + resolve(); + }, duration); + }); + } else { + element.style.opacity = "1"; + element.style.transition = `opacity ${duration}ms ease`; + + requestAnimationFrame(() => { + element.style.opacity = "0"; + setTimeout(() => { + // grid/flex인 경우 display를 none으로 설정하지 않음 + if (!isGridOrFlex) { + element.style.display = "none"; + } + element.style.transition = ""; + element.style.opacity = ""; // 재오픈 시 opacity 초기화 + resolve(); + }, duration); + }); + } + }); + } + + /** + * 요소 슬라이드 + * @param {Element} element - 대상 요소 + * @param {string} type - "up", "down", "left", "right" + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async slide(element, type = "down", duration = 300) { + if (!element) return; + + return new Promise((resolve) => { + const isShow = type === "down" || type === "right"; + const property = type === "up" || type === "down" ? "height" : "width"; + const overflow = element.style.overflow; + + element.style.overflow = "hidden"; + + if (isShow) { + element.style.display = "block"; + const size = element[property === "height" ? "scrollHeight" : "scrollWidth"]; + element.style[property] = "0"; + element.style.transition = `${property} ${duration}ms ease`; + + requestAnimationFrame(() => { + element.style[property] = `${size}px`; + setTimeout(() => { + element.style[property] = ""; + element.style.overflow = overflow; + element.style.transition = ""; + resolve(); + }, duration); + }); + } else { + const size = element[property === "height" ? "offsetHeight" : "offsetWidth"]; + element.style[property] = `${size}px`; + element.style.transition = `${property} ${duration}ms ease`; + + requestAnimationFrame(() => { + element.style[property] = "0"; + setTimeout(() => { + element.style.display = "none"; + element.style[property] = ""; + element.style.overflow = overflow; + element.style.transition = ""; + resolve(); + }, duration); + }); + } + }); + } + + /** + * 숫자 카운팅 애니메이션 + * @param {Element} element - 대상 요소 + * @param {number} start - 시작 값 + * @param {number} end - 종료 값 + * @param {number} duration - 지속 시간 (ms) + * @param {Function} format - 포맷 함수 + * @returns {Promise} + */ + static async countUp(element, start, end, duration = 1000, format = null) { + if (!element) return; + + return new Promise((resolve) => { + const startTime = performance.now(); + const range = end - start; + + const update = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + + // Ease-out 효과 + const easeProgress = 1 - Math.pow(1 - progress, 3); + const current = start + range * easeProgress; + + element.textContent = format ? format(current) : Math.round(current); + + if (progress < 1) { + requestAnimationFrame(update); + } else { + element.textContent = format ? format(end) : end; + resolve(); + } + }; + + requestAnimationFrame(update); + }); + } + + /** + * 스크롤 애니메이션 + * @param {Element|string} target - 대상 요소 또는 선택자 + * @param {Object} options - 옵션 + * @returns {Promise} + */ + static async scrollTo(target, options = {}) { + const { + duration = 500, + offset = 0, + easing = "ease-in-out", + container = window, + } = options; + + const element = typeof target === "string" ? document.querySelector(target) : target; + + if (!element) return; + + return new Promise((resolve) => { + const targetPosition = + element.getBoundingClientRect().top + + (container === window ? window.pageYOffset : container.scrollTop) + + offset; + + const startPosition = container === window ? window.pageYOffset : container.scrollTop; + const distance = targetPosition - startPosition; + const startTime = performance.now(); + + const easingFunctions = { + linear: (t) => t, + "ease-in": (t) => t * t, + "ease-out": (t) => t * (2 - t), + "ease-in-out": (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t), + }; + + const easingFunc = easingFunctions[easing] || easingFunctions["ease-in-out"]; + + const animation = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easedProgress = easingFunc(progress); + + const position = startPosition + distance * easedProgress; + + if (container === window) { + window.scrollTo(0, position); + } else { + container.scrollTop = position; + } + + if (progress < 1) { + requestAnimationFrame(animation); + } else { + resolve(); + } + }; + + requestAnimationFrame(animation); + }); + } + + /** + * 흔들기 애니메이션 + * @param {Element} element - 대상 요소 + * @param {number} intensity - 강도 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async shake(element, intensity = 5, duration = 500) { + if (!element) return; + + return new Promise((resolve) => { + const startTime = performance.now(); + const originalTransform = element.style.transform; + + const animation = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = elapsed / duration; + + if (progress < 1) { + const x = Math.sin(progress * Math.PI * 4) * intensity * (1 - progress); + element.style.transform = `translateX(${x}px)`; + requestAnimationFrame(animation); + } else { + element.style.transform = originalTransform; + resolve(); + } + }; + + requestAnimationFrame(animation); + }); + } + + /** + * 펄스 애니메이션 + * @param {Element} element - 대상 요소 + * @param {number} scale - 스케일 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async pulse(element, scale = 1.1, duration = 500) { + if (!element) return; + + return new Promise((resolve) => { + const originalTransform = element.style.transform; + element.style.transition = `transform ${duration / 2}ms ease-in-out`; + + element.style.transform = `scale(${scale})`; + + setTimeout(() => { + element.style.transform = originalTransform; + setTimeout(() => { + element.style.transition = ""; + resolve(); + }, duration / 2); + }, duration / 2); + }); + } + + /** + * 바운스 애니메이션 + * @param {Element} element - 대상 요소 + * @param {number} height - 바운스 높이 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async bounce(element, height = 20, duration = 600) { + if (!element) return; + + return new Promise((resolve) => { + const startTime = performance.now(); + const originalTransform = element.style.transform; + + const animation = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = elapsed / duration; + + if (progress < 1) { + const bounceProgress = Math.sin(progress * Math.PI); + const y = -height * bounceProgress; + element.style.transform = `translateY(${y}px)`; + requestAnimationFrame(animation); + } else { + element.style.transform = originalTransform; + resolve(); + } + }; + + requestAnimationFrame(animation); + }); + } + + /** + * 회전 애니메이션 + * @param {Element} element - 대상 요소 + * @param {number} degrees - 회전 각도 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async rotate(element, degrees = 360, duration = 500) { + if (!element) return; + + return new Promise((resolve) => { + element.style.transition = `transform ${duration}ms ease`; + element.style.transform = `rotate(${degrees}deg)`; + + setTimeout(() => { + element.style.transition = ""; + resolve(); + }, duration); + }); + } + + /** + * 타이핑 효과 + * @param {Element} element - 대상 요소 + * @param {string} text - 타이핑할 텍스트 + * @param {number} speed - 타이핑 속도 (ms) + * @returns {Promise} + */ + static async typing(element, text, speed = 50) { + if (!element) return; + + return new Promise((resolve) => { + let index = 0; + element.textContent = ""; + + const type = () => { + if (index < text.length) { + element.textContent += text.charAt(index); + index++; + setTimeout(type, speed); + } else { + resolve(); + } + }; + + type(); + }); + } + + /** + * 프로그레스 바 애니메이션 + * @param {Element} element - 대상 요소 + * @param {number} percent - 진행률 (0-100) + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static async progressBar(element, percent, duration = 500) { + if (!element) return; + + return new Promise((resolve) => { + element.style.transition = `width ${duration}ms ease-out`; + element.style.width = `${percent}%`; + + setTimeout(() => { + element.style.transition = ""; + resolve(); + }, duration); + }); + } + + /** + * 파티클 효과 + * @param {Element} container - 컨테이너 요소 + * @param {Object} options - 옵션 + */ + static particles(container, options = {}) { + const { + count = 30, + color = "#4CAF50", + size = 5, + duration = 2000, + spread = 100, + } = options; + + const rect = container.getBoundingClientRect(); + const centerX = rect.width / 2; + const centerY = rect.height / 2; + + for (let i = 0; i < count; i++) { + const particle = document.createElement("div"); + particle.style.cssText = ` + position: absolute; + width: ${size}px; + height: ${size}px; + background: ${color}; + border-radius: 50%; + left: ${centerX}px; + top: ${centerY}px; + pointer-events: none; + `; + + container.appendChild(particle); + + const angle = (Math.PI * 2 * i) / count; + const velocity = spread * (0.5 + Math.random() * 0.5); + const x = Math.cos(angle) * velocity; + const y = Math.sin(angle) * velocity; + + particle.animate( + [ + { transform: "translate(0, 0) scale(1)", opacity: 1 }, + { transform: `translate(${x}px, ${y}px) scale(0)`, opacity: 0 }, + ], + { + duration: duration, + easing: "cubic-bezier(0, 0.5, 0.5, 1)", + } + ).onfinish = () => { + particle.remove(); + }; + } + } + + /** + * 리플 효과 + * @param {Element} element - 대상 요소 + * @param {Event} event - 클릭 이벤트 + * @param {Object} options - 옵션 + */ + static ripple(element, event, options = {}) { + const { color = "rgba(255, 255, 255, 0.6)", duration = 600 } = options; + + const rect = element.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const x = event.clientX - rect.left - size / 2; + const y = event.clientY - rect.top - size / 2; + + const ripple = document.createElement("span"); + ripple.style.cssText = ` + position: absolute; + width: ${size}px; + height: ${size}px; + border-radius: 50%; + background: ${color}; + left: ${x}px; + top: ${y}px; + transform: scale(0); + pointer-events: none; + `; + + // 상대 위치 설정 + const position = window.getComputedStyle(element).position; + if (position !== "relative" && position !== "absolute") { + element.style.position = "relative"; + } + + element.style.overflow = "hidden"; + element.appendChild(ripple); + + ripple.animate( + [ + { transform: "scale(0)", opacity: 1 }, + { transform: "scale(2)", opacity: 0 }, + ], + { + duration: duration, + easing: "ease-out", + } + ).onfinish = () => { + ripple.remove(); + }; + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = AnimationUtils; +} diff --git a/src/js/common/ConfigManager.js b/src/js/common/ConfigManager.js new file mode 100644 index 0000000..82201e8 --- /dev/null +++ b/src/js/common/ConfigManager.js @@ -0,0 +1,414 @@ +/** + * 설정 관리 모듈 + * @module ConfigManager + */ +class ConfigManager { + constructor(defaults = {}) { + this.defaults = defaults; + this.config = { ...defaults }; + } + + /** + * 설정 값 가져오기 + * @param {string} key - 키 (점 표기법 지원) + * @param {*} defaultValue - 기본값 + * @returns {*} + */ + get(key, defaultValue = null) { + return this._getNestedValue(this.config, key, defaultValue); + } + + /** + * 설정 값 설정 + * @param {string} key - 키 (점 표기법 지원) + * @param {*} value - 값 + */ + set(key, value) { + this._setNestedValue(this.config, key, value); + } + + /** + * 여러 설정 값 설정 + * @param {Object} config - 설정 객체 + */ + setMultiple(config) { + this.config = this._deepMerge(this.config, config); + } + + /** + * 설정 값 삭제 + * @param {string} key - 키 + */ + remove(key) { + this._deleteNestedValue(this.config, key); + } + + /** + * 설정 값 존재 확인 + * @param {string} key - 키 + * @returns {boolean} + */ + has(key) { + return this._getNestedValue(this.config, key) !== undefined; + } + + /** + * 모든 설정 가져오기 + * @returns {Object} + */ + getAll() { + return { ...this.config }; + } + + /** + * 설정 초기화 + */ + reset() { + this.config = { ...this.defaults }; + } + + /** + * 기본값으로 병합 + * @param {Object} config - 설정 객체 + * @returns {Object} + */ + mergeWithDefaults(config) { + return this._deepMerge({ ...this.defaults }, config); + } + + /** + * JSON 문자열로 변환 + * @returns {string} + */ + toJSON() { + return JSON.stringify(this.config, null, 2); + } + + /** + * JSON 문자열에서 로드 + * @param {string} json - JSON 문자열 + */ + fromJSON(json) { + try { + const parsed = JSON.parse(json); + this.config = this._deepMerge({ ...this.defaults }, parsed); + } catch (error) { + console.error("Failed to parse JSON:", error); + } + } + + /** + * 로컬 스토리지에 저장 + * @param {string} key - 저장 키 + */ + saveToStorage(key = "app_config") { + try { + localStorage.setItem(key, JSON.stringify(this.config)); + return true; + } catch (error) { + console.error("Failed to save to storage:", error); + return false; + } + } + + /** + * 로컬 스토리지에서 로드 + * @param {string} key - 저장 키 + */ + loadFromStorage(key = "app_config") { + try { + const stored = localStorage.getItem(key); + if (stored) { + const parsed = JSON.parse(stored); + this.config = this._deepMerge({ ...this.defaults }, parsed); + return true; + } + return false; + } catch (error) { + console.error("Failed to load from storage:", error); + return false; + } + } + + /** + * 중첩된 객체 값 가져오기 + * @private + */ + _getNestedValue(obj, key, defaultValue = null) { + const keys = key.split("."); + let value = obj; + + for (const k of keys) { + if (value && typeof value === "object" && k in value) { + value = value[k]; + } else { + return defaultValue; + } + } + + return value !== undefined ? value : defaultValue; + } + + /** + * 중첩된 객체 값 설정 + * @private + */ + _setNestedValue(obj, key, value) { + const keys = key.split("."); + const lastKey = keys.pop(); + let target = obj; + + for (const k of keys) { + if (!(k in target) || typeof target[k] !== "object") { + target[k] = {}; + } + target = target[k]; + } + + target[lastKey] = value; + } + + /** + * 중첩된 객체 값 삭제 + * @private + */ + _deleteNestedValue(obj, key) { + const keys = key.split("."); + const lastKey = keys.pop(); + let target = obj; + + for (const k of keys) { + if (!(k in target) || typeof target[k] !== "object") { + return; + } + target = target[k]; + } + + delete target[lastKey]; + } + + /** + * 깊은 병합 + * @private + */ + _deepMerge(target, source) { + const output = { ...target }; + + if (this._isObject(target) && this._isObject(source)) { + Object.keys(source).forEach((key) => { + if (this._isObject(source[key])) { + if (!(key in target)) { + output[key] = source[key]; + } else { + output[key] = this._deepMerge(target[key], source[key]); + } + } else { + output[key] = source[key]; + } + }); + } + + return output; + } + + /** + * 객체 확인 + * @private + */ + _isObject(item) { + return item && typeof item === "object" && !Array.isArray(item); + } +} + +/** + * 앱 설정 관리 (싱글톤) + */ +class AppConfig extends ConfigManager { + static instance = null; + + constructor(defaults = {}) { + if (AppConfig.instance) { + return AppConfig.instance; + } + + super({ + app: { + name: "My App", + version: "1.0.0", + debug: false, + }, + ui: { + theme: "light", + language: "ko", + animations: true, + }, + features: { + search: true, + notifications: true, + autoSave: true, + }, + ...defaults, + }); + + AppConfig.instance = this; + } + + /** + * 싱글톤 인스턴스 가져오기 + * @returns {AppConfig} + */ + static getInstance() { + if (!AppConfig.instance) { + AppConfig.instance = new AppConfig(); + } + return AppConfig.instance; + } + + /** + * 앱 이름 가져오기 + * @returns {string} + */ + getAppName() { + return this.get("app.name"); + } + + /** + * 앱 버전 가져오기 + * @returns {string} + */ + getAppVersion() { + return this.get("app.version"); + } + + /** + * 디버그 모드 확인 + * @returns {boolean} + */ + isDebugMode() { + return this.get("app.debug", false); + } + + /** + * 테마 가져오기 + * @returns {string} + */ + getTheme() { + return this.get("ui.theme", "light"); + } + + /** + * 테마 설정 + * @param {string} theme - 테마 + */ + setTheme(theme) { + this.set("ui.theme", theme); + this.saveToStorage(); + } + + /** + * 언어 가져오기 + * @returns {string} + */ + getLanguage() { + return this.get("ui.language", "ko"); + } + + /** + * 언어 설정 + * @param {string} language - 언어 + */ + setLanguage(language) { + this.set("ui.language", language); + this.saveToStorage(); + } + + /** + * 애니메이션 사용 여부 + * @returns {boolean} + */ + useAnimations() { + return this.get("ui.animations", true); + } + + /** + * 기능 활성화 여부 + * @param {string} feature - 기능 이름 + * @returns {boolean} + */ + isFeatureEnabled(feature) { + return this.get(`features.${feature}`, false); + } + + /** + * 기능 토글 + * @param {string} feature - 기능 이름 + */ + toggleFeature(feature) { + const current = this.isFeatureEnabled(feature); + this.set(`features.${feature}`, !current); + this.saveToStorage(); + } +} + +/** + * HTML data 속성에서 설정 로드 + */ +class DataAttributeConfig { + /** + * 요소에서 설정 로드 + * @param {Element} element - 대상 요소 + * @param {string} prefix - 속성 접두사 + * @returns {Object} + */ + static loadFromElement(element, prefix = "data-") { + if (!element) return {}; + + const config = {}; + const attributes = element.attributes; + + for (let i = 0; i < attributes.length; i++) { + const attr = attributes[i]; + if (attr.name.startsWith(prefix)) { + const key = attr.name + .substring(prefix.length) + .replace(/-([a-z])/g, (g) => g[1].toUpperCase()); + config[key] = this._parseValue(attr.value); + } + } + + return config; + } + + /** + * 값 파싱 + * @private + */ + static _parseValue(value) { + // boolean + if (value === "true") return true; + if (value === "false") return false; + + // number + if (!isNaN(value) && value !== "") { + return parseFloat(value); + } + + // JSON + if ((value.startsWith("{") || value.startsWith("[")) && + (value.endsWith("}") || value.endsWith("]"))) { + try { + return JSON.parse(value); + } catch (e) { + // JSON 파싱 실패 시 문자열 반환 + } + } + + // string + return value; + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { ConfigManager, AppConfig, DataAttributeConfig }; +} diff --git a/src/js/common/DOMUtils.js b/src/js/common/DOMUtils.js new file mode 100644 index 0000000..6b3ab0d --- /dev/null +++ b/src/js/common/DOMUtils.js @@ -0,0 +1,414 @@ +/** + * DOM 조작 유틸리티 모듈 (DOMUtils.js) + * ======================================== + * document.querySelector 대신 쓰는 간편 함수들입니다. + * + * [초보자용 사용 예] + * DOMUtils.$('.my-class') → 첫 번째 요소 (querySelector) + * DOMUtils.$$('.my-class') → 모든 요소 (querySelectorAll) + * DOMUtils.fadeIn(el, 300) → 페이드 인 (300ms) + * DOMUtils.fadeOut(el, 300) → 페이드 아웃 + * DOMUtils.delegate(parent, 'click', '.btn', handler) → 동적 요소에 이벤트 위임 + * DOMUtils.htmlToElement('
') → HTML 문자열 → Element + * + * @module DOMUtils + */ +class DOMUtils { + /** + * 요소 선택 (단일) + * @param {string} selector - CSS 선택자 + * @param {Element} parent - 부모 요소 + * @returns {Element|null} + */ + static $(selector, parent = document) { + return parent.querySelector(selector); + } + + /** + * 요소 선택 (다중) + * @param {string} selector - CSS 선택자 + * @param {Element} parent - 부모 요소 + * @returns {NodeList} + */ + static $$(selector, parent = document) { + return parent.querySelectorAll(selector); + } + + /** + * 요소 생성 + * @param {string} tag - 태그명 + * @param {Object} attrs - 속성 객체 + * @param {string} content - 내용 + * @returns {Element} + */ + static createElement(tag, attrs = {}, content = "") { + const element = document.createElement(tag); + + Object.entries(attrs).forEach(([key, value]) => { + if (key === "class" || key === "className") { + element.className = value; + } else if (key === "style" && typeof value === "object") { + Object.assign(element.style, value); + } else if (key.startsWith("data-")) { + element.setAttribute(key, value); + } else { + element[key] = value; + } + }); + + if (content) { + if (typeof content === "string") { + element.innerHTML = content; + } else if (content instanceof Node) { + element.appendChild(content); + } + } + + return element; + } + + /** + * 클래스 토글 + * @param {Element} element - 대상 요소 + * @param {string} className - 클래스명 + * @param {boolean} force - 강제 적용 여부 + */ + static toggleClass(element, className, force) { + if (!element) return; + if (force !== undefined) { + element.classList.toggle(className, force); + } else { + element.classList.toggle(className); + } + } + + /** + * 여러 클래스 추가 + * @param {Element} element - 대상 요소 + * @param {...string} classNames - 클래스명들 + */ + static addClasses(element, ...classNames) { + if (!element) return; + element.classList.add(...classNames); + } + + /** + * 여러 클래스 제거 + * @param {Element} element - 대상 요소 + * @param {...string} classNames - 클래스명들 + */ + static removeClasses(element, ...classNames) { + if (!element) return; + element.classList.remove(...classNames); + } + + /** + * 요소의 위치 정보 가져오기 + * @param {Element} element - 대상 요소 + * @returns {Object} + */ + static getPosition(element) { + if (!element) return null; + const rect = element.getBoundingClientRect(); + return { + top: rect.top, + left: rect.left, + right: rect.right, + bottom: rect.bottom, + width: rect.width, + height: rect.height, + x: rect.x, + y: rect.y, + }; + } + + /** + * 요소를 퍼센트 위치로 설정 + * @param {Element} element - 대상 요소 + * @param {number} x - X 위치 (%) + * @param {number} y - Y 위치 (%) + * @param {string} transform - 추가 transform + */ + static setPercentPosition(element, x, y, transform = "translate(-50%, -50%)") { + if (!element) return; + element.style.position = "absolute"; + element.style.left = `${x}%`; + element.style.top = `${y}%`; + if (transform) { + element.style.transform = transform; + } + } + + /** + * 요소 페이드 인 + * @param {Element} element - 대상 요소 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static fadeIn(element, duration = 300) { + if (!element) return Promise.resolve(); + + return new Promise((resolve) => { + // 기존 display 값 저장 (grid, flex 등 유지) + const originalDisplay = element.style.display || window.getComputedStyle(element).display; + const isGridOrFlex = originalDisplay === "grid" || originalDisplay === "flex" || + originalDisplay.includes("grid") || originalDisplay.includes("flex"); + + element.style.opacity = "0"; + // grid/flex인 경우 display를 설정하지 않음 + if (!isGridOrFlex) { + element.style.display = "block"; + } + element.style.transition = `opacity ${duration}ms ease-in-out`; + + setTimeout(() => { + element.style.opacity = "1"; + }, 10); + + setTimeout(() => { + element.style.transition = ""; + // grid/flex인 경우 display 스타일 제거 + if (isGridOrFlex) { + element.style.display = ""; + } + resolve(); + }, duration); + }); + } + + /** + * 요소 페이드 아웃 + * @param {Element} element - 대상 요소 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static fadeOut(element, duration = 300) { + if (!element) return Promise.resolve(); + + return new Promise((resolve) => { + element.style.opacity = "1"; + element.style.transition = `opacity ${duration}ms ease-in-out`; + + setTimeout(() => { + element.style.opacity = "0"; + }, 10); + + setTimeout(() => { + element.style.display = "none"; + element.style.transition = ""; + element.style.opacity = ""; // 재오픈 시 opacity 초기화 + resolve(); + }, duration); + }); + } + + /** + * 요소 슬라이드 다운 + * @param {Element} element - 대상 요소 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static slideDown(element, duration = 300) { + if (!element) return Promise.resolve(); + + return new Promise((resolve) => { + element.style.display = "block"; + const height = element.scrollHeight; + element.style.height = "0"; + element.style.overflow = "hidden"; + element.style.transition = `height ${duration}ms ease-in-out`; + + setTimeout(() => { + element.style.height = `${height}px`; + }, 10); + + setTimeout(() => { + element.style.height = ""; + element.style.overflow = ""; + element.style.transition = ""; + resolve(); + }, duration); + }); + } + + /** + * 요소 슬라이드 업 + * @param {Element} element - 대상 요소 + * @param {number} duration - 지속 시간 (ms) + * @returns {Promise} + */ + static slideUp(element, duration = 300) { + if (!element) return Promise.resolve(); + + return new Promise((resolve) => { + const height = element.scrollHeight; + element.style.height = `${height}px`; + element.style.overflow = "hidden"; + element.style.transition = `height ${duration}ms ease-in-out`; + + setTimeout(() => { + element.style.height = "0"; + }, 10); + + setTimeout(() => { + element.style.display = "none"; + element.style.height = ""; + element.style.overflow = ""; + element.style.transition = ""; + resolve(); + }, duration); + }); + } + + /** + * 이벤트 위임 + * @param {Element} parent - 부모 요소 + * @param {string} eventType - 이벤트 타입 + * @param {string} selector - 자식 선택자 + * @param {Function} handler - 핸들러 함수 + */ + static delegate(parent, eventType, selector, handler) { + if (!parent) return; + + parent.addEventListener(eventType, (event) => { + const target = event.target.closest(selector); + if (target && parent.contains(target)) { + handler.call(target, event); + } + }); + } + + /** + * HTML 문자열을 요소로 변환 + * @param {string} html - HTML 문자열 + * @returns {Element} + */ + static htmlToElement(html) { + const template = document.createElement("template"); + template.innerHTML = html.trim(); + return template.content.firstElementChild; + } + + /** + * HTML 문자열을 요소 배열로 변환 + * @param {string} html - HTML 문자열 + * @returns {Array} + */ + static htmlToElements(html) { + const template = document.createElement("template"); + template.innerHTML = html.trim(); + return Array.from(template.content.children); + } + + /** + * 요소가 뷰포트에 있는지 확인 + * @param {Element} element - 대상 요소 + * @returns {boolean} + */ + static isInViewport(element) { + if (!element) return false; + const rect = element.getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + ); + } + + /** + * 부드러운 스크롤 + * @param {Element|string} target - 대상 요소 또는 선택자 + * @param {Object} options - 옵션 + */ + static smoothScroll(target, options = {}) { + const element = typeof target === "string" ? this.$(target) : target; + if (!element) return; + + const defaultOptions = { + behavior: "smooth", + block: "start", + inline: "nearest", + }; + + element.scrollIntoView({ ...defaultOptions, ...options }); + } + + /** + * 전체 화면 토글 + * @param {Element} element - 대상 요소 + */ + static toggleFullscreen(element = document.documentElement) { + if (!document.fullscreenElement) { + element.requestFullscreen?.() || + element.webkitRequestFullscreen?.() || + element.msRequestFullscreen?.(); + } else { + document.exitFullscreen?.() || + document.webkitExitFullscreen?.() || + document.msExitFullscreen?.(); + } + } + + /** + * 클립보드에 복사 + * @param {string} text - 복사할 텍스트 + * @returns {Promise} + */ + static async copyToClipboard(text) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch (err) { + console.error("Failed to copy:", err); + return false; + } + } + + /** + * 요소의 스타일 가져오기 + * @param {Element} element - 대상 요소 + * @param {string} property - CSS 속성 + * @returns {string} + */ + static getStyle(element, property) { + if (!element) return null; + return window.getComputedStyle(element).getPropertyValue(property); + } + + /** + * 여러 스타일 설정 + * @param {Element} element - 대상 요소 + * @param {Object} styles - 스타일 객체 + */ + static setStyles(element, styles) { + if (!element || !styles) return; + Object.assign(element.style, styles); + } + + /** + * 요소 제거 + * @param {Element} element - 대상 요소 + */ + static remove(element) { + if (element && element.parentNode) { + element.parentNode.removeChild(element); + } + } + + /** + * 요소 내용 비우기 + * @param {Element} element - 대상 요소 + */ + static empty(element) { + if (!element) return; + while (element.firstChild) { + element.removeChild(element.firstChild); + } + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = DOMUtils; +} diff --git a/src/js/common/DependencyInjector.js b/src/js/common/DependencyInjector.js new file mode 100644 index 0000000..09b5445 --- /dev/null +++ b/src/js/common/DependencyInjector.js @@ -0,0 +1,116 @@ +/** + * 의존성 주입 컨테이너 + * 명확한 의존성 관리 및 테스트 용이성 향상 + * @module DependencyInjector + */ +class DependencyInjector { + constructor() { + this.services = new Map(); + this.singletons = new Map(); + } + + /** + * 서비스 등록 + * @param {string} name - 서비스 이름 + * @param {Function|Object} factory - 팩토리 함수 또는 인스턴스 + * @param {boolean} singleton - 싱글톤 여부 + */ + register(name, factory, singleton = true) { + if (typeof factory === 'function') { + this.services.set(name, { factory, singleton }); + } else { + // 이미 인스턴스인 경우 + this.singletons.set(name, factory); + this.services.set(name, { factory: () => factory, singleton: true }); + } + } + + /** + * 서비스 가져오기 + * @param {string} name - 서비스 이름 + * @returns {*} + */ + get(name) { + // 싱글톤 캐시 확인 + if (this.singletons.has(name)) { + return this.singletons.get(name); + } + + const service = this.services.get(name); + if (!service) { + throw new Error(`Service "${name}" is not registered`); + } + + const instance = service.factory(this); + + // 싱글톤인 경우 캐시 + if (service.singleton) { + this.singletons.set(name, instance); + } + + return instance; + } + + /** + * 서비스 존재 여부 확인 + * @param {string} name - 서비스 이름 + * @returns {boolean} + */ + has(name) { + return this.services.has(name); + } + + /** + * 서비스 제거 + * @param {string} name - 서비스 이름 + */ + remove(name) { + this.services.delete(name); + this.singletons.delete(name); + } + + /** + * 모든 서비스 초기화 + */ + clear() { + this.services.clear(); + this.singletons.clear(); + } + + /** + * 여러 서비스 한 번에 등록 + * @param {Object} services - 서비스 객체 + */ + registerAll(services) { + Object.entries(services).forEach(([name, factory]) => { + this.register(name, factory); + }); + } +} + +/** + * 전역 의존성 주입 컨테이너 + */ +const di = new DependencyInjector(); + +// 기본 서비스 등록 (있는 경우) +if (typeof DOMUtils !== 'undefined') { + di.register('DOMUtils', () => DOMUtils, true); +} +if (typeof Utils !== 'undefined') { + di.register('Utils', () => Utils, true); +} +if (typeof AnimationUtils !== 'undefined') { + di.register('AnimationUtils', () => AnimationUtils, true); +} +if (typeof eventManager !== 'undefined') { + di.register('eventManager', () => eventManager, true); +} +if (typeof ErrorHandler !== 'undefined') { + di.register('ErrorHandler', () => ErrorHandler, true); +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { DependencyInjector, di }; +} diff --git a/src/js/common/ErrorHandler.js b/src/js/common/ErrorHandler.js new file mode 100644 index 0000000..1b4e69b --- /dev/null +++ b/src/js/common/ErrorHandler.js @@ -0,0 +1,221 @@ +/** + * 에러 처리 모듈 (ErrorHandler.js) + * ======================================== + * 에러를 한곳에서 처리하고 로깅합니다. + * + * [초보자용] + * ErrorHandler.safeExecute(() => 위험한함수(), 기본값) + * ErrorHandler.handle(error, { context: '내모듈' }) + * + * @module ErrorHandler + */ +class ErrorHandler { + constructor() { + this.errorLog = []; + this.maxLogSize = 100; + this.onErrorCallbacks = []; + } + + /** + * 에러 처리 + * @param {Error|string} error - 에러 객체 또는 메시지 + * @param {Object} context - 컨텍스트 정보 + * @param {boolean} showToUser - 사용자에게 표시할지 여부 + */ + static handle(error, context = {}, showToUser = false) { + const errorInfo = this._normalizeError(error, context); + + // 콘솔에 로그 + console.error('[ErrorHandler]', errorInfo); + + // 에러 로그에 추가 + if (this.instance) { + this.instance._addToLog(errorInfo); + } + + // 콜백 실행 + if (this.instance) { + this.instance.onErrorCallbacks.forEach(callback => { + try { + callback(errorInfo); + } catch (e) { + console.error('[ErrorHandler] Error in callback:', e); + } + }); + } + + // 사용자에게 표시 + if (showToUser) { + this._showToUser(errorInfo); + } + + return errorInfo; + } + + /** + * 에러를 정규화 + * @private + */ + static _normalizeError(error, context) { + const errorInfo = { + message: '', + stack: '', + timestamp: new Date().toISOString(), + context: {}, + ...context, + }; + + if (error instanceof Error) { + errorInfo.message = error.message; + errorInfo.stack = error.stack; + errorInfo.name = error.name; + } else if (typeof error === 'string') { + errorInfo.message = error; + } else { + errorInfo.message = 'Unknown error'; + errorInfo.originalError = error; + } + + return errorInfo; + } + + /** + * 사용자에게 에러 표시 + * @private + */ + static _showToUser(errorInfo) { + // ModalBase가 있으면 사용, 없으면 alert + if (typeof AlertModal !== 'undefined') { + const alert = new AlertModal({ + title: '오류 발생', + message: errorInfo.message || '알 수 없는 오류가 발생했습니다.', + }); + alert.show(); + } else if (typeof ModalBase !== 'undefined') { + // 간단한 알림 모달 생성 + const modal = new ModalBase(); + modal.create({ + content: `
${errorInfo.message || '오류가 발생했습니다.'}
`, + }); + modal.open(); + } else { + // 최후의 수단: alert + alert(errorInfo.message || '오류가 발생했습니다.'); + } + } + + /** + * 에러 로그에 추가 + * @private + */ + _addToLog(errorInfo) { + this.errorLog.push(errorInfo); + + // 최대 크기 초과 시 오래된 항목 제거 + if (this.errorLog.length > this.maxLogSize) { + this.errorLog.shift(); + } + } + + /** + * 에러 콜백 등록 + * @param {Function} callback - 콜백 함수 + */ + onError(callback) { + if (typeof callback === 'function') { + this.onErrorCallbacks.push(callback); + } + } + + /** + * 에러 콜백 제거 + * @param {Function} callback - 콜백 함수 + */ + offError(callback) { + const index = this.onErrorCallbacks.indexOf(callback); + if (index > -1) { + this.onErrorCallbacks.splice(index, 1); + } + } + + /** + * 에러 로그 가져오기 + * @param {number} limit - 최대 개수 + * @returns {Array} + */ + getErrorLog(limit = null) { + if (limit) { + return this.errorLog.slice(-limit); + } + return [...this.errorLog]; + } + + /** + * 에러 로그 초기화 + */ + clearErrorLog() { + this.errorLog = []; + } + + /** + * 안전한 함수 실행 (에러 처리 포함) + * @param {Function} fn - 실행할 함수 + * @param {*} defaultValue - 에러 발생 시 반환할 기본값 + * @param {Object} context - 컨텍스트 정보 + * @returns {*} + */ + static safeExecute(fn, defaultValue = null, context = {}) { + try { + return fn(); + } catch (error) { + this.handle(error, context, false); + return defaultValue; + } + } + + /** + * 안전한 비동기 함수 실행 (에러 처리 포함) + * @param {Function} fn - 실행할 함수 + * @param {*} defaultValue - 에러 발생 시 반환할 기본값 + * @param {Object} context - 컨텍스트 정보 + * @returns {Promise} + */ + static async safeExecuteAsync(fn, defaultValue = null, context = {}) { + try { + return await fn(); + } catch (error) { + this.handle(error, context, false); + return defaultValue; + } + } + + /** + * 전역 에러 핸들러 설정 + */ + static setupGlobalHandlers() { + // 전역 에러 핸들러 + window.addEventListener('error', (event) => { + this.handle(event.error || event.message, { + type: 'global', + filename: event.filename, + lineno: event.lineno, + colno: event.colno, + }, false); + }); + + // Promise rejection 핸들러 + window.addEventListener('unhandledrejection', (event) => { + this.handle(event.reason, { + type: 'unhandledRejection', + }, false); + }); + } +} + +// 싱글톤 인스턴스 +ErrorHandler.instance = new ErrorHandler(); + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = ErrorHandler; +} diff --git a/src/js/common/EventManager.js b/src/js/common/EventManager.js new file mode 100644 index 0000000..eba85ac --- /dev/null +++ b/src/js/common/EventManager.js @@ -0,0 +1,276 @@ +/** + * 이벤트 관리 모듈 (EventManager.js) + * ======================================== + * 이벤트 리스너를 중앙에서 관리합니다. + * 리스너 ID를 저장해두면 나중에 off()로 제거 가능 (메모리 누수 방지). + * + * [초보자용] 전역 변수 eventManager 로 사용: + * eventManager.on(element, 'click', handler) + * eventManager.delegate(parent, 'click', '.btn', handler) + * + * @module EventManager + */ +class EventManager { + constructor() { + this.listeners = new Map(); + this.delegatedListeners = new Map(); + } + + /** + * 이벤트 리스너 등록 + * @param {Element|Window|Document} target - 대상 요소 + * @param {string} eventType - 이벤트 타입 + * @param {Function} handler - 핸들러 함수 + * @param {Object} options - 이벤트 옵션 + * @returns {string} 리스너 ID + */ + on(target, eventType, handler, options = {}) { + if (!target || typeof handler !== 'function') { + console.warn('[EventManager] Invalid target or handler'); + return null; + } + + const listenerId = this._generateId(); + const wrappedHandler = this._wrapHandler(handler, listenerId); + + target.addEventListener(eventType, wrappedHandler, options); + + if (!this.listeners.has(target)) { + this.listeners.set(target, new Map()); + } + this.listeners.get(target).set(listenerId, { + eventType, + handler: wrappedHandler, + originalHandler: handler, + options, + }); + + return listenerId; + } + + /** + * 이벤트 리스너 제거 + * @param {Element|Window|Document} target - 대상 요소 + * @param {string} listenerId - 리스너 ID + */ + off(target, listenerId) { + if (!target || !listenerId) return; + + const targetListeners = this.listeners.get(target); + if (!targetListeners) return; + + const listener = targetListeners.get(listenerId); + if (!listener) return; + + target.removeEventListener(listener.eventType, listener.handler, listener.options); + targetListeners.delete(listenerId); + + if (targetListeners.size === 0) { + this.listeners.delete(target); + } + } + + /** + * 이벤트 위임 등록 + * @param {Element|Window|Document} parent - 부모 요소 + * @param {string} eventType - 이벤트 타입 + * @param {string} selector - 자식 선택자 + * @param {Function} handler - 핸들러 함수 + * @param {Object} options - 이벤트 옵션 + * @returns {string} 리스너 ID + */ + delegate(parent, eventType, selector, handler, options = {}) { + if (!parent || typeof handler !== 'function') { + console.warn('[EventManager] Invalid parent or handler'); + return null; + } + + const listenerId = this._generateId(); + const wrappedHandler = (event) => { + const target = event.target.closest(selector); + if (target && parent.contains(target)) { + handler.call(target, event); + } + }; + + parent.addEventListener(eventType, wrappedHandler, options); + + if (!this.delegatedListeners.has(parent)) { + this.delegatedListeners.set(parent, new Map()); + } + this.delegatedListeners.get(parent).set(listenerId, { + eventType, + selector, + handler: wrappedHandler, + originalHandler: handler, + options, + }); + + return listenerId; + } + + /** + * 이벤트 위임 제거 + * @param {Element|Window|Document} parent - 부모 요소 + * @param {string} listenerId - 리스너 ID + */ + undelegate(parent, listenerId) { + if (!parent || !listenerId) return; + + const delegatedListeners = this.delegatedListeners.get(parent); + if (!delegatedListeners) return; + + const listener = delegatedListeners.get(listenerId); + if (!listener) return; + + parent.removeEventListener(listener.eventType, listener.handler, listener.options); + delegatedListeners.delete(listenerId); + + if (delegatedListeners.size === 0) { + this.delegatedListeners.delete(parent); + } + } + + /** + * 특정 요소의 모든 리스너 제거 + * @param {Element|Window|Document} target - 대상 요소 + */ + removeAll(target) { + // 일반 리스너 제거 + const targetListeners = this.listeners.get(target); + if (targetListeners) { + targetListeners.forEach((listener, listenerId) => { + target.removeEventListener(listener.eventType, listener.handler, listener.options); + }); + this.listeners.delete(target); + } + + // 위임 리스너 제거 + const delegatedListeners = this.delegatedListeners.get(target); + if (delegatedListeners) { + delegatedListeners.forEach((listener, listenerId) => { + target.removeEventListener(listener.eventType, listener.handler, listener.options); + }); + this.delegatedListeners.delete(target); + } + } + + /** + * 모든 리스너 제거 + */ + removeAllListeners() { + // 일반 리스너 제거 + this.listeners.forEach((targetListeners, target) => { + targetListeners.forEach((listener) => { + target.removeEventListener(listener.eventType, listener.handler, listener.options); + }); + }); + this.listeners.clear(); + + // 위임 리스너 제거 + this.delegatedListeners.forEach((delegatedListeners, parent) => { + delegatedListeners.forEach((listener) => { + parent.removeEventListener(listener.eventType, listener.handler, listener.options); + }); + }); + this.delegatedListeners.clear(); + } + + /** + * 한 번만 실행되는 이벤트 리스너 + * @param {Element|Window|Document} target - 대상 요소 + * @param {string} eventType - 이벤트 타입 + * @param {Function} handler - 핸들러 함수 + * @param {Object} options - 이벤트 옵션 + * @returns {string} 리스너 ID + */ + once(target, eventType, handler, options = {}) { + const listenerId = this._generateId(); + const wrappedHandler = (event) => { + handler(event); + this.off(target, listenerId); + }; + + return this.on(target, eventType, wrappedHandler, options); + } + + /** + * 이벤트 발생 (커스텀 이벤트) + * @param {Element|Window|Document} target - 대상 요소 + * @param {string} eventType - 이벤트 타입 + * @param {Object} detail - 이벤트 데이터 + */ + emit(target, eventType, detail = {}) { + if (!target) return; + + const event = new CustomEvent(eventType, { + detail, + bubbles: true, + cancelable: true, + }); + + target.dispatchEvent(event); + } + + /** + * 핸들러 래핑 (에러 처리 포함) + * @private + */ + _wrapHandler(handler, listenerId) { + return (event) => { + try { + handler(event); + } catch (error) { + console.error(`[EventManager] Error in event handler (${listenerId}):`, error); + if (typeof ErrorHandler !== 'undefined') { + ErrorHandler.handle(error, { context: 'EventManager', listenerId }); + } + } + }; + } + + /** + * 고유 ID 생성 + * @private + */ + _generateId() { + return `listener_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + } + + /** + * 등록된 리스너 정보 가져오기 + * @returns {Object} + */ + getListenersInfo() { + const info = { + regular: {}, + delegated: {}, + }; + + this.listeners.forEach((targetListeners, target) => { + const targetKey = target === window ? 'window' : + target === document ? 'document' : + target.id || target.className || 'unknown'; + info.regular[targetKey] = Array.from(targetListeners.keys()); + }); + + this.delegatedListeners.forEach((delegatedListeners, parent) => { + const parentKey = parent === window ? 'window' : + parent === document ? 'document' : + parent.id || parent.className || 'unknown'; + info.delegated[parentKey] = Array.from(delegatedListeners.keys()); + }); + + return info; + } +} + +/** + * 전역 EventManager 인스턴스 (싱글톤) + */ +const eventManager = new EventManager(); + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { EventManager, eventManager }; +} diff --git a/src/js/common/GaugeBase.js b/src/js/common/GaugeBase.js new file mode 100644 index 0000000..bb04b9a --- /dev/null +++ b/src/js/common/GaugeBase.js @@ -0,0 +1,434 @@ +/** + * 게이지 관련 기본 클래스 + * @module GaugeBase + */ +class GaugeBase { + constructor(config = {}) { + this.config = { + size: 400, + strokeWidth: 20, + maxValue: 100, + currentValue: 0, + padding: 10, + startAngle: -90, + endAngle: 270, + animationDuration: 800, + easing: "ease-out", + ...config, + }; + + this.svg = null; + this.path = null; + this.pathLength = 0; + } + + /** + * 각도를 라디안으로 변환 + * @param {number} angle - 각도 + * @returns {number} + */ + static degreesToRadians(angle) { + return (angle * Math.PI) / 180; + } + + /** + * 라디안을 각도로 변환 + * @param {number} radians - 라디안 + * @returns {number} + */ + static radiansToDegrees(radians) { + return (radians * 180) / Math.PI; + } + + /** + * 원형 경로의 좌표 계산 + * @param {number} cx - 중심 X + * @param {number} cy - 중심 Y + * @param {number} radius - 반지름 + * @param {number} angle - 각도 + * @returns {Object} + */ + static polarToCartesian(cx, cy, radius, angle) { + const radians = this.degreesToRadians(angle); + return { + x: cx + radius * Math.cos(radians), + y: cy + radius * Math.sin(radians), + }; + } + + /** + * SVG 원호 경로 생성 + * @param {number} cx - 중심 X + * @param {number} cy - 중심 Y + * @param {number} radius - 반지름 + * @param {number} startAngle - 시작 각도 + * @param {number} endAngle - 종료 각도 + * @returns {string} + */ + static describeArc(cx, cy, radius, startAngle, endAngle) { + const start = this.polarToCartesian(cx, cy, radius, endAngle); + const end = this.polarToCartesian(cx, cy, radius, startAngle); + const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1"; + + return [ + "M", + start.x, + start.y, + "A", + radius, + radius, + 0, + largeArcFlag, + 0, + end.x, + end.y, + ].join(" "); + } + + /** + * 진행률을 각도로 변환 + * @param {number} percent - 진행률 (0-1) + * @param {number} startAngle - 시작 각도 + * @param {number} endAngle - 종료 각도 + * @returns {number} + */ + static percentToAngle(percent, startAngle = -90, endAngle = 270) { + const totalAngle = endAngle - startAngle; + return startAngle + totalAngle * percent; + } + + /** + * SVG 요소 생성 + * @param {string} tag - 태그명 + * @param {Object} attrs - 속성 + * @returns {SVGElement} + */ + static createSVGElement(tag, attrs = {}) { + const element = document.createElementNS("http://www.w3.org/2000/svg", tag); + Object.entries(attrs).forEach(([key, value]) => { + element.setAttribute(key, value); + }); + return element; + } + + /** + * 경로 길이 계산 + * @param {SVGPathElement} path - SVG 경로 요소 + * @returns {number} + */ + static getPathLength(path) { + return path.getTotalLength(); + } + + /** + * 경로상의 특정 지점 좌표 + * @param {SVGPathElement} path - SVG 경로 요소 + * @param {number} percent - 위치 (0-1) + * @returns {DOMPoint} + */ + static getPointAtPercent(path, percent) { + const length = path.getTotalLength(); + return path.getPointAtLength(length * percent); + } + + /** + * 이징 함수 + * @param {number} t - 시간 (0-1) + * @param {string} type - 이징 타입 + * @returns {number} + */ + static easing(t, type = "ease-out") { + const easings = { + linear: (t) => t, + "ease-in": (t) => t * t, + "ease-out": (t) => t * (2 - t), + "ease-in-out": (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t), + "ease-in-cubic": (t) => t * t * t, + "ease-out-cubic": (t) => --t * t * t + 1, + "ease-in-out-cubic": (t) => + t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1, + bounce: (t) => { + if (t < 1 / 2.75) { + return 7.5625 * t * t; + } else if (t < 2 / 2.75) { + return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75; + } else if (t < 2.5 / 2.75) { + return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375; + } else { + return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375; + } + }, + }; + + return easings[type] ? easings[type](t) : easings["ease-out"](t); + } + + /** + * 값을 범위 내로 제한 + * @param {number} value - 값 + * @param {number} min - 최소값 + * @param {number} max - 최대값 + * @returns {number} + */ + static clamp(value, min, max) { + return Math.min(Math.max(value, min), max); + } + + /** + * 값을 범위로 매핑 + * @param {number} value - 값 + * @param {number} inMin - 입력 최소값 + * @param {number} inMax - 입력 최대값 + * @param {number} outMin - 출력 최소값 + * @param {number} outMax - 출력 최대값 + * @returns {number} + */ + static map(value, inMin, inMax, outMin, outMax) { + return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin; + } + + /** + * 선형 보간 + * @param {number} start - 시작값 + * @param {number} end - 종료값 + * @param {number} t - 시간 (0-1) + * @returns {number} + */ + static lerp(start, end, t) { + return start + (end - start) * t; + } +} + +/** + * 원형 게이지 클래스 + */ +class CircularGauge extends GaugeBase { + constructor(config = {}) { + super(config); + this.centerX = this.config.size / 2; + this.centerY = this.config.size / 2; + this.radius = + (this.config.size - this.config.strokeWidth - this.config.padding * 2) / 2; + } + + /** + * 게이지 초기화 + * @param {string|Element} container - 컨테이너 선택자 또는 요소 + * @returns {SVGElement} + */ + init(container) { + const element = + typeof container === "string" ? document.querySelector(container) : container; + + if (!element) { + console.error("Container not found"); + return null; + } + + // SVG 생성 + this.svg = GaugeBase.createSVGElement("svg", { + width: this.config.size, + height: this.config.size, + viewBox: `0 0 ${this.config.size} ${this.config.size}`, + }); + + // 배경 원 + const bgPath = this._createPath("background"); + this.svg.appendChild(bgPath); + + // 진행률 원 + this.path = this._createPath("progress"); + this.svg.appendChild(this.path); + + // 경로 길이 설정 + this.pathLength = GaugeBase.getPathLength(this.path); + this.path.style.strokeDasharray = this.pathLength; + this.path.style.strokeDashoffset = this.pathLength; + + element.appendChild(this.svg); + + return this.svg; + } + + /** + * 경로 생성 + * @private + */ + _createPath(type = "progress") { + const pathData = GaugeBase.describeArc( + this.centerX, + this.centerY, + this.radius, + this.config.startAngle, + this.config.endAngle + ); + + const attrs = { + d: pathData, + fill: "none", + stroke: type === "background" ? "#e0e0e0" : "#4CAF50", + "stroke-width": this.config.strokeWidth, + "stroke-linecap": "round", + }; + + if (type === "background") { + attrs.opacity = "0.3"; + } + + return GaugeBase.createSVGElement("path", attrs); + } + + /** + * 진행률 업데이트 + * @param {number} value - 값 + * @param {boolean} animate - 애니메이션 적용 여부 + */ + update(value, animate = true) { + const percent = GaugeBase.clamp(value / this.config.maxValue, 0, 1); + const targetOffset = this.pathLength * (1 - percent); + + if (animate) { + this._animateProgress(targetOffset); + } else { + this.path.style.strokeDashoffset = targetOffset; + } + } + + /** + * 진행률 애니메이션 + * @private + */ + _animateProgress(targetOffset) { + const startOffset = parseFloat(this.path.style.strokeDashoffset) || this.pathLength; + const startTime = performance.now(); + const duration = this.config.animationDuration; + + const animate = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easedProgress = GaugeBase.easing(progress, this.config.easing); + + const currentOffset = GaugeBase.lerp(startOffset, targetOffset, easedProgress); + this.path.style.strokeDashoffset = currentOffset; + + if (progress < 1) { + requestAnimationFrame(animate); + } + }; + + requestAnimationFrame(animate); + } + + /** + * 색상 변경 + * @param {string} color - 색상 + */ + setColor(color) { + this.path.setAttribute("stroke", color); + } + + /** + * 리셋 + */ + reset() { + this.path.style.strokeDashoffset = this.pathLength; + } +} + +/** + * 선형 게이지 클래스 + */ +class LinearGauge extends GaugeBase { + constructor(config = {}) { + super({ + width: 300, + height: 20, + ...config, + }); + } + + /** + * 게이지 초기화 + * @param {string|Element} container - 컨테이너 선택자 또는 요소 + * @returns {HTMLElement} + */ + init(container) { + const element = + typeof container === "string" ? document.querySelector(container) : container; + + if (!element) { + console.error("Container not found"); + return null; + } + + // 컨테이너 생성 + this.container = document.createElement("div"); + this.container.className = "linear-gauge"; + this.container.style.cssText = ` + width: ${this.config.width}px; + height: ${this.config.height}px; + background: #e0e0e0; + border-radius: ${this.config.height / 2}px; + overflow: hidden; + position: relative; + `; + + // 진행률 바 생성 + this.bar = document.createElement("div"); + this.bar.className = "gauge-bar"; + this.bar.style.cssText = ` + width: 0%; + height: 100%; + background: linear-gradient(90deg, #4CAF50, #8BC34A); + transition: width ${this.config.animationDuration}ms ${this.config.easing}; + `; + + this.container.appendChild(this.bar); + element.appendChild(this.container); + + return this.container; + } + + /** + * 진행률 업데이트 + * @param {number} value - 값 + * @param {boolean} animate - 애니메이션 적용 여부 + */ + update(value, animate = true) { + const percent = GaugeBase.clamp((value / this.config.maxValue) * 100, 0, 100); + + if (!animate) { + this.bar.style.transition = "none"; + void this.bar.offsetHeight; // 강제 리플로우 + } + + this.bar.style.width = `${percent}%`; + + if (!animate) { + // 다음 프레임에서 transition 복원 + requestAnimationFrame(() => { + this.bar.style.transition = `width ${this.config.animationDuration}ms ${this.config.easing}`; + }); + } + } + + /** + * 색상 변경 + * @param {string} color - 색상 + */ + setColor(color) { + this.bar.style.background = color; + } + + /** + * 리셋 + */ + reset() { + this.bar.style.width = "0%"; + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { GaugeBase, CircularGauge, LinearGauge }; +} diff --git a/src/js/common/ModalBase.js b/src/js/common/ModalBase.js new file mode 100644 index 0000000..6ce1b8c --- /dev/null +++ b/src/js/common/ModalBase.js @@ -0,0 +1,520 @@ +/** + * 모달 관련 기본 클래스 + * @module ModalBase + */ +class ModalBase { + constructor(config = {}) { + this.config = { + closeOnEscape: true, + closeOnBackdrop: true, + showCloseButton: true, + animation: "fade", + animationDuration: 300, + backdrop: true, + keyboard: true, + ...config, + }; + + this.modal = null; + this.backdrop = null; + this.isOpen = false; + this.onOpen = config.onOpen || null; + this.onClose = config.onClose || null; + } + + /** + * 모달 생성 + * @param {Object} options - 옵션 + * @returns {HTMLElement} + */ + create(options = {}) { + const { + id = `modal-${Date.now()}`, + className = "", + content = "", + header = null, + footer = null, + } = options; + + // 모달 컨테이너 + this.modal = document.createElement("div"); + this.modal.id = id; + this.modal.className = `modal ${className}`; + this.modal.setAttribute("role", "dialog"); + this.modal.setAttribute("aria-modal", "true"); + this.modal.style.cssText = ` + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + overflow: auto; + `; + + // 백드롭 + if (this.config.backdrop) { + this.backdrop = document.createElement("div"); + this.backdrop.className = "modal-backdrop"; + this.backdrop.style.cssText = ` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: -1; + `; + this.modal.appendChild(this.backdrop); + } + + // 모달 다이얼로그 + const dialog = document.createElement("div"); + dialog.className = "modal-dialog"; + dialog.style.cssText = ` + position: relative; + margin: 50px auto; + max-width: 600px; + background: white; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + `; + + // 모달 컨텐츠 + const modalContent = document.createElement("div"); + modalContent.className = "modal-content"; + + // 헤더 + if (header !== null) { + const modalHeader = document.createElement("div"); + modalHeader.className = "modal-header"; + modalHeader.style.cssText = ` + padding: 20px; + border-bottom: 1px solid #e0e0e0; + display: flex; + justify-content: space-between; + align-items: center; + `; + + if (typeof header === "string") { + modalHeader.innerHTML = header; + } else { + modalHeader.appendChild(header); + } + + // 닫기 버튼 + if (this.config.showCloseButton) { + const closeBtn = document.createElement("button"); + closeBtn.className = "modal-close"; + closeBtn.innerHTML = "×"; + closeBtn.style.cssText = ` + background: none; + border: none; + font-size: 28px; + cursor: pointer; + color: #999; + `; + closeBtn.onclick = () => this.close(); + modalHeader.appendChild(closeBtn); + } + + modalContent.appendChild(modalHeader); + } + + // 바디 + const modalBody = document.createElement("div"); + modalBody.className = "modal-body"; + modalBody.style.cssText = ` + padding: 20px; + `; + + if (typeof content === "string") { + modalBody.innerHTML = content; + } else { + modalBody.appendChild(content); + } + + modalContent.appendChild(modalBody); + + // 푸터 + if (footer !== null) { + const modalFooter = document.createElement("div"); + modalFooter.className = "modal-footer"; + modalFooter.style.cssText = ` + padding: 20px; + border-top: 1px solid #e0e0e0; + display: flex; + justify-content: flex-end; + gap: 10px; + `; + + if (typeof footer === "string") { + modalFooter.innerHTML = footer; + } else { + modalFooter.appendChild(footer); + } + + modalContent.appendChild(modalFooter); + } + + dialog.appendChild(modalContent); + this.modal.appendChild(dialog); + + // 이벤트 리스너 등록 + this._setupEventListeners(); + + return this.modal; + } + + /** + * 이벤트 리스너 설정 + * @private + */ + _setupEventListeners() { + // 백드롭 클릭 + if (this.config.closeOnBackdrop && this.backdrop) { + this.backdrop.onclick = () => this.close(); + } + + // ESC 키 + if (this.config.closeOnEscape) { + this._escapeHandler = (e) => { + if (e.key === "Escape" && this.isOpen) { + this.close(); + } + }; + document.addEventListener("keydown", this._escapeHandler); + } + + // 모달 외부 클릭 + if (this.config.closeOnBackdrop) { + this.modal.onclick = (e) => { + if (e.target === this.modal) { + this.close(); + } + }; + } + } + + /** + * 모달 열기 + * @param {Object} data - 전달할 데이터 + * @returns {Promise} + */ + async open(data = null) { + if (this.isOpen) return; + + if (!this.modal) { + console.error("Modal not created"); + return; + } + + // DOM에 추가 + if (!this.modal.parentElement) { + document.body.appendChild(this.modal); + } + + // onOpen 콜백 + if (this.onOpen) { + await this.onOpen(data); + } + + // 애니메이션 + this.modal.style.display = "block"; + await this._animate("in"); + + this.isOpen = true; + + // body 스크롤 방지 + document.body.style.overflow = "hidden"; + + return this; + } + + /** + * 모달 닫기 + * @returns {Promise} + */ + async close() { + if (!this.isOpen) return; + + // 애니메이션 + await this._animate("out"); + + this.modal.style.display = "none"; + this.isOpen = false; + + // body 스크롤 복원 + document.body.style.overflow = ""; + + // onClose 콜백 + if (this.onClose) { + await this.onClose(); + } + + return this; + } + + /** + * 모달 토글 + * @param {Object} data - 전달할 데이터 + */ + toggle(data = null) { + if (this.isOpen) { + this.close(); + } else { + this.open(data); + } + } + + /** + * 애니메이션 처리 + * @private + */ + async _animate(direction) { + const dialog = this.modal.querySelector(".modal-dialog"); + const { animation, animationDuration } = this.config; + + if (animation === "fade") { + if (direction === "in") { + this.modal.style.opacity = "0"; + await this._delay(10); + this.modal.style.transition = `opacity ${animationDuration}ms`; + this.modal.style.opacity = "1"; + await this._delay(animationDuration); + } else { + this.modal.style.transition = `opacity ${animationDuration}ms`; + this.modal.style.opacity = "0"; + await this._delay(animationDuration); + } + } else if (animation === "slide") { + if (direction === "in") { + dialog.style.transform = "translateY(-50px)"; + dialog.style.opacity = "0"; + await this._delay(10); + dialog.style.transition = `transform ${animationDuration}ms, opacity ${animationDuration}ms`; + dialog.style.transform = "translateY(0)"; + dialog.style.opacity = "1"; + await this._delay(animationDuration); + } else { + dialog.style.transition = `transform ${animationDuration}ms, opacity ${animationDuration}ms`; + dialog.style.transform = "translateY(-50px)"; + dialog.style.opacity = "0"; + await this._delay(animationDuration); + } + } else if (animation === "zoom") { + if (direction === "in") { + dialog.style.transform = "scale(0.7)"; + dialog.style.opacity = "0"; + await this._delay(10); + dialog.style.transition = `transform ${animationDuration}ms, opacity ${animationDuration}ms`; + dialog.style.transform = "scale(1)"; + dialog.style.opacity = "1"; + await this._delay(animationDuration); + } else { + dialog.style.transition = `transform ${animationDuration}ms, opacity ${animationDuration}ms`; + dialog.style.transform = "scale(0.7)"; + dialog.style.opacity = "0"; + await this._delay(animationDuration); + } + } + + // transition 초기화 + this.modal.style.transition = ""; + if (dialog) { + dialog.style.transition = ""; + } + } + + /** + * 딜레이 + * @private + */ + _delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + /** + * 모달 파괴 + */ + destroy() { + if (this.isOpen) { + this.close(); + } + + // 이벤트 리스너 제거 + if (this._escapeHandler) { + document.removeEventListener("keydown", this._escapeHandler); + } + + // DOM에서 제거 + if (this.modal && this.modal.parentElement) { + this.modal.parentElement.removeChild(this.modal); + } + + this.modal = null; + this.backdrop = null; + } + + /** + * 모달 컨텐츠 업데이트 + * @param {string|Element} content - 새 컨텐츠 + */ + updateContent(content) { + const modalBody = this.modal.querySelector(".modal-body"); + if (modalBody) { + if (typeof content === "string") { + modalBody.innerHTML = content; + } else { + modalBody.innerHTML = ""; + modalBody.appendChild(content); + } + } + } + + /** + * 모달 헤더 업데이트 + * @param {string|Element} header - 새 헤더 + */ + updateHeader(header) { + const modalHeader = this.modal.querySelector(".modal-header"); + if (modalHeader) { + if (typeof header === "string") { + modalHeader.innerHTML = header; + } else { + modalHeader.innerHTML = ""; + modalHeader.appendChild(header); + } + + // 닫기 버튼 재추가 + if (this.config.showCloseButton) { + const closeBtn = document.createElement("button"); + closeBtn.className = "modal-close"; + closeBtn.innerHTML = "×"; + closeBtn.style.cssText = ` + background: none; + border: none; + font-size: 28px; + cursor: pointer; + color: #999; + `; + closeBtn.onclick = () => this.close(); + modalHeader.appendChild(closeBtn); + } + } + } +} + +/** + * 확인 모달 (Confirm Dialog) + */ +class ConfirmModal extends ModalBase { + constructor(config = {}) { + super(config); + this.promise = null; + } + + /** + * 확인 모달 표시 + * @param {Object} options - 옵션 + * @returns {Promise} + */ + show(options = {}) { + const { + title = "확인", + message = "계속하시겠습니까?", + confirmText = "확인", + cancelText = "취소", + confirmClass = "btn-primary", + cancelClass = "btn-secondary", + } = options; + + return new Promise((resolve) => { + // 헤더 + const header = document.createElement("div"); + header.innerHTML = `

${title}

`; + + // 컨텐츠 + const content = document.createElement("div"); + content.innerHTML = message; + + // 푸터 + const footer = document.createElement("div"); + + const cancelBtn = document.createElement("button"); + cancelBtn.className = `btn ${cancelClass}`; + cancelBtn.textContent = cancelText; + cancelBtn.onclick = () => { + this.close(); + resolve(false); + }; + + const confirmBtn = document.createElement("button"); + confirmBtn.className = `btn ${confirmClass}`; + confirmBtn.textContent = confirmText; + confirmBtn.onclick = () => { + this.close(); + resolve(true); + }; + + footer.appendChild(cancelBtn); + footer.appendChild(confirmBtn); + + // 모달 생성 및 열기 + this.create({ header, content, footer }); + this.open(); + }); + } +} + +/** + * 알림 모달 (Alert Dialog) + */ +class AlertModal extends ModalBase { + /** + * 알림 모달 표시 + * @param {Object} options - 옵션 + * @returns {Promise} + */ + show(options = {}) { + const { + title = "알림", + message = "", + confirmText = "확인", + confirmClass = "btn-primary", + } = options; + + return new Promise((resolve) => { + // 헤더 + const header = document.createElement("div"); + header.innerHTML = `

${title}

`; + + // 컨텐츠 + const content = document.createElement("div"); + content.innerHTML = message; + + // 푸터 + const footer = document.createElement("div"); + + const confirmBtn = document.createElement("button"); + confirmBtn.className = `btn ${confirmClass}`; + confirmBtn.textContent = confirmText; + confirmBtn.onclick = () => { + this.close(); + resolve(); + }; + + footer.appendChild(confirmBtn); + + // 모달 생성 및 열기 + this.create({ header, content, footer }); + this.open(); + }); + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { ModalBase, ConfirmModal, AlertModal }; +} diff --git a/src/js/common/ModalUtils.js b/src/js/common/ModalUtils.js new file mode 100644 index 0000000..4afdb6f --- /dev/null +++ b/src/js/common/ModalUtils.js @@ -0,0 +1,194 @@ +/** + * 모달 공통 유틸리티 모듈 + * 모든 모달에서 공통으로 사용되는 기능들을 모듈화 + * @module ModalUtils + */ + +class ModalUtils { + /** + * 모달 닫기 이벤트 설정 (공통) + * @param {HTMLElement} modalElement - 모달 요소 + * @param {Object} options - 옵션 + * @param {Function} options.onClose - 닫기 시 실행할 콜백 함수 + * @param {Function} options.onCleanup - 정리 작업 콜백 함수 + * @param {string} options.closeSelector - 닫기 버튼 셀렉터 (기본: ".close") + * @param {boolean} options.closeOnBackdrop - 배경 클릭 시 닫기 (기본: true) + * @param {boolean} options.closeOnEscape - ESC 키로 닫기 (기본: true) + * @returns {Object} 정리 함수들을 담은 객체 + */ + static setupCloseEvents(modalElement, options = {}) { + const { + onClose = null, + onCleanup = null, + closeSelector = ".close", + closeOnBackdrop = true, + closeOnEscape = true, + } = options; + + const cleanupFunctions = []; + + // 닫기 함수 + const closeModal = () => { + if (onClose && typeof onClose === "function") { + onClose(); + } else { + // 기본 닫기 동작 + ModalUtils.stopVideo(modalElement); + modalElement.style.display = "none"; + setTimeout(() => { + if (modalElement && modalElement.parentNode) { + modalElement.parentNode.removeChild(modalElement); + } + }, 300); + } + + // 정리 작업 실행 + if (onCleanup && typeof onCleanup === "function") { + onCleanup(); + } + + // 등록된 정리 함수들 실행 + cleanupFunctions.forEach((fn) => { + if (typeof fn === "function") { + fn(); + } + }); + }; + + // 닫기 버튼 이벤트 + const closeBtn = modalElement.querySelector(closeSelector); + if (closeBtn) { + closeBtn.onclick = closeModal; + } + + // 배경 클릭 이벤트 + if (closeOnBackdrop) { + modalElement.onclick = (e) => { + if (e.target === modalElement) { + closeModal(); + } + }; + } + + // ESC 키 이벤트 + let escHandler = null; + if (closeOnEscape) { + escHandler = (e) => { + if (e.key === "Escape") { + closeModal(); + document.removeEventListener("keydown", escHandler); + } + }; + document.addEventListener("keydown", escHandler); + } + + // 정리 함수 반환 + return { + close: closeModal, + cleanup: () => { + if (escHandler) { + document.removeEventListener("keydown", escHandler); + } + if (closeBtn) { + closeBtn.onclick = null; + } + modalElement.onclick = null; + }, + addCleanup: (fn) => { + if (typeof fn === "function") { + cleanupFunctions.push(fn); + } + }, + }; + } + + /** + * 비디오 정지 (공통) + * @param {HTMLElement} modalElement - 모달 요소 + */ + static stopVideo(modalElement) { + // iframe 비디오 정지 + const iframe = modalElement.querySelector("#videoFrame"); + if (iframe) { + // VideoBase가 있으면 사용, 없으면 직접 정지 + if (typeof VideoBase !== "undefined" && VideoBase.stop) { + VideoBase.stop(iframe); + } else { + iframe.src = ""; + } + } + + // video 태그 비디오 정지 + const video = modalElement.querySelector("video"); + if (video) { + video.pause(); + video.currentTime = 0; + } + } + + /** + * Observer 정리 (공통) + * @param {HTMLElement} modalElement - 모달 요소 + */ + static cleanupObservers(modalElement) { + // ResizeObserver 정리 + if (modalElement._resizeObserver) { + modalElement._resizeObserver.disconnect(); + modalElement._resizeObserver = null; + } + + // MutationObserver 정리 + if (modalElement._mutationObserver) { + modalElement._mutationObserver.disconnect(); + modalElement._mutationObserver = null; + } + + // 타이머 정리 + if (modalElement._heightAdjustTimer) { + clearTimeout(modalElement._heightAdjustTimer); + modalElement._heightAdjustTimer = null; + } + + // window resize 이벤트 리스너 제거 + if (modalElement._windowResizeHandler) { + window.removeEventListener("resize", modalElement._windowResizeHandler); + modalElement._windowResizeHandler = null; + } + } + + /** + * 모달 완전 정리 (비디오 정지 + Observer 정리) + * @param {HTMLElement} modalElement - 모달 요소 + */ + static cleanup(modalElement) { + ModalUtils.stopVideo(modalElement); + ModalUtils.cleanupObservers(modalElement); + } + + /** + * 모달 제거 (애니메이션 포함) + * @param {HTMLElement} modalElement - 모달 요소 + * @param {Object} options - 옵션 + * @param {number} options.duration - 애니메이션 지속 시간 (기본: 300ms) + * @param {Function} options.onComplete - 완료 콜백 + */ + static remove(modalElement, options = {}) { + const { duration = 300, onComplete = null } = options; + + // 정리 작업 + ModalUtils.cleanup(modalElement); + + // 애니메이션 + modalElement.style.opacity = "0"; + modalElement.style.transition = `opacity ${duration}ms ease`; + + setTimeout(() => { + if (modalElement && modalElement.parentNode) { + modalElement.parentNode.removeChild(modalElement); + } + if (onComplete && typeof onComplete === "function") { + onComplete(); + } + }, duration); + } +} diff --git a/src/js/common/Utils.js b/src/js/common/Utils.js new file mode 100644 index 0000000..6aa8bc5 --- /dev/null +++ b/src/js/common/Utils.js @@ -0,0 +1,277 @@ +/** + * 공통 유틸리티 함수 모듈 (Utils.js) + * ======================================== + * 자주 쓰는 헬퍼 함수들을 모아둔 정적 클래스입니다. + * + * [초보자용 사용 예] + * Utils.delay(1000) → 1초 대기 후 Promise 반환 (async/await와 함께) + * Utils.debounce(fn, 300) → 입력 등 연속 호출 방지 (마지막 호출만 실행) + * Utils.throttle(fn, 100) → resize 등 빈번한 이벤트 제한 (100ms마다 1번) + * Utils.formatDate(date) → "YYYY-MM-DD" 형식 + * Utils.formatNumber(1234) → "1,234" (천단위 콤마) + * Utils.storage.set('key', value) → localStorage 쉽게 사용 + * + * @module Utils + */ +class Utils { + /** + * 딜레이 함수 + * @param {number} ms - 지연 시간 (밀리초) + * @returns {Promise} + */ + static delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + /** + * 디바운스 함수 + * @param {Function} func - 실행할 함수 + * @param {number} wait - 대기 시간 + * @returns {Function} + */ + static debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + + /** + * 쓰로틀 함수 + * @param {Function} func - 실행할 함수 + * @param {number} limit - 제한 시간 + * @returns {Function} + */ + static throttle(func, limit) { + let inThrottle; + return function (...args) { + if (!inThrottle) { + func.apply(this, args); + inThrottle = true; + setTimeout(() => (inThrottle = false), limit); + } + }; + } + + /** + * 랜덤 ID 생성 + * @param {number} length - ID 길이 + * @returns {string} + */ + static generateId(length = 8) { + return Math.random() + .toString(36) + .substring(2, length + 2); + } + + /** + * 깊은 복사 + * @param {*} obj - 복사할 객체 + * @returns {*} + */ + static deepClone(obj) { + if (obj === null || typeof obj !== "object") return obj; + if (obj instanceof Date) return new Date(obj.getTime()); + if (obj instanceof Array) return obj.map((item) => this.deepClone(item)); + + const clonedObj = {}; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + clonedObj[key] = this.deepClone(obj[key]); + } + } + return clonedObj; + } + + /** + * 객체 병합 + * @param {Object} target - 대상 객체 + * @param {Object} source - 소스 객체 + * @returns {Object} + */ + static mergeDeep(target, source) { + const output = { ...target }; + if (this.isObject(target) && this.isObject(source)) { + Object.keys(source).forEach((key) => { + if (this.isObject(source[key])) { + if (!(key in target)) { + Object.assign(output, { [key]: source[key] }); + } else { + output[key] = this.mergeDeep(target[key], source[key]); + } + } else { + Object.assign(output, { [key]: source[key] }); + } + }); + } + return output; + } + + /** + * 객체 확인 + * @param {*} item - 확인할 항목 + * @returns {boolean} + */ + static isObject(item) { + return item && typeof item === "object" && !Array.isArray(item); + } + + /** + * URL 파라미터 파싱 + * @param {string} url - URL 문자열 + * @returns {Object} + */ + static parseUrlParams(url = window.location.search) { + const params = new URLSearchParams(url); + const result = {}; + for (const [key, value] of params) { + result[key] = value; + } + return result; + } + + /** + * 퍼센트 계산 + * @param {number} current - 현재 값 + * @param {number} total - 전체 값 + * @param {number} decimals - 소수점 자리수 + * @returns {number} + */ + static calculatePercent(current, total, decimals = 0) { + if (total === 0) return 0; + const percent = (current / total) * 100; + return decimals > 0 ? parseFloat(percent.toFixed(decimals)) : Math.round(percent); + } + + /** + * 배열 섞기 (Fisher-Yates shuffle) + * @param {Array} array - 섞을 배열 + * @returns {Array} + */ + static shuffleArray(array) { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + } + + /** + * 배열 청크 분할 + * @param {Array} array - 분할할 배열 + * @param {number} size - 청크 크기 + * @returns {Array} + */ + static chunkArray(array, size) { + const chunks = []; + for (let i = 0; i < array.length; i += size) { + chunks.push(array.slice(i, i + size)); + } + return chunks; + } + + /** + * 로컬 스토리지 관리 + */ + static storage = { + set(key, value) { + try { + localStorage.setItem(key, JSON.stringify(value)); + return true; + } catch (e) { + console.error("Storage set error:", e); + return false; + } + }, + get(key, defaultValue = null) { + try { + const item = localStorage.getItem(key); + return item ? JSON.parse(item) : defaultValue; + } catch (e) { + console.error("Storage get error:", e); + return defaultValue; + } + }, + remove(key) { + try { + localStorage.removeItem(key); + return true; + } catch (e) { + console.error("Storage remove error:", e); + return false; + } + }, + clear() { + try { + localStorage.clear(); + return true; + } catch (e) { + console.error("Storage clear error:", e); + return false; + } + }, + }; + + /** + * 날짜 포맷팅 + * @param {Date} date - 포맷할 날짜 + * @param {string} format - 포맷 문자열 (YYYY-MM-DD, YYYY.MM.DD 등) + * @returns {string} + */ + static formatDate(date, format = "YYYY-MM-DD") { + const d = new Date(date); + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, "0"); + const day = String(d.getDate()).padStart(2, "0"); + const hour = String(d.getHours()).padStart(2, "0"); + const minute = String(d.getMinutes()).padStart(2, "0"); + const second = String(d.getSeconds()).padStart(2, "0"); + + return format + .replace("YYYY", year) + .replace("MM", month) + .replace("DD", day) + .replace("HH", hour) + .replace("mm", minute) + .replace("ss", second); + } + + /** + * 숫자 포맷팅 (천단위 콤마) + * @param {number} num - 포맷할 숫자 + * @returns {string} + */ + static formatNumber(num) { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + } + + /** + * 파일 크기 포맷팅 + * @param {number} bytes - 바이트 크기 + * @param {number} decimals - 소수점 자리수 + * @returns {string} + */ + static formatFileSize(bytes, decimals = 2) { + if (bytes === 0) return "0 Bytes"; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = Utils; +} + diff --git a/src/js/common/VideoBase.js b/src/js/common/VideoBase.js new file mode 100644 index 0000000..f411127 --- /dev/null +++ b/src/js/common/VideoBase.js @@ -0,0 +1,409 @@ +/** + * 비디오 관련 기본 클래스 + * @module VideoBase + */ +class VideoBase { + constructor(config = {}) { + this.config = { + autoplay: false, + controls: true, + loop: false, + muted: false, + ...config, + }; + } + + /** + * YouTube 비디오 URL 생성 + * @param {string} videoId - YouTube 비디오 ID + * @param {Object} options - 추가 옵션 + * @returns {string} + */ + static getYouTubeUrl(videoId, options = {}) { + const { + autoplay = 0, + controls = 1, + loop = 0, + muted = 0, + rel = 0, + modestbranding = 1, + start = 0, + } = options; + + const params = new URLSearchParams({ + autoplay, + controls, + loop, + muted, + rel, + modestbranding, + ...(start > 0 && { start }), + }); + + return `https://www.youtube.com/embed/${videoId}?${params.toString()}`; + } + + /** + * YouTube 썸네일 URL 생성 + * @param {string} videoId - YouTube 비디오 ID + * @param {string} quality - 품질 (default, hq, mq, sd, maxres) + * @returns {string} + */ + static getYouTubeThumbnail(videoId, quality = "sddefault") { + const qualities = { + default: "default.jpg", + hq: "hqdefault.jpg", + mq: "mqdefault.jpg", + sd: "sddefault.jpg", + maxres: "maxresdefault.jpg", + }; + + const thumbnailFile = qualities[quality] || qualities.sd; + return `https://img.youtube.com/vi/${videoId}/${thumbnailFile}`; + } + + /** + * 비디오 ID 추출 (YouTube URL에서) + * @param {string} url - YouTube URL + * @returns {string|null} + */ + static extractYouTubeId(url) { + const patterns = [ + /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, + /youtube\.com\/v\/([^&\n?#]+)/, + ]; + + for (const pattern of patterns) { + const match = url.match(pattern); + if (match && match[1]) { + return match[1]; + } + } + + return null; + } + + /** + * 비디오 iframe 생성 + * @param {string} videoId - 비디오 ID + * @param {Object} options - iframe 옵션 + * @returns {HTMLIFrameElement} + */ + static createIframe(videoId, options = {}) { + const { + width = "100%", + height = "100%", + autoplay = 0, + controls = 1, + className = "", + id = "", + } = options; + + const iframe = document.createElement("iframe"); + iframe.width = width; + iframe.height = height; + iframe.src = this.getYouTubeUrl(videoId, { autoplay, controls }); + iframe.frameBorder = "0"; + iframe.allow = + "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"; + iframe.allowFullscreen = true; + + if (className) iframe.className = className; + if (id) iframe.id = id; + + return iframe; + } + + /** + * 비디오 재생 + * @param {HTMLIFrameElement} iframe - iframe 요소 + */ + static play(iframe) { + if (!iframe || !iframe.contentWindow) return; + iframe.contentWindow.postMessage( + '{"event":"command","func":"playVideo","args":""}', + "*" + ); + } + + /** + * 비디오 일시정지 + * @param {HTMLIFrameElement} iframe - iframe 요소 + */ + static pause(iframe) { + if (!iframe || !iframe.contentWindow) return; + iframe.contentWindow.postMessage( + '{"event":"command","func":"pauseVideo","args":""}', + "*" + ); + } + + /** + * 비디오 정지 + * @param {HTMLIFrameElement} iframe - iframe 요소 + */ + static stop(iframe) { + if (!iframe || !iframe.contentWindow) return; + iframe.contentWindow.postMessage( + '{"event":"command","func":"stopVideo","args":""}', + "*" + ); + } + + /** + * 비디오 시간 이동 + * @param {HTMLIFrameElement} iframe - iframe 요소 + * @param {number} seconds - 이동할 시간 (초) + */ + static seekTo(iframe, seconds) { + if (!iframe || !iframe.contentWindow) return; + iframe.contentWindow.postMessage( + `{"event":"command","func":"seekTo","args":[${seconds}, true]}`, + "*" + ); + } + + /** + * 비디오 볼륨 설정 + * @param {HTMLIFrameElement} iframe - iframe 요소 + * @param {number} volume - 볼륨 (0-100) + */ + static setVolume(iframe, volume) { + if (!iframe || !iframe.contentWindow) return; + const vol = Math.max(0, Math.min(100, volume)); + iframe.contentWindow.postMessage( + `{"event":"command","func":"setVolume","args":[${vol}]}`, + "*" + ); + } + + /** + * 비디오 음소거 토글 + * @param {HTMLIFrameElement} iframe - iframe 요소 + * @param {boolean} mute - 음소거 여부 + */ + static toggleMute(iframe, mute) { + if (!iframe || !iframe.contentWindow) return; + const func = mute ? "mute" : "unMute"; + iframe.contentWindow.postMessage( + `{"event":"command","func":"${func}","args":""}`, + "*" + ); + } +} + +/** + * 비디오 데이터 모델 + */ +class VideoModel { + constructor(data = {}) { + this.id = data.id || null; + this.url = data.url || ""; + this.title = data.title || ""; + this.category = data.category || ""; + this.subcate = data.subcate || ""; + this.keywords = data.keywords || []; + this.bookmark = data.bookmark || false; + this.completed = data.completed || false; + this.picker = data.picker || ""; + this.type = data.type || "main"; + this.gauge = data.gauge || 0; + this.description = data.description || ""; + this.duration = data.duration || 0; + this.createdAt = data.createdAt || new Date(); + this.updatedAt = data.updatedAt || new Date(); + } + + /** + * 비디오 ID 가져오기 + * @returns {string} + */ + getVideoId() { + return VideoBase.extractYouTubeId(this.url) || this.url; + } + + /** + * 썸네일 URL 가져오기 + * @param {string} quality - 품질 + * @returns {string} + */ + getThumbnailUrl(quality = "sd") { + const videoId = this.getVideoId(); + return VideoBase.getYouTubeThumbnail(videoId, quality); + } + + /** + * 임베드 URL 가져오기 + * @param {Object} options - 옵션 + * @returns {string} + */ + getEmbedUrl(options = {}) { + const videoId = this.getVideoId(); + return VideoBase.getYouTubeUrl(videoId, options); + } + + /** + * 북마크 토글 + */ + toggleBookmark() { + this.bookmark = !this.bookmark; + this.updatedAt = new Date(); + } + + /** + * 완료 상태 설정 + * @param {boolean} completed - 완료 여부 + */ + setCompleted(completed) { + this.completed = completed; + this.updatedAt = new Date(); + } + + /** + * JSON으로 변환 + * @returns {Object} + */ + toJSON() { + return { + id: this.id, + url: this.url, + title: this.title, + category: this.category, + subcate: this.subcate, + keywords: this.keywords, + bookmark: this.bookmark, + completed: this.completed, + picker: this.picker, + type: this.type, + gauge: this.gauge, + description: this.description, + duration: this.duration, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + }; + } +} + +/** + * 비디오 컬렉션 관리 + */ +class VideoCollection { + constructor(videos = []) { + this.videos = videos.map((v) => (v instanceof VideoModel ? v : new VideoModel(v))); + } + + /** + * 비디오 추가 + * @param {Object|VideoModel} video - 비디오 데이터 + */ + add(video) { + const model = video instanceof VideoModel ? video : new VideoModel(video); + this.videos.push(model); + } + + /** + * 비디오 제거 + * @param {number|string} id - 비디오 ID + */ + remove(id) { + this.videos = this.videos.filter((v) => v.id !== id); + } + + /** + * ID로 비디오 찾기 + * @param {number|string} id - 비디오 ID + * @returns {VideoModel|null} + */ + findById(id) { + return this.videos.find((v) => v.id === id) || null; + } + + /** + * 필터링 + * @param {Function} predicate - 필터 함수 + * @returns {VideoCollection} + */ + filter(predicate) { + return new VideoCollection(this.videos.filter(predicate)); + } + + /** + * 카테고리로 필터링 + * @param {string} category - 카테고리 + * @returns {VideoCollection} + */ + filterByCategory(category) { + return this.filter((v) => v.category === category); + } + + /** + * 키워드로 필터링 + * @param {Array} keywords - 키워드 배열 + * @returns {VideoCollection} + */ + filterByKeywords(keywords) { + return this.filter((v) => keywords.some((k) => v.keywords.includes(k))); + } + + /** + * 북마크된 비디오만 + * @returns {VideoCollection} + */ + getBookmarked() { + return this.filter((v) => v.bookmark); + } + + /** + * 완료된 비디오만 + * @returns {VideoCollection} + */ + getCompleted() { + return this.filter((v) => v.completed); + } + + /** + * 미완료 비디오만 + * @returns {VideoCollection} + */ + getIncomplete() { + return this.filter((v) => !v.completed); + } + + /** + * 정렬 + * @param {Function} compareFn - 비교 함수 + * @returns {VideoCollection} + */ + sort(compareFn) { + return new VideoCollection([...this.videos].sort(compareFn)); + } + + /** + * 개수 + * @returns {number} + */ + count() { + return this.videos.length; + } + + /** + * 배열로 변환 + * @returns {Array} + */ + toArray() { + return this.videos; + } + + /** + * JSON으로 변환 + * @returns {Array} + */ + toJSON() { + return this.videos.map((v) => v.toJSON()); + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { VideoBase, VideoModel, VideoCollection }; +} diff --git a/src/js/common/VideoModalBase.js b/src/js/common/VideoModalBase.js new file mode 100644 index 0000000..50c5212 --- /dev/null +++ b/src/js/common/VideoModalBase.js @@ -0,0 +1,1259 @@ +/** + * 비디오 모달 기본 클래스 + * ModalBase와 VideoBase를 활용한 통합 비디오 모달 모듈 + * @module VideoModalBase + */ + +class VideoModalBase extends ModalBase { + constructor(config = {}) { + super({ + animation: "fade", + animationDuration: 300, + closeOnEscape: true, + closeOnBackdrop: true, + ...config, + }); + + this.config = { + // 비디오 관련 설정 + videos: config.videos || [], + modalPath: config.modalPath || "./_modal/video.html", + modalPathTemplate: config.modalPathTemplate || "./_modal/video-{type}.html", + + // 이벤트 콜백 + onVideoLoad: config.onVideoLoad || null, + onModalReady: config.onModalReady || null, + + // 레이아웃 설정 + enableHeightAdjustment: config.enableHeightAdjustment !== false, + enableCommentResizer: config.enableCommentResizer !== false, + enableCommentBox: config.enableCommentBox !== false, + + ...config, + }; + + this.currentVideo = null; + this.currentModalElement = null; + this.resizeObserver = null; + this.mutationObserver = null; + this.heightAdjustTimer = null; + this.resizerCleanup = null; + this._retryCount = 0; + this._isAdjustingHeight = false; + this._pendingHeightAdjust = null; // 배치 처리를 위한 대기 중인 조정 + this._lastHeightValues = {}; // 마지막 높이 값 캐시 (불필요한 재계산 방지) + } + + /** + * 비디오 모달 로드 및 열기 + * @param {number|string|Object} videoIdOrData - 비디오 ID 또는 비디오 데이터 객체 + * @returns {Promise} + */ + async openVideo(videoIdOrData) { + try { + // 기존 모달 정리 + this.destroy(); + + // 비디오 데이터 가져오기 + let videoData; + if (typeof videoIdOrData === "object") { + videoData = videoIdOrData; + } else { + const videoId = typeof videoIdOrData === "string" ? parseInt(videoIdOrData) : videoIdOrData; + videoData = this.config.videos.find((v) => v.id === videoId); + } + + if (!videoData) { + throw new Error("비디오 데이터를 찾을 수 없습니다"); + } + + this.currentVideo = videoData; + + // 모달 타입 결정 + const modalType = videoData.type || "main"; + + // 모달 HTML 로드 + const modalHTML = await this.loadModalHTML(modalType); + + // 모달 생성 + const modalElement = this.createModalFromHTML(modalHTML, modalType); + + // DOM에 추가 + document.body.appendChild(modalElement); + this.currentModalElement = modalElement; + + // 비디오 설정 + this.setupVideo(modalElement, videoData); + + // 모달 컨텐츠 업데이트 + this.updateModalContent(modalElement, videoData); + + // 스크립트 실행 + this.executeModalScripts(modalElement); + + // 모달 열기 (애니메이션) + await Utils.delay(50); + await AnimationUtils.fade(modalElement, "in", 300); + + // user-text 높이 조정 (모달이 완전히 열린 후) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + this.adjustUserTextHeights(modalElement); + }); + }); + await Utils.delay(100); + + // 이벤트 설정 + this.setupModalEvents(modalType, modalElement); + + // onVideoLoad 콜백 + if (this.config.onVideoLoad) { + await this.config.onVideoLoad(videoData, modalElement); + } + + // onModalReady 콜백 + if (this.config.onModalReady) { + await this.config.onModalReady(modalElement); + } + + // body 스크롤 잠금 + if (typeof bodyLock === "function") { + bodyLock(); + } + + return this; + } catch (error) { + console.error("비디오 모달 로드 오류:", error); + if (typeof alert === "function") { + alert("비디오를 로드하는 중 오류가 발생했습니다."); + } + throw error; + } + } + + /** + * 모달 HTML 로드 + * @param {string} modalType - 모달 타입 + * @returns {Promise} + */ + async loadModalHTML(modalType) { + let modalPath = this.config.modalPath; + + if (modalType !== "main" && this.config.modalPathTemplate) { + modalPath = this.config.modalPathTemplate.replace("{type}", modalType); + } + + if (!modalPath) { + throw new Error("모달 경로가 설정되지 않았습니다"); + } + + const response = await fetch(`${modalPath}?t=${Date.now()}`); + if (!response.ok) { + throw new Error(`모달 로드 실패: ${modalPath}`); + } + + return await response.text(); + } + + /** + * HTML에서 모달 요소 생성 + * @param {string} modalHTML - 모달 HTML 문자열 + * @param {string} modalType - 모달 타입 + * @returns {HTMLElement} + */ + createModalFromHTML(modalHTML, modalType) { + const parser = new DOMParser(); + const doc = parser.parseFromString(modalHTML, "text/html"); + const modalElement = doc.querySelector(".modal.video"); + + if (!modalElement) { + throw new Error("모달 요소를 찾을 수 없습니다"); + } + + modalElement.id = "videoModal"; + modalElement.setAttribute("data-type", modalType); + + return modalElement; + } + + /** + * 비디오 설정 + * @param {HTMLElement} modalElement - 모달 요소 + * @param {Object} videoData - 비디오 데이터 + */ + setupVideo(modalElement, videoData) { + const iframe = DOMUtils.$("#videoFrame", modalElement); + if (iframe) { + let videoId; + + // VideoModel 사용 (있는 경우) + if (typeof VideoModel !== "undefined") { + const videoModel = videoData instanceof VideoModel + ? videoData + : new VideoModel(videoData); + + videoId = videoModel.getVideoId + ? videoModel.getVideoId() + : videoData.url || videoData.id; + } else { + // VideoModel이 없으면 직접 사용 + videoId = videoData.url || videoData.id; + } + + // VideoBase를 사용하여 YouTube URL 생성 + iframe.src = VideoBase.getYouTubeUrl(videoId, { autoplay: 1 }); + } + } + + /** + * 모달 컨텐츠 업데이트 + * @param {HTMLElement} modalElement - 모달 요소 + * @param {Object} videoData - 비디오 데이터 + */ + updateModalContent(modalElement, videoData) { + const metaEm = modalElement.querySelector(".meta em"); + const hasSubcate = videoData.subcate && videoData.subcate.trim() !== ""; + + const categorySpan = modalElement.querySelector(".meta span"); + if (categorySpan) { + categorySpan.textContent = hasSubcate + ? videoData.category + : videoData.category; + } + + const titleH3 = modalElement.querySelector(".tit-box h3"); + if (titleH3) { + titleH3.textContent = videoData.title; + } + + if (metaEm) { + if (hasSubcate) { + metaEm.textContent = videoData.subcate; + metaEm.style.display = ""; + } else { + metaEm.textContent = ""; + metaEm.style.display = "none"; + } + } + } + + /** + * 모달 스크립트 실행 + * @param {HTMLElement} modalElement - 모달 요소 + */ + executeModalScripts(modalElement) { + const scripts = DOMUtils.$$("script", modalElement); + + scripts.forEach((oldScript, index) => { + const newScript = document.createElement("script"); + + if (oldScript.type) { + newScript.type = oldScript.type; + } + + if (oldScript.src) { + // 외부 스크립트 + newScript.src = oldScript.src; + newScript.onload = () => { + console.log(`외부 스크립트 로드 완료: ${oldScript.src}`); + }; + newScript.onerror = (e) => { + console.error(`외부 스크립트 로드 실패: ${oldScript.src}`, e); + }; + } else { + // 인라인 스크립트 + newScript.textContent = oldScript.textContent; + console.log(`인라인 스크립트 실행 #${index + 1}`); + } + + // 기존 스크립트를 새 스크립트로 교체 + oldScript.parentNode.replaceChild(newScript, oldScript); + }); + } + + /** + * 타입별 이벤트 설정 + * @param {string} modalType - 모달 타입 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupModalEvents(modalType, modalElement) { + console.log("모달 이벤트 설정:", modalType); + + // 닫기 이벤트 설정 + this.setupModalCloseEvents(modalElement); + + // 타입별 이벤트 설정 + switch (modalType) { + case "main": + if (this.config.enableCommentResizer) { + this.setupCommentResizer(modalElement); + } + if (this.config.enableCommentBox) { + this.setupCommentBox(modalElement); + } + if (this.config.enableHeightAdjustment) { + this.initializeHeightAdjustment(modalElement); + } + break; + + case "comment": + if (this.config.enableCommentBox) { + this.setupCommentBox(modalElement); + } + this.adjustCommentOnlyLayout(modalElement); + break; + + case "onboarding": + if (this.config.enableCommentBox) { + this.setupCommentBox(modalElement); + } + break; + + case "essential": + this.setupEssentialLayout(modalElement); + break; + + case "learning": + this.setupLearningLayout(modalElement); + break; + + default: + console.warn("알 수 없는 모달 타입:", modalType); + } + } + + /** + * 모달 닫기 이벤트 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupModalCloseEvents(modalElement) { + const closeBtn = modalElement.querySelector(".close"); + if (closeBtn) { + closeBtn.onclick = () => { + this.close(); + }; + } + + modalElement.onclick = (e) => { + if (e.target === modalElement) { + this.close(); + } + }; + + const escHandler = (e) => { + if (e.key === "Escape") { + this.close(); + document.removeEventListener("keydown", escHandler); + } + }; + document.addEventListener("keydown", escHandler); + } + + /** + * 모달 닫기 (오버라이드) + * @returns {Promise} + */ + async close() { + if (this.currentModalElement) { + // 비디오 중지 + const iframe = this.currentModalElement.querySelector("#videoFrame"); + if (iframe) { + VideoBase.stop(iframe); + } + + // Observer들 정리 + this.cleanupObservers(); + + // 페이드아웃 효과 + await AnimationUtils.fade(this.currentModalElement, "out", 300); + + // DOM에서 제거 + if (this.currentModalElement && this.currentModalElement.parentNode) { + this.currentModalElement.parentNode.removeChild(this.currentModalElement); + } + } + + this.currentModalElement = null; + this.currentVideo = null; + + // body 스크롤 해제 + if (typeof bodyUnlock === "function") { + bodyUnlock(); + } + + return this; + } + + /** + * 모달 파괴 (오버라이드) + */ + destroy() { + this.cleanupObservers(); + this.close(); + } + + /** + * Observer 정리 + */ + cleanupObservers() { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + this.mutationObserver = null; + } + + if (this.heightAdjustTimer) { + clearTimeout(this.heightAdjustTimer); + this.heightAdjustTimer = null; + } + + if (this.resizerCleanup) { + this.resizerCleanup(); + this.resizerCleanup = null; + } + + this._retryCount = 0; + } + + // ======================================== + // 레이아웃 관련 메서드 (기존 로직 유지) + // ======================================== + + /** + * 높이 조정 초기화 (리플로우 최적화) + * @param {HTMLElement} modalElement - 모달 요소 + */ + initializeHeightAdjustment(modalElement) { + // 1단계: 즉시 시도 (첫 렌더링) + this._scheduleHeightAdjust(modalElement); + + // 2단계: requestAnimationFrame (2프레임 대기) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + this._scheduleHeightAdjust(modalElement); + }); + }); + + // 3-5단계: 지연 시도 (배치 처리) + [50, 100, 200].forEach((delay) => { + setTimeout(() => { + this._scheduleHeightAdjust(modalElement); + }, delay); + }); + + // 6단계: ResizeObserver 설정 + this.setupResizeObserver(modalElement); + + // 7단계: MutationObserver 설정 + this.setupMutationObserver(modalElement); + + // 8단계: 이미지 로딩 대기 + this.waitForImagesAndAdjust(modalElement); + } + + /** + * 높이 조정 스케줄링 (리플로우 최소화를 위한 배치 처리) + * @private + * @param {HTMLElement} modalElement - 모달 요소 + */ + _scheduleHeightAdjust(modalElement) { + if (this._pendingHeightAdjust) { + cancelAnimationFrame(this._pendingHeightAdjust); + } + this._pendingHeightAdjust = requestAnimationFrame(() => { + this.adjustVideoListHeight(modalElement); + this._pendingHeightAdjust = null; + }); + } + + /** + * ResizeObserver 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupResizeObserver(modalElement) { + const videoSide = modalElement?.querySelector(".video-side"); + const commentWrap = modalElement?.querySelector(".comment-wrap"); + + if (!videoSide) return; + + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } + + // throttled 높이 조정 함수 (리플로우 최소화) + const throttledAdjustHeight = Utils.throttle(() => { + this._scheduleHeightAdjust(modalElement); + }, 100); // 100ms throttle + + this.resizeObserver = new ResizeObserver((entries) => { + throttledAdjustHeight(); + }); + + this.resizeObserver.observe(videoSide); + if (commentWrap) { + this.resizeObserver.observe(commentWrap); + } + } + + /** + * MutationObserver 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupMutationObserver(modalElement) { + const videoList = modalElement?.querySelector(".video-list"); + + if (!videoList) return; + + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + } + + // throttled 높이 조정 함수 (리플로우 최소화) + const throttledAdjustHeight = Utils.throttle(() => { + this._scheduleHeightAdjust(modalElement); + }, 100); // 100ms throttle + + this.mutationObserver = new MutationObserver((mutations) => { + throttledAdjustHeight(); + }); + + this.mutationObserver.observe(videoList, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["style", "class"], + }); + } + + /** + * 이미지 로딩 대기 후 높이 조정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + async waitForImagesAndAdjust(modalElement) { + const videoSide = modalElement?.querySelector(".video-side"); + if (!videoSide) return; + + const images = videoSide.querySelectorAll("img"); + + if (images.length === 0) { + return; + } + + console.log(`이미지 ${images.length}개 로딩 대기 중...`); + + const imagePromises = Array.from(images).map((img) => { + if (img.complete) return Promise.resolve(); + + return new Promise((resolve) => { + img.onload = () => resolve(); + img.onerror = () => resolve(); + setTimeout(resolve, 10000); // 10초 타임아웃 + }); + }); + + await Promise.all(imagePromises); + console.log("모든 이미지 로딩 완료: 높이 재조정"); + this.adjustVideoListHeight(modalElement); + } + + /** + * 높이 조정 (main, learning, onboarding 타입 지원) + * @param {HTMLElement} modalElement - 모달 요소 + */ + adjustVideoListHeight(modalElement) { + const videoSide = modalElement?.querySelector(".video-side"); + const videoHeader = modalElement?.querySelector(".video-header"); + const videoList = modalElement?.querySelector(".video-list"); + const learningList = modalElement?.querySelector(".learning-list"); + const commentWrap = modalElement?.querySelector(".comment-wrap"); + + // learning-list가 있으면 더 상세한 계산 사용, 없으면 video-list 사용 + const targetList = learningList || videoList; + + if (!videoSide || !videoHeader || !targetList) { + console.warn("[VideoModalBase] 필요한 요소를 찾을 수 없습니다"); + return false; + } + + // 높이 조정 중 플래그 설정 + if (!this._isAdjustingHeight) { + this._isAdjustingHeight = true; + } + + // 스크롤 위치 저장 + const savedScrollTop = targetList.scrollTop; + + // 전체 높이 + const totalHeight = videoSide.clientHeight; + + // 높이가 0이거나 비정상적으로 작으면 DOM이 아직 렌더링되지 않은 것 + if (totalHeight < 100) { + console.warn( + `[VideoModalBase] videoSide 높이가 비정상적으로 작습니다: ${totalHeight}px. 재측정 예약...` + ); + + // 최대 3번까지만 재시도 + if (!this._retryCount) this._retryCount = 0; + if (this._retryCount < 3) { + this._retryCount++; + this._isAdjustingHeight = false; + setTimeout(() => this.adjustVideoListHeight(modalElement), 100); + } else { + console.error("[VideoModalBase] 높이 측정 재시도 횟수 초과"); + this._retryCount = 0; + this._isAdjustingHeight = false; + } + return false; + } + + // 재시도 카운터 초기화 + this._retryCount = 0; + + // 헤더 높이 + const headerHeight = videoHeader.offsetHeight; + + // comment-wrap 높이 + const commentWrapHeight = commentWrap ? commentWrap.offsetHeight : 0; + + // learning-list가 있는 경우 더 상세한 계산 + let availableHeight; + if (learningList) { + // video-list가 있으면 제목과 padding 고려 (learning-list는 video-list 내부에 있음) + let titleHeight = 0; + let paddingTop = 0; + let paddingBottom = 0; + + if (videoList) { + // video-list의 제목 높이 + const videoListTitle = videoList.querySelector("h5.tit"); + titleHeight = videoListTitle ? videoListTitle.offsetHeight : 0; + + // video-list의 padding 값 계산 + const videoListStyle = window.getComputedStyle(videoList); + paddingTop = parseInt(videoListStyle.paddingTop) || 0; + paddingBottom = parseInt(videoListStyle.paddingBottom) || 0; + } + + // learning-list에 사용 가능한 최대 높이 + // learning-list는 video-list 내부에 있으므로 video-list의 제목과 padding을 고려해야 함 + // comment-wrap이 없어도 (commentWrapHeight = 0) 정상 작동 + // commentInfoOffset은 comment-wrap 내부 요소이므로 이미 commentWrapHeight에 포함됨 + availableHeight = + totalHeight - + headerHeight - + commentWrapHeight - + titleHeight - + paddingTop - + paddingBottom - + 10; + } else { + // video-list만 있는 경우 (기존 로직) + availableHeight = totalHeight - headerHeight - commentWrapHeight; + } + + // 사용 가능한 높이가 음수이거나 너무 작으면 경고 + if (availableHeight < 50) { + console.warn( + `[VideoModalBase] 사용 가능한 높이가 너무 작습니다: ${availableHeight}px` + ); + this._isAdjustingHeight = false; + return false; + } + + // 리스트의 실제 컨텐츠 높이 측정 (스타일 제거 후) + const originalHeight = targetList.style.height; + const originalOverflow = targetList.style.overflowY; + + targetList.style.height = "auto"; + targetList.style.overflowY = "visible"; + + const listContentHeight = targetList.scrollHeight; + + targetList.style.height = originalHeight; + targetList.style.overflowY = originalOverflow; + + // 컨텐츠가 적으면 컨텐츠 높이만큼, 많으면 사용 가능한 높이만큼 + const listHeight = Math.min(listContentHeight, availableHeight); + + // 최소 높이 보장 + const finalHeight = Math.max(listHeight, 100); + + // 현재 설정된 높이 확인 + const currentHeight = targetList.style.height + ? parseInt(targetList.style.height) + : targetList.offsetHeight; + + // 스크롤 필요 여부 확인 + const needsScroll = listContentHeight > availableHeight; + + console.log("[VideoModalBase] 높이 측정 성공:", { + totalHeight, + headerHeight, + commentWrapHeight, + availableHeight, + listContentHeight, + listHeight, + finalHeight, + currentHeight, + needsScroll, + savedScrollTop, + hasLearningList: !!learningList, + }); + + // 높이가 실제로 변경되는 경우에만 스타일 업데이트 (리플로우 최소화) + const heightChanged = Math.abs(currentHeight - finalHeight) > 1; + + // 캐시 키 생성 + const cacheKey = `${targetList.className}-${modalElement.id || 'default'}`; + const lastHeight = this._lastHeightValues[cacheKey]; + + // 높이가 변경되지 않고, 스크롤 여부도 동일하면 스타일 업데이트 생략 + if (!heightChanged && lastHeight === finalHeight && + targetList.style.overflowY === (needsScroll ? "auto" : "hidden")) { + this._isAdjustingHeight = false; + return true; // 변경 없음, 조기 종료 + } + + // 스타일 업데이트를 requestAnimationFrame으로 배치 + // learning-list의 height와 overflow-y는 CSS로 관리 (인라인 스타일 제거) + requestAnimationFrame(() => { + // learning-list가 아닌 경우에만 height 설정 (video-list만 있는 경우) + if (heightChanged && !learningList) { + targetList.style.height = finalHeight + "px"; + } + // learning-list의 overflow-y는 CSS로 관리 + if (!learningList) { + targetList.style.overflowY = needsScroll ? "hidden" : "hidden"; + } + + // 캐시 업데이트 + this._lastHeightValues[cacheKey] = finalHeight; + }); + + // video-list가 별도로 있는 경우에도 스크롤 여부 체크 (리플로우 최소화) + // overflow-y: hidden 제거, CSS 기본값 사용 + if (videoList && learningList) { + // learning-list가 있는 경우에도 video-list의 높이 설정 + // video-list는 learning-list를 포함하므로, 전체 사용 가능한 높이에서 제목과 padding을 빼면 됨 + const videoListTitle = videoList.querySelector("h5.tit"); + const videoListTitleHeight = videoListTitle ? videoListTitle.offsetHeight : 0; + const videoListStyle = window.getComputedStyle(videoList); + const videoListPaddingTop = parseInt(videoListStyle.paddingTop) || 0; + const videoListPaddingBottom = parseInt(videoListStyle.paddingBottom) || 0; + + // video-list에 사용 가능한 높이 (전체 높이에서 헤더, 댓글, 제목, padding 제외) + const videoListAvailableHeight = + totalHeight - + headerHeight - + commentWrapHeight - + videoListTitleHeight - + videoListPaddingTop - + videoListPaddingBottom - + 10; + + // video-list의 실제 컨텐츠 높이 측정 (learning-list 포함) + const videoListOriginalHeight = videoList.style.height; + const videoListOriginalOverflow = videoList.style.overflowY; + + videoList.style.height = "auto"; + videoList.style.overflowY = "visible"; + + const videoListContentHeight = videoList.scrollHeight; + + videoList.style.height = videoListOriginalHeight; + videoList.style.overflowY = videoListOriginalOverflow; + + // video-list의 높이 계산 (learning-list를 포함한 전체 높이) + const videoListHeight = Math.min(videoListContentHeight, videoListAvailableHeight); + const videoListFinalHeight = Math.max(videoListHeight, 100); + + // video-list의 현재 높이 확인 + const videoListCurrentHeight = videoList.style.height + ? parseInt(videoList.style.height) + : videoList.offsetHeight; + + const videoListHeightChanged = Math.abs(videoListCurrentHeight - videoListFinalHeight) > 1; + const videoListNeedsScroll = videoListContentHeight > videoListFinalHeight; + + // video-list 높이 및 스크롤 설정 + requestAnimationFrame(() => { + if (videoListHeightChanged) { + videoList.style.height = videoListFinalHeight + "px"; + } + if (videoListNeedsScroll) { + videoList.style.overflowY = "auto"; + } else { + videoList.style.overflowY = ""; // CSS 기본값 사용 + } + }); + } else if (videoList && !learningList) { + // video-list만 있는 경우 스크롤 여부 체크 + const videoListContentHeight = videoList.scrollHeight; + const videoListAvailableHeight = videoList.clientHeight; + const videoListNeedsScroll = videoListContentHeight > videoListAvailableHeight; + + // requestAnimationFrame으로 배치하여 리플로우 최소화 + requestAnimationFrame(() => { + // 스크롤이 필요할 때만 auto 설정, 필요 없을 때는 CSS 기본값 사용 + if (videoListNeedsScroll) { + videoList.style.overflowY = "auto"; + } else { + videoList.style.overflowY = ""; // CSS 기본값 사용 + } + }); + } + + // 컨텐츠 높이를 CSS 변수로 설정 (::before 요소에서 사용) + if (learningList) { + learningList.style.setProperty("--scroll-height", `${listContentHeight}px`); + } + + // 스크롤 위치 복원 (높이 변경 여부와 관계없이) + requestAnimationFrame(() => { + targetList.scrollTop = savedScrollTop; + // 높이 조정 완료 후 플래그 해제 + this._isAdjustingHeight = false; + }); + + return true; + } + + /** + * 댓글 전용 레이아웃 조정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + adjustCommentOnlyLayout(modalElement) { + const commentWrap = modalElement?.querySelector(".comment-wrap"); + if (commentWrap) { + if (this.config.enableCommentResizer) { + this.setupCommentResizer(modalElement); + } + if (this.config.enableCommentBox) { + this.setupCommentBox(modalElement); + } + } + } + + /** + * 필수 교육 레이아웃 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupEssentialLayout(modalElement) { + console.log("필수 교육 레이아웃 설정"); + // 예: 진도율 표시, 완료 체크 등 + } + + /** + * 학습 레이아웃 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupLearningLayout(modalElement) { + console.log("학습 레이아웃 설정"); + // 예: 퀴즈, 학습 노트 등 + } + + /** + * 댓글 리사이저 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupCommentResizer(modalElement) { + const resizer = modalElement?.querySelector(".comment-resizer"); + const commentListWrap = modalElement?.querySelector(".comment-list-wrap"); + const commentWrap = modalElement?.querySelector(".comment-wrap"); + const videoSide = modalElement?.querySelector(".video-side"); + + if (!resizer || !commentListWrap || !commentWrap) { + console.warn("리사이저 요소를 찾을 수 없습니다"); + return; + } + + let isResizing = false; + let startY = 0; + let startHeight = 0; + const minHeight = 52; + const maxHeight = 600; + + const commentList = commentListWrap.querySelector(".comment-list"); + const hasComments = commentList && commentList.children.length > 0; + + console.log(`댓글 리사이저 초기화: 댓글 ${hasComments ? "있음" : "없음"}`); + + if (!hasComments) { + resizer.style.display = "none"; + commentListWrap.style.height = "0px"; + commentListWrap.style.flex = ""; + } else { + resizer.style.display = "block"; + this.adjustCommentListWrapHeight(commentListWrap); + const h = commentListWrap.offsetHeight; + commentListWrap.style.flex = "0 0 " + h + "px"; + } + + resizer.addEventListener("mousedown", (e) => { + isResizing = true; + startY = e.clientY; + startHeight = commentListWrap.offsetHeight; + resizer.classList.add("resizing"); + document.body.style.cursor = "ns-resize"; + document.body.style.userSelect = "none"; + e.preventDefault(); + }); + + const onMouseMove = (e) => { + if (!isResizing) return; + const delta = startY - e.clientY; + const newHeight = Math.min(Math.max(startHeight + delta, minHeight), maxHeight); + commentListWrap.style.flex = "0 0 " + newHeight + "px"; + commentListWrap.style.height = newHeight + "px"; + + if (videoSide && this.config.enableHeightAdjustment) { + requestAnimationFrame(() => { + this.adjustVideoListHeight(modalElement); + }); + } + }; + + const onMouseUp = () => { + if (!isResizing) return; + isResizing = false; + resizer.classList.remove("resizing"); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + }; + + document.addEventListener("mousemove", onMouseMove); + document.addEventListener("mouseup", onMouseUp); + + this.resizerCleanup = () => { + document.removeEventListener("mousemove", onMouseMove); + document.removeEventListener("mouseup", onMouseUp); + }; + } + + /** + * 댓글 입력 기능 설정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + setupCommentBox(modalElement) { + const textarea = modalElement?.querySelector(".comment-box textarea"); + const btnCancel = modalElement?.querySelector(".btn-cancel"); + const btnSave = modalElement?.querySelector(".btn-save"); + + if (!textarea || !btnCancel || !btnSave) { + console.warn("댓글 박스 요소를 찾을 수 없습니다"); + return; + } + + const adjustTextareaHeight = (textareaEl) => { + // 높이 설정: 1줄=32px, 2줄=52px, 3줄 이상=72px + const singleLineHeight = 32; + const twoLineHeight = 52; + const maxHeight = 72; // 3줄 이상 최대 높이 + + // 입력값이 없으면 기본 32px로 설정 + if (!textareaEl.value || textareaEl.value.trim().length === 0) { + textareaEl.style.height = singleLineHeight + "px"; + textareaEl.style.overflowY = "hidden"; + textareaEl.style.overflowX = "hidden"; + return; + } + + // 스크롤바가 보이지 않도록 먼저 overflow를 hidden으로 설정 + textareaEl.style.overflowY = "hidden"; + textareaEl.style.overflowX = "hidden"; + + // 높이를 auto로 설정하여 실제 컨텐츠 높이 측정 + textareaEl.style.height = "auto"; + + // 강제 리플로우 (높이 계산을 위해) + void textareaEl.offsetHeight; + + const scrollHeight = textareaEl.scrollHeight; + const computedStyle = window.getComputedStyle(textareaEl); + const lineHeight = parseFloat(computedStyle.lineHeight) || 20; + const paddingTop = parseFloat(computedStyle.paddingTop) || 0; + const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0; + + // 실제 줄 수 계산을 위한 높이 기준 + const actualSingleLineHeight = lineHeight + paddingTop + paddingBottom; + const actualTwoLineHeight = (lineHeight * 2) + paddingTop + paddingBottom; + const actualThreeLineHeight = (lineHeight * 3) + paddingTop + paddingBottom; + + // 높이 설정: 1줄=32px, 2줄=52px, 3줄 이상=72px + if (scrollHeight <= actualSingleLineHeight) { + // 1줄: 32px + textareaEl.style.height = singleLineHeight + "px"; + textareaEl.style.overflowY = "hidden"; + } else if (scrollHeight <= actualTwoLineHeight) { + // 2줄: 52px + textareaEl.style.height = twoLineHeight + "px"; + textareaEl.style.overflowY = "hidden"; + } else if (scrollHeight <= actualThreeLineHeight) { + // 3줄: 72px + textareaEl.style.height = maxHeight + "px"; + textareaEl.style.overflowY = "hidden"; + } else { + // 3줄 초과: 72px 고정, 스크롤 활성화 + textareaEl.style.height = maxHeight + "px"; + textareaEl.style.overflowY = "auto"; + } + + textareaEl.style.overflowX = "hidden"; + }; + + adjustTextareaHeight(textarea); + + textarea.addEventListener("input", (e) => { + const hasValue = e.target.value.trim().length > 0; + adjustTextareaHeight(e.target); + + if (hasValue) { + btnCancel.removeAttribute("disabled"); + btnSave.removeAttribute("disabled"); + btnSave.classList.add("btn-active"); + } else { + btnCancel.setAttribute("disabled", "disabled"); + btnSave.setAttribute("disabled", "disabled"); + btnSave.classList.remove("btn-active"); + } + }); + + btnCancel.addEventListener("click", (e) => { + e.preventDefault(); + textarea.value = ""; + adjustTextareaHeight(textarea); + btnCancel.setAttribute("disabled", "disabled"); + btnSave.setAttribute("disabled", "disabled"); + btnSave.classList.remove("btn-active"); + textarea.focus(); + }); + + btnSave.addEventListener("click", (e) => { + e.preventDefault(); + const comment = textarea.value.trim(); + if (comment) { + console.log("댓글 작성:", comment); + // TODO: 실제 댓글 추가 로직 + this.showCommentSection(modalElement); + textarea.value = ""; + adjustTextareaHeight(textarea); + btnCancel.setAttribute("disabled", "disabled"); + btnSave.setAttribute("disabled", "disabled"); + btnSave.classList.remove("btn-active"); + } + }); + } + + /** + * 댓글 섹션 표시 (첫 댓글 작성 시) + * @param {HTMLElement} modalElement - 모달 요소 + */ + showCommentSection(modalElement) { + const commentListWrap = modalElement?.querySelector(".comment-list-wrap"); + const resizer = modalElement?.querySelector(".comment-resizer"); + + if (!commentListWrap || !resizer) return; + + if (commentListWrap.offsetHeight === 0) { + resizer.style.display = "block"; + // 댓글이 있으면 컨텐츠 높이만큼 설정 + this.adjustCommentListWrapHeight(commentListWrap); + console.log("첫 댓글 추가: 댓글 섹션 표시 (컨텐츠 높이)"); + + if (this.config.enableHeightAdjustment) { + requestAnimationFrame(() => { + this.adjustVideoListHeight(modalElement); + }); + } + } + + // user-text 높이 조정 + this.adjustUserTextHeights(modalElement); + } + + /** + * .comment-list-wrap의 높이를 컨텐츠에 맞게 조정 + * @param {HTMLElement} commentListWrap - 댓글 리스트 래퍼 요소 + */ + adjustCommentListWrapHeight(commentListWrap) { + if (!commentListWrap) return; + + const modalElement = commentListWrap.closest(".modal"); + if (!modalElement) return; + + const commentList = commentListWrap.querySelector(".comment-list"); + if (!commentList || commentList.children.length === 0) { + commentListWrap.style.height = "0px"; + // 댓글이 없을 때도 video-list 스크롤 재측정 + if (this.config.enableHeightAdjustment) { + requestAnimationFrame(() => { + this.adjustVideoListHeight(modalElement); + }); + } + return; + } + + // 현재 높이 저장 + const originalHeight = commentListWrap.style.height; + const originalOverflow = commentListWrap.style.overflowY; + + // 높이를 auto로 설정하여 실제 컨텐츠 높이 측정 + commentListWrap.style.height = "auto"; + commentListWrap.style.overflowY = "hidden"; + + const scrollHeight = commentListWrap.scrollHeight; + const computedStyle = window.getComputedStyle(commentListWrap); + const paddingTop = parseFloat(computedStyle.paddingTop) || 0; + const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0; + + // 컨텐츠 높이 + padding + const contentHeight = scrollHeight + paddingTop + paddingBottom; + + // 최소 높이 보장 (52px) + const minHeight = 52; + const finalHeight = Math.max(contentHeight, minHeight); + + // CSS max-height 제한 확인 + const maxHeight = parseFloat(computedStyle.maxHeight) || Infinity; + const adjustedHeight = Math.min(finalHeight, maxHeight); + + commentListWrap.style.height = adjustedHeight + "px"; + // overflow-y는 CSS로 관리 (인라인 스타일 제거) + commentListWrap.style.overflowY = ""; + + console.log("[VideoModalBase] comment-list-wrap 높이 조정:", { + scrollHeight, + paddingTop, + paddingBottom, + contentHeight, + finalHeight, + adjustedHeight, + maxHeight + }); + + // comment-list-wrap 높이가 변경되었으므로 video-list 스크롤 재측정 + if (this.config.enableHeightAdjustment) { + requestAnimationFrame(() => { + this.adjustVideoListHeight(modalElement); + }); + } + } + + /** + * .user-text 요소들의 높이를 컨텐츠에 맞게 조정 + * @param {HTMLElement} modalElement - 모달 요소 + */ + adjustUserTextHeights(modalElement) { + const userTexts = modalElement?.querySelectorAll(".user-text"); + if (!userTexts || userTexts.length === 0) { + console.log("[VideoModalBase] .user-text 요소를 찾을 수 없습니다"); + return; + } + + console.log(`[VideoModalBase] .user-text 요소 ${userTexts.length}개 발견`); + + // 먼저 모든 textarea를 완전히 숨김 (깜박임 방지) + userTexts.forEach((textarea) => { + // 초기 상태 저장 + const originalVisibility = textarea.style.visibility; + const originalOpacity = textarea.style.opacity; + const originalOverflow = textarea.style.overflowY; + + // 완전히 숨김 처리 + textarea.style.visibility = "hidden"; + textarea.style.opacity = "0"; + // 스크롤이 보이지 않도록 확실히 설정 + textarea.style.overflowY = "hidden"; + + // 원래 값 저장 (나중에 복원용) + textarea._originalStyles = { + visibility: originalVisibility, + opacity: originalOpacity, + overflow: originalOverflow + }; + }); + + // 높이 계산 및 설정 + userTexts.forEach((textarea, index) => { + // 기본 높이 32px (1줄) + const defaultHeight = 32; + + // 스크롤바가 보이지 않도록 먼저 overflow를 hidden으로 설정 + textarea.style.overflowY = "hidden"; + textarea.style.overflowX = "hidden"; + + // 높이를 auto로 설정하여 실제 컨텐츠 높이 측정 + // 이때도 overflow는 hidden으로 유지하여 스크롤이 보이지 않도록 + textarea.style.height = "auto"; + + // 강제 리플로우 (높이 계산을 위해) + void textarea.offsetHeight; + + const scrollHeight = textarea.scrollHeight; + const computedStyle = window.getComputedStyle(textarea); + const lineHeight = parseFloat(computedStyle.lineHeight) || 20; + const paddingTop = parseFloat(computedStyle.paddingTop) || 0; + const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0; + + // 1줄 높이 계산 + const singleLineHeight = lineHeight + paddingTop + paddingBottom; + // 2줄 높이 계산 (2줄 이상부터 컨텐츠 높이 적용) + const twoLineHeight = (lineHeight * 2) + paddingTop + paddingBottom; + + console.log(`[VideoModalBase] textarea #${index + 1}:`, { + scrollHeight, + lineHeight, + paddingTop, + paddingBottom, + singleLineHeight, + twoLineHeight, + defaultHeight, + isTwoLinesOrMore: scrollHeight > twoLineHeight + }); + + // 2줄 이상이면 컨텐츠 높이만큼 설정, 1줄이면 기본 32px 유지 + if (scrollHeight > twoLineHeight) { + textarea.style.height = scrollHeight + "px"; + textarea.style.overflowY = "hidden"; + textarea.style.overflowX = "hidden"; + console.log(`[VideoModalBase] textarea #${index + 1} 높이 조정: ${scrollHeight}px (2줄 이상)`); + } else { + // 1줄이면 기본 32px 유지 (인라인 스타일 제거하여 CSS 기본값 사용) + textarea.style.height = ""; + textarea.style.overflowY = "hidden"; // overflow는 hidden 유지 + textarea.style.overflowX = "hidden"; + console.log(`[VideoModalBase] textarea #${index + 1} 높이 유지: CSS 기본값 32px (1줄)`); + } + }); + + // 모든 높이 계산이 완료된 후 한 번에 표시 (2프레임 대기하여 확실히) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + userTexts.forEach((textarea) => { + // 원래 스타일 복원 또는 기본값으로 설정 + if (textarea._originalStyles) { + textarea.style.visibility = textarea._originalStyles.visibility || ""; + textarea.style.opacity = textarea._originalStyles.opacity || ""; + // overflow는 hidden 유지 (스크롤 방지) + textarea.style.overflowY = "hidden"; + delete textarea._originalStyles; + } else { + textarea.style.visibility = ""; + textarea.style.opacity = ""; + textarea.style.overflowY = "hidden"; + } + }); + }); + }); + } +} + +// ES6 모듈 내보내기 +if (typeof module !== "undefined" && module.exports) { + module.exports = { VideoModalBase }; +} + diff --git a/src/js/intro/animation.js b/src/js/intro/animation.js new file mode 100644 index 0000000..060cb33 --- /dev/null +++ b/src/js/intro/animation.js @@ -0,0 +1,492 @@ +/** + * intro-animation.js + * 인트로 페이지 애니메이션 함수들 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, Utils, AnimationUtils) + */ + +// 전역 의존성 (폴백 포함) +const _domUtils = typeof DOMUtils !== 'undefined' ? DOMUtils : null; +const _errorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; +const _utils = typeof Utils !== 'undefined' ? Utils : null; +const _animationUtils = typeof AnimationUtils !== 'undefined' ? AnimationUtils : null; + +/** + * 에러 처리 헬퍼 + * @private + */ +function _handleError(error, context, additionalInfo = {}) { + if (_errorHandler) { + _errorHandler.handle(error, { + context: `IntroAnimation.${context}`, + component: 'IntroAnimation', + ...additionalInfo + }, false); + } else { + console.error(`[IntroAnimation] ${context}:`, error, additionalInfo); + } +} + +/** + * Section 1: 이름 타이핑 애니메이션 + */ +function typeName() { + try { + // 안전성 검사 + if (typeof INTRO_CONFIG === 'undefined' || typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_CONFIG or INTRO_STATE is not defined'), 'typeName'); + return; + } + + const typedNameEl = _domUtils?.$("#typedName") || document.getElementById("typedName"); + const cursorEl = _domUtils?.$("#cursor") || document.getElementById("cursor"); + + if (!typedNameEl) { + _handleError(new Error('typedName element not found'), 'typeName'); + return; + } + + if (!INTRO_CONFIG.fullName || typeof INTRO_CONFIG.fullName !== 'string' || INTRO_CONFIG.fullName.length === 0) { + _handleError(new Error('INTRO_CONFIG.fullName is empty or invalid'), 'typeName'); + return; + } + + if (INTRO_STATE.typedIndex < INTRO_CONFIG.fullName.length) { + const currentText = INTRO_CONFIG.fullName.slice(0, INTRO_STATE.typedIndex + 1); + typedNameEl.textContent = currentText; + INTRO_STATE.typedIndex++; + + const delay = INTRO_CONFIG.typingSpeed || 100; + if (_utils && _utils.delay) { + _utils.delay(delay).then(() => typeName()); + } else { + setTimeout(typeName, delay); + } + } else { + if (cursorEl) { + if (_domUtils && _domUtils.setStyles) { + _domUtils.setStyles(cursorEl, { opacity: '0' }); + } else { + cursorEl.style.opacity = "0"; + } + } + + const delay = 600; + if (_utils && _utils.delay) { + _utils.delay(delay).then(() => animateWelcome()); + } else { + setTimeout(animateWelcome, delay); + } + } + } catch (error) { + _handleError(error, 'typeName'); + } +} + +/** + * Section 1: 환영 메시지 애니메이션 + */ +function animateWelcome() { + try { + if (typeof INTRO_CONFIG === 'undefined' || typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_CONFIG or INTRO_STATE is not defined'), 'animateWelcome'); + return; + } + + const container = _domUtils?.$("#welcome") || document.getElementById("welcome"); + if (!container) { + _handleError(new Error('welcome element not found'), 'animateWelcome'); + return; + } + + if (!INTRO_CONFIG.welcomeText || typeof INTRO_CONFIG.welcomeText !== 'string') { + _handleError(new Error('INTRO_CONFIG.welcomeText is empty or invalid'), 'animateWelcome'); + return; + } + + const tempDiv = _domUtils?.createElement('div') || document.createElement("div"); + tempDiv.innerHTML = INTRO_CONFIG.welcomeText; + + let html = ""; + tempDiv.childNodes.forEach((node) => { + try { + if (node.nodeType === Node.TEXT_NODE) { + const text = node.textContent || ''; + html += text + .split("") + .map((c) => { + const escaped = c === " " ? " " : (c === "<" ? "<" : c === ">" ? ">" : c === "&" ? "&" : c); + return `${escaped}`; + }) + .join(""); + } else if (node.nodeType === Node.ELEMENT_NODE) { + const innerText = node.textContent || ''; + const tagName = node.tagName.toLowerCase(); + const escapedText = innerText + .split("") + .map((c) => { + const escaped = c === " " ? " " : (c === "<" ? "<" : c === ">" ? ">" : c === "&" ? "&" : c); + return `${escaped}`; + }) + .join(""); + html += `<${tagName}>${escapedText}`; + } + } catch (error) { + _handleError(error, 'animateWelcome.processNode', { node }); + } + }); + + container.innerHTML = html; + const spans = container.querySelectorAll("span"); + const charSpeed = INTRO_CONFIG.welcomeCharSpeed || 50; + + spans.forEach((char, i) => { + const delay = i * charSpeed; + if (_utils && _utils.delay) { + _utils.delay(delay).then(() => { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(char, 'show'); + } else { + char.classList.add("show"); + } + }); + } else { + setTimeout(() => { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(char, 'show'); + } else { + char.classList.add("show"); + } + }, delay); + } + }); + + // 애니메이션 완료 후 자동 스크롤 타이머 시작 + const fullNameLength = INTRO_CONFIG.fullName ? INTRO_CONFIG.fullName.length : 0; + const totalDelay = fullNameLength * charSpeed + 1000; + + if (_utils && _utils.delay) { + _utils.delay(totalDelay).then(() => startAutoScrollTimer()); + } else { + setTimeout(() => startAutoScrollTimer(), totalDelay); + } + } catch (error) { + _handleError(error, 'animateWelcome'); + } +} + +/** + * Section 2: 업데이트 텍스트 + 카드 애니메이션 + * - 모바일(≤992px): section2Text 애니메이션 → 페이드아웃 → card-list 표시 + * - 데스크탑(>992px): section2Text + card-list 동시 표시 + */ +function animateSection2() { + try { + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE is not defined'), 'animateSection2'); + return; + } + + const isMobile = window.innerWidth <= 992; + const lines = ["line1", "line2", "line3"]; + const cards = ["card1", "card2", "card3", "card4"]; + const lineDelay = 600; + const cardDelay = isMobile ? 375 : 250; + const totalLineDelay = lines.length * lineDelay + 300; + const delayFn = (_utils && _utils.delay) + ? (ms) => _utils.delay(ms) + : (ms) => new Promise(resolve => setTimeout(resolve, ms)); + + // 라인 순차 애니메이션 + lines.forEach((id, i) => { + const element = _domUtils?.$(`#${id}`) || document.getElementById(id); + if (!element) { + console.warn(`[IntroAnimation] Element #${id} not found`); + return; + } + delayFn(i * lineDelay).then(() => { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(element, 'show'); + } else { + element.classList.add("show"); + } + }); + }); + + // 카드 순차 애니메이션 헬퍼 + const showCards = () => { + cards.forEach((id, i) => { + const cardElement = _domUtils?.$(`#${id}`) || document.getElementById(id); + if (!cardElement) { + console.warn(`[IntroAnimation] Element #${id} not found`); + return; + } + delayFn(i * cardDelay).then(() => { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(cardElement, 'show'); + } else { + cardElement.classList.add("show"); + } + if (i === 3) { + INTRO_STATE.section2AnimDone = true; + startAutoScrollTimer(); + } + }); + }); + }; + + delayFn(totalLineDelay).then(() => { + if (isMobile) { + // 모바일: section2Text 페이드아웃 → cardsContainer 표시 → 카드 애니메이션 + const section2Text = _domUtils?.$("#section2Text") || document.getElementById("section2Text"); + const cardsContainer = _domUtils?.$("#cardsContainer") || document.getElementById("cardsContainer"); + + if (section2Text) { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(section2Text, 'fade-out'); + } else { + section2Text.classList.add("fade-out"); + } + } + + delayFn(400).then(() => { + if (section2Text) { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(section2Text, 'hidden'); + _domUtils.removeClasses(section2Text, 'fade-out'); + } else { + section2Text.classList.add("hidden"); + section2Text.classList.remove("fade-out"); + } + } + + // 카드 표시 시 스크롤 인디케이터 숨기기 + const scrollIndicator = _domUtils?.$("#scrollIndicator") || document.getElementById("scrollIndicator"); + if (scrollIndicator) { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(scrollIndicator, 'hidden'); + } else { + scrollIndicator.classList.add("hidden"); + } + } + + if (cardsContainer) { + if (_domUtils && _domUtils.setStyles) { + _domUtils.setStyles(cardsContainer, { display: 'flex' }); + } else { + cardsContainer.style.display = "flex"; + } + } + showCards(); + }); + } else { + // 데스크탑: 바로 카드 표시 + showCards(); + } + }); + } catch (error) { + _handleError(error, 'animateSection2'); + } +} + +/** + * Section 2: 애니메이션 리셋 + */ +function resetSection2() { + try { + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE is not defined'), 'resetSection2'); + return; + } + + const lines = ["line1", "line2", "line3"]; + const cards = ["card1", "card2", "card3", "card4"]; + + lines.forEach((id) => { + const element = _domUtils?.$(`#${id}`) || document.getElementById(id); + if (element) { + if (_domUtils && _domUtils.removeClasses) { + _domUtils.removeClasses(element, 'show'); + } else { + element.classList.remove("show"); + } + } + }); + + cards.forEach((id) => { + const element = _domUtils?.$(`#${id}`) || document.getElementById(id); + if (element) { + if (_domUtils && _domUtils.removeClasses) { + _domUtils.removeClasses(element, 'show'); + } else { + element.classList.remove("show"); + } + } + }); + + INTRO_STATE.section2AnimDone = false; + } catch (error) { + _handleError(error, 'resetSection2'); + } +} + +/** + * Section 3: CTA 애니메이션 + */ +function animateSection3() { + try { + const ctaBtn = _domUtils?.$("#ctaBtn") || document.getElementById("ctaBtn"); + const ctaLine1 = _domUtils?.$("#ctaLine1") || document.getElementById("ctaLine1"); + const ctaLine2 = _domUtils?.$("#ctaLine2") || document.getElementById("ctaLine2"); + + const addShowClass = (element, delay) => { + if (!element) { + console.warn(`[IntroAnimation] Element not found`); + return; + } + + if (_utils && _utils.delay) { + _utils.delay(delay).then(() => { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(element, 'show'); + } else { + element.classList.add("show"); + } + }); + } else { + setTimeout(() => { + if (_domUtils && _domUtils.addClasses) { + _domUtils.addClasses(element, 'show'); + } else { + element.classList.add("show"); + } + }, delay); + } + }; + + addShowClass(ctaBtn, 100); + addShowClass(ctaLine1, 300); + addShowClass(ctaLine2, 500); + + // 모바일: 마스크 중앙에서 전체 화면으로 자동 확장 + if (window.innerWidth <= 992) { + const maskContainer = _domUtils?.$("#maskContainer") || document.getElementById("maskContainer"); + if (maskContainer) { + if (_utils && _utils.delay) { + _utils.delay(200).then(() => maskContainer.classList.add("expand")); + } else { + setTimeout(() => maskContainer.classList.add("expand"), 200); + } + } + } + } catch (error) { + _handleError(error, 'animateSection3'); + } +} + +/** + * Section 3: 애니메이션 리셋 + */ +function resetSection3() { + try { + const ctaLine1 = _domUtils?.$("#ctaLine1") || document.getElementById("ctaLine1"); + const ctaLine2 = _domUtils?.$("#ctaLine2") || document.getElementById("ctaLine2"); + const ctaBtn = _domUtils?.$("#ctaBtn") || document.getElementById("ctaBtn"); + + const removeShowClass = (element) => { + if (element) { + if (_domUtils && _domUtils.removeClasses) { + _domUtils.removeClasses(element, 'show'); + } else { + element.classList.remove("show"); + } + } + }; + + removeShowClass(ctaLine1); + removeShowClass(ctaLine2); + removeShowClass(ctaBtn); + + // 모바일: 마스크 확장 리셋 + const maskContainer = _domUtils?.$("#maskContainer") || document.getElementById("maskContainer"); + if (maskContainer) { + maskContainer.classList.remove("expand"); + } + } catch (error) { + _handleError(error, 'resetSection3'); + } +} + +/** + * 자동 스크롤 타이머 시작 + */ +function startAutoScrollTimer() { + try { + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE is not defined'), 'startAutoScrollTimer'); + return; + } + + if (typeof handleScrollDown !== 'function') { + _handleError(new Error('handleScrollDown function is not defined'), 'startAutoScrollTimer'); + return; + } + + // 기존 타이머 정리 + if (INTRO_STATE.autoScrollTimer) { + clearTimeout(INTRO_STATE.autoScrollTimer); + INTRO_STATE.autoScrollTimer = null; + } + + // 첫 번째 세션(섹션 0)일 때만 1500ms, 이후는 3000ms + const delay = INTRO_STATE.currentSection === 0 ? 1500 : 3000; + + if (_utils && _utils.delay) { + _utils.delay(delay).then(() => { + // 마지막 상호작용 후 설정된 시간이 지났는지 확인 + const now = Date.now(); + const lastInteraction = INTRO_STATE.lastInteractionTime || 0; + + if (now - lastInteraction >= delay) { + try { + handleScrollDown(); + } catch (error) { + _handleError(error, 'startAutoScrollTimer.handleScrollDown'); + } + } + }); + } else { + INTRO_STATE.autoScrollTimer = setTimeout(() => { + try { + // 마지막 상호작용 후 설정된 시간이 지났는지 확인 + const now = Date.now(); + const lastInteraction = INTRO_STATE.lastInteractionTime || 0; + + if (now - lastInteraction >= delay) { + handleScrollDown(); + } + } catch (error) { + _handleError(error, 'startAutoScrollTimer.handleScrollDown'); + } + }, delay); + } + } catch (error) { + _handleError(error, 'startAutoScrollTimer'); + } +} + +/** + * 사용자 상호작용 감지 - 자동 스크롤 타이머 리셋 + */ +function resetAutoScrollTimer() { + try { + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE is not defined'), 'resetAutoScrollTimer'); + return; + } + + INTRO_STATE.lastInteractionTime = Date.now(); + startAutoScrollTimer(); + } catch (error) { + _handleError(error, 'resetAutoScrollTimer'); + } +} \ No newline at end of file diff --git a/src/js/intro/events.js b/src/js/intro/events.js new file mode 100644 index 0000000..7f30744 --- /dev/null +++ b/src/js/intro/events.js @@ -0,0 +1,162 @@ +/** + * intro-events.js + * 이벤트 리스너 설정 + */ + +/** + * 모든 이벤트 리스너 등록 + */ +function initEventListeners() { + // 마우스 휠 이벤트 + document.addEventListener("wheel", (e) => { + resetAutoScrollTimer(); + if (e.deltaY > 0) handleScrollDown(); + else handleScrollUp(); + }); + + // 터치 이벤트 + let touchStartY = 0; + document.addEventListener( + "touchstart", + (e) => { + touchStartY = e.touches[0].clientY; + resetAutoScrollTimer(); + }, + { passive: true } + ); + + document.addEventListener( + "touchend", + (e) => { + const diff = touchStartY - e.changedTouches[0].clientY; + if (Math.abs(diff) > 60) { + if (diff > 0) handleScrollDown(); + else handleScrollUp(); + } + }, + { passive: true } + ); + + // 키보드 이벤트 + document.addEventListener("keydown", (e) => { + if (["ArrowDown", "PageDown", " "].includes(e.key)) { + e.preventDefault(); + resetAutoScrollTimer(); + handleScrollDown(); + } else if (["ArrowUp", "PageUp"].includes(e.key)) { + e.preventDefault(); + resetAutoScrollTimer(); + handleScrollUp(); + } + }); + + // 마우스 움직임 이벤트 (Section 3 마스크 효과) + // requestAnimationFrame을 사용하여 부드러운 업데이트 보장 + let rafId = null; + let lastMouseEvent = null; + + document.addEventListener("mousemove", (e) => { + if (typeof INTRO_STATE !== 'undefined' && INTRO_STATE.currentSection === 2) { + // 마지막 마우스 이벤트 저장 + lastMouseEvent = e; + + // 이미 요청된 애니메이션 프레임이 없으면 새로 요청 + if (rafId === null) { + rafId = requestAnimationFrame(() => { + if (lastMouseEvent) { + handleMouseMoveOnSection3(lastMouseEvent); + } + rafId = null; + lastMouseEvent = null; + }); + } + } + }); +} + +/** + * Section 3의 마우스 움직임 처리 (마스크 효과) + * @param {MouseEvent} e - 마우스 이벤트 + */ +function handleMouseMoveOnSection3(e) { + try { + // 입력 검증 + if (!e || typeof e.clientX !== 'number' || typeof e.clientY !== 'number') { + return; + } + + const maskContainer = document.getElementById("maskContainer"); + if (!maskContainer) { + return; // 요소가 없으면 조용히 종료 + } + + const mouseX = e.clientX; + const mouseY = e.clientY; + + const mainContainer = document.getElementById("mainContainer"); + if (!mainContainer) { + return; + } + + const mainContainerRect = mainContainer.getBoundingClientRect(); + const h1Elements = mainContainer.querySelectorAll("p"); + const ctaBtn = document.querySelector(".cta-btn"); + let hovering = false; + let isCtaBtnHovering = false; + + // .cta-btn 위에 마우스가 있는지 먼저 체크 + if (ctaBtn) { + try { + const ctaRect = ctaBtn.getBoundingClientRect(); + if ( + mouseX >= ctaRect.left && + mouseX <= ctaRect.right && + mouseY >= ctaRect.top && + mouseY <= ctaRect.bottom + ) { + hovering = true; + isCtaBtnHovering = true; + } + } catch (error) { + console.warn('[IntroEvents] CTA 버튼 체크 중 오류:', error); + } + } + + // p 요소들 체크 + try { + h1Elements.forEach((element) => { + try { + const h1Rect = element.getBoundingClientRect(); + if ( + mouseX >= h1Rect.left && + mouseX <= h1Rect.right && + mouseY >= h1Rect.top && + mouseY <= h1Rect.bottom + ) { + hovering = true; + } + } catch (error) { + // 개별 요소 체크 실패는 무시 + } + }); + } catch (error) { + console.warn('[IntroEvents] 요소 체크 중 오류:', error); + } + + // .cta-btn 위에 있을 때만 세로 위치 고정, 그 외에는 마우스 위치 따라감 + const targetY = isCtaBtnHovering + ? mainContainerRect.top + mainContainerRect.height / 2.3 + : mouseY; + + // CSS 변수 설정 (안전하게) + const x = Math.max(0, Math.min(mouseX, window.innerWidth)); + const y = Math.max(0, Math.min(targetY, window.innerHeight)); + const targetSize = hovering ? 380 : 30; + + maskContainer.style.setProperty("--x", `${x}px`); + maskContainer.style.setProperty("--y", `${y}px`); + maskContainer.style.setProperty("--size", `${targetSize}px`); + } catch (error) { + console.error('[IntroEvents] handleMouseMoveOnSection3 오류:', error); + } +} \ No newline at end of file diff --git a/src/js/intro/init.js b/src/js/intro/init.js new file mode 100644 index 0000000..f4ae476 --- /dev/null +++ b/src/js/intro/init.js @@ -0,0 +1,267 @@ +/** + * intro-init.js + * 인트로 페이지 초기화 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, Utils, EventManager) + */ + +// 전역 의존성 (폴백 포함) +const _initDomUtils = typeof DOMUtils !== 'undefined' ? DOMUtils : null; +const _initErrorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; +const _initUtils = typeof Utils !== 'undefined' ? Utils : null; +const _initEventManager = typeof eventManager !== 'undefined' ? eventManager : null; + +/** + * 에러 처리 헬퍼 + * @private + */ +function _handleError(error, context, additionalInfo = {}) { + if (_initErrorHandler) { + _initErrorHandler.handle(error, { + context: `IntroInit.${context}`, + component: 'IntroInit', + ...additionalInfo + }, false); + } else { + console.error(`[IntroInit] ${context}:`, error, additionalInfo); + } +} + +/** + * 페이지 초기 설정 + */ +function initializeIntroPage() { + try { + // DOM 요소 선택 + const cardsContainer = _initDomUtils?.$("#cardsContainer") || document.getElementById("cardsContainer"); + const ctaBtn = _initDomUtils?.$("#ctaBtn") || document.getElementById("ctaBtn"); + const maskContainer = _initDomUtils?.$("#maskContainer") || document.getElementById("maskContainer"); + const typedNameEl = _initDomUtils?.$("#typedName") || document.getElementById("typedName"); + const cursorEl = _initDomUtils?.$("#cursor") || document.getElementById("cursor"); + + // 초기 요소 숨김 처리 + if (cardsContainer) { + if (_initDomUtils && _initDomUtils.setStyles) { + _initDomUtils.setStyles(cardsContainer, { display: 'none' }); + } else { + cardsContainer.style.display = "none"; + } + } else { + console.warn('[IntroInit] cardsContainer 요소를 찾을 수 없습니다.'); + } + + if (ctaBtn) { + if (_initDomUtils && _initDomUtils.setStyles) { + _initDomUtils.setStyles(ctaBtn, { display: 'none' }); + } else { + ctaBtn.style.display = "none"; + } + } else { + console.warn('[IntroInit] ctaBtn 요소를 찾을 수 없습니다.'); + } + + if (maskContainer) { + maskContainer.style.setProperty("--size", "30px"); + } else { + console.warn('[IntroInit] maskContainer 요소를 찾을 수 없습니다.'); + } + + // 이벤트 리스너 등록 + if (typeof initEventListeners === 'function') { + try { + initEventListeners(); + } catch (error) { + _handleError(error, 'initializeIntroPage.initEventListeners'); + } + } else { + console.warn('[IntroInit] initEventListeners 함수를 찾을 수 없습니다.'); + } + + // typedIndex 초기화 및 typedName 요소 초기화 + if (typeof INTRO_STATE !== 'undefined') { + INTRO_STATE.typedIndex = 0; + } else { + console.warn('[IntroInit] INTRO_STATE가 정의되지 않았습니다.'); + } + + if (typedNameEl) { + typedNameEl.textContent = ""; + } else { + console.warn('[IntroInit] typedName 요소를 찾을 수 없습니다.'); + } + + if (cursorEl) { + if (_initDomUtils && _initDomUtils.setStyles) { + _initDomUtils.setStyles(cursorEl, { opacity: '1' }); + } else { + cursorEl.style.opacity = "1"; + } + } else { + console.warn('[IntroInit] cursor 요소를 찾을 수 없습니다.'); + } + + // 타이핑 애니메이션 시작 (INTRO_CONFIG가 준비된 후) + const animationDelay = 800; + const delayFn = _initUtils && _initUtils.delay ? _initUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms)); + + delayFn(animationDelay).then(() => { + try { + if (typeof INTRO_CONFIG !== 'undefined' && typeof INTRO_STATE !== 'undefined' && INTRO_CONFIG.fullName) { + // text-ani 섹션 표시 (CSS에서 display: none이므로 animating 클래스 추가) + const textAniSection = _initDomUtils?.$('.text-ani') || document.querySelector('.text-ani'); + if (textAniSection) { + if (_initDomUtils && _initDomUtils.addClasses) { + _initDomUtils.addClasses(textAniSection, 'animating'); + } else { + textAniSection.classList.add('animating'); + } + } + + INTRO_STATE.typedIndex = 0; + + if (typeof typeName === 'function') { + typeName(); + } else { + console.warn('[IntroInit] typeName 함수를 찾을 수 없습니다.'); + } + } else { + console.warn('[IntroInit] INTRO_CONFIG 또는 INTRO_STATE가 준비되지 않았거나 fullName이 없습니다.'); + } + } catch (error) { + _handleError(error, 'initializeIntroPage.animationStart'); + } + }); + } catch (error) { + _handleError(error, 'initializeIntroPage'); + } +} + +// DOM이 로드되면 초기화 실행 +function setupInitialization() { + try { + if (document.readyState === "loading") { + // EventManager를 사용하여 이벤트 등록 (폴백 포함) + if (_initEventManager) { + _initEventManager.once(document, "DOMContentLoaded", initializeIntroPage); + } else { + document.addEventListener("DOMContentLoaded", initializeIntroPage, { once: true }); + } + } else { + // DOMContentLoaded 이벤트가 이미 발생한 경우 + initializeIntroPage(); + } + } catch (error) { + _handleError(error, 'setupInitialization'); + // 폴백: 에러가 발생해도 초기화 시도 + if (document.readyState !== "loading") { + initializeIntroPage(); + } + } +} + +setupInitialization(); + +/** + * 외부에서 사용자 이름을 설정하고 다시 시작하는 함수 + * @param {string} name - 사용자 이름 + */ +function restartIntroWithName(name) { + try { + // 입력 검증 + if (typeof name !== 'string' || name.trim() === '') { + _handleError(new Error('유효하지 않은 사용자 이름'), 'restartIntroWithName', { name }); + return; + } + + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'restartIntroWithName'); + return; + } + + // 이름 설정 + if (typeof setUserName === 'function') { + try { + setUserName(name); + } catch (error) { + _handleError(error, 'restartIntroWithName.setUserName'); + } + } else { + console.warn('[IntroInit] setUserName 함수를 찾을 수 없습니다.'); + } + + // 상태 초기화 + INTRO_STATE.currentSection = 0; + INTRO_STATE.typedIndex = 0; + INTRO_STATE.isScrolling = false; + INTRO_STATE.isAnimating = false; + INTRO_STATE.section2AnimDone = false; + clearTimeout(INTRO_STATE.autoScrollTimer); + INTRO_STATE.lastInteractionTime = Date.now(); + + // DOM 요소 선택 + const typedNameEl = _initDomUtils?.$("#typedName") || document.getElementById("typedName"); + const cursorEl = _initDomUtils?.$("#cursor") || document.getElementById("cursor"); + const welcomeEl = _initDomUtils?.$("#welcome") || document.getElementById("welcome"); + const textAniSection = _initDomUtils?.$('.text-ani') || document.querySelector('.text-ani'); + + // 화면 리셋 + if (typedNameEl) { + typedNameEl.textContent = ""; + } else { + console.warn('[IntroInit] typedName 요소를 찾을 수 없습니다.'); + } + + if (cursorEl) { + if (_initDomUtils && _initDomUtils.setStyles) { + _initDomUtils.setStyles(cursorEl, { opacity: '1' }); + } else { + cursorEl.style.opacity = "1"; + } + } else { + console.warn('[IntroInit] cursor 요소를 찾을 수 없습니다.'); + } + + if (welcomeEl) { + welcomeEl.innerHTML = ""; + } else { + console.warn('[IntroInit] welcome 요소를 찾을 수 없습니다.'); + } + + // text-ani 섹션 표시 + if (textAniSection) { + if (_initDomUtils && _initDomUtils.addClasses) { + _initDomUtils.addClasses(textAniSection, 'animating'); + } else { + textAniSection.classList.add('animating'); + } + } + + // 섹션 1로 이동 + if (typeof goToSection === 'function') { + try { + goToSection(0); + } catch (error) { + _handleError(error, 'restartIntroWithName.goToSection'); + } + } else { + console.warn('[IntroInit] goToSection 함수를 찾을 수 없습니다.'); + } + + // 타이핑 재시작 + const animationDelay = 800; + const delayFn = _initUtils && _initUtils.delay ? _initUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms)); + + delayFn(animationDelay).then(() => { + try { + if (typeof typeName === 'function') { + typeName(); + } else { + console.warn('[IntroInit] typeName 함수를 찾을 수 없습니다.'); + } + } catch (error) { + _handleError(error, 'restartIntroWithName.typeName'); + } + }); + } catch (error) { + _handleError(error, 'restartIntroWithName'); + } +} \ No newline at end of file diff --git a/src/js/intro/section.js b/src/js/intro/section.js new file mode 100644 index 0000000..65d032b --- /dev/null +++ b/src/js/intro/section.js @@ -0,0 +1,279 @@ +/** + * intro-section.js + * 섹션 전환 및 스크롤 처리 로직 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, AnimationUtils, Utils) + */ + +// 전역 의존성 (폴백 포함) +const _sectionDomUtils = typeof DOMUtils !== 'undefined' ? DOMUtils : null; +const _sectionErrorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; +const _sectionUtils = typeof Utils !== 'undefined' ? Utils : null; +const _sectionAnimationUtils = typeof AnimationUtils !== 'undefined' ? AnimationUtils : null; + +/** + * 에러 처리 헬퍼 + * @private + */ +function _handleError(error, context, additionalInfo = {}) { + if (_sectionErrorHandler) { + _sectionErrorHandler.handle(error, { + context: `IntroSection.${context}`, + component: 'IntroSection', + ...additionalInfo + }, false); + } else { + console.error(`[IntroSection] ${context}:`, error, additionalInfo); + } +} + +/** + * 특정 섹션으로 이동 + * @param {number} index - 이동할 섹션 인덱스 (0, 1, 2) + */ +function goToSection(index) { + try { + // 입력 검증 + if (typeof index !== 'number' || index < 0 || index > 2) { + _handleError(new Error(`유효하지 않은 섹션 인덱스: ${index}`), 'goToSection'); + return; + } + + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'goToSection'); + return; + } + + if (INTRO_STATE.isAnimating) return; + INTRO_STATE.isAnimating = true; + + // 자동 스크롤 타이머 정지 + clearTimeout(INTRO_STATE.autoScrollTimer); + + // DOM 요소 선택 + const section1Text = _sectionDomUtils?.$("#section1Text") || document.getElementById("section1Text"); + const section2Text = _sectionDomUtils?.$("#section2Text") || document.getElementById("section2Text"); + const section3Text = _sectionDomUtils?.$("#section3Text") || document.getElementById("section3Text"); + const cardsContainer = _sectionDomUtils?.$("#cardsContainer") || document.getElementById("cardsContainer"); + const ctaBtn = _sectionDomUtils?.$("#ctaBtn") || document.getElementById("ctaBtn"); + const scrollIndicator = _sectionDomUtils?.$("#scrollIndicator") || document.getElementById("scrollIndicator"); + + // 요소 존재 확인 + if (!section1Text || !section2Text || !section3Text || !cardsContainer || !ctaBtn || !scrollIndicator) { + _handleError(new Error('필수 DOM 요소를 찾을 수 없습니다'), 'goToSection'); + INTRO_STATE.isAnimating = false; + return; + } + + // 현재 섹션 페이드 아웃 + const currentSection = INTRO_STATE.currentSection; + if (currentSection === 0) { + if (_sectionDomUtils && _sectionDomUtils.addClasses) { + _sectionDomUtils.addClasses(section1Text, 'fade-out'); + } else { + section1Text.classList.add("fade-out"); + } + } else if (currentSection === 1) { + if (_sectionDomUtils && _sectionDomUtils.addClasses) { + _sectionDomUtils.addClasses(section2Text, 'fade-out'); + } else { + section2Text.classList.add("fade-out"); + } + + // 카드 숨기기 + ["card1", "card2", "card3", "card4"].forEach((id) => { + const card = _sectionDomUtils?.$(`#${id}`) || document.getElementById(id); + if (card) { + if (_sectionDomUtils && _sectionDomUtils.removeClasses) { + _sectionDomUtils.removeClasses(card, 'show'); + } else { + card.classList.remove("show"); + } + } + }); + } else if (currentSection === 2) { + if (_sectionDomUtils && _sectionDomUtils.addClasses) { + _sectionDomUtils.addClasses(section3Text, 'fade-out'); + } else { + section3Text.classList.add("fade-out"); + } + if (_sectionDomUtils && _sectionDomUtils.removeClasses) { + _sectionDomUtils.removeClasses(ctaBtn, 'show'); + } else { + ctaBtn.classList.remove("show"); + } + } + + // 전환 애니메이션 지연 + const transitionDelay = 400; + const delayFn = _sectionUtils && _sectionUtils.delay ? _sectionUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms)); + + delayFn(transitionDelay).then(() => { + try { + // 모든 섹션 숨기기 + if (_sectionDomUtils && _sectionDomUtils.addClasses) { + _sectionDomUtils.addClasses(section1Text, 'hidden'); + _sectionDomUtils.addClasses(section2Text, 'hidden'); + _sectionDomUtils.addClasses(section3Text, 'hidden'); + } else { + section1Text.classList.add("hidden"); + section2Text.classList.add("hidden"); + section3Text.classList.add("hidden"); + } + + if (_sectionDomUtils && _sectionDomUtils.removeClasses) { + _sectionDomUtils.removeClasses(scrollIndicator, 'sec2'); + _sectionDomUtils.removeClasses(section1Text, 'fade-out'); + _sectionDomUtils.removeClasses(section2Text, 'fade-out'); + _sectionDomUtils.removeClasses(section3Text, 'fade-out'); + } else { + scrollIndicator.classList.remove("sec2"); + section1Text.classList.remove("fade-out"); + section2Text.classList.remove("fade-out"); + section3Text.classList.remove("fade-out"); + } + + // 목표 섹션 표시 + if (index === 0) { + if (_sectionDomUtils && _sectionDomUtils.removeClasses) { + _sectionDomUtils.removeClasses(section1Text, 'hidden'); + _sectionDomUtils.removeClasses(scrollIndicator, 'hidden'); + } else { + section1Text.classList.remove("hidden"); + scrollIndicator.classList.remove("hidden"); + } + + if (_sectionDomUtils && _sectionDomUtils.setStyles) { + _sectionDomUtils.setStyles(cardsContainer, { display: 'none' }); + _sectionDomUtils.setStyles(ctaBtn, { display: 'none' }); + } else { + cardsContainer.style.display = "none"; + ctaBtn.style.display = "none"; + } + } else if (index === 1) { + if (_sectionDomUtils && _sectionDomUtils.addClasses) { + _sectionDomUtils.addClasses(scrollIndicator, 'sec2'); + _sectionDomUtils.removeClasses(section2Text, 'hidden'); + _sectionDomUtils.removeClasses(scrollIndicator, 'hidden'); + } else { + scrollIndicator.classList.add("sec2"); + section2Text.classList.remove("hidden"); + scrollIndicator.classList.remove("hidden"); + } + + // 모바일(≤992px): section2Text 먼저 보여준 뒤 animateSection2에서 카드 표시 + const isMobile = window.innerWidth <= 992; + if (_sectionDomUtils && _sectionDomUtils.setStyles) { + _sectionDomUtils.setStyles(cardsContainer, { display: isMobile ? 'none' : 'flex' }); + _sectionDomUtils.setStyles(ctaBtn, { display: 'none' }); + } else { + cardsContainer.style.display = isMobile ? "none" : "flex"; + ctaBtn.style.display = "none"; + } + + resetSection2(); + setTimeout(animateSection2, 200); + } else if (index === 2) { + if (_sectionDomUtils && _sectionDomUtils.removeClasses) { + _sectionDomUtils.removeClasses(section3Text, 'hidden'); + _sectionDomUtils.addClasses(scrollIndicator, 'hidden'); + _sectionDomUtils.removeClasses(scrollIndicator, 'sec2'); + } else { + section3Text.classList.remove("hidden"); + scrollIndicator.classList.add("hidden"); + scrollIndicator.classList.remove("sec2"); + } + + if (_sectionDomUtils && _sectionDomUtils.setStyles) { + _sectionDomUtils.setStyles(cardsContainer, { display: 'none' }); + _sectionDomUtils.setStyles(ctaBtn, { display: 'block' }); + } else { + cardsContainer.style.display = "none"; + ctaBtn.style.display = "block"; + } + + resetSection3(); + setTimeout(animateSection3, 200); + } + + INTRO_STATE.currentSection = index; + + // 애니메이션 완료 플래그 리셋 + const resetDelay = 500; + delayFn(resetDelay).then(() => { + INTRO_STATE.isAnimating = false; + }); + } catch (error) { + _handleError(error, 'goToSection.transition'); + INTRO_STATE.isAnimating = false; + } + }); + } catch (error) { + _handleError(error, 'goToSection'); + if (typeof INTRO_STATE !== 'undefined') { + INTRO_STATE.isAnimating = false; + } + } +} + +/** + * 다음 섹션으로 스크롤 + */ +function handleScrollDown() { + try { + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'handleScrollDown'); + return; + } + + if (INTRO_STATE.isScrolling || INTRO_STATE.isAnimating) return; + + const delayFn = _sectionUtils && _sectionUtils.delay ? _sectionUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms)); + const scrollCooldown = 700; + + if (INTRO_STATE.currentSection === 0) { + INTRO_STATE.isScrolling = true; + delayFn(scrollCooldown).then(() => { + INTRO_STATE.isScrolling = false; + }); + goToSection(1); + } else if (INTRO_STATE.currentSection === 1 && INTRO_STATE.section2AnimDone) { + INTRO_STATE.isScrolling = true; + delayFn(scrollCooldown).then(() => { + INTRO_STATE.isScrolling = false; + }); + goToSection(2); + } + } catch (error) { + _handleError(error, 'handleScrollDown'); + } +} + +/** + * 이전 섹션으로 스크롤 + */ +function handleScrollUp() { + try { + if (typeof INTRO_STATE === 'undefined') { + _handleError(new Error('INTRO_STATE가 정의되지 않았습니다'), 'handleScrollUp'); + return; + } + + if (INTRO_STATE.isScrolling || INTRO_STATE.isAnimating) return; + + INTRO_STATE.isScrolling = true; + const delayFn = _sectionUtils && _sectionUtils.delay ? _sectionUtils.delay : (ms) => new Promise(resolve => setTimeout(resolve, ms)); + const scrollCooldown = 700; + + delayFn(scrollCooldown).then(() => { + INTRO_STATE.isScrolling = false; + }); + + if (INTRO_STATE.currentSection === 2) { + goToSection(1); + } else if (INTRO_STATE.currentSection === 1) { + goToSection(0); + } + } catch (error) { + _handleError(error, 'handleScrollUp'); + } +} \ No newline at end of file diff --git a/src/js/learning/chapter-cards.js b/src/js/learning/chapter-cards.js new file mode 100644 index 0000000..c5fd9f9 --- /dev/null +++ b/src/js/learning/chapter-cards.js @@ -0,0 +1,546 @@ +/** + * 챕터 카드 관리 클래스 (CSS 기반 디자인) + * 개선된 공통 모듈 활용 (EventManager, ErrorHandler, DOMUtils) + */ +class ChapterCardManager { + constructor(config, gaugeManager, dependencies = {}) { + this.config = config; + this.gaugeManager = gaugeManager; + this.chapterCards = []; + this.cardsContainer = null; + this.modalInstance = null; + + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + // CSS 스타일 주입 + this._injectStyles(); + } + + /** + * CSS 스타일 주입 + * @private + */ + _injectStyles() { + try { + if (document.getElementById("chapter-card-styles")) return; + + const style = this.domUtils?.createElement('style', { id: 'chapter-card-styles' }) || document.createElement("style"); + style.id = "chapter-card-styles"; + style.textContent = ` + .chapter-card:hover .card-play-button { + transform: translate(-50%, -50%) scale(1.1); + } + + /* 호버 효과 */ + .chapter-card:hover .chapter-card-inner { + transform: translateY(-4px); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15); + } + + .chapter-card.current:hover .chapter-card-inner { + box-shadow: 0 12px 32px rgba(31, 155, 118, 0.3); + } + + .chapter-card.completed:hover .chapter-card-inner { + box-shadow: 0 12px 32px rgba(171, 61, 0, 0.25); + } + `; + document.head.appendChild(style); + } catch (error) { + this._handleError(error, 'ChapterCardManager._injectStyles'); + } + } + + /** + * 모달 인스턴스 설정 + * @param {VideoModal} modal - 모달 인스턴스 + */ + setModalInstance(modal) { + this.modalInstance = modal; + } + + /** + * 챕터 카드 생성 + */ + createChapterCards() { + try { + const gaugeElement = this.domUtils?.$(".lessons-gauge") || document.querySelector(".lessons-gauge"); + if (!gaugeElement) { + console.warn('[ChapterCardManager] .lessons-gauge 요소를 찾을 수 없습니다.'); + return; + } + + this.cardsContainer = this.domUtils?.$(".chapter-list", gaugeElement) || gaugeElement.querySelector(".chapter-list"); + + if (!this.cardsContainer) { + this.cardsContainer = this.domUtils?.createElement('ul', { class: 'chapter-list' }) || document.createElement("ul"); + this.cardsContainer.className = "chapter-list"; + gaugeElement.appendChild(this.cardsContainer); + } + + this.domUtils?.empty(this.cardsContainer) || (this.cardsContainer.innerHTML = ""); + + this.chapterCards = []; + + this.config.chapters.forEach((chapter, chapterIndex) => { + // 새 구조: 챕터 자체가 마커이므로 chapter에서 직접 정보 가져오기 + if (chapter.type === "chapter") { + this._createCard(chapter, chapterIndex, chapter); + } + }); + + console.log( + `[ChapterCardManager] ${this.chapterCards.length}개의 챕터 카드 생성 완료` + ); + } catch (error) { + this._handleError(error, 'ChapterCardManager.createChapterCards'); + } + } + + /** + * 개별 챕터 카드 생성 + * @private + */ + _createCard(chapter, chapterIndex, chapterLesson) { + try { + const li = this.domUtils?.createElement('li', { class: 'chapter-card' }) || document.createElement("li"); + li.classList.add("chapter-card"); + + const state = this._getChapterState(chapter); + if (state) { + this.domUtils?.addClasses(li, state) || li.classList.add(state); + } + + li.style.cursor = "pointer"; + + // 이벤트 리스너 등록 (EventManager 사용) + const clickHandler = () => { + this._handleCardClick(chapter, chapterIndex); + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(li, "click", clickHandler); + this.listenerIds.push({ element: li, id: listenerId }); + } else { + li.addEventListener("click", clickHandler); + } + + const cardContent = this._createCardContent(chapter, state, chapterIndex); + li.appendChild(cardContent); + + this._positionCard(li, chapterLesson); + + this.cardsContainer.appendChild(li); + + this.chapterCards.push({ + element: li, + chapter: chapter, + state: state, + chapterIndex: chapterIndex, + chapterLesson: chapterLesson, + }); + } catch (error) { + this._handleError(error, 'ChapterCardManager._createCard', { chapter, chapterIndex }); + } + } + + /** + * 카드 콘텐츠 생성 + * @private + */ + _createCardContent(chapter, state, chapterIndex) { + try { + const inner = this.domUtils?.createElement('div', { class: 'chapter-card-inner' }) || document.createElement("div"); + inner.className = "chapter-card-inner"; + + // 썸네일 영역 + const thumbnailContainer = this.domUtils?.createElement('div', { class: 'card-thumbnail-container' }) || document.createElement("div"); + thumbnailContainer.className = "card-thumbnail-container"; + + const thumbnail = this.domUtils?.createElement('img', { + class: 'card-thumbnail', + src: `./assets/images/learning/img_learning_0${(chapterIndex % 6) + 1}.jpg`, + alt: chapter.name, + loading: 'lazy' + }) || document.createElement("img"); + + if (!this.domUtils) { + thumbnail.className = "card-thumbnail"; + thumbnail.src = `./assets/images/learning/img_learning_0${(chapterIndex % 6) + 1}.jpg`; + thumbnail.alt = chapter.name; + thumbnail.loading = "lazy"; + } + + thumbnailContainer.appendChild(thumbnail); + + // 플레이 버튼 + const playButton = this.domUtils?.createElement('img', { + class: 'card-play-button', + src: this._getPlayButtonImagePath(state), + alt: '재생', + loading: 'lazy' + }) || document.createElement("img"); + + if (!this.domUtils) { + playButton.className = "card-play-button"; + playButton.src = this._getPlayButtonImagePath(state); + playButton.alt = "재생"; + playButton.loading = "lazy"; + } + + thumbnailContainer.appendChild(playButton); + + // 스탬프 아이콘 추가 (completed 상태일 때만 표시) + if (state === "completed") { + const stamp = this.domUtils?.createElement('div', { class: 'ico-stamp' }) || document.createElement("div"); + stamp.className = "ico-stamp"; + inner.appendChild(stamp); + } + + // 게이지바 추가 + const gaugeBar = this.domUtils?.createElement('div', { class: 'card-gauge-bar' }) || document.createElement("div"); + gaugeBar.className = "card-gauge-bar"; + + const gaugeFill = this.domUtils?.createElement('div', { class: 'card-gauge-fill' }) || document.createElement("div"); + gaugeFill.className = "card-gauge-fill"; + + const progressPercent = this._calculateChapterProgress(chapter); + gaugeFill.style.width = progressPercent + "%"; + + gaugeBar.appendChild(gaugeFill); + thumbnailContainer.appendChild(gaugeBar); + inner.appendChild(thumbnailContainer); + + // 제목 + const title = this.domUtils?.createElement('div', { class: 'card-title' }, chapter.name) || document.createElement("div"); + if (!this.domUtils) { + title.className = "card-title"; + title.textContent = chapter.name; + } + inner.appendChild(title); + + // 스탬프 + const stamp = this.domUtils?.createElement('div', { class: 'card-stamp' }) || document.createElement("div"); + stamp.className = "card-stamp"; + inner.appendChild(stamp); + + // 그림자 + const shadow = this.domUtils?.createElement('div', { class: 'shadow-effect' }) || document.createElement("div"); + shadow.className = "shadow-effect"; + inner.appendChild(shadow); + + return inner; + } catch (error) { + this._handleError(error, 'ChapterCardManager._createCardContent', { chapter, state, chapterIndex }); + // 에러 발생 시 최소한의 요소라도 반환 + const fallback = document.createElement("div"); + fallback.className = "chapter-card-inner"; + fallback.textContent = chapter.name || "Chapter"; + return fallback; + } + } + + /** + * 카드 클릭 핸들러 + * @private + */ + _handleCardClick(chapter, chapterIndex) { + try { + if (!this.modalInstance) { + console.warn('[ChapterCardManager] 모달 인스턴스가 설정되지 않았습니다.'); + return; + } + + console.log( + `[ChapterCardManager] 챕터 카드 클릭: ${chapter.name} (챕터 ${chapterIndex + 1})` + ); + + // 챕터는 시작점 표시용이므로 항상 첫 번째 미완료 lesson부터 시작 + let targetLessonIndex = 0; // 첫 번째 lesson + + // 첫 번째 미완료 lesson 찾기 + for (let i = 0; i < chapter.lessons.length; i++) { + if (!chapter.lessons[i].completed) { + targetLessonIndex = i; + break; + } + } + + // 모든 lesson이 완료된 경우 첫 번째 lesson으로 + if (targetLessonIndex >= chapter.lessons.length) { + targetLessonIndex = 0; + } + + const globalIndex = this.config.toGlobalIndex( + chapterIndex, + targetLessonIndex + ); + + const targetLesson = chapter.lessons[targetLessonIndex]; + if (!targetLesson) { + console.error('[ChapterCardManager] 대상 lesson을 찾을 수 없습니다.'); + return; + } + + const targetLabel = targetLesson.label; + + console.log( + `[ChapterCardManager] 대상 학습: ${targetLabel} (글로벌 인덱스: ${globalIndex})` + ); + + this.modalInstance.loadChapter(chapter, chapterIndex, globalIndex); + } catch (error) { + this._handleError(error, 'ChapterCardManager._handleCardClick', { chapter, chapterIndex }); + } + } + + /** + * 챕터 상태 결정 + * @private + */ + _getChapterState(chapter) { + // 새 구조: chapter.completed 사용 (자동 업데이트됨) + if (chapter.completed) return "completed"; + + const anyCompleted = chapter.lessons.some((lesson) => lesson.completed); + if (anyCompleted) return "current"; + + // 챕터 자체가 활성화되어 있는지 확인 + const isActive = this._isChapterActive(chapter); + if (isActive) return "current"; + + return "base"; + } + + /** + * 챕터 활성화 여부 확인 + * @private + */ + _isChapterActive(chapter) { + const allMarkers = this.config.getAllMarkers(); + const chapterMarkerIndex = allMarkers.findIndex( + (m) => m.pathPercent === chapter.pathPercent && m.isChapterMarker === true + ); + + if (chapterMarkerIndex === -1) return false; + if (chapterMarkerIndex === 0) return true; + return allMarkers[chapterMarkerIndex - 1].completed; + } + + /** + * 플레이 버튼 이미지 경로 반환 + * @private + */ + _getPlayButtonImagePath(state) { + switch (state) { + case "completed": + return "./assets/images/learning/btn_play_completed.png"; + case "current": + return "./assets/images/learning/btn_play_current.png"; + default: + return "./assets/images/learning/btn_play_base.png"; + } + } + + /** + * 챕터 진행률 계산 + * @private + * @param {Object} chapter - 챕터 객체 + * @returns {number} 진행률 (0-100) + */ + _calculateChapterProgress(chapter) { + const completedCount = chapter.lessons.filter( + (lesson) => lesson.completed + ).length; + const totalCount = chapter.lessons.length; + const progressPercent = Math.round((completedCount / totalCount) * 100); + + console.log( + `[ChapterCardManager] 챕터 "${chapter.name}" 진행률: ${completedCount}/${totalCount} (${progressPercent}%)` + ); + + return progressPercent; + } + + /** + * 카드 위치 설정 + * @private + */ + _positionCard(li, chapterLesson) { + try { + const gaugeSvg = document.getElementById("gauge-svg"); + if (!gaugeSvg) { + console.warn('[ChapterCardManager] gauge-svg 요소를 찾을 수 없습니다.'); + return; + } + + const viewBox = gaugeSvg.viewBox.baseVal; + if (!viewBox || !viewBox.width || !viewBox.height) { + console.warn('[ChapterCardManager] SVG viewBox가 유효하지 않습니다.'); + return; + } + + const point = this.gaugeManager.getPointAtPercent( + chapterLesson.pathPercent + ); + + if (!point || typeof point.x !== 'number' || typeof point.y !== 'number') { + console.warn('[ChapterCardManager] 유효하지 않은 포인트입니다.'); + return; + } + + const percentX = (point.x / viewBox.width) * 100 + 1.3; + const percentY = (point.y / viewBox.height) * 100 - 3; + + if (this.domUtils) { + this.domUtils.setStyles(li, { + position: "absolute", + left: `${percentX}%`, + top: `${percentY}%`, + transform: "translate(-50%, -105%)", + zIndex: "10" + }); + } else { + li.style.position = "absolute"; + li.style.left = `${percentX}%`; + li.style.top = `${percentY}%`; + li.style.transform = "translate(-50%, -105%)"; + li.style.zIndex = "10"; + } + + console.log( + `[ChapterCardManager] 카드 위치: (${percentX.toFixed(2)}%, ${percentY.toFixed(2)}%)` + ); + } catch (error) { + this._handleError(error, 'ChapterCardManager._positionCard', { chapterLesson }); + } + } + + /** + * 챕터 카드 상태 업데이트 + * @param {boolean} forceUpdate - 강제 업데이트 여부 + */ + updateChapterCards(forceUpdate = false) { + try { + this.chapterCards.forEach((card, index) => { + try { + const chapter = card.chapter; + const newState = this._getChapterState(chapter); + const newProgress = this._calculateChapterProgress(chapter); + + const gaugeFill = this.domUtils?.$(".card-gauge-fill", card.element) || card.element.querySelector(".card-gauge-fill"); + const currentProgress = gaugeFill + ? parseInt(gaugeFill.style.width) || 0 + : 0; + + const shouldUpdate = + forceUpdate || + card.state !== newState || + currentProgress !== newProgress; + + if (shouldUpdate) { + console.log( + `[ChapterCardManager] 챕터 ${index + 1} ${forceUpdate ? "강제 " : ""}업데이트` + ); + + // 클래스 업데이트 + card.element.className = "chapter-card"; + if (newState) { + if (this.domUtils) { + this.domUtils.addClasses(card.element, newState); + } else { + card.element.classList.add(newState); + } + } + + // 플레이 버튼 업데이트 + const playButton = this.domUtils?.$(".card-play-button", card.element) || card.element.querySelector(".card-play-button"); + if (playButton) { + playButton.src = this._getPlayButtonImagePath(newState); + } + + // 게이지바 업데이트 (애니메이션 적용 가능) + if (gaugeFill) { + if (this.animationUtils) { + this.animationUtils.progressBar(gaugeFill, newProgress, 300); + } else { + gaugeFill.style.width = newProgress + "%"; + } + } + + // 스탬프 업데이트 + const inner = this.domUtils?.$(".chapter-card-inner", card.element) || card.element.querySelector(".chapter-card-inner"); + if (inner) { + const existingStamp = this.domUtils?.$(".ico-stamp", inner) || inner.querySelector(".ico-stamp"); + + if (newState === "completed" && !existingStamp) { + const stamp = this.domUtils?.createElement('div', { class: 'ico-stamp' }) || document.createElement("div"); + stamp.className = "ico-stamp"; + // 제목 앞에 삽입 + const title = this.domUtils?.$(".card-title", inner) || inner.querySelector(".card-title"); + if (title) { + inner.insertBefore(stamp, title); + } else { + inner.appendChild(stamp); + } + } else if (newState !== "completed" && existingStamp) { + this.domUtils?.remove(existingStamp) || existingStamp.remove(); + } + } + + card.state = newState; + } + } catch (error) { + this._handleError(error, 'ChapterCardManager.updateChapterCards.card', { index }); + } + }); + } catch (error) { + this._handleError(error, 'ChapterCardManager.updateChapterCards'); + } + } + + /** + * 에러 처리 헬퍼 메서드 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context, + ...additionalInfo, + component: 'ChapterCardManager' + }, false); + } else { + console.error(`[ChapterCardManager] ${context}:`, error, additionalInfo); + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 카드 배열 초기화 + this.chapterCards = []; + this.cardsContainer = null; + this.modalInstance = null; + } catch (error) { + this._handleError(error, 'ChapterCardManager.destroy'); + } + } +} diff --git a/src/js/learning/config.js b/src/js/learning/config.js new file mode 100644 index 0000000..a1c6b38 --- /dev/null +++ b/src/js/learning/config.js @@ -0,0 +1,666 @@ +/** + * 학습 경로 설정 + * 공통 모듈 활용 (ErrorHandler, Utils, ConfigManager) + */ +const LEARNING_CONFIG = { + // 마커 설정 - 챕터별로 그룹화 + chapters: [ + { + id: 1, + name: "개인정보보호", + type: "chapter", + pathPercent: 0.108, + gaugePercent: 0.108, // 게이지 라인 위치 (없으면 pathPercent 사용) + url: "OXTYn3JkkCQ", + completed: false, // 하위 lessons가 모두 완료되면 자동으로 true + lessons: [ + { + pathPercent: 0.137, + gaugePercent: 0.137, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 1", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.159, + gaugePercent: 0.156, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 2", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.182, + gaugePercent: 0.178, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 3", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.205, + gaugePercent: 0.202, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 4", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.228, + gaugePercent: 0.226, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 5", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.25, + gaugePercent: 0.246, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 6", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.272, + gaugePercent: 0.268, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 7", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.298, + gaugePercent: 0.296, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "개인정보보호 8", + url: "OXTYn3JkkCQ", + completed: false, + }, + ], + }, + { + id: 2, + name: "직장내 괴롭힘 예방", + type: "chapter", + pathPercent: 0.325, + gaugePercent: 0.325, // 게이지 라인 위치 (없으면 pathPercent 사용) + url: "OXTYn3JkkCQ", + completed: false, + lessons: [ + { + pathPercent: 0.367, + gaugePercent: 0.358, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "직장내 괴롭힘 예방 1", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.41, + gaugePercent: 0.40, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "직장내 괴롭힘 예방 2", + url: "OXTYn3JkkCQ", + completed: false, + }, + ], + }, + { + id: 3, + name: "장애인 인식 개선", + type: "chapter", + pathPercent: 0.442, + gaugePercent: 0.442, // 게이지 라인 위치 (없으면 pathPercent 사용) + url: "OXTYn3JkkCQ", + completed: false, + lessons: [ + { + pathPercent: 0.486, + gaugePercent: 0.476, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "장애인 인식 개선 1", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.531, + gaugePercent: 0.526, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "장애인 인식 개선 2", + url: "OXTYn3JkkCQ", + completed: false, + }, + ], + }, + { + id: 4, + name: "성희롱 예방 교육", + type: "chapter", + pathPercent: 0.555, + gaugePercent: 0.555, // 게이지 라인 위치 (없으면 pathPercent 사용) + url: "OXTYn3JkkCQ", + completed: false, + lessons: [ + { + pathPercent: 0.585, + gaugePercent: 0.582, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "성희롱 예방 교육 1", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.635, + gaugePercent: 0.632, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "성희롱 예방 교육 2", + url: "OXTYn3JkkCQ", + completed: false, + }, + ], + }, + { + id: 5, + name: "산업안전 보건", + type: "chapter", + pathPercent: 0.67, + gaugePercent: 0.67, // 게이지 라인 위치 (없으면 pathPercent 사용) + url: "OXTYn3JkkCQ", + completed: false, + lessons: [ + { + pathPercent: 0.69, + gaugePercent: 0.686, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "산업안전 보건 1", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.718, + gaugePercent: 0.71, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "산업안전 보건 2", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.736, + gaugePercent: 0.728, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "산업안전 보건 3", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.785, + gaugePercent: 0.778, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "산업안전 보건 4", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.82, + gaugePercent: 0.812, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "산업안전 보건 5", + url: "OXTYn3JkkCQ", + completed: false, + }, + { + pathPercent: 0.86, + gaugePercent: 0.85, // 게이지 라인 위치 (없으면 pathPercent 사용) + type: "normal", + label: "산업안전 보건 6", + url: "OXTYn3JkkCQ", + completed: false, + }, + ], + }, + ], + + // 평균 학습량 설정 (전체 학습 항목 대비 %) + averageProgress: { + threshold: 60, // 평균 학습량: 전체의 60% + }, + + // 마커 이미지 경로 + markerImages: { + normal: { + base: "./assets/images/learning/mark_base.png", + current: "./assets/images/learning/mark_current.png", + completed: "./assets/images/learning/mark_completed.png", + }, + chapter: { + base: "./assets/images/learning/mark_chapter_base.png", + current: "./assets/images/learning/mark_chapter_current.png", + completed: "./assets/images/learning/mark_chapter_completed.png", + }, + }, + + // 상태 이미지 경로 + stateImages: { + below: "./assets/images/learning/img_state_01.svg", // 평균 이하 + average: "./assets/images/learning/img_state_02.svg", // 평균 + above: "./assets/images/learning/img_state_03.svg", // 평균 이상 + }, + + // 모달 경로 + modalPath: "./_modal/video-learning.html", + + // 비활성 마커 클릭 설정 + settings: { + allowDisabledClick: true, // true: 비활성 마커도 클릭 가능, false: 비활성 마커 클릭 불가 + disabledClickMessage: "이전 학습을 먼저 완료해주세요.", // 비활성 마커 클릭 시 메시지 + showDisabledAlert: false, // true: 알림 표시, false: 콘솔 로그만 + }, + + /** + * 챕터의 완료 상태 자동 업데이트 + * 하위 lessons가 모두 완료되면 챕터도 completed = true로 변경 + */ + updateChapterCompletionStatus() { + try { + if (!this.chapters || !Array.isArray(this.chapters)) { + this._handleError(new Error('chapters가 배열이 아닙니다.'), 'updateChapterCompletionStatus'); + return; + } + + this.chapters.forEach((chapter, index) => { + try { + if (!chapter || !chapter.lessons || !Array.isArray(chapter.lessons)) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}의 lessons가 유효하지 않습니다.`); + return; + } + + const allLessonsCompleted = chapter.lessons.every( + (lesson) => lesson && lesson.completed === true + ); + chapter.completed = allLessonsCompleted; + } catch (error) { + this._handleError(error, 'updateChapterCompletionStatus.chapter', { chapterIndex: index }); + } + }); + } catch (error) { + this._handleError(error, 'updateChapterCompletionStatus'); + } + }, + + /** + * 전체 마커 배열 반환 (챕터 + lessons flat) + * @returns {Array} + */ + getAllMarkers() { + try { + // 챕터 완료 상태 업데이트 + this.updateChapterCompletionStatus(); + + if (!this.chapters || !Array.isArray(this.chapters)) { + this._handleError(new Error('chapters가 배열이 아닙니다.'), 'getAllMarkers'); + return []; + } + + const markers = []; + this.chapters.forEach((chapter, chapterIndex) => { + try { + if (!chapter) { + console.warn(`[LEARNING_CONFIG] 챕터 ${chapterIndex}가 null입니다.`); + return; + } + + // 챕터 자체를 마커로 추가 (시작점 표시용, 클릭 불가) + markers.push({ + pathPercent: chapter.pathPercent || 0, + gaugePercent: chapter.gaugePercent !== undefined ? chapter.gaugePercent : (chapter.pathPercent || 0), + type: chapter.type || 'chapter', + label: chapter.name || `챕터 ${chapterIndex + 1}`, + url: chapter.url || '', + completed: chapter.completed === true, + chapterId: chapter.id || chapterIndex + 1, + isChapterMarker: true, + isLearningContent: false, // 강의 아님 + isClickable: false, // 클릭 불가 + }); + + // 하위 lessons 추가 (실제 강의) + if (chapter.lessons && Array.isArray(chapter.lessons)) { + chapter.lessons.forEach((lesson, lessonIndex) => { + try { + if (!lesson) { + console.warn(`[LEARNING_CONFIG] 챕터 ${chapterIndex}의 레슨 ${lessonIndex}가 null입니다.`); + return; + } + + markers.push({ + pathPercent: lesson.pathPercent || 0, + gaugePercent: lesson.gaugePercent !== undefined ? lesson.gaugePercent : (lesson.pathPercent || 0), + type: lesson.type || 'normal', + label: lesson.label || `레슨 ${lessonIndex + 1}`, + url: lesson.url || '', + completed: lesson.completed === true, + chapterId: chapter.id || chapterIndex + 1, + isChapterMarker: false, + isLearningContent: true, // 실제 강의 + isClickable: true, // 클릭 가능 + }); + } catch (error) { + this._handleError(error, 'getAllMarkers.lesson', { chapterIndex, lessonIndex }); + } + }); + } + } catch (error) { + this._handleError(error, 'getAllMarkers.chapter', { chapterIndex }); + } + }); + + return markers; + } catch (error) { + this._handleError(error, 'getAllMarkers'); + return []; + } + }, + + /** + * 특정 인덱스의 챕터 정보 반환 + * @param {number} globalIndex - 전체 마커 기준 인덱스 + * @returns {Object|null} { chapterIndex, chapterData, lessonIndex, lessonData, isChapterMarker } + */ + getChapterByGlobalIndex(globalIndex) { + try { + if (typeof globalIndex !== 'number' || globalIndex < 0) { + this._handleError(new Error(`유효하지 않은 globalIndex: ${globalIndex}`), 'getChapterByGlobalIndex'); + return null; + } + + const allMarkers = this.getAllMarkers(); + + if (!Array.isArray(allMarkers) || globalIndex >= allMarkers.length) { + console.warn(`[LEARNING_CONFIG] globalIndex ${globalIndex}가 범위를 벗어났습니다. (총 ${allMarkers.length}개)`); + return null; + } + + const marker = allMarkers[globalIndex]; + if (!marker) { + console.warn(`[LEARNING_CONFIG] globalIndex ${globalIndex}의 마커를 찾을 수 없습니다.`); + return null; + } + + const chapterIndex = this.chapters.findIndex( + (ch) => ch && ch.id === marker.chapterId + ); + + if (chapterIndex === -1) { + console.warn(`[LEARNING_CONFIG] chapterId ${marker.chapterId}에 해당하는 챕터를 찾을 수 없습니다.`); + return null; + } + + const chapter = this.chapters[chapterIndex]; + if (!chapter) { + console.warn(`[LEARNING_CONFIG] 챕터 인덱스 ${chapterIndex}의 데이터가 없습니다.`); + return null; + } + + if (marker.isChapterMarker) { + // 챕터 마커인 경우 + return { + chapterIndex, + chapterData: chapter, + lessonIndex: -1, // 챕터 자체이므로 -1 + lessonData: marker, + isChapterMarker: true, + }; + } else { + // 일반 레슨인 경우 + if (!chapter.lessons || !Array.isArray(chapter.lessons)) { + console.warn(`[LEARNING_CONFIG] 챕터 ${chapterIndex}의 lessons가 유효하지 않습니다.`); + return null; + } + + const lessonIndex = chapter.lessons.findIndex( + (lesson) => lesson && lesson.pathPercent === marker.pathPercent + ); + + if (lessonIndex === -1) { + console.warn(`[LEARNING_CONFIG] pathPercent ${marker.pathPercent}에 해당하는 레슨을 찾을 수 없습니다.`); + return null; + } + + return { + chapterIndex, + chapterData: chapter, + lessonIndex, + lessonData: marker, + isChapterMarker: false, + }; + } + } catch (error) { + this._handleError(error, 'getChapterByGlobalIndex', { globalIndex }); + return null; + } + }, + + /** + * 로컬 인덱스를 글로벌 인덱스로 변환 + * @param {number} chapterIndex - 챕터 인덱스 + * @param {number} lessonIndex - 챕터 내 학습 인덱스 (-1이면 챕터 자체) + * @returns {number|null} + */ + toGlobalIndex(chapterIndex, lessonIndex) { + try { + if (typeof chapterIndex !== 'number' || chapterIndex < 0) { + this._handleError(new Error(`유효하지 않은 chapterIndex: ${chapterIndex}`), 'toGlobalIndex'); + return null; + } + + if (typeof lessonIndex !== 'number') { + this._handleError(new Error(`유효하지 않은 lessonIndex: ${lessonIndex}`), 'toGlobalIndex'); + return null; + } + + if (!this.chapters || !Array.isArray(this.chapters)) { + this._handleError(new Error('chapters가 배열이 아닙니다.'), 'toGlobalIndex'); + return null; + } + + if (chapterIndex >= this.chapters.length) { + console.warn(`[LEARNING_CONFIG] chapterIndex ${chapterIndex}가 범위를 벗어났습니다. (총 ${this.chapters.length}개)`); + return null; + } + + let globalIndex = 0; + + for (let i = 0; i < chapterIndex; i++) { + const chapter = this.chapters[i]; + if (!chapter) { + console.warn(`[LEARNING_CONFIG] 챕터 인덱스 ${i}가 null입니다.`); + continue; + } + + globalIndex += 1; // 챕터 마커 + + if (chapter.lessons && Array.isArray(chapter.lessons)) { + globalIndex += chapter.lessons.length; // 하위 lessons + } + } + + if (lessonIndex === -1) { + // 챕터 마커 자체 + return globalIndex; + } else { + // 하위 lesson + const chapter = this.chapters[chapterIndex]; + if (!chapter || !chapter.lessons || !Array.isArray(chapter.lessons)) { + console.warn(`[LEARNING_CONFIG] 챕터 ${chapterIndex}의 lessons가 유효하지 않습니다.`); + return null; + } + + if (lessonIndex >= chapter.lessons.length) { + console.warn(`[LEARNING_CONFIG] lessonIndex ${lessonIndex}가 범위를 벗어났습니다. (총 ${chapter.lessons.length}개)`); + return null; + } + + return globalIndex + 1 + lessonIndex; + } + } catch (error) { + this._handleError(error, 'toGlobalIndex', { chapterIndex, lessonIndex }); + return null; + } + }, + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (typeof ErrorHandler !== 'undefined' && ErrorHandler) { + ErrorHandler.handle(error, { + context: `LEARNING_CONFIG.${context}`, + component: 'LEARNING_CONFIG', + ...additionalInfo + }, false); + } else { + console.error(`[LEARNING_CONFIG] ${context}:`, error, additionalInfo); + } + }, + + /** + * 설정 유효성 검증 + * @returns {boolean} + */ + validate() { + try { + if (!this.chapters || !Array.isArray(this.chapters)) { + this._handleError(new Error('chapters가 배열이 아닙니다.'), 'validate'); + return false; + } + + let isValid = true; + this.chapters.forEach((chapter, index) => { + if (!chapter) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}가 null입니다.`); + isValid = false; + return; + } + + if (!chapter.id) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}에 id가 없습니다.`); + isValid = false; + } + + if (!chapter.name) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}에 name이 없습니다.`); + isValid = false; + } + + if (!chapter.lessons || !Array.isArray(chapter.lessons)) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}의 lessons가 유효하지 않습니다.`); + isValid = false; + } else { + chapter.lessons.forEach((lesson, lessonIndex) => { + if (!lesson) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}의 레슨 ${lessonIndex}가 null입니다.`); + isValid = false; + } else { + if (!lesson.label) { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}의 레슨 ${lessonIndex}에 label이 없습니다.`); + isValid = false; + } + if (typeof lesson.pathPercent !== 'number') { + console.warn(`[LEARNING_CONFIG] 챕터 ${index}의 레슨 ${lessonIndex}에 pathPercent가 없거나 숫자가 아닙니다.`); + isValid = false; + } + } + }); + } + }); + + return isValid; + } catch (error) { + this._handleError(error, 'validate'); + return false; + } + }, +}; + +/** + * HTML에서 설정된 learningConfigData를 LEARNING_CONFIG에 적용 + * window.learningConfigData가 있으면 completed 상태를 업데이트 + */ +if (typeof window !== "undefined" && window.learningConfigData) { + try { + const configData = window.learningConfigData; + + if (!configData || typeof configData !== 'object') { + console.warn('[LEARNING_CONFIG] learningConfigData가 유효하지 않습니다.'); + } else { + LEARNING_CONFIG.chapters.forEach((chapter, chapterIndex) => { + try { + if (!chapter || !chapter.id) { + console.warn(`[LEARNING_CONFIG] 챕터 ${chapterIndex}가 유효하지 않습니다.`); + return; + } + + const chapterData = configData[chapter.id]; + + if (chapterData) { + // 챕터 완료 상태 업데이트 + if (chapterData.completed !== undefined) { + chapter.completed = chapterData.completed === true; + } + + // 레슨 완료 상태 업데이트 + if (chapterData.lessons && Array.isArray(chapterData.lessons)) { + if (!chapter.lessons || !Array.isArray(chapter.lessons)) { + console.warn(`[LEARNING_CONFIG] 챕터 ${chapterIndex}의 lessons가 유효하지 않습니다.`); + return; + } + + chapterData.lessons.forEach((lessonData, index) => { + try { + if (chapter.lessons[index] && lessonData && lessonData.completed !== undefined) { + chapter.lessons[index].completed = lessonData.completed === true; + } + } catch (error) { + console.error(`[LEARNING_CONFIG] 레슨 ${index} 업데이트 에러:`, error); + } + }); + } + } + } catch (error) { + if (typeof ErrorHandler !== 'undefined' && ErrorHandler) { + ErrorHandler.handle(error, { + context: 'LEARNING_CONFIG.loadFromHTML.chapter', + chapterIndex + }, false); + } else { + console.error(`[LEARNING_CONFIG] 챕터 ${chapterIndex} 업데이트 에러:`, error); + } + } + }); + + console.log( + "[LEARNING_CONFIG] learningConfigData 적용 완료:", + LEARNING_CONFIG + ); + + // 설정 유효성 검증 + if (LEARNING_CONFIG.validate) { + const isValid = LEARNING_CONFIG.validate(); + if (!isValid) { + console.warn('[LEARNING_CONFIG] 설정 유효성 검증 실패'); + } + } + } + } catch (error) { + if (typeof ErrorHandler !== 'undefined' && ErrorHandler) { + ErrorHandler.handle(error, { + context: 'LEARNING_CONFIG.loadFromHTML' + }, false); + } else { + console.error('[LEARNING_CONFIG] learningConfigData 적용 에러:', error); + } + } +} diff --git a/src/js/learning/gauge.js b/src/js/learning/gauge.js new file mode 100644 index 0000000..25ce6b4 --- /dev/null +++ b/src/js/learning/gauge.js @@ -0,0 +1,479 @@ +/** + * 게이지 진행률 관리 클래스 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, AnimationUtils, Utils) + */ +class GaugeManager { + constructor(dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + + try { + this.maskPath = this.domUtils?.$("#maskPath") || document.getElementById("maskPath"); + this.gaugeSvg = this.domUtils?.$("#gauge-svg") || document.getElementById("gauge-svg"); + this.pathLength = 0; + + if (!this.maskPath) { + this._handleError(new Error('maskPath 요소를 찾을 수 없습니다.'), 'GaugeManager.constructor'); + } + if (!this.gaugeSvg) { + this._handleError(new Error('gauge-svg 요소를 찾을 수 없습니다.'), 'GaugeManager.constructor'); + } + } catch (error) { + this._handleError(error, 'GaugeManager.constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `GaugeManager.${context}`, + component: 'GaugeManager', + ...additionalInfo + }, false); + } else { + console.error(`[GaugeManager] ${context}:`, error, additionalInfo); + } + } + + /** + * 진행률 설정 + * @param {number} percent - 진행률 (0-100) 또는 pathPercent (0-1) + * @param {boolean} isPathPercent - percent가 pathPercent인지 여부 (기본값: false) + * @param {boolean} animate - 애니메이션 적용 여부 (기본값: true) + */ + setProgress(percent, isPathPercent = false, animate = true) { + try { + if (!this.maskPath) { + this._handleError(new Error('maskPath 요소를 찾을 수 없습니다.'), 'setProgress'); + return; + } + + // 입력값 유효성 검증 + if (typeof percent !== 'number' || isNaN(percent)) { + this._handleError(new Error(`유효하지 않은 percent 값: ${percent}`), 'setProgress'); + return; + } + + if (this.pathLength === 0) { + this.pathLength = this.maskPath.getTotalLength(); + if (this.pathLength === 0) { + this._handleError(new Error('pathLength가 0입니다.'), 'setProgress'); + return; + } + } + + let targetPathPercent; + + if (isPathPercent) { + // pathPercent를 직접 사용 (0-1) + targetPathPercent = Math.max(0, Math.min(1, percent)); + } else { + // percent를 pathPercent로 변환 (0-100 -> 0-1) + targetPathPercent = Math.max(0, Math.min(1, percent / 100)); + } + + // maskPath는 아래에서 위로 채워지므로, pathPercent에 해당하는 길이까지만 채움 + // pathPercent가 0이면 시작점, 1이면 끝점 + const targetLength = this.pathLength * targetPathPercent; + const targetOffset = this.pathLength - targetLength; + + // 애니메이션 처리 + if (animate) { + // AnimationUtils 활용 (있는 경우) + if (this.animationUtils) { + // progressBar 애니메이션을 SVG path에 적용하기 어려우므로 기존 방식 유지 + // 하지만 transition은 DOMUtils로 관리 가능 + if (this.domUtils) { + this.domUtils.setStyles(this.maskPath, { + transition: "stroke-dashoffset 0.8s ease-out" + }); + } else { + if (!this.maskPath.style.transition) { + this.maskPath.style.transition = "stroke-dashoffset 0.8s ease-out"; + } + } + } else { + // 애니메이션을 위한 transition 추가 + if (!this.maskPath.style.transition) { + this.maskPath.style.transition = "stroke-dashoffset 0.8s ease-out"; + } + } + } else { + // 초기 로딩 시 애니메이션 없이 즉시 적용 + // transition을 먼저 none으로 설정하여 이전 애니메이션 방지 + if (this.domUtils) { + this.domUtils.setStyles(this.maskPath, { + transition: "none" + }); + } else { + this.maskPath.style.transition = "none"; + } + + // 강제로 레이아웃 계산하여 transition 변경사항 즉시 적용 + void this.maskPath.offsetHeight; + } + + // stroke-dasharray를 pathLength로 설정하여 gap이 없도록 함 + // [dash, gap] 형태에서 gap을 0으로 설정하면 연속된 선이 됨 + // setAttribute를 사용하여 인라인 스타일을 확실하게 덮어쓰기 + this.maskPath.setAttribute('stroke-dasharray', `${this.pathLength} 0`); + + // offset 업데이트 + if (this.domUtils) { + this.domUtils.setStyles(this.maskPath, { + strokeDashoffset: targetOffset + }); + } else { + this.maskPath.style.strokeDashoffset = targetOffset; + } + + // 애니메이션 비활성화 후 다음 업데이트를 위해 transition 복원 + if (!animate) { + // 강제로 레이아웃 계산하여 값 변경사항 즉시 적용 + void this.maskPath.offsetHeight; + + // 다음 프레임에서 transition 복원 (현재 변경사항 적용 후) + requestAnimationFrame(() => { + if (this.domUtils) { + this.domUtils.setStyles(this.maskPath, { + transition: "stroke-dashoffset 0.8s ease-out" + }); + } else { + this.maskPath.style.transition = "stroke-dashoffset 0.8s ease-out"; + } + }); + } + + console.log( + `[GaugeManager] setProgress: pathPercent=${targetPathPercent.toFixed(4)}, targetOffset=${targetOffset.toFixed(2)}, pathLength=${this.pathLength.toFixed(2)}, animate=${animate}` + ); + } catch (error) { + this._handleError(error, 'setProgress', { percent, isPathPercent, animate }); + } + } + + /** + * 경로상의 특정 위치 좌표 반환 + * @param {number} percent - 위치 (0-1) + * @returns {DOMPoint|null} 좌표 + */ + getPointAtPercent(percent) { + try { + if (!this.maskPath) { + this._handleError(new Error('maskPath 요소를 찾을 수 없습니다.'), 'getPointAtPercent'); + return null; + } + + // 입력값 유효성 검증 + if (typeof percent !== 'number' || isNaN(percent)) { + this._handleError(new Error(`유효하지 않은 percent 값: ${percent}`), 'getPointAtPercent'); + return null; + } + + // percent를 0-1 범위로 제한 + const clampedPercent = Math.max(0, Math.min(1, percent)); + + if (this.pathLength === 0) { + this.pathLength = this.maskPath.getTotalLength(); + if (this.pathLength === 0) { + this._handleError(new Error('pathLength가 0입니다.'), 'getPointAtPercent'); + return null; + } + } + + return this.maskPath.getPointAtLength(this.pathLength * clampedPercent); + } catch (error) { + this._handleError(error, 'getPointAtPercent', { percent }); + return null; + } + } + + /** + * 마커의 실제 DOM 위치에 가장 가까운 maskPath 지점 찾기 + * @param {number} markerPercentX - 마커의 X 위치 (퍼센트) + * @param {number} markerPercentY - 마커의 Y 위치 (퍼센트) + * @returns {number} 가장 가까운 지점의 pathPercent (0-1) + */ + findClosestPathPercent(markerPercentX, markerPercentY) { + try { + if (!this.maskPath || !this.gaugeSvg) { + this._handleError(new Error('maskPath 또는 gaugeSvg 요소를 찾을 수 없습니다.'), 'findClosestPathPercent'); + return 0; + } + + // 입력값 유효성 검증 + if (typeof markerPercentX !== 'number' || isNaN(markerPercentX) || + typeof markerPercentY !== 'number' || isNaN(markerPercentY)) { + this._handleError(new Error(`유효하지 않은 좌표 값: (${markerPercentX}, ${markerPercentY})`), 'findClosestPathPercent'); + return 0; + } + + if (this.pathLength === 0) { + this.pathLength = this.maskPath.getTotalLength(); + if (this.pathLength === 0) { + this._handleError(new Error('pathLength가 0입니다.'), 'findClosestPathPercent'); + return 0; + } + } + + const viewBox = this.gaugeSvg.viewBox.baseVal; + if (!viewBox || !viewBox.width || !viewBox.height) { + this._handleError(new Error('viewBox가 유효하지 않습니다.'), 'findClosestPathPercent'); + return 0; + } + + const markerX = (markerPercentX / 100) * viewBox.width; + const markerY = (markerPercentY / 100) * viewBox.height; + + // maskPath를 따라 여러 지점을 샘플링하여 가장 가까운 지점 찾기 + const samples = 200; // 샘플링 개수 (정확도와 성능의 균형) + let closestDistance = Infinity; + let closestPercent = 0; + + for (let i = 0; i <= samples; i++) { + try { + const percent = i / samples; + const point = this.maskPath.getPointAtLength(this.pathLength * percent); + + if (!point) { + continue; + } + + // 마커 위치와의 거리 계산 + const dx = point.x - markerX; + const dy = point.y - markerY; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < closestDistance) { + closestDistance = distance; + closestPercent = percent; + } + } catch (error) { + // 개별 샘플링 에러는 무시하고 계속 진행 + continue; + } + } + + return Math.max(0, Math.min(1, closestPercent)); + } catch (error) { + this._handleError(error, 'findClosestPathPercent', { markerPercentX, markerPercentY }); + return 0; + } + } + + /** + * 초기 진행률 계산 (타겟 마커 config 반환) + * @param {Array} allMarkers - 전체 마커 배열 + * @param {Object} config - 설정 객체 + * @returns {Object|null} 타겟 마커 config 객체 + */ + calculateInitialProgress(allMarkers, config) { + try { + // 입력값 유효성 검증 + if (!allMarkers || !Array.isArray(allMarkers)) { + this._handleError(new Error('allMarkers가 배열이 아닙니다.'), 'calculateInitialProgress'); + return null; + } + + if (!config || typeof config !== 'object') { + this._handleError(new Error('config가 유효하지 않습니다.'), 'calculateInitialProgress'); + return null; + } + + const settings = config?.settings || {}; + + if (settings.allowDisabledClick) { + // 비활성 마커 클릭 허용 모드: 완료된 개수만큼 앞에서부터 채우기 + return this._calculateProgressByCount(allMarkers); + } else { + // 순차 학습 모드: 다음 학습 위치 + return this._calculateProgressBySequence(allMarkers); + } + } catch (error) { + this._handleError(error, 'calculateInitialProgress', { allMarkers, config }); + return null; + } + } + + /** + * 완료된 개수 기준 진행률 계산 (allowDisabledClick: true) + * @private + */ + _calculateProgressByCount(allMarkers) { + try { + if (!allMarkers || !Array.isArray(allMarkers)) { + this._handleError(new Error('allMarkers가 배열이 아닙니다.'), '_calculateProgressByCount'); + return null; + } + + // 실제 강의만 필터링 (챕터 제외) + const learningMarkers = allMarkers.filter( + (m) => m && m.isLearningContent !== false + ); + const completedLearningCount = learningMarkers.filter( + (m) => m && m.completed === true + ).length; + + if (completedLearningCount === 0) { + // 완료된 학습 없음 → 첫 번째 챕터 마커까지 + const firstChapterMarker = allMarkers.find( + (m) => m && m.isChapterMarker === true + ); + if (firstChapterMarker) { + console.log( + `[GaugeManager] 완료 기준 진행률: 첫 챕터 마커 (0개 강의 완료)` + ); + return firstChapterMarker; // 마커 config 반환 + } + return null; + } + + if (completedLearningCount >= learningMarkers.length) { + // 모든 강의 완료 → 마지막 마커 위치 + const lastMarker = allMarkers[allMarkers.length - 1]; + if (lastMarker) { + console.log( + `[GaugeManager] 완료 기준 진행률: 마지막 마커 (전체 ${learningMarkers.length}개 강의 완료)` + ); + return lastMarker; // 마커 config 반환 + } + return null; + } + + // 다음 학습할 강의 위치 (현재 학습 중인 마커) + const nextLearningMarker = learningMarkers[completedLearningCount]; + if (!nextLearningMarker) { + console.warn(`[GaugeManager] 다음 학습 마커를 찾을 수 없습니다. (인덱스: ${completedLearningCount})`); + return null; + } + + const nextMarkerIndex = allMarkers.findIndex( + (m) => + m && + m.pathPercent === nextLearningMarker.pathPercent && + m.label === nextLearningMarker.label + ); + + if (nextMarkerIndex === -1) { + console.warn(`[GaugeManager] 타겟 마커를 찾을 수 없습니다.`); + return null; + } + + const targetMarker = allMarkers[nextMarkerIndex]; + if (!targetMarker) { + console.warn(`[GaugeManager] 타겟 마커가 null입니다.`); + return null; + } + + console.log( + `[GaugeManager] 완료 기준 진행률: ${completedLearningCount}/${learningMarkers.length}개 강의 완료, 현재 학습: ${nextLearningMarker.label}` + ); + return targetMarker; // 마커 config 반환 + } catch (error) { + this._handleError(error, '_calculateProgressByCount'); + return null; + } + } + + /** + * 순차 학습 기준 진행률 계산 (allowDisabledClick: false) + * @private + */ + _calculateProgressBySequence(allMarkers) { + try { + if (!allMarkers || !Array.isArray(allMarkers)) { + this._handleError(new Error('allMarkers가 배열이 아닙니다.'), '_calculateProgressBySequence'); + return null; + } + + // 실제 강의만 필터링 (챕터 제외) + const learningMarkers = allMarkers.filter( + (m) => m && m.isLearningContent !== false + ); + + // 마지막으로 완료된 강의의 인덱스 찾기 (순차적) + let lastCompletedLearningIndex = -1; + + for (let i = 0; i < learningMarkers.length; i++) { + if (learningMarkers[i] && learningMarkers[i].completed === true) { + lastCompletedLearningIndex = i; + } else { + // 완료되지 않은 학습을 만나면 중단 + break; + } + } + + // 완료된 강의가 없는 경우 → 첫 번째 챕터 마커까지 + if (lastCompletedLearningIndex === -1) { + const firstChapterMarker = allMarkers.find( + (m) => m && m.isChapterMarker === true + ); + if (firstChapterMarker) { + console.log( + `[GaugeManager] 순차 진행률: 첫 챕터 마커 (강의 완료 없음)` + ); + return firstChapterMarker; // 마커 config 반환 + } + return null; + } + + // 모든 강의가 완료된 경우 → 마지막 마커 위치 + if (lastCompletedLearningIndex === learningMarkers.length - 1) { + const lastMarker = allMarkers[allMarkers.length - 1]; + if (lastMarker) { + console.log( + `[GaugeManager] 순차 진행률: 마지막 마커 (전체 ${learningMarkers.length}개 강의 완료)` + ); + return lastMarker; // 마커 config 반환 + } + return null; + } + + // 다음 학습할 강의 위치 (현재 학습 중인 마커) + const nextIndex = lastCompletedLearningIndex + 1; + if (nextIndex >= learningMarkers.length) { + console.warn(`[GaugeManager] 다음 학습 인덱스가 범위를 벗어났습니다.`); + return null; + } + + const nextLearningMarker = learningMarkers[nextIndex]; + if (!nextLearningMarker) { + console.warn(`[GaugeManager] 다음 학습 마커를 찾을 수 없습니다.`); + return null; + } + + const nextMarkerIndex = allMarkers.findIndex( + (m) => + m && + m.pathPercent === nextLearningMarker.pathPercent && + m.label === nextLearningMarker.label + ); + + if (nextMarkerIndex === -1) { + console.warn(`[GaugeManager] 타겟 마커를 찾을 수 없습니다.`); + return null; + } + + const targetMarker = allMarkers[nextMarkerIndex]; + if (!targetMarker) { + console.warn(`[GaugeManager] 타겟 마커가 null입니다.`); + return null; + } + + console.log( + `[GaugeManager] 순차 진행률: 현재 학습: ${nextLearningMarker.label}` + ); + return targetMarker; // 마커 config 반환 + } catch (error) { + this._handleError(error, '_calculateProgressBySequence'); + return null; + } + } +} \ No newline at end of file diff --git a/src/js/learning/main.js b/src/js/learning/main.js new file mode 100644 index 0000000..b27af5e --- /dev/null +++ b/src/js/learning/main.js @@ -0,0 +1,397 @@ +/** + * 학습 페이지 초기화 및 관리 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils) + */ +class LearningApp { + constructor(dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + // HTML data 속성에서 설정 읽기 + this._loadSettingsFromHTML(); + + // 의존성 전달을 위한 객체 생성 + const commonDependencies = { + domUtils: this.domUtils, + errorHandler: this.errorHandler, + eventManager: this.eventManager, + utils: this.utils, + animationUtils: this.animationUtils + }; + + // GaugeManager 초기화 (의존성 주입) + this.gauge = new GaugeManager(commonDependencies); + + // MarkerManager 초기화 + this.markerManager = new MarkerManager(this.gauge, LEARNING_CONFIG); + + // ChapterCardManager 초기화 (의존성 주입) + this.chapterCardManager = new ChapterCardManager( + LEARNING_CONFIG, + this.gauge, + commonDependencies + ); + + // ProgressIndicator 초기화 + this.progressIndicator = new ProgressIndicator( + LEARNING_CONFIG, + this.gauge, + this.markerManager + ); + + // VideoModal 초기화 + this.modal = new VideoModal(LEARNING_CONFIG, this.markerManager); + + // 마커 매니저와 챕터 카드 매니저에 모달 인스턴스 전달 + if (this.markerManager && typeof this.markerManager.setModalInstance === 'function') { + this.markerManager.setModalInstance(this.modal); + } + if (this.chapterCardManager && typeof this.chapterCardManager.setModalInstance === 'function') { + this.chapterCardManager.setModalInstance(this.modal); + } + + this.init(); + } catch (error) { + this._handleError(error, 'LearningApp.constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `LearningApp.${context}`, + component: 'LearningApp', + ...additionalInfo + }, false); + } else { + console.error(`[LearningApp] ${context}:`, error, additionalInfo); + } + } + + /** + * HTML data 속성에서 설정 읽기 + * @private + */ + _loadSettingsFromHTML() { + try { + const learningGauge = this.domUtils?.$(".lessons-gauge") || document.querySelector(".lessons-gauge"); + if (!learningGauge) { + console.warn("[LearningApp] .lessons-gauge 요소를 찾을 수 없습니다."); + return; + } + + // data-allow-disabled-click + const allowDisabledClick = learningGauge.dataset.allowDisabledClick; + if (allowDisabledClick !== undefined) { + LEARNING_CONFIG.settings.allowDisabledClick = + allowDisabledClick === "true"; + } + + // data-show-disabled-alert + const showDisabledAlert = learningGauge.dataset.showDisabledAlert; + if (showDisabledAlert !== undefined) { + LEARNING_CONFIG.settings.showDisabledAlert = showDisabledAlert === "true"; + } + + // data-disabled-click-message + const disabledClickMessage = learningGauge.dataset.disabledClickMessage; + if (disabledClickMessage) { + LEARNING_CONFIG.settings.disabledClickMessage = disabledClickMessage; + } + + console.log("[LearningApp] HTML 설정 로드 완료:", LEARNING_CONFIG.settings); + } catch (error) { + this._handleError(error, '_loadSettingsFromHTML'); + } + } + + /** + * 초기화 + */ + init() { + try { + const initHandler = () => { + try { + this._initializeComponents(); + } catch (error) { + this._handleError(error, 'init.initHandler'); + } + }; + + // DOMContentLoaded 이벤트 처리 + if (document.readyState === 'loading') { + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "DOMContentLoaded", initHandler); + this.listenerIds.push({ element: window, id: listenerId, type: 'DOMContentLoaded' }); + } else { + window.addEventListener("DOMContentLoaded", initHandler); + } + } else { + // 이미 로드된 경우 즉시 실행 + initHandler(); + } + } catch (error) { + this._handleError(error, 'init'); + } + } + + /** + * 컴포넌트 초기화 + * @private + */ + _initializeComponents() { + try { + // 마커 생성 + if (this.markerManager && typeof this.markerManager.createMarkers === 'function') { + this.markerManager.createMarkers(); + } else { + console.warn("[LearningApp] markerManager.createMarkers를 호출할 수 없습니다."); + } + + // 챕터 카드 생성 + if (this.chapterCardManager && typeof this.chapterCardManager.createChapterCards === 'function') { + this.chapterCardManager.createChapterCards(); + } else { + console.warn("[LearningApp] chapterCardManager.createChapterCards를 호출할 수 없습니다."); + } + + // 진행률 표시 생성 + if (this.progressIndicator && typeof this.progressIndicator.createIndicator === 'function') { + this.progressIndicator.createIndicator(); + } else { + console.warn("[LearningApp] progressIndicator.createIndicator를 호출할 수 없습니다."); + } + + // 초기 진행률 설정 + this._initializeProgress(); + } catch (error) { + this._handleError(error, '_initializeComponents'); + } + } + + /** + * 초기 진행률 설정 + * @private + */ + _initializeProgress() { + try { + if (!this.gauge || !this.markerManager) { + console.warn("[LearningApp] gauge 또는 markerManager가 없습니다."); + return; + } + + // 초기 진행률 설정 (마커의 실제 DOM 위치 기반) + const targetMarkerConfig = this.gauge.calculateInitialProgress( + this.markerManager.allMarkers, + LEARNING_CONFIG + ); + + if (!targetMarkerConfig) { + console.warn("[LearningApp] 타겟 마커 설정을 찾을 수 없습니다."); + return; + } + + // 실제 강의만 카운트 (챕터 제외) + const learningMarkers = (this.markerManager.allMarkers || []).filter( + (m) => m && m.isLearningContent !== false + ); + const completedLearningCount = learningMarkers.filter( + (m) => m && m.completed === true + ).length; + + // 마커의 실제 DOM 위치를 찾아서 가장 가까운 pathPercent 계산 + let initialPathPercent = 0; + + // 100% 완료 시 게이지바를 100%로 설정 + if (completedLearningCount >= learningMarkers.length) { + initialPathPercent = 1.0; // 100% 완료 + console.log( + `[LearningApp] 초기 진행률: 모든 학습 완료, 게이지바 100%로 설정` + ); + } else if (targetMarkerConfig) { + // 타겟 마커 찾기 (pathPercent와 label로 비교) + const targetMarker = (this.markerManager.markers || []).find( + (m) => + m && + m.config && + m.config.pathPercent === targetMarkerConfig.pathPercent && + m.config.label === targetMarkerConfig.label + ); + + if (targetMarker && targetMarker.element) { + // gaugePercent가 있으면 우선 사용, 없으면 마커의 실제 DOM 위치 기반으로 계산 + if (targetMarkerConfig.gaugePercent !== undefined) { + initialPathPercent = targetMarkerConfig.gaugePercent; + console.log( + `[LearningApp] 초기 진행률: gaugePercent 사용: ${(initialPathPercent * 100).toFixed(1)}%` + ); + } else { + // 마커의 실제 DOM 위치 가져오기 + const markerLeft = parseFloat(targetMarker.element.style.left) || 0; + const markerTop = parseFloat(targetMarker.element.style.top) || 0; + + // maskPath에서 마커 위치에 가장 가까운 지점 찾기 + const closestPercent = this.gauge.findClosestPathPercent(markerLeft, markerTop); + if (closestPercent !== null && closestPercent !== undefined) { + initialPathPercent = closestPercent; + } + + console.log( + `[LearningApp] 초기 진행률: 마커 실제 위치 (${markerLeft.toFixed(2)}%, ${markerTop.toFixed(2)}%) → pathPercent: ${initialPathPercent.toFixed(4)}` + ); + } + } else { + // 마커를 찾을 수 없는 경우 gaugePercent 우선 사용, 없으면 pathPercent 사용 + initialPathPercent = targetMarkerConfig.gaugePercent !== undefined + ? targetMarkerConfig.gaugePercent + : (targetMarkerConfig.pathPercent || 0); + console.log( + `[LearningApp] 초기 진행률: 마커를 찾을 수 없음, ${targetMarkerConfig.gaugePercent !== undefined ? 'gaugePercent' : 'pathPercent'} 직접 사용: ${(initialPathPercent * 100).toFixed(1)}%` + ); + } + } + + // 마커 실제 위치에 가장 가까운 pathPercent를 사용하여 채움 (초기 로딩 시 애니메이션 없음) + if (this.gauge && typeof this.gauge.setProgress === 'function') { + this.gauge.setProgress(initialPathPercent, true, false); + } + + // 진행률 표시 업데이트 + if (this.progressIndicator && typeof this.progressIndicator.updateProgress === 'function') { + this.progressIndicator.updateProgress(this.markerManager.allMarkers); + } + } catch (error) { + this._handleError(error, '_initializeProgress'); + } + } + + /** + * 학습 완료 후 챕터 카드 및 진행률 표시 업데이트 + */ + updateChapterCards() { + try { + if (this.chapterCardManager && typeof this.chapterCardManager.updateChapterCards === 'function') { + this.chapterCardManager.updateChapterCards(); + } + + if (this.progressIndicator && typeof this.progressIndicator.updateProgress === 'function' && this.markerManager) { + this.progressIndicator.updateProgress(this.markerManager.allMarkers); + } + } catch (error) { + this._handleError(error, 'updateChapterCards'); + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 컴포넌트 정리 + if (this.chapterCardManager && typeof this.chapterCardManager.destroy === 'function') { + this.chapterCardManager.destroy(); + } + + // 참조 정리 + this.gauge = null; + this.markerManager = null; + this.chapterCardManager = null; + this.progressIndicator = null; + this.modal = null; + } catch (error) { + this._handleError(error, 'destroy'); + } + } +} + +/** + * 학습 앱 초기화 함수 (에러 처리 포함) + * @param {Object} dependencies - 의존성 객체 + */ +function initLearningApp(dependencies = {}) { + try { + // VideoModal이 정의될 때까지 대기 + if (typeof VideoModal === 'undefined') { + // VideoModal이 아직 로드되지 않았으면 재시도 + const retryDelay = dependencies.retryDelay || 10; + setTimeout(() => initLearningApp(dependencies), retryDelay); + return; + } + + // 의존성 주입 (없으면 자동 감지) + const finalDependencies = { + domUtils: dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null), + errorHandler: dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null), + eventManager: dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null), + utils: dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null), + animationUtils: dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null), + ...dependencies + }; + + // LEARNING_CONFIG 유효성 검증 + if (typeof LEARNING_CONFIG === 'undefined') { + const error = new Error('LEARNING_CONFIG가 정의되지 않았습니다.'); + if (finalDependencies.errorHandler) { + finalDependencies.errorHandler.handle(error, { + context: 'initLearningApp' + }, true); // 사용자에게 표시 + } else { + console.error('[LearningApp]', error); + alert('학습 설정을 불러올 수 없습니다.'); + } + return; + } + + // LearningApp 인스턴스 생성 + window.learningApp = new LearningApp(finalDependencies); + + if (!window.learningApp) { + throw new Error('LearningApp 인스턴스 생성 실패'); + } + + console.log('[LearningApp] 초기화 완료'); + } catch (error) { + const errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + if (errorHandler) { + errorHandler.handle(error, { + context: 'initLearningApp' + }, true); // 사용자에게 표시 + } else { + console.error('[LearningApp] 초기화 에러:', error); + alert('학습 앱 초기화 중 오류가 발생했습니다.'); + } + } +} + +// 초기화 실행 +if (document.readyState === 'loading') { + // DOMContentLoaded는 defer 스크립트가 모두 로드된 후에 발생 + if (typeof eventManager !== 'undefined' && eventManager) { + eventManager.on(document, 'DOMContentLoaded', () => initLearningApp()); + } else { + document.addEventListener('DOMContentLoaded', () => initLearningApp()); + } +} else { + // 이미 로드된 경우 즉시 시도 + initLearningApp(); +} diff --git a/src/js/learning/markers.js b/src/js/learning/markers.js new file mode 100644 index 0000000..8efc3af --- /dev/null +++ b/src/js/learning/markers.js @@ -0,0 +1,1175 @@ +/** + * 마커 관리 클래스 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils) + */ +class MarkerManager { + constructor(gaugeManager, config, dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + this.gaugeManager = gaugeManager; + this.config = config; + this.markers = []; + + this.gaugeSvg = this.domUtils?.$("#gauge-svg") || document.getElementById("gauge-svg"); + this.markersContainer = this.domUtils?.$("#markers-container") || document.getElementById("markers-container"); + + if (!this.gaugeSvg) { + this._handleError(new Error('gauge-svg 요소를 찾을 수 없습니다.'), 'constructor'); + } + if (!this.markersContainer) { + this._handleError(new Error('markers-container 요소를 찾을 수 없습니다.'), 'constructor'); + } + + this.allMarkers = config.getAllMarkers ? config.getAllMarkers() : []; // flat 배열로 저장 + this.modalInstance = null; // 모달 인스턴스 저장 + this.completionAnimationShown = false; // 완료 애니메이션 표시 여부 + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `MarkerManager.${context}`, + component: 'MarkerManager', + ...additionalInfo + }, false); + } else { + console.error(`[MarkerManager] ${context}:`, error, additionalInfo); + } + } + + /** + * 모달 인스턴스 설정 + * @param {VideoModal} modal - 모달 인스턴스 + */ + setModalInstance(modal) { + this.modalInstance = modal; + } + + /** + * Start-line 이미지 업데이트 + * @private + */ + _updateStartLine() { + try { + const startLineImg = this.domUtils?.$(".start-line img") || document.querySelector(".start-line img"); + if (!startLineImg) { + console.warn("[MarkerManager] .start-line img 요소를 찾을 수 없습니다."); + return; + } + + // 실제 강의만 카운트 (챕터 제외) + const learningMarkers = (this.allMarkers || []).filter( + (m) => m && m.isLearningContent !== false + ); + const completedCount = learningMarkers.filter((m) => m && m.completed === true).length; + const allCompleted = completedCount >= learningMarkers.length; + + // 완료된 학습이 1개 이상 있고, 모든 학습이 완료되지 않았으면 on + // 완료된 학습이 없거나, 모든 학습이 완료되었으면 off + const hasCompletedLessons = completedCount > 0; + const shouldShowOn = hasCompletedLessons && !allCompleted; + + const imagePath = shouldShowOn + ? "./assets/images/learning/img_start_on.svg" + : "./assets/images/learning/img_start_off.svg"; + + startLineImg.src = imagePath; + + console.log( + `[MarkerManager] Start-line 상태: ${shouldShowOn ? "ON" : "OFF"} (완료된 학습 ${completedCount}/${learningMarkers.length}개, 전체 완료: ${allCompleted})` + ); + } catch (error) { + this._handleError(error, '_updateStartLine'); + } + } + + /** + * 모든 마커 생성 + */ + createMarkers() { + try { + if (!this.gaugeSvg) { + this._handleError(new Error('gaugeSvg가 없습니다.'), 'createMarkers'); + return; + } + + if (!this.markersContainer) { + this._handleError(new Error('markersContainer가 없습니다.'), 'createMarkers'); + return; + } + + // SVG 컨테이너 크기 가져오기 (초기 계산용) + const viewBox = this.gaugeSvg.viewBox.baseVal; + if (!viewBox || !viewBox.width || !viewBox.height) { + this._handleError(new Error('viewBox가 유효하지 않습니다.'), 'createMarkers'); + return; + } + + if (!this.allMarkers || !Array.isArray(this.allMarkers)) { + this._handleError(new Error('allMarkers가 배열이 아닙니다.'), 'createMarkers'); + return; + } + + this.allMarkers.forEach((config, index) => { + try { + this._createMarker(config, index, viewBox); + } catch (error) { + this._handleError(error, 'createMarkers.marker', { index }); + } + }); + + // 마커 생성 후 즉시 이미지 업데이트 + this.updateMarkers(); + + // Start-line 상태 업데이트 + this._updateStartLine(); + + // 초기 로드 시 모든 학습이 완료 상태면 배경 표시 (애니메이션 없이) + const allCompleted = this.allMarkers.every((marker) => marker && marker.completed === true); + if (allCompleted) { + this._showCompletionBackgroundStatic(); + this.completionAnimationShown = true; // 플래그 설정하여 중복 방지 + } + + // 윈도우 리사이즈 시 마커 위치 재계산 + this._setupResizeHandler(); + } catch (error) { + this._handleError(error, 'createMarkers'); + } + } + + /** + * 리사이즈 핸들러 설정 + * @private + */ + _setupResizeHandler() { + try { + const resizeHandler = () => { + try { + console.log("[MarkerManager] 리사이즈 감지: 마커 위치 재계산"); + this._updateMarkerPositions(); + } catch (error) { + this._handleError(error, '_setupResizeHandler.resizeHandler'); + } + }; + + // Utils.throttle 사용 (있는 경우) + if (this.utils && this.utils.throttle) { + const throttledResize = this.utils.throttle(resizeHandler, 100); + + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "resize", throttledResize); + this.listenerIds.push({ element: window, id: listenerId, type: 'resize' }); + } else { + window.addEventListener("resize", throttledResize); + } + } else { + // 폴백: debounce 구현 + let resizeTimer; + const debouncedResize = () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(resizeHandler, 100); + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "resize", debouncedResize); + this.listenerIds.push({ element: window, id: listenerId, type: 'resize' }); + } else { + window.addEventListener("resize", debouncedResize); + } + } + } catch (error) { + this._handleError(error, '_setupResizeHandler'); + } + } + + /** + * 마커 위치 업데이트 (리사이즈 시) + * @private + */ + _updateMarkerPositions() { + try { + if (!this.gaugeSvg || !this.gaugeManager) { + console.warn("[MarkerManager] gaugeSvg 또는 gaugeManager가 없습니다."); + return; + } + + const viewBox = this.gaugeSvg.viewBox.baseVal; + if (!viewBox || !viewBox.width || !viewBox.height) { + console.warn("[MarkerManager] viewBox가 유효하지 않습니다."); + return; + } + + this.markers.forEach((marker, index) => { + try { + if (!marker || !marker.config || !marker.element) { + console.warn(`[MarkerManager] 마커 ${index}가 유효하지 않습니다.`); + return; + } + + const point = this.gaugeManager.getPointAtPercent( + marker.config.pathPercent + ); + + if (!point) { + console.warn(`[MarkerManager] 마커 ${index}의 포인트를 가져올 수 없습니다.`); + return; + } + + const percentX = (point.x / viewBox.width) * 100; + const percentY = (point.y / viewBox.height) * 100; + + if (this.domUtils) { + this.domUtils.setStyles(marker.element, { + left: `${percentX}%`, + top: `${percentY}%` + }); + } else { + marker.element.style.left = `${percentX}%`; + marker.element.style.top = `${percentY}%`; + } + } catch (error) { + this._handleError(error, '_updateMarkerPositions.marker', { index }); + } + }); + } catch (error) { + this._handleError(error, '_updateMarkerPositions'); + } + } + + /** + * 개별 마커 생성 + * @private + */ + _createMarker(config, index, viewBox) { + try { + if (!config) { + console.warn(`[MarkerManager] 마커 ${index}의 config가 없습니다.`); + return; + } + + if (!this.markersContainer) { + this._handleError(new Error('markersContainer가 없습니다.'), '_createMarker'); + return; + } + + if (!viewBox || !viewBox.width || !viewBox.height) { + this._handleError(new Error('viewBox가 유효하지 않습니다.'), '_createMarker'); + return; + } + + const point = this.gaugeManager.getPointAtPercent(config.pathPercent); + if (!point) { + console.warn(`[MarkerManager] 마커 ${index}의 포인트를 가져올 수 없습니다.`); + return; + } + + const marker = this.domUtils?.createElement('div', { class: 'marker' }) || document.createElement("div"); + marker.className = "marker"; + if (config.type === "chapter") { + this.domUtils?.addClasses(marker, 'chapter') || marker.classList.add("chapter"); + } + + marker.dataset.index = index; + marker.dataset.type = config.type || 'normal'; + marker.dataset.percent = config.pathPercent || 0; + + const img = this.domUtils?.createElement('img', { + style: { height: 'auto' }, + loading: 'lazy' + }) || document.createElement("img"); + + if (!this.domUtils) { + img.style.height = "auto"; + img.loading = "lazy"; + } + + marker.appendChild(img); + this.markersContainer.appendChild(marker); + + // 퍼센트 기반 위치 계산 + const percentX = (point.x / viewBox.width) * 100; + const percentY = (point.y / viewBox.height) * 100; + + if (this.domUtils) { + this.domUtils.setStyles(marker, { + left: `${percentX}%`, + top: `${percentY}%`, + cursor: "default", + pointerEvents: "none" + }); + } else { + marker.style.left = `${percentX}%`; + marker.style.top = `${percentY}%`; + marker.style.cursor = "default"; + marker.style.pointerEvents = "none"; // 클릭 이벤트 완전 차단 + } + + console.log( + `[MarkerManager] 마커 [${index}] 생성: (${percentX.toFixed(2)}%, ${percentY.toFixed(2)}%)` + ); + + this._setMarkerState(marker, index); + + this.markers.push({ + element: marker, + img: img, + config: config, + point: point, + }); + } catch (error) { + this._handleError(error, '_createMarker', { index, config }); + } + } + + /** + * 마커 상태 설정 + * @private + */ + _setMarkerState(marker, index) { + const config = this.allMarkers[index]; + + // 기존 상태 클래스 제거 + marker.classList.remove("current", "completed"); + + if (config.completed) { + // 완료된 마커 + marker.classList.add("completed"); + } else if (this.isMarkerClickable(index)) { + // 현재 학습 가능한 마커 (current) + marker.classList.add("current"); + } + + console.log( + `[MarkerManager] 마커 [${index}] 상태: ${config.completed ? "completed" : this.isMarkerClickable(index) ? "current" : "base"}` + ); + } + + /** + * 마커 클릭 가능 여부 확인 (진행 상태 판단용) + * @param {number} index - 마커 인덱스 + * @returns {boolean} + */ + isMarkerClickable(index) { + const currentMarker = this.allMarkers[index]; + + // 챕터 마커는 시작점 표시용이므로 클릭 불가 + if (currentMarker.isChapterMarker) { + return false; + } + + // 첫 번째 실제 강의는 항상 활성화 + if (index === 0) return true; + + // 이전 마커 확인 (챕터 마커는 건너뛰고 실제 강의만 체크) + for (let i = index - 1; i >= 0; i--) { + const prevMarker = this.allMarkers[i]; + + // 챕터 마커는 건너뛰기 + if (prevMarker.isChapterMarker) { + continue; + } + + // 이전 실제 강의가 완료되었으면 현재 마커 활성화 + return prevMarker.completed; + } + + // 이전에 실제 강의가 없으면 활성화 + return true; + } + /** + * 마커 상태 업데이트 + * @param {number} progress - 현재 진행률 (선택적) + */ + updateMarkers(progress) { + this.markers.forEach((marker, index) => { + // allMarkers에서 최신 상태 가져오기 + const config = this.allMarkers[index]; + const imgSrc = this._getMarkerImage(config, index); + marker.img.src = imgSrc; + + // marker.config도 업데이트하여 참조 동기화 + marker.config = config; + + // ========== 이미지에 따른 클래스 업데이트 ========== + const element = marker.element; + element.classList.remove("current", "completed"); + + // 이미지 경로에서 상태 판단 + if ( + imgSrc.includes("_completed.png") || + imgSrc.includes("_complete.png") + ) { + element.classList.add("completed"); + } else if (imgSrc.includes("_current.png")) { + element.classList.add("current"); + } + // ================================================== + }); + } + + /** + * 마커 이미지 결정 + * @private + */ + _getMarkerImage(config, index) { + const images = this.config.markerImages[config.type]; + + // 챕터 마커인 경우 특별 처리 + if (config.isChapterMarker) { + // 하위 lessons가 모두 완료되면 completed + if (config.completed) { + return images.completed; + } + + // 하위 lessons 중 1개라도 완료 또는 학습 중이면 current + const chapter = this.config.chapters.find( + (ch) => ch.id === config.chapterId + ); + if (chapter) { + const anyLessonStarted = chapter.lessons.some( + (lesson) => lesson.completed + ); + if (anyLessonStarted) { + return images.current; // mark_chapter_current.png 반환 + } + } + + // 챕터가 활성화되었는지 체크 (이전 챕터 완료) + if (this._isChapterActive(index)) { + return images.current; // mark_chapter_current.png 반환 + } + + return images.base; + } + + // 일반 마커인 경우 + if (config.completed) { + return images.completed; + } else if (this.isMarkerClickable(index) && !config.completed) { + return images.current; + } else { + return images.base; + } + } + + /** + * 챕터 마커 활성화 여부 확인 + * @private + */ + _isChapterActive(chapterMarkerIndex) { + if (chapterMarkerIndex === 0) return true; // 첫 챕터는 항상 활성 + + // 이전 마커들 중 마지막 실제 강의가 완료되었는지 확인 + for (let i = chapterMarkerIndex - 1; i >= 0; i--) { + const prevMarker = this.allMarkers[i]; + + // 챕터 마커는 건너뛰기 + if (prevMarker.isChapterMarker) { + continue; + } + + // 이전 실제 강의가 완료되었으면 현재 챕터 활성화 + return prevMarker.completed; + } + + return true; + } + + /** + * 마커 클릭 가능 여부 업데이트 + */ + updateMarkerClickability() { + this.markers.forEach((marker, index) => { + const element = marker.element; + const config = marker.config; + + // 기존 상태 클래스 제거 + element.classList.remove("current", "completed"); + + // 상태에 따라 클래스 업데이트 + if (config.completed) { + element.classList.add("completed"); + } else if (this.isMarkerClickable(index)) { + element.classList.add("current"); + } + }); + + console.log("[MarkerManager] 마커 상태 업데이트 완료"); + } + + /** + * 학습 완료 처리 + * @param {number} index - 완료할 학습 인덱스 + */ + completeLesson(index) { + try { + // 입력값 유효성 검증 + if (typeof index !== 'number' || index < 0) { + this._handleError(new Error(`유효하지 않은 index: ${index}`), 'completeLesson'); + return; + } + + if (!this.allMarkers || !Array.isArray(this.allMarkers) || index >= this.allMarkers.length) { + this._handleError(new Error(`index ${index}가 범위를 벗어났습니다.`), 'completeLesson'); + return; + } + + if (!this.allMarkers[index]) { + this._handleError(new Error(`마커 ${index}가 null입니다.`), 'completeLesson'); + return; + } + + // 1. allMarkers 업데이트 + this.allMarkers[index].completed = true; + + // 2. 원본 config의 lessons도 업데이트 + const markerData = this.allMarkers[index]; + const chapterId = markerData.chapterId; + const chapter = this.config.chapters.find((ch) => ch.id === chapterId); + + if (chapter && !markerData.isChapterMarker) { + // 실제 강의인 경우 원본 lessons 배열에서 찾아서 업데이트 + const lesson = chapter.lessons.find( + (l) => + l.pathPercent === markerData.pathPercent && + l.label === markerData.label + ); + if (lesson) { + lesson.completed = true; + console.log( + `[MarkerManager] 원본 config 업데이트: ${lesson.label} completed = true` + ); + } + } + + // 3. 챕터 완료 상태 업데이트 + this.config.updateChapterCompletionStatus(); + + // 4. allMarkers 재로드 (챕터 completed 상태 반영) + this.allMarkers = this.config.getAllMarkers(); + + const settings = this.config.settings || {}; + let progressPercent; + + // 실제 강의만 카운트 (챕터 제외) + const learningMarkers = this.allMarkers.filter( + (m) => m.isLearningContent !== false + ); + const completedLearningCount = learningMarkers.filter( + (m) => m.completed + ).length; + + let targetPathPercent = 0; + let targetConfig = null; + + if (settings.allowDisabledClick) { + // 비활성 마커 클릭 허용 모드: 다음 학습 마커까지 + if (completedLearningCount === 0) { + // 첫 번째 챕터 마커까지 + const firstChapterMarker = this.allMarkers.find( + (m) => m.isChapterMarker === true + ); + targetConfig = firstChapterMarker; + } else if (completedLearningCount >= learningMarkers.length) { + // 전체 완료: 마지막 마커 위치 + targetConfig = this.allMarkers[this.allMarkers.length - 1]; + } else { + // 다음 학습할 강의 마커 위치 + const nextLearningMarker = learningMarkers[completedLearningCount]; + const nextMarkerIndex = this.allMarkers.findIndex( + (m) => + m.pathPercent === nextLearningMarker.pathPercent && + m.label === nextLearningMarker.label + ); + targetConfig = this.allMarkers[nextMarkerIndex]; + } + } else { + // 순차 학습 모드: 다음 학습 마커까지 + if (completedLearningCount === 0) { + // 첫 번째 챕터 마커까지 + const firstChapterMarker = this.allMarkers.find( + (m) => m.isChapterMarker === true + ); + targetConfig = firstChapterMarker; + } else if (completedLearningCount >= learningMarkers.length) { + // 전체 완료: 마지막 마커 위치 + targetConfig = this.allMarkers[this.allMarkers.length - 1]; + } else { + // 다음 학습할 강의 마커 위치 + const nextLearningMarker = learningMarkers[completedLearningCount]; + const nextMarkerIndex = this.allMarkers.findIndex( + (m) => + m.pathPercent === nextLearningMarker.pathPercent && + m.label === nextLearningMarker.label + ); + targetConfig = this.allMarkers[nextMarkerIndex]; + } + } + + // 타겟 마커 찾기 (pathPercent로 비교) + const targetMarker = targetConfig + ? this.markers.find( + (m) => + m.config && + m.config.pathPercent === targetConfig.pathPercent && + m.config.label === targetConfig.label + ) + : null; + + // 100% 완료 시 게이지바를 100%로 채움 + if (completedLearningCount >= learningMarkers.length) { + targetPathPercent = 1.0; // 100% 완료 + progressPercent = 100; + console.log( + `[MarkerManager] 모든 학습 완료: 게이지바 100%로 설정` + ); + } else if (targetMarker && targetMarker.element) { + // gaugePercent가 있으면 우선 사용, 없으면 마커의 실제 DOM 위치 기반으로 계산 + if (targetConfig.gaugePercent !== undefined) { + targetPathPercent = targetConfig.gaugePercent; + progressPercent = targetPathPercent * 100; + console.log( + `[MarkerManager] gaugePercent 사용: ${progressPercent.toFixed(1)}%` + ); + } else { + // 마커의 실제 DOM 위치 가져오기 + const markerLeft = parseFloat(targetMarker.element.style.left) || 0; + const markerTop = parseFloat(targetMarker.element.style.top) || 0; + + // maskPath에서 마커 위치에 가장 가까운 지점 찾기 + targetPathPercent = this.gaugeManager.findClosestPathPercent(markerLeft, markerTop); + + progressPercent = targetPathPercent * 100; // 로그용 + console.log( + `[MarkerManager] 마커 실제 위치 기반: (${markerLeft.toFixed(2)}%, ${markerTop.toFixed(2)}%) → pathPercent: ${targetPathPercent.toFixed(4)} (${progressPercent.toFixed(1)}%)` + ); + } + } else if (targetConfig) { + // 마커를 찾을 수 없는 경우 gaugePercent 우선 사용, 없으면 pathPercent 사용 + targetPathPercent = targetConfig.gaugePercent !== undefined ? targetConfig.gaugePercent : (targetConfig.pathPercent || 0); + progressPercent = targetPathPercent * 100; + console.log( + `[MarkerManager] 마커를 찾을 수 없음, ${targetConfig.gaugePercent !== undefined ? 'gaugePercent' : 'pathPercent'} 직접 사용: ${progressPercent.toFixed(1)}%` + ); + } + + // 마커 실제 위치에 가장 가까운 pathPercent를 사용하여 채움 + this.gaugeManager.setProgress(targetPathPercent, true); + this.updateMarkers(progressPercent); + this.updateMarkerClickability(); + + // Start-line 상태 업데이트 (첫 학습 완료 시 OFF → ON 전환) + this._updateStartLine(); + + // 100% 완료 시 폭죽 애니메이션 표시 (실제 강의 기준) + const allLearningCompleted = learningMarkers.every((m) => m.completed); + if (allLearningCompleted && !this.completionAnimationShown) { + // 기존 정적 배경이 있으면 제거 + const existingStaticBg = document.querySelector(".completion-background"); + if (existingStaticBg) { + existingStaticBg.remove(); + } + this._showCompletionAnimation(); + this.completionAnimationShown = true; + } + + // 챕터 카드 상태 업데이트 + if (window.learningApp && window.learningApp.updateChapterCards) { + window.learningApp.updateChapterCards(); + } + + console.log( + `[MarkerManager] 학습 ${index + 1} 완료! (${completedLearningCount}/${learningMarkers.length} 강의) ` + + `진행률: ${progressPercent ? progressPercent.toFixed(1) : 0}%` + ); + } catch (error) { + this._handleError(error, 'completeLesson', { index }); + } + } + + /** + * 수강완료 시 page-title 업데이트 + * @private + */ + _updatePageTitleOnCompletion() { + try { + const pageTitle = this.domUtils?.$(".page-title") || document.querySelector(".page-title"); + if (!pageTitle) { + console.warn("[MarkerManager] .page-title 요소를 찾을 수 없습니다."); + return; + } + + // 현재 이름 추출 (h3 em 태그에서) + const h3 = this.domUtils?.$("h3", pageTitle) || pageTitle.querySelector("h3"); + const em = h3 ? (this.domUtils?.$("em", h3) || h3.querySelector("em")) : null; + let userName = ""; + + if (em) { + // "님" 제거하여 이름만 추출 + userName = em.textContent.replace(/님$/, "").trim(); + } + + // h3 업데이트 + if (h3) { + h3.innerHTML = ` + ${userName}님, 수고하셨습니다. + `; + } + + // p 태그 업데이트 + const p = this.domUtils?.$("p", pageTitle) || pageTitle.querySelector("p"); + if (p) { + const currentYear = new Date().getFullYear(); + p.innerHTML = ` + ${currentYear}년 법정의무교육 시청을 + 완료하셨습니다. + `; + } + + console.log("[MarkerManager] page-title 업데이트 완료"); + } catch (error) { + this._handleError(error, '_updatePageTitleOnCompletion'); + } + } + + /** + * 학습 완료 배경 표시 + * @private + */ + _showCompletionAnimation() { + try { + console.log("[MarkerManager] 🎉 모든 학습 완료! 완료 배경 표시"); + + // page-title 업데이트 + this._updatePageTitleOnCompletion(); + + // 기존 완료 배경이 있으면 제거 + const existingBg = this.domUtils?.$(".completion-background") || document.querySelector(".completion-background"); + if (existingBg) { + this.domUtils?.remove(existingBg) || existingBg.remove(); + } + + // 완료 배경 컨테이너 생성 + const backgroundContainer = this.domUtils?.createElement('div', { + class: 'completion-background', + style: { + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + pointerEvents: 'none', + zIndex: '1' + } + }) || document.createElement("div"); + + if (!this.domUtils) { + backgroundContainer.className = "completion-background"; + backgroundContainer.style.cssText = ` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1; + `; + } + + // SVG 배경 추가 + const svgWrapper = this.domUtils?.createElement('div', { + style: { + position: 'absolute', + top: '49%', + left: '51%', + width: '100%', + height: '100%', + opacity: '0', + translate: '-50% -50%', + animation: 'fadeIn 1.5s ease-in-out forwards' + } + }) || document.createElement("div"); + + if (!this.domUtils) { + svgWrapper.style.cssText = ` + position: absolute; + top: 49%; + left: 51%; + width: 100%; + height: 100%; + opacity: 0; + translate: -50% -50%; + animation: fadeIn 1.5s ease-in-out forwards; + `; + } + + // SVG 로드 및 추가 + fetch("./assets/images/learning/completion-bg.svg") + .then((response) => { + if (!response.ok) { + throw new Error(`SVG 로드 실패: ${response.status}`); + } + return response.text(); + }) + .then((svgContent) => { + try { + svgWrapper.innerHTML = svgContent; + const svg = svgWrapper.querySelector("svg"); + if (svg) { + // 패턴 ID 중복 방지: 고유한 ID로 변경 + const uniqueId = `completion-bg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + const patterns = svg.querySelectorAll( + "pattern[id], filter[id], mask[id], linearGradient[id], radialGradient[id]" + ); + const idMap = new Map(); + + patterns.forEach((pattern) => { + const oldId = pattern.getAttribute("id"); + if (oldId) { + const newId = `${oldId}-${uniqueId}`; + idMap.set(oldId, newId); + pattern.setAttribute("id", newId); + } + }); + + // 패턴을 참조하는 모든 요소의 url(#...) 업데이트 + const allElements = svg.querySelectorAll("*"); + allElements.forEach((el) => { + // fill, stroke, filter, mask 등 속성 업데이트 + ["fill", "stroke", "filter", "mask"].forEach((attr) => { + const value = el.getAttribute(attr); + if (value && value.startsWith("url(#")) { + const match = value.match(/url\(#([^)]+)\)/); + if (match) { + const oldId = match[1]; + if (idMap.has(oldId)) { + el.setAttribute(attr, `url(#${idMap.get(oldId)})`); + } + } + } + }); + }); + + console.log(`[MarkerManager] 패턴 ID 고유화 완료: ${uniqueId}`); + + // 흰색 배경 rect/path 요소 제거 (x="-34" y="-19" 또는 fill="white"인 요소) + const whiteBgElements = svg.querySelectorAll( + 'rect[fill="white"], path[fill="#fff"], path[fill="white"]' + ); + whiteBgElements.forEach((el) => { + const x = el.getAttribute("x"); + const y = el.getAttribute("y"); + // SVG 파일의 흰색 배경 rect (x="-34" y="-19") 제거 + if ( + (x === "-34" && y === "-19") || + el.getAttribute("fill") === "white" || + el.getAttribute("fill") === "#fff" + ) { + el.remove(); + console.log("[MarkerManager] 흰색 배경 요소 제거"); + } + }); + + // "수강완료" 텍스트 요소 제거 또는 숨김 + const allTexts = svg.querySelectorAll("text, tspan"); + allTexts.forEach((textEl) => { + const textContent = textEl.textContent || ""; + if ( + textContent.includes("수강완료") || + textContent.includes("완료") + ) { + textEl.style.display = "none"; + console.log("[MarkerManager] '수강완료' 텍스트 요소 숨김"); + } + }); + + // 화면 크기에 맞게 viewBox 동적 조정 + const screenWidth = window.innerWidth; + const screenHeight = window.innerHeight; + const screenRatio = screenWidth / screenHeight; + const svgRatio = 1911 / 918; + + let newViewBox; + if (screenRatio > svgRatio) { + // 화면이 더 넓음 - 세로 기준으로 viewBox 확장 + const newWidth = 918 * screenRatio; + const offsetX = (newWidth - 1911) / 2; + newViewBox = `${-offsetX} 0 ${newWidth} 918`; + } else { + // 화면이 더 높음 - 가로 기준으로 viewBox 확장 + const newHeight = 1911 / screenRatio; + const offsetY = (newHeight - 918) / 2; + newViewBox = `0 ${-offsetY} 1911 ${newHeight}`; + } + + svg.setAttribute("viewBox", newViewBox); + svg.removeAttribute("width"); + svg.removeAttribute("height"); + svg.setAttribute("preserveAspectRatio", "xMidYMid slice"); + svg.style.cssText = ` + width: 100%; + height: 100%; + `; + + console.log(`[MarkerManager] SVG viewBox 조정: ${newViewBox}`); + } + } catch (error) { + this._handleError(error, '_showCompletionAnimation.svgProcessing'); + } + }) + .catch((error) => { + this._handleError(error, '_showCompletionAnimation.fetch'); + console.warn("[MarkerManager] SVG 로드 실패"); + }); + + backgroundContainer.appendChild(svgWrapper); + + // CSS 애니메이션 정의 및 상단 왼쪽 영역 숨김 (처음 표시될 때만) + if (!(this.domUtils?.$("#completion-animation-style") || document.getElementById("completion-animation-style"))) { + const style = this.domUtils?.createElement('style', { id: 'completion-animation-style' }) || document.createElement("style"); + style.id = "completion-animation-style"; + style.textContent = ` + @keyframes fadeIn { + 0% { opacity: 0; transform: scale(0.95); } + 100% { opacity: 1; transform: scale(1); } + } + /* 상단 왼쪽 영역의 "수강완료" 텍스트 숨김 */ + .completion-background { + overflow: hidden; + } + .completion-background svg { + position: relative; + } + `; + document.head.appendChild(style); + } + + const container = this.domUtils?.$(".container") || document.querySelector(".container"); + if (container) { + container.appendChild(backgroundContainer); + } else { + console.warn("[MarkerManager] .container 요소를 찾을 수 없습니다."); + } + } catch (error) { + this._handleError(error, '_showCompletionAnimation'); + } + } + + /** + * 초기 완료 배경 표시 (애니메이션 없이) + * @private + */ + _showCompletionBackgroundStatic() { + try { + console.log("[MarkerManager] 학습 완료 상태 - 배경 표시"); + + // page-title 업데이트 + this._updatePageTitleOnCompletion(); + + // 기존 완료 배경이 있으면 제거 + const existingBg = this.domUtils?.$(".completion-background") || document.querySelector(".completion-background"); + if (existingBg) { + this.domUtils?.remove(existingBg) || existingBg.remove(); // 기존 배경 제거 후 새로 생성 + } + + // 완료 배경 컨테이너 생성 + const backgroundContainer = this.domUtils?.createElement('div', { + class: 'completion-background', + style: { + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + pointerEvents: 'none', + zIndex: '1' + } + }) || document.createElement("div"); + + if (!this.domUtils) { + backgroundContainer.className = "completion-background"; + backgroundContainer.style.cssText = ` + position: fixed; + top:0; + left:0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1; + `; + } + + // SVG 배경 추가 (애니메이션 없이) + const svgWrapper = this.domUtils?.createElement('div', { + style: { + position: 'absolute', + top: '49%', + left: '51%', + width: '100%', + height: '100%', + opacity: '1', + translate: '-50% -50%' + } + }) || document.createElement("div"); + + if (!this.domUtils) { + svgWrapper.style.cssText = ` + position: absolute; + top: 49%; + left: 51%; + width: 100%; + height: 100%; + opacity: 1; + translate: -50% -50%; + `; + } + + // SVG 로드 및 추가 + fetch("./assets/images/learning/completion-bg.svg") + .then((response) => { + if (!response.ok) { + throw new Error(`SVG 로드 실패: ${response.status}`); + } + return response.text(); + }) + .then((svgContent) => { + try { + svgWrapper.innerHTML = svgContent; + const svg = svgWrapper.querySelector("svg"); + if (svg) { + // 패턴 ID 중복 방지: 고유한 ID로 변경 + const uniqueId = `completion-bg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + const patterns = svg.querySelectorAll( + "pattern[id], filter[id], mask[id], linearGradient[id], radialGradient[id]" + ); + const idMap = new Map(); + + patterns.forEach((pattern) => { + const oldId = pattern.getAttribute("id"); + if (oldId) { + const newId = `${oldId}-${uniqueId}`; + idMap.set(oldId, newId); + pattern.setAttribute("id", newId); + } + }); + + // 패턴을 참조하는 모든 요소의 url(#...) 업데이트 + const allElements = svg.querySelectorAll("*"); + allElements.forEach((el) => { + // fill, stroke, filter, mask 등 속성 업데이트 + ["fill", "stroke", "filter", "mask"].forEach((attr) => { + const value = el.getAttribute(attr); + if (value && value.startsWith("url(#")) { + const match = value.match(/url\(#([^)]+)\)/); + if (match) { + const oldId = match[1]; + if (idMap.has(oldId)) { + el.setAttribute(attr, `url(#${idMap.get(oldId)})`); + } + } + } + }); + }); + + console.log(`[MarkerManager] 패턴 ID 고유화 완료: ${uniqueId}`); + + // 흰색 배경 rect/path 요소 제거 (x="-34" y="-19" 또는 fill="white"인 요소) + const whiteBgElements = svg.querySelectorAll( + 'rect[fill="white"], path[fill="#fff"], path[fill="white"]' + ); + whiteBgElements.forEach((el) => { + const x = el.getAttribute("x"); + const y = el.getAttribute("y"); + // SVG 파일의 흰색 배경 rect (x="-34" y="-19") 제거 + if ( + (x === "-34" && y === "-19") || + el.getAttribute("fill") === "white" || + el.getAttribute("fill") === "#fff" + ) { + el.remove(); + console.log("[MarkerManager] 흰색 배경 요소 제거"); + } + }); + + // "수강완료" 텍스트 요소 제거 또는 숨김 + const allTexts = svg.querySelectorAll("text, tspan"); + allTexts.forEach((textEl) => { + const textContent = textEl.textContent || ""; + if ( + textContent.includes("수강완료") || + textContent.includes("완료") + ) { + textEl.style.display = "none"; + console.log("[MarkerManager] '수강완료' 텍스트 요소 숨김"); + } + }); + + // 화면 크기에 맞게 viewBox 동적 조정 + const screenWidth = window.innerWidth; + const screenHeight = window.innerHeight; + const screenRatio = screenWidth / screenHeight; + const svgRatio = 1911 / 918; + + let newViewBox; + if (screenRatio > svgRatio) { + // 화면이 더 넓음 - 세로 기준으로 viewBox 확장 + const newWidth = 918 * screenRatio; + const offsetX = (newWidth - 1911) / 2; + newViewBox = `${-offsetX} 0 ${newWidth} 918`; + } else { + // 화면이 더 높음 - 가로 기준으로 viewBox 확장 + const newHeight = 1911 / screenRatio; + const offsetY = (newHeight - 918) / 2; + newViewBox = `0 ${-offsetY} 1911 ${newHeight}`; + } + + svg.setAttribute("viewBox", newViewBox); + svg.removeAttribute("width"); + svg.removeAttribute("height"); + svg.setAttribute("preserveAspectRatio", "xMidYMid slice"); + svg.style.cssText = ` + width: 100%; + height: 100%; + `; + + console.log(`[MarkerManager] SVG viewBox 조정: ${newViewBox}`); + } + } catch (error) { + this._handleError(error, '_showCompletionBackgroundStatic.svgProcessing'); + } + }) + .catch((error) => { + this._handleError(error, '_showCompletionBackgroundStatic.fetch'); + console.warn("[MarkerManager] SVG 로드 실패"); + }); + + backgroundContainer.appendChild(svgWrapper); + + const container = this.domUtils?.$(".container") || document.querySelector(".container"); + if (container) { + container.appendChild(backgroundContainer); + } else { + console.warn("[MarkerManager] .container 요소를 찾을 수 없습니다."); + } + } catch (error) { + this._handleError(error, '_showCompletionBackgroundStatic'); + } + } +} diff --git a/src/js/learning/modal.js b/src/js/learning/modal.js new file mode 100644 index 0000000..d87e227 --- /dev/null +++ b/src/js/learning/modal.js @@ -0,0 +1,1343 @@ +/** + * 비디오 모달 관리 클래스 (VideoModalBase 활용) + * 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils) + */ +class VideoModal extends VideoModalBase { + constructor(config, markerManager, dependencies = {}) { + super({ + videos: [], // 학습 페이지는 videos 배열을 사용하지 않음 + modalPath: config.modalPath || "./_modal/video-learning.html", + modalPathTemplate: config.modalPathTemplate || "./_modal/video-{type}.html", + enableHeightAdjustment: true, + enableCommentResizer: false, + enableCommentBox: false, + }); + + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + // VideoModalBase의 config와 학습 페이지 config 병합 + this.config = { ...this.config, ...config }; + this.markerManager = markerManager; + this.currentModal = null; + this.currentLearningIndex = null; + this.currentChapterInfo = null; + + // 높이 조정 관련 (VideoModalBase의 것과 별도로 관리) + this.resizeObserver = null; + this.mutationObserver = null; + this.heightAdjustTimer = null; + this._retryCount = 0; + this._isAdjustingHeight = false; // 높이 조정 중 플래그 + this._windowResizeHandler = null; // window resize 핸들러 저장 + + this._setupKeyboardEvents(); + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `VideoModal.${context}`, + component: 'VideoModal', + ...additionalInfo + }, false); + } else { + console.error(`[VideoModal] ${context}:`, error, additionalInfo); + } + } + + /** + * 챕터 기반으로 비디오 모달 로드 + * @param {Object} chapter - 챕터 데이터 + * @param {number} chapterIndex - 챕터 인덱스 + * @param {number} initialLessonIndex - 초기 표시할 학습의 글로벌 인덱스 + */ + async loadChapter(chapter, chapterIndex, initialLessonIndex) { + try { + // 입력값 유효성 검증 + if (!chapter || typeof chapterIndex !== 'number' || typeof initialLessonIndex !== 'number') { + this._handleError(new Error('유효하지 않은 입력값'), 'loadChapter', { chapter, chapterIndex, initialLessonIndex }); + return; + } + + if (!this.markerManager || !this.markerManager.allMarkers) { + this._handleError(new Error('markerManager가 없습니다.'), 'loadChapter'); + return; + } + + if (initialLessonIndex < 0 || initialLessonIndex >= this.markerManager.allMarkers.length) { + this._handleError(new Error(`initialLessonIndex ${initialLessonIndex}가 범위를 벗어났습니다.`), 'loadChapter'); + return; + } + + this.destroy(); + this.currentLearningIndex = initialLessonIndex; + + // 챕터 정보 저장 (새 구조: 챕터 마커가 첫 번째) + const globalStartIndex = this.config.toGlobalIndex ? this.config.toGlobalIndex(chapterIndex, -1) : null; + if (globalStartIndex === null) { + this._handleError(new Error('globalStartIndex를 계산할 수 없습니다.'), 'loadChapter'); + return; + } + + this.currentChapterInfo = { + chapterIndex: chapterIndex, + chapterData: chapter, + globalStartIndex: globalStartIndex, // 챕터 마커 인덱스 + }; + + const modalHTML = await this._fetchModal(); + const modalElement = this._parseModal(modalHTML, "learning"); + + if (!modalElement) { + this._handleError(new Error('모달 요소를 생성할 수 없습니다.'), 'loadChapter'); + return; + } + + document.body.appendChild(modalElement); + + this.currentModal = modalElement; + this.currentModalElement = modalElement; // VideoModalBase 호환성 + + const initialLesson = this.markerManager.allMarkers[initialLessonIndex]; + if (!initialLesson || !initialLesson.url) { + this._handleError(new Error('초기 학습 데이터가 유효하지 않습니다.'), 'loadChapter'); + return; + } + + this._setupVideo(modalElement, initialLesson.url); + this._updateContent(modalElement, initialLesson, initialLessonIndex); + this._show(modalElement); + } catch (error) { + this._handleError(error, 'loadChapter', { chapter, chapterIndex, initialLessonIndex }); + if (this.errorHandler) { + // ErrorHandler가 있으면 사용자 알림은 ErrorHandler가 처리 + } else { + alert("비디오를 로드하는 중 오류가 발생했습니다."); + } + } + } + + /** + * 비디오 모달 로드 (기존 호환성 유지) + * @param {Object} videoData - 비디오 데이터 + * @param {number} currentIndex - 현재 전역 인덱스 + */ + async load(videoData, currentIndex) { + try { + // 입력값 유효성 검증 + if (!videoData || typeof currentIndex !== 'number' || currentIndex < 0) { + this._handleError(new Error('유효하지 않은 입력값'), 'load', { videoData, currentIndex }); + return; + } + + console.log( + `[VideoModal] load 호출: index=${currentIndex}, label=${videoData?.label}` + ); + + // 새로운 챕터 정보 가져오기 + if (!this.config || typeof this.config.getChapterByGlobalIndex !== 'function') { + this._handleError(new Error('config.getChapterByGlobalIndex가 없습니다.'), 'load'); + return; + } + + const newChapterInfo = this.config.getChapterByGlobalIndex(currentIndex); + + if (!newChapterInfo) { + throw new Error(`챕터 정보를 찾을 수 없습니다: index=${currentIndex}`); + } + + // 같은 챕터 내에서 학습 변경인지 확인 + const isSameChapter = + this.currentModal && + this.currentChapterInfo && + this.currentChapterInfo.chapterIndex === newChapterInfo.chapterIndex; + + if (isSameChapter) { + // 같은 챕터: 비디오와 컨텐츠만 업데이트 + console.log(`[VideoModal] 같은 챕터 내 학습 변경: ${videoData.label}`); + + // 이전 학습 완료 처리 + if ( + this.currentLearningIndex !== null && + this.currentLearningIndex !== currentIndex + ) { + const previousLesson = + this.markerManager.allMarkers[this.currentLearningIndex]; + if (!previousLesson.isChapterMarker && !previousLesson.completed) { + console.log( + `[VideoModal] 이전 학습 완료 처리: [${this.currentLearningIndex}] ${previousLesson.label}` + ); + this.markerManager.completeLesson(this.currentLearningIndex); + + // 완료 처리 후 챕터 정보 다시 가져오기 + this.currentChapterInfo = + this.config.getChapterByGlobalIndex(currentIndex); + } + } + + // 현재 학습 인덱스 업데이트 + this.currentLearningIndex = currentIndex; + + // 비디오와 컨텐츠만 업데이트 (목록은 유지) + this._setupVideo(this.currentModal, videoData.url); + this._updateContent(this.currentModal, videoData, currentIndex, false); // recreateList = false + + // 스크롤을 현재 학습으로 이동 + this._scrollToCurrentLesson(); + } else { + // 다른 챕터: 모달 완전히 재생성 + console.log( + `[VideoModal] 챕터 변경: ${newChapterInfo.chapterData.name}` + ); + + this.destroy(); + this.currentLearningIndex = currentIndex; + this.currentChapterInfo = newChapterInfo; + + console.log(`[VideoModal] 챕터 정보:`, this.currentChapterInfo); + + const modalHTML = await this._fetchModal(); + const modalElement = this._parseModal(modalHTML, videoData.type || "learning"); + + if (!modalElement) { + this._handleError(new Error('모달 요소를 생성할 수 없습니다.'), 'load'); + return; + } + + document.body.appendChild(modalElement); + + this.currentModal = modalElement; + this.currentModalElement = modalElement; // VideoModalBase 호환성 + + if (!videoData.url) { + this._handleError(new Error('videoData.url이 없습니다.'), 'load'); + return; + } + + this._setupVideo(modalElement, videoData.url); + this._updateContent(modalElement, videoData, currentIndex); + this._show(modalElement); + } + } catch (error) { + this._handleError(error, 'load', { + currentIndex, + videoData, + currentChapterInfo: this.currentChapterInfo, + }); + if (this.errorHandler) { + // ErrorHandler가 있으면 사용자 알림은 ErrorHandler가 처리 + } else { + alert(`비디오를 로드하는 중 오류가 발생했습니다.\n${error.message}`); + } + } + } + + /** + * 모달 HTML 가져오기 (VideoModalBase 활용) + * @private + */ + async _fetchModal() { + // VideoModalBase의 loadModalHTML 메서드 활용 + return await this.loadModalHTML("learning"); + } + + /** + * 모달 HTML 파싱 (VideoModalBase 활용) + * @private + */ + _parseModal(modalHTML, modalType = "learning") { + // VideoModalBase의 createModalFromHTML 메서드 활용 + return this.createModalFromHTML(modalHTML, modalType); + } + + /** + * 비디오 설정 (VideoModalBase 활용) + * @private + */ + _setupVideo(modalElement, videoUrl) { + // VideoModalBase의 setupVideo 메서드 활용 + const videoData = { url: videoUrl, id: videoUrl }; + this.setupVideo(modalElement, videoData); + } + + /** + * 모달 컨텐츠 업데이트 + * @private + */ + _updateContent(modalElement, videoData, currentIndex, recreateList = true) { + this._updateTitle(modalElement, videoData.label); + this._updateProgress(modalElement, currentIndex); + + if (recreateList) { + // 새 챕터: 목록 재생성 + this._createLearningList(modalElement, currentIndex); + } else { + // 같은 챕터: 활성 상태만 업데이트 + this._updateLearningListState(modalElement, currentIndex); + } + } + + /** + * 제목 업데이트 + * @private + */ + _updateTitle(modalElement, label) { + try { + if (!modalElement || !label) { + console.warn("[VideoModal] _updateTitle: 유효하지 않은 입력값"); + return; + } + + const videoTitle = this.domUtils?.$(".tit-box h3", modalElement) || modalElement.querySelector(".tit-box h3"); + const currentLesson = this.domUtils?.$(".sub-txt", modalElement) || modalElement.querySelector(".sub-txt"); + + if (videoTitle) videoTitle.textContent = label; + if (currentLesson) currentLesson.textContent = label; + } catch (error) { + this._handleError(error, '_updateTitle', { label }); + } + } + + /** + * 진행률 업데이트 (현재 챕터 기준) + * @private + */ + _updateProgress(modalElement, currentIndex) { + try { + if (!this.currentChapterInfo) { + console.warn("[VideoModal] currentChapterInfo가 없습니다"); + return; + } + + if (!modalElement) { + console.warn("[VideoModal] modalElement가 없습니다"); + return; + } + + const currentChapter = this.currentChapterInfo.chapterData; + const lessons = currentChapter?.lessons; + + if (!lessons || lessons.length === 0) { + console.warn("[VideoModal] lessons가 비어있습니다"); + return; + } + + // 현재 챕터의 완료된 학습 개수 + const completedCount = lessons.filter((lesson) => lesson && lesson.completed === true).length; + const totalCount = lessons.length; + const progressPercent = Math.round((completedCount / totalCount) * 100); + + // 현재 학습의 챕터 내 로컬 인덱스 계산 + // globalStartIndex는 챕터 마커 인덱스이므로, +1 해서 첫 번째 lesson 인덱스 시작 + const globalStartIndex = this.currentChapterInfo.globalStartIndex; + const localIndex = currentIndex - globalStartIndex - 1; // 챕터 마커 다음부터 시작 + + console.log(`[VideoModal] 인덱스 계산:`, { + currentIndex, + globalStartIndex, + localIndex, + lessonLabel: this.currentChapterInfo.lessonData?.label, + }); + + const gaugeFill = this.domUtils?.$("#gaugeFill", modalElement) || modalElement.querySelector("#gaugeFill"); + const labelElement = this.domUtils?.$(".gauge-labels .label:not(.current)", modalElement) || modalElement.querySelector( + ".gauge-labels .label:not(.current)" + ); + const progressText = this.domUtils?.$(".gauge-labels .label.current em", modalElement) || modalElement.querySelector( + ".gauge-labels .label.current em" + ); + + // 게이지바: 챕터 진행률 + if (gaugeFill) { + if (this.domUtils) { + this.domUtils.setStyles(gaugeFill, { width: `${progressPercent}%` }); + } else { + gaugeFill.style.width = progressPercent + "%"; + } + } + + // 차시 업데이트: "현재차시 / 총차시" 형식 + if (labelElement) { + const currentText = localIndex + 1; + labelElement.innerHTML = `${currentText} / ${totalCount} 강`; + console.log(`[VideoModal] 차시 업데이트: ${currentText} / ${totalCount}`); + } else { + console.warn("[VideoModal] 차시 표시 요소를 찾을 수 없습니다."); + } + + // 진행률 퍼센트: 챕터 진행률 + if (progressText) progressText.textContent = progressPercent; + + console.log( + `[VideoModal] 챕터 진행률: ${completedCount}/${totalCount} (${progressPercent}%), 현재 차시: ${localIndex + 1}` + ); + } catch (error) { + this._handleError(error, '_updateProgress', { currentIndex }); + } + } + + /** + * 학습 목차 생성 (현재 챕터만) + * @private + */ + _createLearningList(modalElement, currentGlobalIndex) { + const list = modalElement.querySelector(".learning-list"); + if (!list) return; + + list.innerHTML = ""; + + if (!this.currentChapterInfo) { + console.error("[VideoModal] currentChapterInfo가 없습니다"); + return; + } + + // 현재 챕터 정보 + const currentChapter = this.currentChapterInfo.chapterData; + const globalStartIndex = this.currentChapterInfo.globalStartIndex; + + console.log("[VideoModal] 목차 생성:", { + chapterName: currentChapter.name, + globalStartIndex, + lessonsCount: currentChapter.lessons?.length, + }); + + if (globalStartIndex === undefined || globalStartIndex === null) { + console.error( + "[VideoModal] globalStartIndex가 유효하지 않습니다:", + globalStartIndex + ); + return; + } + + // 챕터는 시작점 표시용이므로 목차에서 제외 + // 하위 lessons만 추가 (실제 강의) + const currentChapterLessons = currentChapter.lessons; + + if (!currentChapterLessons || currentChapterLessons.length === 0) { + console.warn("[VideoModal] lessons가 비어있습니다"); + return; + } + + currentChapterLessons.forEach((lesson, localIndex) => { + const globalIndex = globalStartIndex + 1 + localIndex; // 챕터 다음부터 + + console.log( + `[VideoModal] 목차 항목 생성: ${lesson.label}, globalIndex=${globalIndex}, localIndex=${localIndex}` + ); + + const li = this._createListItem( + lesson, + globalIndex, + localIndex, + currentGlobalIndex + ); + list.appendChild(li); + }); + } + + /** + * 학습 목차 상태만 업데이트 (목록 재생성 없이) + * @private + */ + _updateLearningListState(modalElement, currentGlobalIndex) { + const list = modalElement.querySelector(".learning-list"); + if (!list) return; + + console.log( + `[VideoModal] 목차 상태 업데이트: currentIndex=${currentGlobalIndex}` + ); + + // 모든 항목의 상태 업데이트 + const listItems = list.querySelectorAll("li"); + + listItems.forEach((li) => { + const link = li.querySelector(".list"); + if (!link) return; + + const globalIndex = parseInt(link.dataset.globalIndex); + + // allMarkers에서 최신 completed 상태 가져오기 + const lessonMarker = this.markerManager.allMarkers[globalIndex]; + + if (!lessonMarker) { + console.warn( + `[VideoModal] 마커를 찾을 수 없음: globalIndex=${globalIndex}` + ); + return; + } + + const isCurrentLesson = globalIndex === currentGlobalIndex; + const isRelearning = isCurrentLesson && lessonMarker.completed; + + console.log( + `[VideoModal] 항목 상태 업데이트: [${globalIndex}] ${lessonMarker.label}, completed=${lessonMarker.completed}, isCurrentLesson=${isCurrentLesson}` + ); + + // 클래스 업데이트 + li.className = ""; // 초기화 + if (isRelearning) { + li.className = "active"; + } else if (lessonMarker.completed) { + li.className = "complet"; + } else if (isCurrentLesson) { + li.className = "active"; + } + + // data-current 속성 업데이트 + if (isCurrentLesson) { + li.setAttribute("data-current", "true"); + } else { + li.removeAttribute("data-current"); + } + + // 상태 텍스트 업데이트 + const stateElement = li.querySelector(".state"); + if (stateElement) { + stateElement.textContent = this._getStateText( + lessonMarker, + isCurrentLesson, + isRelearning + ); + } + }); + } + + /** + * 목차 항목 생성 + * @private + */ + _createListItem(lesson, globalIndex, localIndex, currentGlobalIndex) { + const li = document.createElement("li"); + + // globalIndex 유효성 검사 + if ( + globalIndex === undefined || + globalIndex === null || + isNaN(globalIndex) + ) { + console.error("[VideoModal] 유효하지 않은 globalIndex:", globalIndex); + globalIndex = 0; // 폴백 + } + + const isCurrentLesson = globalIndex === currentGlobalIndex; + const isRelearning = isCurrentLesson && lesson.completed; // 재학습 여부 + + // 클래스 설정 (disabled 상태 없음) + if (isRelearning) { + // 재학습 중: active 클래스 (학습중 스타일) + li.className = "active"; + } else if (lesson.completed) { + // 완료된 학습 + li.className = "complet"; + } else if (isCurrentLesson) { + // 처음 학습 중 + li.className = "active"; + } + + // 모든 항목 클릭 가능 + li.style.cursor = "pointer"; + + // 현재 학습 중인 항목에 data 속성 추가 + if (isCurrentLesson) { + li.setAttribute("data-current", "true"); + } + + // 이미지 번호 계산 (1-6 순환) + const imageNum = (globalIndex % 6) + 1; + + // HTML 생성 + li.innerHTML = ` + + ${localIndex + 1}차시 +
+
+ +
+
+
${lesson.label}
+ ${this._getStateText(lesson, isCurrentLesson, isRelearning)} +
+
+
+ `; + + // 이벤트 설정 (항상 클릭 가능) + this._setupListItemEvent(li, globalIndex); + + return li; + } + + /** + * 상태 텍스트 반환 + * @private + */ + _getStateText(lesson, isCurrentLesson, isRelearning) { + if (isRelearning) return "학습중"; + if (lesson.completed) return "학습완료"; + if (isCurrentLesson) return "학습중"; + return "미진행"; + } + + /** + * 목차 항목 이벤트 설정 + * @private + */ + _setupListItemEvent(li, globalIndex) { + const link = li.querySelector(".list"); + + link.addEventListener("click", (e) => { + e.preventDefault(); + + // 모달에서는 모든 항목 클릭 가능 + const clickedIndex = parseInt(e.currentTarget.dataset.globalIndex); + const allMarkers = this.markerManager.allMarkers; + this.load(allMarkers[clickedIndex], clickedIndex); + }); + } + + /** + * 모달 표시 + * @private + */ + _show(modalElement) { + // 모달을 먼저 숨긴 상태로 표시 (높이 조정이 보이지 않도록) + modalElement.style.display = "block"; + modalElement.style.visibility = "hidden"; + modalElement.style.opacity = "0"; + + setTimeout(() => { + this._setupCloseEvents(modalElement); + // 높이 조정 완료 후 모달 표시 + this._initializeHeightAdjustment(() => { + // 높이 조정 완료 후 모달을 보이게 함 + modalElement.style.visibility = "visible"; + modalElement.style.opacity = "1"; + modalElement.style.transition = "opacity 0.3s ease"; + this._scrollToCurrentLesson(); // 현재 학습으로 스크롤 + }); + }, 50); + } + + // ============================================ + // 스크롤 관리 + // ============================================ + + /** + * 현재 학습 중인 항목으로 스크롤 + * @private + */ + _scrollToCurrentLesson() { + const learningList = this.currentModal?.querySelector(".learning-list"); + if (!learningList) { + console.warn("학습 목록을 찾을 수 없습니다"); + return; + } + + // 현재 학습 중인 항목 찾기 + const currentItem = learningList.querySelector('li[data-current="true"]'); + if (!currentItem) { + console.warn("현재 학습 항목을 찾을 수 없습니다"); + return; + } + + // 스크롤 위치 계산 + const listRect = learningList.getBoundingClientRect(); + const itemRect = currentItem.getBoundingClientRect(); + + // 목록 중앙에 현재 항목이 오도록 스크롤 + const scrollOffset = + itemRect.top - listRect.top - listRect.height / 2 + itemRect.height / 2; + + // 부드러운 스크롤 + learningList.scrollTo({ + top: learningList.scrollTop + scrollOffset, + behavior: "smooth", + }); + + console.log(`[VideoModal] 현재 학습으로 스크롤 이동:`, { + currentItem: currentItem.querySelector(".title")?.textContent, + scrollOffset, + }); + + // 시각적 강조 효과 (선택사항) + this._highlightCurrentLesson(currentItem); + } + + /** + * 현재 학습 항목 시각적 강조 + * @private + */ + _highlightCurrentLesson(currentItem) { + // 이미 active 클래스가 있으므로 추가 효과는 선택사항 + // 예: 깜빡임 효과, 테두리 강조 등 + currentItem.style.transition = "all 0.3s ease"; + + // 간단한 강조 효과 (선택사항) + setTimeout(() => { + currentItem.style.transition = ""; + }, 1000); + } + + // ============================================ + // 높이 조정 + // ============================================ + + /** + * 높이 조정 초기화 + * @private + * @param {Function} onComplete - 높이 조정 완료 후 실행할 콜백 + */ + _initializeHeightAdjustment(onComplete) { + // 1단계: 즉시 시도 + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + + // 2단계: 다음 프레임에서 시도 + requestAnimationFrame(() => { + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + }); + + // 3단계: 50ms 후 시도 + setTimeout(() => { + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + }, 50); + + // 4단계: 100ms 후 시도 + setTimeout(() => { + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + }, 100); + + // 5단계: 200ms 후 시도 (높이 조정 완료로 간주) + setTimeout(() => { + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + + // 높이 조정 완료 콜백 실행 + if (onComplete && typeof onComplete === 'function') { + onComplete(); + } + }, 200); + + // 6단계: ResizeObserver 설정 + this._setupResizeObserver(); + + // 7단계: MutationObserver 설정 + this._setupMutationObserver(); + + // 8단계: 이미지 로딩 대기 (이미지 로딩 후에도 높이 재조정) + this._waitForImagesAndAdjust(); + } + + /** + * 모달 컨텐츠 높이 조정 + * 높이가 화면 높이의 80% 이상일 때만 조절하고, 아닐 경우 80%로 유지 + * @private + */ + _adjustModalContentHeight() { + const modalContent = this.currentModal?.querySelector(".modal-content"); + if (!modalContent) { + console.warn("modal-content 요소를 찾을 수 없습니다"); + return; + } + + // 화면 높이의 80% 계산 + const viewportHeight = window.innerHeight; + const maxHeight = viewportHeight * 0.8; + + // 현재 모달 컨텐츠의 실제 높이 (스타일이 적용되기 전의 자연스러운 높이) + // 높이 스타일을 임시로 제거하여 실제 컨텐츠 높이 측정 + const originalHeight = modalContent.style.height; + modalContent.style.height = "auto"; + const actualHeight = modalContent.scrollHeight; + modalContent.style.height = originalHeight; + + // 실제 높이가 80% 이상이면 조절하지 않고 그대로 유지, 그렇지 않으면 80%로 설정 + if (actualHeight >= maxHeight) { + // 80% 이상이면 높이를 조절하지 않음 (스타일 제거하여 자연스러운 높이 유지) + modalContent.style.height = "auto"; + modalContent.style.overflowY = "auto"; + } else { + // 80% 미만이면 80%로 설정 + modalContent.style.height = maxHeight + "px"; + modalContent.style.overflowY = "auto"; + } + + console.log("모달 컨텐츠 높이 조정:", { + viewportHeight, + maxHeight, + actualHeight, + finalHeight: modalContent.style.height, + }); + } + + /** + * 학습 목록 높이 조정 + * @private + */ + _adjustLearningListHeight() { + const videoSide = this.currentModal?.querySelector(".video-side"); + const videoHeader = this.currentModal?.querySelector(".video-header"); + const videoList = this.currentModal?.querySelector(".video-list"); + const learningList = this.currentModal?.querySelector(".learning-list"); + const gaugeWrap = this.currentModal?.querySelector(".gauge-wrap"); + const commentWrap = this.currentModal?.querySelector(".comment-wrap"); + + if (!videoSide || !videoHeader || !learningList) { + console.warn("필요한 요소를 찾을 수 없습니다"); + this._isAdjustingHeight = false; // 실패 시 플래그 해제 + return; + } + + // 높이 조정 중 플래그 설정 + this._isAdjustingHeight = true; + + // 스크롤 위치 저장 + const savedScrollTop = learningList.scrollTop; + + // 전체 높이 + const totalHeight = videoSide.clientHeight; + + // 높이가 0이거나 비정상적으로 작으면 DOM이 아직 렌더링되지 않은 것 + if (totalHeight < 100) { + console.warn( + `videoSide 높이가 비정상적으로 작습니다: ${totalHeight}px. 재측정 예약...` + ); + + // 최대 3번까지만 재시도 + if (this._retryCount < 3) { + this._retryCount++; + this._isAdjustingHeight = false; // 재시도 전 플래그 해제 + setTimeout(() => this._adjustLearningListHeight(), 100); + } else { + console.error("높이 측정 재시도 횟수 초과"); + this._retryCount = 0; + this._isAdjustingHeight = false; // 실패 시 플래그 해제 + } + return; + } + + // 재시도 카운터 초기화 + this._retryCount = 0; + + // 헤더 높이 (gaugeHeight는 headerHeight에 이미 포함되어 있음) + const headerHeight = videoHeader.offsetHeight; + + // learning-list에 사용 가능한 최대 높이 계산 + // video-list가 있으면 제목과 padding 고려 + let titleHeight = 0; + let paddingTop = 0; + let paddingBottom = 0; + + if (videoList) { + // video-list의 제목 높이 + const videoListTitle = videoList.querySelector("h5.tit"); + titleHeight = videoListTitle ? videoListTitle.offsetHeight : 0; + + // video-list의 padding 값 계산 + const videoListStyle = window.getComputedStyle(videoList); + paddingTop = parseInt(videoListStyle.paddingTop) || 0; + paddingBottom = parseInt(videoListStyle.paddingBottom) || 0; + } + + // comment-wrap 높이 + const commentWrapHeight = commentWrap && commentWrap.offsetHeight > 0 ? commentWrap.offsetHeight : 0; + + // learning-list에 사용 가능한 최대 높이 + // learning-list는 video-list 내부에 있으므로 video-list의 제목과 padding을 고려해야 함 + const availableHeight = + totalHeight - + headerHeight - + commentWrapHeight - + titleHeight - + paddingTop - + paddingBottom - + 10; + + // 사용 가능한 높이가 음수이거나 너무 작으면 경고 + if (availableHeight < 50) { + console.warn(`사용 가능한 높이가 너무 작습니다: ${availableHeight}px`); + this._isAdjustingHeight = false; // 실패 시 플래그 해제 + return; + } + + // 현재 설정된 높이 확인 + const currentHeight = learningList.style.height + ? parseInt(learningList.style.height) + : learningList.offsetHeight; + + // 리스트의 실제 컨텐츠 높이 (스타일 제거 후 측정) + const originalHeight = learningList.style.height; + learningList.style.height = "auto"; + const listContentHeight = learningList.scrollHeight; + learningList.style.height = originalHeight; + + // 컨텐츠가 적으면 컨텐츠 높이만큼, 많으면 사용 가능한 높이만큼 + const listHeight = Math.min(listContentHeight, availableHeight); + + // 최소 높이 보장 + const finalHeight = Math.max(listHeight, 100); + + // 컨텐츠 높이를 CSS 변수로 설정 (::before 요소에서 사용) + learningList.style.setProperty("--scroll-height", listContentHeight + "px"); + + // learning-list의 height와 overflow-y는 CSS로 관리 (인라인 스타일 제거) + + // video-list가 있는 경우 높이 계산 및 스크롤 설정 + if (videoList) { + // video-list의 제목 높이 + const videoListTitle = videoList.querySelector("h5.tit"); + const videoListTitleHeight = videoListTitle ? videoListTitle.offsetHeight : 0; + + // video-list의 padding 값 계산 + const videoListStyle = window.getComputedStyle(videoList); + const videoListPaddingTop = parseInt(videoListStyle.paddingTop) || 0; + const videoListPaddingBottom = parseInt(videoListStyle.paddingBottom) || 0; + + // video-list에 사용 가능한 높이 계산 + // video-list는 learning-list를 포함하므로, 전체 높이에서 헤더, 댓글, 제목, padding을 제외 + // gaugeHeight는 headerHeight에 이미 포함되어 있으므로 별도로 빼지 않음 + const commentWrapHeight = commentWrap && commentWrap.offsetHeight > 0 ? commentWrap.offsetHeight : 0; + const videoListAvailableHeight = + totalHeight - + headerHeight - + commentWrapHeight - + + 10; + + // video-list의 실제 컨텐츠 높이 측정 (learning-list 포함) + const videoListOriginalHeight = videoList.style.height; + const videoListOriginalOverflow = videoList.style.overflowY; + + videoList.style.height = "auto"; + videoList.style.overflowY = "visible"; + + const videoListContentHeight = videoList.scrollHeight; + + videoList.style.height = videoListOriginalHeight; + videoList.style.overflowY = videoListOriginalOverflow; + + // video-list의 높이 계산 + const videoListHeight = Math.min(videoListContentHeight, videoListAvailableHeight); + const videoListFinalHeight = Math.max(videoListHeight, 100); + + // video-list의 현재 높이 확인 + const videoListCurrentHeight = videoList.style.height + ? parseInt(videoList.style.height) + : videoList.offsetHeight; + + const videoListHeightChanged = Math.abs(videoListCurrentHeight - videoListFinalHeight) > 1; + const videoListNeedsScroll = videoListContentHeight > videoListFinalHeight; + + // video-list 높이 및 스크롤 설정 + requestAnimationFrame(() => { + if (videoListHeightChanged) { + videoList.style.height = videoListFinalHeight + "px"; + } + if (videoListNeedsScroll) { + videoList.style.overflowY = "auto"; + } else { + videoList.style.overflowY = ""; // CSS 기본값 사용 + } + }); + } + + // 스크롤 위치 복원 (높이 변경 여부와 관계없이) + requestAnimationFrame(() => { + learningList.scrollTop = savedScrollTop; + // 높이 조정 완료 후 플래그 해제 + this._isAdjustingHeight = false; + }); + + console.log("높이 측정 성공:", { + totalHeight, + headerHeight, + availableHeight, + listContentHeight, + listHeight, + finalHeight, + currentHeight, + savedScrollTop, + }); + } + + /** + * ResizeObserver 설정 + * @private + */ + _setupResizeObserver() { + const videoSide = this.currentModal?.querySelector(".video-side"); + const gaugeWrap = this.currentModal?.querySelector(".gauge-wrap"); + const modalContent = this.currentModal?.querySelector(".modal-content"); + + if (!videoSide) return; + + // 기존 observer 정리 + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } + + this.resizeObserver = new ResizeObserver((entries) => { + // 디바운스 처리 + clearTimeout(this.heightAdjustTimer); + this.heightAdjustTimer = setTimeout(() => { + console.log("ResizeObserver 감지: 높이 재조정"); + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + }, 50); + }); + + this.resizeObserver.observe(videoSide); + if (gaugeWrap) { + this.resizeObserver.observe(gaugeWrap); + } + if (modalContent) { + this.resizeObserver.observe(modalContent); + } + + // window resize 이벤트도 감지 + this._windowResizeHandler = () => { + try { + clearTimeout(this.heightAdjustTimer); + + // Utils.throttle 사용 (있는 경우) + const adjustHeight = () => { + console.log("Window resize 감지: 높이 재조정"); + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + }; + + if (this.utils && this.utils.throttle) { + const throttledAdjust = this.utils.throttle(adjustHeight, 50); + this.heightAdjustTimer = setTimeout(throttledAdjust, 50); + } else { + this.heightAdjustTimer = setTimeout(adjustHeight, 50); + } + } catch (error) { + this._handleError(error, '_setupResizeObserver.windowResizeHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "resize", this._windowResizeHandler); + this.listenerIds.push({ element: window, id: listenerId, type: 'resize' }); + } else { + window.addEventListener("resize", this._windowResizeHandler); + } + } + + /** + * MutationObserver 설정 + * @private + */ + _setupMutationObserver() { + const learningList = this.currentModal?.querySelector(".learning-list"); + + if (!learningList) return; + + // 기존 observer 정리 + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + } + + this.mutationObserver = new MutationObserver((mutations) => { + // 높이 조정 중이면 무시 (무한 루프 방지) + if (this._isAdjustingHeight) { + return; + } + + // learning-list의 style 속성 변경은 무시 (우리가 조정한 것) + const hasRelevantChange = mutations.some((mutation) => { + // learning-list 자체의 style 변경은 무시 + if ( + mutation.type === "attributes" && + mutation.attributeName === "style" && + mutation.target === learningList + ) { + return false; + } + // childList 변경이나 다른 요소의 변경만 감지 + return mutation.type === "childList" || + (mutation.type === "attributes" && mutation.target !== learningList); + }); + + if (!hasRelevantChange) { + return; + } + + // 디바운스 처리 + clearTimeout(this.heightAdjustTimer); + this.heightAdjustTimer = setTimeout(() => { + console.log("MutationObserver 감지: 높이 재조정"); + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + }, 50); + }); + + this.mutationObserver.observe(learningList, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["style", "class"], + }); + } + + /** + * 이미지 로딩 대기 후 높이 조정 + * @private + */ + async _waitForImagesAndAdjust() { + const learningList = this.currentModal?.querySelector(".learning-list"); + if (!learningList) return; + + const images = learningList.querySelectorAll("img"); + + if (images.length === 0) { + console.log("학습 목록에 이미지 없음"); + return; + } + + console.log(`이미지 ${images.length}개 로딩 대기 중...`); + + const imagePromises = Array.from(images).map((img) => { + if (img.complete) return Promise.resolve(); + + return new Promise((resolve) => { + img.onload = () => { + console.log("이미지 로드 완료:", img.src); + resolve(); + }; + img.onerror = () => { + console.warn("이미지 로드 실패:", img.src); + resolve(); + }; + + // 10초 타임아웃 + setTimeout(resolve, 10000); + }); + }); + + await Promise.all(imagePromises); + + console.log("모든 이미지 로딩 완료: 높이 재조정"); + this._adjustModalContentHeight(); + this._adjustLearningListHeight(); + + // 이미지 로딩 후 다시 스크롤 (높이가 변경될 수 있으므로) + setTimeout(() => { + this._scrollToCurrentLesson(); + }, 100); + } + + // ============================================ + // 이벤트 핸들러 + // ============================================ + + /** + * 닫기 이벤트 설정 (ModalUtils 활용) + * @private + */ + _setupCloseEvents(modalElement) { + // ModalUtils를 사용하여 공통 닫기 이벤트 설정 + this._closeEventHandlers = ModalUtils.setupCloseEvents(modalElement, { + onClose: () => this.close(), + onCleanup: () => { + // Observer 정리는 close() 메서드에서 처리 + }, + }); + } + + /** + * 키보드 이벤트 설정 + * @private + */ + _setupKeyboardEvents() { + try { + const keyboardHandler = (e) => { + try { + if (e.key === "Escape") { + if (this.currentModal && this.currentModal.style.display === "block") { + this.close(); + } + } + } catch (error) { + this._handleError(error, '_setupKeyboardEvents.keyboardHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(document, "keydown", keyboardHandler); + this.listenerIds.push({ element: document, id: listenerId, type: 'keydown' }); + } else { + document.addEventListener("keydown", keyboardHandler); + } + } catch (error) { + this._handleError(error, '_setupKeyboardEvents'); + } + } + + /** + * 모달 닫기 (ModalUtils 활용) + */ + close() { + if (!this.currentModal) return; + + // ModalUtils를 사용하여 비디오 정지 + ModalUtils.stopVideo(this.currentModal); + + this.currentModal.style.display = "none"; + + if (this.currentLearningIndex !== null) { + const currentLesson = + this.markerManager.allMarkers[this.currentLearningIndex]; + + // 챕터 마커는 시작점 표시용이므로 완료 처리 안 함 + if (currentLesson.isChapterMarker) { + console.log( + `[VideoModal] 챕터 마커는 완료 처리 안 함: ${currentLesson.label}` + ); + this.currentLearningIndex = null; + this.currentChapterInfo = null; + return; + } + + // 완료된 학습을 다시 본 경우 (재학습) + if (currentLesson.completed) { + console.log( + `[VideoModal] 재학습 완료: [${this.currentLearningIndex}] ${currentLesson.label} - 게이지 유지` + ); + // 게이지바는 이동하지 않음 (completeLesson 호출하지 않음) + } else { + // 새로운 학습 완료 + console.log( + `[VideoModal] 새 학습 완료: [${this.currentLearningIndex}] ${currentLesson.label}` + ); + this.markerManager.completeLesson(this.currentLearningIndex); + } + + this.currentLearningIndex = null; + this.currentChapterInfo = null; + } + } + + /** + * 모달 제거 (VideoModalBase 활용) + */ + destroy() { + try { + if (this.currentModal) { + // 이전 모달 닫을 때 완료 처리 (학습 페이지 특화) + if (this.currentLearningIndex !== null && this.markerManager && this.markerManager.allMarkers) { + const currentLesson = + this.markerManager.allMarkers[this.currentLearningIndex]; + + if (currentLesson) { + // 챕터 마커는 완료 처리 안 함 + if (!currentLesson.isChapterMarker) { + // 완료된 학습을 다시 본 경우 (재학습) + if (currentLesson.completed) { + console.log( + `[VideoModal] 재학습 완료 (destroy): [${this.currentLearningIndex}] ${currentLesson.label} - 게이지 유지` + ); + } else { + // 새로운 학습 완료 + console.log( + `[VideoModal] 새 학습 완료 (destroy): [${this.currentLearningIndex}] ${currentLesson.label}` + ); + if (this.markerManager && typeof this.markerManager.completeLesson === 'function') { + this.markerManager.completeLesson(this.currentLearningIndex); + } + } + } + } + + this.currentLearningIndex = null; + this.currentChapterInfo = null; + } + + // ModalUtils를 사용하여 비디오 정지 및 Observer 정리 + if (typeof ModalUtils !== 'undefined' && ModalUtils && typeof ModalUtils.cleanup === 'function') { + ModalUtils.cleanup(this.currentModal); + } + + // 학습 페이지 특화 Observer 정리 + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + this.mutationObserver = null; + } + + // 타이머 정리 + if (this.heightAdjustTimer) { + clearTimeout(this.heightAdjustTimer); + this.heightAdjustTimer = null; + } + + // window resize 이벤트 리스너 제거 + if (this._windowResizeHandler) { + if (this.eventManager && this.listenerIds.length > 0) { + // EventManager로 등록된 리스너 제거 + this.listenerIds.forEach(({ element, id }) => { + if (element === window && id) { + this.eventManager.off(element, id); + } + }); + this.listenerIds = this.listenerIds.filter(({ element }) => element !== window); + } else { + window.removeEventListener("resize", this._windowResizeHandler); + } + this._windowResizeHandler = null; + } + + // 키보드 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 재시도 카운터 초기화 + this._retryCount = 0; + + // VideoModalBase의 cleanupObservers도 호출 + if (super.cleanupObservers && typeof super.cleanupObservers === 'function') { + super.cleanupObservers(); + } + + // 모달 제거 + if (this.domUtils && this.domUtils.remove) { + this.domUtils.remove(this.currentModal); + } else { + this.currentModal.remove(); + } + this.currentModal = null; + this.currentModalElement = null; // VideoModalBase 호환성 + } + } catch (error) { + this._handleError(error, 'destroy'); + } + } +} diff --git a/src/js/learning/progress-indicator.js b/src/js/learning/progress-indicator.js new file mode 100644 index 0000000..9c7a64f --- /dev/null +++ b/src/js/learning/progress-indicator.js @@ -0,0 +1,1017 @@ +/** + * 진행률 표시 관리 클래스 + * 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils) + */ +class ProgressIndicator { + constructor(config, gaugeManager, markerManager = null, dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + this.config = config; + this.gaugeManager = gaugeManager; + this.markerManager = markerManager; // 마커 매니저 참조 추가 + this.indicator = null; + this.stateIndicator = null; // 평균 상태 표시 요소 + + this.gaugeSvg = this.domUtils?.$("#gauge-svg") || document.getElementById("gauge-svg"); + if (!this.gaugeSvg) { + this._handleError(new Error('gauge-svg 요소를 찾을 수 없습니다.'), 'constructor'); + } + + this.lastMarkerIndex = -1; // 이전 마커 인덱스 추적 + this.lastIndicatorPosition = null; // 이전 indicator 위치 저장 + this.animationFrameId = null; // 애니메이션 프레임 ID + + // 곡선 스타일 설정 (변경 가능) + // 'arc-up': 위로 휘어지는 호 (기본) + // 'arc-down': 아래로 휘어지는 호 + // 'wave': 물결 모양 + // 'steep-arc': 가파른 호 + // 'gentle-arc': 완만한 호 + this.curveStyle = "arc-up"; + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `ProgressIndicator.${context}`, + component: 'ProgressIndicator', + ...additionalInfo + }, false); + } else { + console.error(`[ProgressIndicator] ${context}:`, error, additionalInfo); + } + } + + /** + * 곡선 경로 반환 + * @private + * @returns {string} SVG path 문자열 + */ + _getCurvePath() { + switch (this.curveStyle) { + case "arc-up": + // 위로 휘어지는 호 (상단 배치) + return ''; + + case "arc-down": + // 아래로 휘어지는 호 + return ''; + + case "wave": + // 물결 모양 + return ''; + + case "steep-arc": + // 가파른 호 + return ''; + + case "gentle-arc": + // 완만한 호 + return ''; + + default: + return ''; + } + } + + /** + * 진행률 표시 생성 + */ + createIndicator() { + try { + // 컨테이너 생성 + this.indicator = this.domUtils?.createElement('div', { + class: 'progress-indicator', + id: 'progress-indicator' + }) || document.createElement("div"); + + if (!this.domUtils) { + this.indicator.className = "progress-indicator"; + this.indicator.id = "progress-indicator"; + } + + // 곡선 경로 가져오기 + const curvePath = this._getCurvePath(); + + // SVG 이미지 추가 + this.indicator.innerHTML = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${curvePath} + + + + + + 진도율 + 0% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; + + // 게이지 컨테이너에 추가 + const gaugeContainer = this.domUtils?.$(".lessons-gauge") || document.querySelector(".lessons-gauge"); + if (gaugeContainer) { + gaugeContainer.appendChild(this.indicator); + } else { + console.warn("[ProgressIndicator] .lessons-gauge 요소를 찾을 수 없습니다."); + } + + // 위치 설정 + this._positionIndicator(); + + // 리사이즈 핸들러 설정 + this._setupResizeHandler(); + + // 평균 상태 표시 생성 + this._createStateIndicator(); + } catch (error) { + this._handleError(error, 'createIndicator'); + } + } + + /** + * 평균 상태 표시 생성 + * @private + */ + _createStateIndicator() { + try { + this.stateIndicator = this.domUtils?.createElement('div', { + class: 'state-indicator', + id: 'state-indicator' + }) || document.createElement("div"); + + if (!this.domUtils) { + this.stateIndicator.className = "state-indicator"; + this.stateIndicator.id = "state-indicator"; + } + + const gaugeContainer = this.domUtils?.$(".lessons-gauge") || document.querySelector(".lessons-gauge"); + if (gaugeContainer) { + gaugeContainer.appendChild(this.stateIndicator); + } else { + console.warn("[ProgressIndicator] .lessons-gauge 요소를 찾을 수 없습니다."); + } + + console.log("[ProgressIndicator] 평균 상태 표시 요소 생성 완료"); + } catch (error) { + this._handleError(error, '_createStateIndicator'); + } + } + + /** + * 평균 상태 업데이트 + * @private + * @param {number} currentProgress - 현재 진행률 (0-100) + */ + _updateStateIndicator(currentProgress) { + if (!this.stateIndicator || !this.config.averageProgress) return; + + // 모든 챕터가 완료되었는지 확인 + if (this.config.chapters && this.config.chapters.length > 0) { + const allChaptersCompleted = this.config.chapters.every( + (chapter) => chapter.completed === true + ); + if (allChaptersCompleted) { + this.stateIndicator.style.display = "none"; + console.log(`[ProgressIndicator] 모든 챕터 완료: 상태 이미지 숨김`); + return; + } + } + + const threshold = this.config.averageProgress.threshold; + let stateImage = ""; + let stateType = ""; + + // 상태 결정 + if (currentProgress < threshold - 5) { + // 평균 이하 (여유 범위 5% 적용) + stateImage = this.config.stateImages.below; + stateType = "below"; + } else if ( + currentProgress >= threshold - 5 && + currentProgress <= threshold + 5 + ) { + // 평균 + stateImage = this.config.stateImages.average; + stateType = "average"; + } else { + // 평균 이상 + stateImage = this.config.stateImages.above; + stateType = "above"; + } + + // SVG 캐시 초기화 + if (!this._svgCache) { + this._svgCache = {}; + } + + // 이미 로드된 SVG가 있으면 재사용 + if (this._svgCache[stateImage]) { + this._applySvgContent( + this._svgCache[stateImage], + stateType, + currentProgress + ); + console.log(`[ProgressIndicator] SVG 캐시 사용: ${stateType}`); + return; + } + + // SVG를 인라인으로 로드 + fetch(stateImage) + .then((response) => { + if (!response.ok) throw new Error("SVG 로드 실패"); + return response.text(); + }) + .then((svgContent) => { + // 캐시에 저장 + this._svgCache[stateImage] = svgContent; + this._applySvgContent(svgContent, stateType, currentProgress); + }) + .catch((error) => { + console.error("[ProgressIndicator] SVG 로드 실패:", error); + // 폴백: img 태그 사용 + this.stateIndicator.innerHTML = ` + 학습 상태 + `; + this._positionStateIndicatorOnMarker(currentProgress); + }); + + console.log( + `[ProgressIndicator] 평균 상태 업데이트: ${stateType} (현재: ${currentProgress}%, 평균: ${threshold}%)` + ); + } + + /** + * SVG 컨텐츠 적용 + * @private + */ + _applySvgContent(svgContent, stateType, currentProgress) { + if (!this.stateIndicator) return; + + this.stateIndicator.innerHTML = svgContent; + this.stateIndicator.classList.add("state-image", stateType); + + // SVG DOM이 완전히 로드될 때까지 대기 + requestAnimationFrame(() => { + // 현재 진행 위치의 마커 위에 배치 + this._positionStateIndicatorOnMarker(currentProgress); + }); + } + + /** + * 마커 위에 상태 표시 위치 설정 + * @private + * @param {number} currentProgress - 현재 진행률 (0-100) + */ + _positionStateIndicatorOnMarker(currentProgress) { + if (!this.stateIndicator) return; + + // 모든 챕터가 완료되었는지 확인 + if (this.config.chapters && this.config.chapters.length > 0) { + const allChaptersCompleted = this.config.chapters.every( + (chapter) => chapter.completed === true + ); + if (allChaptersCompleted) { + this.stateIndicator.style.display = "none"; + console.log(`[ProgressIndicator] 모든 챕터 완료: 상태 이미지 숨김`); + return; + } + } + + const allMarkers = this.config.getAllMarkers(); + // 실제 강의만 카운트 (챕터 제외) + const learningMarkers = allMarkers.filter( + (m) => m.isLearningContent !== false + ); + const completedCount = learningMarkers.filter((m) => m.completed).length; + + // 완료된 강의가 0개인 초기 상태: 상태 이미지 숨김 + if (completedCount === 0) { + this.stateIndicator.style.display = "none"; + console.log(`[ProgressIndicator] 학습 완료 0개: 상태 이미지 숨김`); + return; + } + + // 모든 강의 완료 상태: 상태 이미지 숨김 + if (completedCount >= learningMarkers.length) { + this.stateIndicator.style.display = "none"; + console.log(`[ProgressIndicator] 전체 학습 완료: 상태 이미지 숨김`); + return; + } + + // 다음 학습할 실제 강의 마커 찾기 + const targetLearningMarker = learningMarkers[completedCount]; // 다음 학습할 강의 + + if (!targetLearningMarker) { + this.stateIndicator.style.display = "none"; + return; + } + + // 전체 마커 배열에서 해당 강의의 인덱스 찾기 + const targetMarkerIndex = allMarkers.findIndex( + (m) => + m.pathPercent === targetLearningMarker.pathPercent && + m.label === targetLearningMarker.label + ); + + // 챕터 마커인 경우 상태 이미지 숨김 (안전장치) + if (targetLearningMarker.type === "chapter") { + this.stateIndicator.style.display = "none"; + console.log( + `[ProgressIndicator] 챕터 마커(${targetLearningMarker.label})이므로 상태 이미지 숨김` + ); + return; + } + + // 일반 마커인 경우 표시 + this.stateIndicator.style.display = "block"; + + // 마커 매니저에서 실제 마커 DOM 요소 찾기 + const markerElements = document.querySelectorAll(".marker"); + if (!markerElements || markerElements.length === 0) { + // 마커가 없으면 게이지 라인 기준으로 표시 + const currentPercent = currentProgress / 100; + this._positionStateIndicator(currentPercent); + return; + } + + const targetMarker = markerElements[targetMarkerIndex]; + if (!targetMarker) { + // 타겟 마커가 없으면 게이지 라인 기준으로 표시 + const currentPercent = currentProgress / 100; + this._positionStateIndicator(currentPercent); + return; + } + + // maskPath 기준으로 끝 지점 계산 (가장 정확함) + const maskPath = document.getElementById("maskPath"); + + console.log("타겟 마커 : " + targetMarkerIndex); + // ========== 특정 범위로 제한 ========== + const allowedRanges = [ + [1, 8], // 1부터 8까지 + [16, 19], // 16부터 19까지 + [24, 25], + ]; + + const isInAllowedRange = allowedRanges.some( + ([start, end]) => targetMarkerIndex >= start && targetMarkerIndex <= end + ); + + // maskPath 기준으로 끝 지점 계산 (가장 정확함) + if (maskPath && this.gaugeSvg) { + // 기존 애니메이션 프레임 취소 + if (this.animationFrameId) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + const maskPathLength = maskPath.getTotalLength(); + + // maskPath와 동일한 transition 속도로 자연스럽게 이동 + if (!this.stateIndicator.style.transition) { + this.stateIndicator.style.transition = + "left 0.8s ease-out, top 0.8s ease-out"; + } + + // transition 중 실시간으로 위치 업데이트 + const updatePosition = () => { + if (!this.stateIndicator || !maskPath) return; + + // 모든 챕터가 완료되었는지 확인 + if (this.config.chapters && this.config.chapters.length > 0) { + const allChaptersCompleted = this.config.chapters.every( + (chapter) => chapter.completed === true + ); + if (allChaptersCompleted) { + this.stateIndicator.style.display = "none"; + if (this.animationFrameId) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + return; + } + } + + // state-indicator 표시 + if (this.stateIndicator.style.display === "none") { + this.stateIndicator.style.display = ""; + } + + // 마커의 pathPercent를 사용하여 위치 계산 (게이지 라인이 아닌 마커 위치) + const markerPathPercent = targetLearningMarker.pathPercent || 0; + const point = maskPath.getPointAtLength(maskPathLength * markerPathPercent); + + const viewBox = this.gaugeSvg.viewBox.baseVal; + + // SVG 좌표를 퍼센트로 변환 + const percentX = (point.x / viewBox.width) * 100; + const percentY = (point.y / viewBox.height) * 100; + + // state-indicator를 maskPath의 채워진 끝 지점으로 이동 + this.stateIndicator.style.position = "absolute"; + this.stateIndicator.style.left = `${percentX}%`; + this.stateIndicator.style.top = `${percentY}%`; + this.stateIndicator.style.zIndex = "9"; + this.stateIndicator.style.pointerEvents = "none"; + + // transform 설정 (마커 위에 위치하도록) + if (isInAllowedRange) { + this.stateIndicator.style.transformOrigin = "center center"; + this.stateIndicator.style.transform = + "translate(-20%, -110%) scaleX(-1)"; + } else { + this.stateIndicator.style.transformOrigin = ""; + this.stateIndicator.style.transform = "translate(-20%, -110%)"; + } + + // SVG 내부 요소 처리 + const svg = this.stateIndicator.querySelector("svg"); + if (svg) { + const emoji = svg.querySelector(".emoji"); + const emojiText = svg.querySelector(".emoji-text"); + + if (isInAllowedRange) { + const threshold = this.config.averageProgress.threshold; + let emojiTranslateX, emojiTextTranslateX; + + if (currentProgress < threshold - 5) { + emojiTranslateX = "85%"; + emojiTextTranslateX = "108%"; + } else if ( + currentProgress >= threshold - 5 && + currentProgress <= threshold + 5 + ) { + emojiTranslateX = "88%"; + emojiTextTranslateX = "108%"; + } else { + emojiTranslateX = "82%"; + emojiTextTranslateX = "108%"; + } + + if (emoji) { + emoji.style.transform = `translate(${emojiTranslateX}, 0%) scaleX(-1)`; + } + if (emojiText) { + emojiText.style.transform = `translate(${emojiTextTranslateX}, 0%) scaleX(-1)`; + } + } else { + if (emoji) { + emoji.style.transform = ""; + } + if (emojiText) { + emojiText.style.transform = ""; + } + } + } + + // 위치 저장 + this.lastIndicatorPosition = { + left: percentX, + top: percentY, + }; + + // pathPercent 기반이므로 한 번만 업데이트하고 종료 + if (this.animationFrameId) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + }; + + // 위치 업데이트 실행 + updatePosition(); + } else { + // maskPath가 없으면 pathPercent 기반으로 위치 계산 + const markerPathPercent = targetLearningMarker.pathPercent || 0; + const point = this.gaugeManager.getPointAtPercent(markerPathPercent); + const viewBox = this.gaugeSvg.viewBox.baseVal; + + const percentX = (point.x / viewBox.width) * 100; + const percentY = (point.y / viewBox.height) * 100; + + this.stateIndicator.style.position = "absolute"; + this.stateIndicator.style.left = `${percentX}%`; + this.stateIndicator.style.top = `${percentY}%`; + this.stateIndicator.style.zIndex = "9"; + this.stateIndicator.style.pointerEvents = "none"; + + this.lastIndicatorPosition = { + left: percentX, + top: percentY, + }; + } + + // 현재 마커 인덱스 저장 + this.lastMarkerIndex = targetMarkerIndex; + + // 챕터1일 때만 미러링 (emoji/emoji-text 제외) + if (isInAllowedRange) { + // transform-origin을 중앙으로 설정하여 제자리에서 반전 + this.stateIndicator.style.transformOrigin = "center center"; + this.stateIndicator.style.transform = "translate(-20%, -110%) scaleX(-1)"; + + // SVG 내부의 emoji와 emoji-text 요소를 다시 반전 + const svg = this.stateIndicator.querySelector("svg"); + if (svg) { + const emoji = svg.querySelector(".emoji"); + const emojiText = svg.querySelector(".emoji-text"); + + // 상태에 따라 다른 translate 값 적용 + const threshold = this.config.averageProgress.threshold; + let emojiTranslateX, emojiTextTranslateX; + + if (currentProgress < threshold - 5) { + // 평균 이하 (below) + emojiTranslateX = "85%"; + emojiTextTranslateX = "108%"; + } else if ( + currentProgress >= threshold - 5 && + currentProgress <= threshold + 5 + ) { + // 평균 (average) + emojiTranslateX = "88%"; + emojiTextTranslateX = "108%"; + } else { + // 평균 이상 (above) + emojiTranslateX = "82%"; + emojiTextTranslateX = "108%"; + } + if (emoji) { + emoji.style.transform = `translate(${emojiTranslateX}, 0%) scaleX(-1)`; + } + if (emojiText) { + emojiText.style.transform = `translate(${emojiTextTranslateX}, 0%) scaleX(-1)`; + } + } + } else { + // 챕터2 이상일 때는 기본 상태 + this.stateIndicator.style.transformOrigin = ""; + this.stateIndicator.style.transform = "translate(-20%, -110%)"; + + const svg = this.stateIndicator.querySelector("svg"); + if (svg) { + const emoji = svg.querySelector(".emoji"); + const emojiText = svg.querySelector(".emoji-text"); + + if (emoji) { + emoji.style.transform = ""; + } + if (emojiText) { + emojiText.style.transform = ""; + } + } + } + } + + /** + * 경로를 따라 애니메이션으로 이동 + * @private + * @param {SVGPathElement} pathFill - path-fill 경로 요소 + * @param {number} startProgress - 시작 진행률 (0-1) + * @param {number} endProgress - 끝 진행률 (0-1) + * @param {number} targetMarkerLeft - 목표 마커의 left 위치 (%) + * @param {number} targetMarkerTop - 목표 마커의 top 위치 (%) + * @param {boolean} isInAllowedRange - 특정 범위 여부 + * @param {number} currentProgress - 현재 진행률 (0-100) + */ + _animateAlongPath( + pathFill, + startProgress, + endProgress, + targetMarkerLeft, + targetMarkerTop, + isInAllowedRange, + currentProgress + ) { + if (!this.stateIndicator || !this.gaugeSvg) return; + + const pathLength = pathFill.getTotalLength(); + const viewBox = this.gaugeSvg.viewBox.baseVal; + const duration = 800; // 애니메이션 지속 시간 (ms) + const startTime = performance.now(); + + const animate = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + + // easing 함수 (ease-out) + const easedProgress = 1 - Math.pow(1 - progress, 3); + + // 현재 경로상의 위치 계산 + const currentPathProgress = + startProgress + (endProgress - startProgress) * easedProgress; + const point = pathFill.getPointAtLength(pathLength * currentPathProgress); + + const pathPercentX = (point.x / viewBox.width) * 100; + const pathPercentY = (point.y / viewBox.height) * 100; + + // 경로상 위치와 마커 위치를 보간 (마지막에는 마커 위치로 수렴) + const finalProgress = Math.min(progress * 1.2, 1); // 1.2배로 해서 마커 위치로 더 빨리 수렴 + const percentX = + pathPercentX + (targetMarkerLeft - pathPercentX) * finalProgress; + const percentY = + pathPercentY + (targetMarkerTop - pathPercentY) * finalProgress; + + // 위치 업데이트 + this.stateIndicator.style.left = `${percentX}%`; + this.stateIndicator.style.top = `${percentY}%`; + + // transform 설정 + if (isInAllowedRange) { + this.stateIndicator.style.transformOrigin = "center center"; + this.stateIndicator.style.transform = + "translate(-20%, -110%) scaleX(-1)"; + } else { + this.stateIndicator.style.transformOrigin = ""; + this.stateIndicator.style.transform = "translate(-20%, -110%)"; + } + + // SVG 내부 요소 처리 + const svg = this.stateIndicator.querySelector("svg"); + if (svg) { + const emoji = svg.querySelector(".emoji"); + const emojiText = svg.querySelector(".emoji-text"); + + if (isInAllowedRange) { + const threshold = this.config.averageProgress.threshold; + let emojiTranslateX, emojiTextTranslateX; + + if (currentProgress < threshold - 5) { + emojiTranslateX = "85%"; + emojiTextTranslateX = "108%"; + } else if ( + currentProgress >= threshold - 5 && + currentProgress <= threshold + 5 + ) { + emojiTranslateX = "88%"; + emojiTextTranslateX = "108%"; + } else { + emojiTranslateX = "82%"; + emojiTextTranslateX = "108%"; + } + + if (emoji) { + emoji.style.transform = `translate(${emojiTranslateX}, 0%) scaleX(-1)`; + } + if (emojiText) { + emojiText.style.transform = `translate(${emojiTextTranslateX}, 0%) scaleX(-1)`; + } + } else { + if (emoji) { + emoji.style.transform = ""; + } + if (emojiText) { + emojiText.style.transform = ""; + } + } + } + + if (progress < 1) { + requestAnimationFrame(animate); + } else { + // 애니메이션 완료 후 정확히 마커 위치로 설정 + this.stateIndicator.style.left = `${targetMarkerLeft}%`; + this.stateIndicator.style.top = `${targetMarkerTop}%`; + } + }; + + requestAnimationFrame(animate); + } + + /** + * 상태 표시 위치 설정 + * @private + * @param {number} percent - 위치 퍼센트 (0-1) + */ + _positionStateIndicator(percent) { + if (!this.stateIndicator || !this.gaugeSvg) return; + + const viewBox = this.gaugeSvg.viewBox.baseVal; + const point = this.gaugeManager.getPointAtPercent(percent); + + // 퍼센트 기반 위치 계산 + const percentX = (point.x / viewBox.width) * 100; + const percentY = (point.y / viewBox.height) * 100; + + // 위치 설정 (게이지 라인 위쪽에 배치) + this.stateIndicator.style.position = "absolute"; + this.stateIndicator.style.left = `${percentX}%`; + this.stateIndicator.style.top = `${percentY}%`; + this.stateIndicator.style.transform = "translate(-50%, -150%)"; // 라인 위쪽으로 배치 + this.stateIndicator.style.zIndex = "12"; + this.stateIndicator.style.pointerEvents = "none"; + + console.log( + `[ProgressIndicator] 상태 표시 위치: (${percentX.toFixed(2)}%, ${percentY.toFixed(2)}%)` + ); + } + + /** + * 트로피 위치 설정 + * @private + */ + _positionIndicator() { + if (!this.gaugeSvg || !this.indicator) return; + + const viewBox = this.gaugeSvg.viewBox.baseVal; + + // 경로의 끝 지점 (100% 위치) + const endPoint = this.gaugeManager.getPointAtPercent(1.0); + + // 완료 상태 확인 + const isCompleted = this.indicator.classList.contains("completed"); + + // 퍼센트 기반 위치 계산 + let percentX, percentY, transform; + + if (isCompleted) { + // 완료 상태: 다른 위치와 변형 + percentX = (endPoint.x / viewBox.width) * 100 + 26; + percentY = (endPoint.y / viewBox.height) * 100; + transform = "translate(-50%, -100%)"; // 완료 트로피는 덜 올림 + } else { + // 진행 중 상태: 기존 위치 + percentX = (endPoint.x / viewBox.width) * 100 + 26; + percentY = (endPoint.y / viewBox.height) * 100 + 5; + transform = "translate(-50%, -120%)"; // 위로 120% 이동 + } + + // 위치 설정 (경로 끝점 위쪽에 배치) + this.indicator.style.position = "absolute"; + this.indicator.style.left = `${percentX}%`; + this.indicator.style.top = `${percentY}%`; + this.indicator.style.transform = transform; + this.indicator.style.zIndex = "15"; + this.indicator.style.pointerEvents = "none"; // 클릭 이벤트 통과 + + console.log( + `[ProgressIndicator] 위치 설정 (${isCompleted ? "완료" : "진행중"}): (${percentX.toFixed(2)}%, ${percentY.toFixed(2)}%)` + ); + } + + /** + * 리사이즈 핸들러 설정 + * @private + */ + _setupResizeHandler() { + try { + const resizeHandler = () => { + try { + console.log("[ProgressIndicator] 리사이즈 감지: 위치 재계산"); + this._positionIndicator(); + + // 상태 표시도 재계산 (현재 진행률 기준, 마커 위) + if (this.stateIndicator && this.config && this.config.averageProgress) { + const valueSpan = this.domUtils?.$(".progress-value", this.indicator) || this.indicator?.querySelector(".progress-value"); + if (valueSpan) { + const currentProgress = parseInt(valueSpan.textContent) || 0; + this._positionStateIndicatorOnMarker(currentProgress); + } + } + } catch (error) { + this._handleError(error, '_setupResizeHandler.resizeHandler'); + } + }; + + // Utils.throttle 사용 (있는 경우) + if (this.utils && this.utils.throttle) { + const throttledResize = this.utils.throttle(resizeHandler, 100); + + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "resize", throttledResize); + this.listenerIds.push({ element: window, id: listenerId, type: 'resize' }); + } else { + window.addEventListener("resize", throttledResize); + } + } else { + // 폴백: debounce 구현 + let resizeTimer; + const debouncedResize = () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(resizeHandler, 100); + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "resize", debouncedResize); + this.listenerIds.push({ element: window, id: listenerId, type: 'resize' }); + } else { + window.addEventListener("resize", debouncedResize); + } + } + } catch (error) { + this._handleError(error, '_setupResizeHandler'); + } + } + + /** + * 진행률 업데이트 + * @param {Array} allMarkers - 전체 마커 배열 + */ + updateProgress(allMarkers) { + try { + if (!this.indicator) { + console.warn("[ProgressIndicator] indicator가 없습니다."); + return; + } + + if (!allMarkers || !Array.isArray(allMarkers)) { + this._handleError(new Error('allMarkers가 배열이 아닙니다.'), 'updateProgress'); + return; + } + + // 실제 강의만 카운트 (챕터 제외) + const learningContent = allMarkers.filter( + (m) => m && m.isLearningContent !== false + ); + const completedCount = learningContent.filter((m) => m && m.completed === true).length; + const totalCount = learningContent.length; + const percent = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0; + + // tspan 요소 찾기 + const valueSpan = this.domUtils?.$(".progress-value", this.indicator) || this.indicator.querySelector(".progress-value"); + if (valueSpan) { + valueSpan.textContent = `${percent}%`; + } + + // 평균 상태 업데이트 + if (this.config && this.config.averageProgress) { + this._updateStateIndicator(percent); + } + + // 100% 완료 시 트로피 이미지 교체 + if (percent === 100 && !this.indicator.classList.contains("completed")) { + this._replaceWithCompletedTrophy(); + } + + console.log( + `[ProgressIndicator] 진행률 업데이트: ${percent}% (${completedCount}/${totalCount} 강의)` + ); + } catch (error) { + this._handleError(error, 'updateProgress', { allMarkers }); + } + } + + /** + * 완료된 트로피 이미지로 교체 + * @private + */ + async _replaceWithCompletedTrophy() { + try { + if (!this.indicator) { + console.warn("[ProgressIndicator] indicator가 없습니다."); + return; + } + + // 외부 SVG 파일 경로 + const svgPath = "./assets/images/learning/img_trophy_completed.svg"; + + // SVG 파일 로드 + const response = await fetch(svgPath); + if (!response.ok) { + throw new Error(`SVG 로드 실패: ${response.status}`); + } + + const svgText = await response.text(); + + // 기존 indicator의 내용을 완전히 교체 + this.indicator.innerHTML = svgText; + + // completed 클래스 추가 + if (this.domUtils) { + this.domUtils.addClasses(this.indicator, 'completed'); + } else { + this.indicator.classList.add("completed"); + } + + // 위치 재계산 + this._positionIndicator(); + + console.log("[ProgressIndicator] 완료 트로피로 교체 완료"); + } catch (error) { + this._handleError(error, '_replaceWithCompletedTrophy'); + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 애니메이션 프레임 취소 + if (this.animationFrameId) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + // 참조 정리 + this.indicator = null; + this.stateIndicator = null; + this.config = null; + this.gaugeManager = null; + this.markerManager = null; + this.gaugeSvg = null; + this.lastMarkerIndex = -1; + this.lastIndicatorPosition = null; + } catch (error) { + this._handleError(error, 'destroy'); + } + } +} diff --git a/src/js/lib/MotionPathPlugin.min.js b/src/js/lib/MotionPathPlugin.min.js new file mode 100644 index 0000000..f26ce03 --- /dev/null +++ b/src/js/lib/MotionPathPlugin.min.js @@ -0,0 +1,1533 @@ +/*! + * MotionPathPlugin 3.12.2 + * https://greensock.com + * + * @license Copyright 2023, GreenSock. All rights reserved. + * Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership. + * @author: Jack Doyle, jack@greensock.com + */ + +!(function (t, e) { + "object" == typeof exports && "undefined" != typeof module + ? e(exports) + : "function" == typeof define && define.amd + ? define(["exports"], e) + : e(((t = t || self).window = t.window || {})); +})(this, function (t) { + "use strict"; + function p(t) { + return "string" == typeof t; + } + function x(t) { + return Math.round(1e10 * t) / 1e10 || 0; + } + function y(t, e, n, r) { + var a = t[e], + o = 1 === r ? 6 : subdivideSegment(a, n, r); + if (o && o + n + 2 < a.length) + return t.splice(e, 0, a.slice(0, n + o + 2)), a.splice(0, n + o), 1; + } + function C(t, e) { + var n = t.length, + r = t[n - 1] || [], + a = r.length; + n && + e[0] === r[a - 2] && + e[1] === r[a - 1] && + ((e = r.concat(e.slice(2))), n--), + (t[n] = e); + } + var M = /[achlmqstvz]|(-?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/gi, + T = /(?:(-)?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/gi, + L = /[\+\-]?\d*\.?\d+e[\+\-]?\d+/gi, + r = /(^[#\.][a-z]|[a-y][a-z])/i, + V = Math.PI / 180, + s = 180 / Math.PI, + F = Math.sin, + U = Math.cos, + H = Math.abs, + $ = Math.sqrt, + l = Math.atan2, + A = 1e8, + h = function _isNumber(t) { + return "number" == typeof t; + }, + S = {}, + _ = {}, + e = 1e5, + d = function _wrapProgress(t) { + return Math.round(((t + A) % 1) * e) / e || (t < 0 ? 0 : 1); + }, + N = function _round(t) { + return Math.round(t * e) / e || 0; + }, + m = function _getSampleIndex(t, e, n) { + var r = t.length, + a = ~~(n * r); + if (t[a] > e) { + for (; --a && t[a] > e; ); + a < 0 && (a = 0); + } else for (; t[++a] < e && a < r; ); + return a < r ? a : r - 1; + }, + O = function _copyMetaData(t, e) { + return ( + (e.totalLength = t.totalLength), + t.samples + ? ((e.samples = t.samples.slice(0)), + (e.lookup = t.lookup.slice(0)), + (e.minLength = t.minLength), + (e.resolution = t.resolution)) + : t.totalPoints && (e.totalPoints = t.totalPoints), + e + ); + }; + function getRawPath(t) { + var e, + n = (t = (p(t) && r.test(t) && document.querySelector(t)) || t) + .getAttribute + ? t + : 0; + return n && (t = t.getAttribute("d")) + ? (n._gsPath || (n._gsPath = {}), + (e = n._gsPath[t]) && !e._dirty + ? e + : (n._gsPath[t] = stringToRawPath(t))) + : t + ? p(t) + ? stringToRawPath(t) + : h(t[0]) + ? [t] + : t + : console.warn("Expecting a element or an SVG path data string"); + } + function reverseSegment(t) { + var e, + n = 0; + for (t.reverse(); n < t.length; n += 2) + (e = t[n]), (t[n] = t[n + 1]), (t[n + 1] = e); + t.reversed = !t.reversed; + } + var B = { + rect: "rx,ry,x,y,width,height", + circle: "r,cx,cy", + ellipse: "rx,ry,cx,cy", + line: "x1,x2,y1,y2", + }; + function convertToPath(t, e) { + var n, + r, + a, + o, + i, + s, + l, + h, + u, + g, + f, + c, + p, + d, + m, + v, + y, + x, + w, + P, + b, + M, + R = t.tagName.toLowerCase(), + L = 0.552284749831; + return "path" !== R && t.getBBox + ? ((s = (function _createPath(t, e) { + var n, + r = document.createElementNS("http://www.w3.org/2000/svg", "path"), + a = [].slice.call(t.attributes), + o = a.length; + for (e = "," + e + ","; -1 < --o; ) + (n = a[o].nodeName.toLowerCase()), + e.indexOf("," + n + ",") < 0 && + r.setAttributeNS(null, n, a[o].nodeValue); + return r; + })(t, "x,y,width,height,cx,cy,rx,ry,r,x1,x2,y1,y2,points")), + (M = (function _attrToObj(t, e) { + for (var n = e ? e.split(",") : [], r = {}, a = n.length; -1 < --a; ) + r[n[a]] = +t.getAttribute(n[a]) || 0; + return r; + })(t, B[R])), + "rect" === R + ? ((o = M.rx), + (i = M.ry || o), + (r = M.x), + (a = M.y), + (g = M.width - 2 * o), + (f = M.height - 2 * i), + (n = + o || i + ? "M" + + (v = (d = (p = r + o) + g) + o) + + "," + + (x = a + i) + + " V" + + (w = x + f) + + " C" + + [ + v, + (P = w + i * L), + (m = d + o * L), + (b = w + i), + d, + b, + d - (d - p) / 3, + b, + p + (d - p) / 3, + b, + p, + b, + (c = r + o * (1 - L)), + b, + r, + P, + r, + w, + r, + w - (w - x) / 3, + r, + x + (w - x) / 3, + r, + x, + r, + (y = a + i * (1 - L)), + c, + a, + p, + a, + p + (d - p) / 3, + a, + d - (d - p) / 3, + a, + d, + a, + m, + a, + v, + y, + v, + x, + ].join(",") + + "z" + : "M" + + (r + g) + + "," + + a + + " v" + + f + + " h" + + -g + + " v" + + -f + + " h" + + g + + "z")) + : "circle" === R || "ellipse" === R + ? ((h = + "circle" === R + ? (o = i = M.r) * L + : ((o = M.rx), (i = M.ry) * L)), + (n = + "M" + + ((r = M.cx) + o) + + "," + + (a = M.cy) + + " C" + + [ + r + o, + a + h, + r + (l = o * L), + a + i, + r, + a + i, + r - l, + a + i, + r - o, + a + h, + r - o, + a, + r - o, + a - h, + r - l, + a - i, + r, + a - i, + r + l, + a - i, + r + o, + a - h, + r + o, + a, + ].join(",") + + "z")) + : "line" === R + ? (n = "M" + M.x1 + "," + M.y1 + " L" + M.x2 + "," + M.y2) + : ("polyline" !== R && "polygon" !== R) || + ((n = + "M" + + (r = (u = + (t.getAttribute("points") + "").match(T) || []).shift()) + + "," + + (a = u.shift()) + + " L" + + u.join(",")), + "polygon" === R && (n += "," + r + "," + a + "z")), + s.setAttribute( + "d", + rawPathToString((s._gsRawPath = stringToRawPath(n))) + ), + e && + t.parentNode && + (t.parentNode.insertBefore(s, t), t.parentNode.removeChild(t)), + s) + : t; + } + function getRotationAtBezierT(t, e, n) { + var r, + a = t[e], + o = t[e + 2], + i = t[e + 4]; + return ( + (a += (o - a) * n), + (a += ((o += (i - o) * n) - a) * n), + (r = o + (i + (t[e + 6] - i) * n - o) * n - a), + (a = t[e + 1]), + (a += ((o = t[e + 3]) - a) * n), + (a += ((o += ((i = t[e + 5]) - o) * n) - a) * n), + N(l(o + (i + (t[e + 7] - i) * n - o) * n - a, r) * s) + ); + } + function sliceRawPath(t, e, n) { + (n = (function _isUndefined(t) { + return void 0 === t; + })(n) + ? 1 + : x(n) || 0), + (e = x(e) || 0); + var r = Math.max(0, ~~(H(n - e) - 1e-8)), + a = (function copyRawPath(t) { + for (var e = [], n = 0; n < t.length; n++) + e[n] = O(t[n], t[n].slice(0)); + return O(t, e); + })(t); + if ( + (n < e && + ((e = 1 - e), + (n = 1 - n), + (function _reverseRawPath(t, e) { + var n = t.length; + for (e || t.reverse(); n--; ) t[n].reversed || reverseSegment(t[n]); + })(a), + (a.totalLength = 0)), + e < 0 || n < 0) + ) { + var o = Math.abs(~~Math.min(e, n)) + 1; + (e += o), (n += o); + } + a.totalLength || cacheRawPathMeasurements(a); + var i, + s, + l, + h, + u, + g, + f, + c, + p = 1 < n, + d = getProgressData(a, e, S, !0), + m = getProgressData(a, n, _), + v = m.segment, + w = d.segment, + P = m.segIndex, + b = d.segIndex, + M = m.i, + R = d.i, + L = b === P, + T = M === R && L; + if (p || r) { + for ( + i = P < b || (L && M < R) || (T && m.t < d.t), + y(a, b, R, d.t) && + (b++, + i || + (P++, + T ? ((m.t = (m.t - d.t) / (1 - d.t)), (M = 0)) : L && (M -= R))), + Math.abs(1 - (n - e)) < 1e-5 + ? (P = b - 1) + : !m.t && P + ? P-- + : y(a, P, M, m.t) && i && b++, + 1 === d.t && (b = (b + 1) % a.length), + u = [], + f = 1 + (g = a.length) * r, + f += (g - (c = b) + P) % g, + h = 0; + h < f; + h++ + ) + C(u, a[c++ % g]); + a = u; + } else if (((l = 1 === m.t ? 6 : subdivideSegment(v, M, m.t)), e !== n)) + for ( + s = subdivideSegment(w, R, T ? d.t / m.t : d.t), + L && (l += s), + v.splice(M + l + 2), + (s || R) && w.splice(0, R + s), + h = a.length; + h--; + + ) + (h < b || P < h) && a.splice(h, 1); + else + (v.angle = getRotationAtBezierT(v, M + l, 0)), + (d = v[(M += l)]), + (m = v[M + 1]), + (v.length = v.totalLength = 0), + (v.totalPoints = a.totalPoints = 8), + v.push(d, m, d, m, d, m, d, m); + return (a.totalLength = 0), a; + } + function measureSegment(t, e, n) { + (e = e || 0), t.samples || ((t.samples = []), (t.lookup = [])); + var r, + a, + o, + i, + s, + l, + h, + u, + g, + f, + c, + p, + d, + m, + v, + y, + x, + w = ~~t.resolution || 12, + P = 1 / w, + b = n ? e + 6 * n + 1 : t.length, + M = t[e], + R = t[e + 1], + L = e ? (e / 6) * w : 0, + T = t.samples, + S = t.lookup, + C = (e ? t.minLength : A) || A, + _ = T[L + n * w - 1], + N = e ? T[L - 1] : 0; + for (T.length = S.length = 0, a = e + 2; a < b; a += 6) { + if ( + ((o = t[a + 4] - M), + (i = t[a + 2] - M), + (s = t[a] - M), + (u = t[a + 5] - R), + (g = t[a + 3] - R), + (f = t[a + 1] - R), + (l = h = c = p = 0), + H(o) < 0.01 && H(u) < 0.01 && H(s) + H(f) < 0.01) + ) + 8 < t.length && (t.splice(a, 6), (a -= 6), (b -= 6)); + else + for (r = 1; r <= w; r++) + (l = + h - + (h = + ((m = P * r) * m * o + 3 * (d = 1 - m) * (m * i + d * s)) * m)), + (c = p - (p = (m * m * u + 3 * d * (m * g + d * f)) * m)), + (y = $(c * c + l * l)) < C && (C = y), + (N += y), + (T[L++] = N); + (M += o), (R += u); + } + if (_) for (_ -= N; L < T.length; L++) T[L] += _; + if (T.length && C) { + if ( + ((t.totalLength = x = T[T.length - 1] || 0), + x / (t.minLength = C) < 9999) + ) + for (y = v = 0, r = 0; r < x; r += C) S[y++] = T[v] < r ? ++v : v; + } else t.totalLength = T[0] = 0; + return e ? N - T[e / 2 - 1] : N; + } + function cacheRawPathMeasurements(t, e) { + var n, r, a; + for (a = n = r = 0; a < t.length; a++) + (t[a].resolution = ~~e || 12), + (r += t[a].length), + (n += measureSegment(t[a])); + return (t.totalPoints = r), (t.totalLength = n), t; + } + function subdivideSegment(t, e, n) { + if (n <= 0 || 1 <= n) return 0; + var r = t[e], + a = t[e + 1], + o = t[e + 2], + i = t[e + 3], + s = t[e + 4], + l = t[e + 5], + h = r + (o - r) * n, + u = o + (s - o) * n, + g = a + (i - a) * n, + f = i + (l - i) * n, + c = h + (u - h) * n, + p = g + (f - g) * n, + d = s + (t[e + 6] - s) * n, + m = l + (t[e + 7] - l) * n; + return ( + (u += (d - u) * n), + (f += (m - f) * n), + t.splice( + e + 2, + 4, + N(h), + N(g), + N(c), + N(p), + N(c + (u - c) * n), + N(p + (f - p) * n), + N(u), + N(f), + N(d), + N(m) + ), + t.samples && + t.samples.splice(((e / 6) * t.resolution) | 0, 0, 0, 0, 0, 0, 0, 0), + 6 + ); + } + function getProgressData(t, e, n, r) { + (n = n || {}), + t.totalLength || cacheRawPathMeasurements(t), + (e < 0 || 1 < e) && (e = d(e)); + var a, + o, + i, + s, + l, + h, + u, + g = 0, + f = t[0]; + if (e) + if (1 === e) (u = 1), (h = (f = t[(g = t.length - 1)]).length - 8); + else { + if (1 < t.length) { + for ( + i = t.totalLength * e, l = h = 0; + (l += t[h++].totalLength) < i; + + ) + g = h; + e = (i - (s = l - (f = t[g]).totalLength)) / (l - s) || 0; + } + (a = f.samples), + (o = f.resolution), + (i = f.totalLength * e), + (s = (h = f.lookup.length + ? f.lookup[~~(i / f.minLength)] || 0 + : m(a, i, e)) + ? a[h - 1] + : 0), + (l = a[h]) < i && ((s = l), (l = a[++h])), + (u = (1 / o) * ((i - s) / (l - s) + (h % o))), + (h = 6 * ~~(h / o)), + r && + 1 === u && + (h + 6 < f.length + ? ((h += 6), (u = 0)) + : g + 1 < t.length && ((h = u = 0), (f = t[++g]))); + } + else (u = h = g = 0), (f = t[0]); + return ( + (n.t = u), (n.i = h), (n.path = t), (n.segment = f), (n.segIndex = g), n + ); + } + function getPositionOnPath(t, e, n, r) { + var a, + o, + i, + s, + l, + h, + u, + g, + f, + c = t[0], + p = r || {}; + if ( + ((e < 0 || 1 < e) && (e = d(e)), + c.lookup || cacheRawPathMeasurements(t), + 1 < t.length) + ) { + for (i = t.totalLength * e, l = h = 0; (l += t[h++].totalLength) < i; ) + c = t[h]; + e = (i - (s = l - c.totalLength)) / (l - s) || 0; + } + return ( + (a = c.samples), + (o = c.resolution), + (i = c.totalLength * e), + (s = (h = c.lookup.length + ? c.lookup[e < 1 ? ~~(i / c.minLength) : c.lookup.length - 1] || 0 + : m(a, i, e)) + ? a[h - 1] + : 0), + (l = a[h]) < i && ((s = l), (l = a[++h])), + (f = 1 - (u = (1 / o) * ((i - s) / (l - s) + (h % o)) || 0)), + (g = c[(h = 6 * ~~(h / o))]), + (p.x = N( + (u * u * (c[h + 6] - g) + + 3 * f * (u * (c[h + 4] - g) + f * (c[h + 2] - g))) * + u + + g + )), + (p.y = N( + (u * u * (c[h + 7] - (g = c[h + 1])) + + 3 * f * (u * (c[h + 5] - g) + f * (c[h + 3] - g))) * + u + + g + )), + n && + (p.angle = c.totalLength + ? getRotationAtBezierT(c, h, 1 <= u ? 1 - 1e-9 : u || 1e-9) + : c.angle || 0), + p + ); + } + function transformRawPath(t, e, n, r, a, o, i) { + for (var s, l, h, u, g, f = t.length; -1 < --f; ) + for (l = (s = t[f]).length, h = 0; h < l; h += 2) + (u = s[h]), + (g = s[h + 1]), + (s[h] = u * e + g * r + o), + (s[h + 1] = u * n + g * a + i); + return (t._dirty = 1), t; + } + function arcToSegment(t, e, n, r, a, o, i, s, l) { + if (t !== s || e !== l) { + (n = H(n)), (r = H(r)); + var h = (a % 360) * V, + u = U(h), + g = F(h), + f = Math.PI, + c = 2 * f, + p = (t - s) / 2, + d = (e - l) / 2, + m = u * p + g * d, + v = -g * p + u * d, + y = m * m, + x = v * v, + w = y / (n * n) + x / (r * r); + 1 < w && ((n = $(w) * n), (r = $(w) * r)); + var P = n * n, + b = r * r, + M = (P * b - P * x - b * y) / (P * x + b * y); + M < 0 && (M = 0); + var R = (o === i ? -1 : 1) * $(M), + L = ((n * v) / r) * R, + T = ((-r * m) / n) * R, + S = u * L - g * T + (t + s) / 2, + C = g * L + u * T + (e + l) / 2, + _ = (m - L) / n, + N = (v - T) / r, + A = (-m - L) / n, + O = (-v - T) / r, + B = _ * _ + N * N, + I = (N < 0 ? -1 : 1) * Math.acos(_ / $(B)), + D = + (_ * O - N * A < 0 ? -1 : 1) * + Math.acos((_ * A + N * O) / $(B * (A * A + O * O))); + isNaN(D) && (D = f), + !i && 0 < D ? (D -= c) : i && D < 0 && (D += c), + (I %= c), + (D %= c); + var E, + X = Math.ceil(H(D) / (c / 4)), + k = [], + z = D / X, + G = ((4 / 3) * F(z / 2)) / (1 + U(z / 2)), + Z = u * n, + q = g * n, + Y = g * -r, + j = u * r; + for (E = 0; E < X; E++) + (m = U((a = I + E * z))), + (v = F(a)), + (_ = U((a += z))), + (N = F(a)), + k.push(m - G * v, v + G * m, _ + G * N, N - G * _, _, N); + for (E = 0; E < k.length; E += 2) + (m = k[E]), + (v = k[E + 1]), + (k[E] = m * Z + v * Y + S), + (k[E + 1] = m * q + v * j + C); + return (k[E - 2] = s), (k[E - 1] = l), k; + } + } + function stringToRawPath(t) { + function Cf(t, e, n, r) { + (u = (n - t) / 3), + (g = (r - e) / 3), + s.push(t + u, e + g, n - u, r - g, n, r); + } + var e, + n, + r, + a, + o, + i, + s, + l, + h, + u, + g, + f, + c, + p, + d, + m = + (t + "") + .replace(L, function (t) { + var e = +t; + return e < 1e-4 && -1e-4 < e ? 0 : e; + }) + .match(M) || [], + v = [], + y = 0, + x = 0, + w = m.length, + P = 0, + b = "ERROR: malformed path: " + t; + if (!t || !isNaN(m[0]) || isNaN(m[1])) return console.log(b), v; + for (e = 0; e < w; e++) + if ( + ((c = o), + isNaN(m[e]) ? (i = (o = m[e].toUpperCase()) !== m[e]) : e--, + (r = +m[e + 1]), + (a = +m[e + 2]), + i && ((r += y), (a += x)), + e || ((l = r), (h = a)), + "M" === o) + ) + s && (s.length < 8 ? --v.length : (P += s.length)), + (y = l = r), + (x = h = a), + (s = [r, a]), + v.push(s), + (e += 2), + (o = "L"); + else if ("C" === o) + i || (y = x = 0), + (s = s || [0, 0]).push( + r, + a, + y + 1 * m[e + 3], + x + 1 * m[e + 4], + (y += 1 * m[e + 5]), + (x += 1 * m[e + 6]) + ), + (e += 6); + else if ("S" === o) + (u = y), + (g = x), + ("C" !== c && "S" !== c) || + ((u += y - s[s.length - 4]), (g += x - s[s.length - 3])), + i || (y = x = 0), + s.push(u, g, r, a, (y += 1 * m[e + 3]), (x += 1 * m[e + 4])), + (e += 4); + else if ("Q" === o) + (u = y + (2 / 3) * (r - y)), + (g = x + (2 / 3) * (a - x)), + i || (y = x = 0), + (y += 1 * m[e + 3]), + (x += 1 * m[e + 4]), + s.push(u, g, y + (2 / 3) * (r - y), x + (2 / 3) * (a - x), y, x), + (e += 4); + else if ("T" === o) + (u = y - s[s.length - 4]), + (g = x - s[s.length - 3]), + s.push( + y + u, + x + g, + r + (2 / 3) * (y + 1.5 * u - r), + a + (2 / 3) * (x + 1.5 * g - a), + (y = r), + (x = a) + ), + (e += 2); + else if ("H" === o) Cf(y, x, (y = r), x), (e += 1); + else if ("V" === o) Cf(y, x, y, (x = r + (i ? x - y : 0))), (e += 1); + else if ("L" === o || "Z" === o) + "Z" === o && ((r = l), (a = h), (s.closed = !0)), + ("L" === o || 0.5 < H(y - r) || 0.5 < H(x - a)) && + (Cf(y, x, r, a), "L" === o && (e += 2)), + (y = r), + (x = a); + else if ("A" === o) { + if ( + ((p = m[e + 4]), + (d = m[e + 5]), + (u = m[e + 6]), + (g = m[e + 7]), + (n = 7), + 1 < p.length && + (p.length < 3 + ? ((g = u), (u = d), n--) + : ((g = d), (u = p.substr(2)), (n -= 2)), + (d = p.charAt(1)), + (p = p.charAt(0))), + (f = arcToSegment( + y, + x, + +m[e + 1], + +m[e + 2], + +m[e + 3], + +p, + +d, + (i ? y : 0) + 1 * u, + (i ? x : 0) + 1 * g + )), + (e += n), + f) + ) + for (n = 0; n < f.length; n++) s.push(f[n]); + (y = s[s.length - 2]), (x = s[s.length - 1]); + } else console.log(b); + return ( + (e = s.length) < 6 + ? (v.pop(), (e = 0)) + : s[0] === s[e - 2] && s[1] === s[e - 1] && (s.closed = !0), + (v.totalPoints = P + e), + v + ); + } + function flatPointsToSegment(t, e) { + void 0 === e && (e = 1); + for (var n = t[0], r = 0, a = [n, r], o = 2; o < t.length; o += 2) + a.push(n, r, t[o], (r = ((t[o] - n) * e) / 2), (n = t[o]), -r); + return a; + } + function pointsToSegment(t, e) { + H(t[0] - t[2]) < 1e-4 && H(t[1] - t[3]) < 1e-4 && (t = t.slice(2)); + var n, + r, + a, + o, + i, + s, + l, + h, + u, + g, + f, + c, + p, + d, + m = t.length - 2, + v = +t[0], + y = +t[1], + x = +t[2], + w = +t[3], + P = [v, y, v, y], + b = x - v, + M = w - y, + R = Math.abs(t[m] - v) < 0.001 && Math.abs(t[m + 1] - y) < 0.001; + for ( + R && + (t.push(x, w), + (x = v), + (w = y), + (v = t[m - 2]), + (y = t[m - 1]), + t.unshift(v, y), + (m += 4)), + e = e || 0 === e ? +e : 1, + a = 2; + a < m; + a += 2 + ) + (n = v), + (r = y), + (v = x), + (y = w), + (x = +t[a + 2]), + (w = +t[a + 3]), + (v === x && y === w) || + ((o = b), + (i = M), + (b = x - v), + (M = w - y), + (h = + (((s = $(o * o + i * i)) + (l = $(b * b + M * M))) * e * 0.25) / + $(Math.pow(b / l + o / s, 2) + Math.pow(M / l + i / s, 2))), + (f = + v - + ((u = v - (v - n) * (s ? h / s : 0)) + + ((((g = v + (x - v) * (l ? h / l : 0)) - u) * + ((3 * s) / (s + l) + 0.5)) / + 4 || 0))), + (d = + y - + ((c = y - (y - r) * (s ? h / s : 0)) + + ((((p = y + (w - y) * (l ? h / l : 0)) - c) * + ((3 * s) / (s + l) + 0.5)) / + 4 || 0))), + (v === n && y === r) || + P.push(N(u + f), N(c + d), N(v), N(y), N(g + f), N(p + d))); + return ( + v !== x || y !== w || P.length < 4 + ? P.push(N(x), N(w), N(x), N(w)) + : (P.length -= 2), + 2 === P.length + ? P.push(v, y, v, y, v, y) + : R && (P.splice(0, 6), (P.length = P.length - 6)), + P + ); + } + function rawPathToString(t) { + h(t[0]) && (t = [t]); + var e, + n, + r, + a, + o = "", + i = t.length; + for (n = 0; n < i; n++) { + for ( + a = t[n], + o += "M" + N(a[0]) + "," + N(a[1]) + " C", + e = a.length, + r = 2; + r < e; + r++ + ) + o += + N(a[r++]) + + "," + + N(a[r++]) + + " " + + N(a[r++]) + + "," + + N(a[r++]) + + " " + + N(a[r++]) + + "," + + N(a[r]) + + " "; + a.closed && (o += "z"); + } + return o; + } + function R(t) { + var e = t.ownerDocument || t; + !(k in t.style) && + "msTransform" in t.style && + (z = (k = "msTransform") + "Origin"); + for (; e.parentNode && (e = e.parentNode); ); + if (((v = window), (I = new Y()), e)) { + (w = (c = e).documentElement), + (P = e.body), + ((D = c.createElementNS( + "http://www.w3.org/2000/svg", + "g" + )).style.transform = "none"); + var n = e.createElement("div"), + r = e.createElement("div"); + P.appendChild(n), + n.appendChild(r), + (n.style.position = "static"), + (n.style[k] = "translate3d(0,0,1px)"), + (E = r.offsetParent !== n), + P.removeChild(n); + } + return e; + } + function X(t) { + return ( + t.ownerSVGElement || ("svg" === (t.tagName + "").toLowerCase() ? t : null) + ); + } + function Z(t, e) { + if (t.parentNode && (c || R(t))) { + var n = X(t), + r = n + ? n.getAttribute("xmlns") || "http://www.w3.org/2000/svg" + : "http://www.w3.org/1999/xhtml", + a = n ? (e ? "rect" : "g") : "div", + o = 2 !== e ? 0 : 100, + i = 3 === e ? 100 : 0, + s = + "position:absolute;display:block;pointer-events:none;margin:0;padding:0;", + l = c.createElementNS + ? c.createElementNS(r.replace(/^https/, "http"), a) + : c.createElement(a); + return ( + e && + (n + ? ((b = b || Z(t)), + l.setAttribute("width", 0.01), + l.setAttribute("height", 0.01), + l.setAttribute("transform", "translate(" + o + "," + i + ")"), + b.appendChild(l)) + : (f || ((f = Z(t)).style.cssText = s), + (l.style.cssText = + s + + "width:0.1px;height:0.1px;top:" + + i + + "px;left:" + + o + + "px"), + f.appendChild(l))), + l + ); + } + throw "Need document and parent."; + } + function aa(t, e) { + var n, + r, + a, + o, + i, + s, + l = X(t), + h = t === l, + u = l ? G : q, + g = t.parentNode; + if (t === v) return t; + if ((u.length || u.push(Z(t, 1), Z(t, 2), Z(t, 3)), (n = l ? b : f), l)) + h + ? ((o = + -(a = (function _getCTM(t) { + var e, + n = t.getCTM(); + return ( + n || + ((e = t.style[k]), + (t.style[k] = "none"), + t.appendChild(D), + (n = D.getCTM()), + t.removeChild(D), + e + ? (t.style[k] = e) + : t.style.removeProperty( + k.replace(/([A-Z])/g, "-$1").toLowerCase() + )), + n || I.clone() + ); + })(t)).e / a.a), + (i = -a.f / a.d), + (r = I)) + : t.getBBox + ? ((a = t.getBBox()), + (o = + (r = (r = t.transform ? t.transform.baseVal : {}).numberOfItems + ? 1 < r.numberOfItems + ? (function _consolidate(t) { + for (var e = new Y(), n = 0; n < t.numberOfItems; n++) + e.multiply(t.getItem(n).matrix); + return e; + })(r) + : r.getItem(0).matrix + : I).a * + a.x + + r.c * a.y), + (i = r.b * a.x + r.d * a.y)) + : ((r = new Y()), (o = i = 0)), + e && "g" === t.tagName.toLowerCase() && (o = i = 0), + (h ? l : g).appendChild(n), + n.setAttribute( + "transform", + "matrix(" + + r.a + + "," + + r.b + + "," + + r.c + + "," + + r.d + + "," + + (r.e + o) + + "," + + (r.f + i) + + ")" + ); + else { + if (((o = i = 0), E)) + for ( + r = t.offsetParent, a = t; + (a = a && a.parentNode) && a !== r && a.parentNode; + + ) + 4 < (v.getComputedStyle(a)[k] + "").length && + ((o = a.offsetLeft), (i = a.offsetTop), (a = 0)); + if ( + "absolute" !== (s = v.getComputedStyle(t)).position && + "fixed" !== s.position + ) + for (r = t.offsetParent; g && g !== r; ) + (o += g.scrollLeft || 0), (i += g.scrollTop || 0), (g = g.parentNode); + ((a = n.style).top = t.offsetTop - i + "px"), + (a.left = t.offsetLeft - o + "px"), + (a[k] = s[k]), + (a[z] = s[z]), + (a.position = "fixed" === s.position ? "fixed" : "absolute"), + t.parentNode.appendChild(n); + } + return n; + } + function ba(t, e, n, r, a, o, i) { + return (t.a = e), (t.b = n), (t.c = r), (t.d = a), (t.e = o), (t.f = i), t; + } + var c, + v, + w, + P, + f, + b, + I, + D, + E, + n, + k = "transform", + z = k + "Origin", + G = [], + q = [], + Y = + (((n = Matrix2D.prototype).inverse = function inverse() { + var t = this.a, + e = this.b, + n = this.c, + r = this.d, + a = this.e, + o = this.f, + i = t * r - e * n || 1e-10; + return ba( + this, + r / i, + -e / i, + -n / i, + t / i, + (n * o - r * a) / i, + -(t * o - e * a) / i + ); + }), + (n.multiply = function multiply(t) { + var e = this.a, + n = this.b, + r = this.c, + a = this.d, + o = this.e, + i = this.f, + s = t.a, + l = t.c, + h = t.b, + u = t.d, + g = t.e, + f = t.f; + return ba( + this, + s * e + h * r, + s * n + h * a, + l * e + u * r, + l * n + u * a, + o + g * e + f * r, + i + g * n + f * a + ); + }), + (n.clone = function clone() { + return new Matrix2D(this.a, this.b, this.c, this.d, this.e, this.f); + }), + (n.equals = function equals(t) { + var e = this.a, + n = this.b, + r = this.c, + a = this.d, + o = this.e, + i = this.f; + return ( + e === t.a && + n === t.b && + r === t.c && + a === t.d && + o === t.e && + i === t.f + ); + }), + (n.apply = function apply(t, e) { + void 0 === e && (e = {}); + var n = t.x, + r = t.y, + a = this.a, + o = this.b, + i = this.c, + s = this.d, + l = this.e, + h = this.f; + return ( + (e.x = n * a + r * i + l || 0), (e.y = n * o + r * s + h || 0), e + ); + }), + Matrix2D); + function Matrix2D(t, e, n, r, a, o) { + void 0 === t && (t = 1), + void 0 === e && (e = 0), + void 0 === n && (n = 0), + void 0 === r && (r = 1), + void 0 === a && (a = 0), + void 0 === o && (o = 0), + ba(this, t, e, n, r, a, o); + } + function getGlobalMatrix(t, e, n, r) { + if (!t || !t.parentNode || (c || R(t)).documentElement === t) + return new Y(); + var a = (function _forceNonZeroScale(t) { + for (var e, n; t && t !== P; ) + (n = t._gsap) && n.uncache && n.get(t, "x"), + n && + !n.scaleX && + !n.scaleY && + n.renderTransform && + ((n.scaleX = n.scaleY = 1e-4), + n.renderTransform(1, n), + e ? e.push(n) : (e = [n])), + (t = t.parentNode); + return e; + })(t), + o = X(t) ? G : q, + i = aa(t, n), + s = o[0].getBoundingClientRect(), + l = o[1].getBoundingClientRect(), + h = o[2].getBoundingClientRect(), + u = i.parentNode, + g = + !r && + (function _isFixed(t) { + return ( + "fixed" === v.getComputedStyle(t).position || + ((t = t.parentNode) && 1 === t.nodeType ? _isFixed(t) : void 0) + ); + })(t), + f = new Y( + (l.left - s.left) / 100, + (l.top - s.top) / 100, + (h.left - s.left) / 100, + (h.top - s.top) / 100, + s.left + + (g + ? 0 + : (function _getDocScrollLeft() { + return ( + v.pageXOffset || + c.scrollLeft || + w.scrollLeft || + P.scrollLeft || + 0 + ); + })()), + s.top + + (g + ? 0 + : (function _getDocScrollTop() { + return ( + v.pageYOffset || + c.scrollTop || + w.scrollTop || + P.scrollTop || + 0 + ); + })()) + ); + if ((u.removeChild(i), a)) + for (s = a.length; s--; ) + ((l = a[s]).scaleX = l.scaleY = 0), l.renderTransform(1, l); + return e ? f.inverse() : f; + } + function na(t, e, n, r) { + for (var a = e.length, o = 2 === r ? 0 : r, i = 0; i < a; i++) + (t[o] = parseFloat(e[i][n])), 2 === r && (t[o + 1] = 0), (o += 2); + return t; + } + function oa(t, e, n) { + return parseFloat(t._gsap.get(t, e, n || "px")) || 0; + } + function pa(t) { + var e, + n = t[0], + r = t[1]; + for (e = 2; e < t.length; e += 2) (n = t[e] += n), (r = t[e + 1] += r); + } + function qa(t, e, n, r, a, o, i, s, l) { + return ( + (e = + "cubic" === i.type + ? [e] + : (!1 !== i.fromCurrent && + e.unshift(oa(n, r, s), a ? oa(n, a, l) : 0), + i.relative && pa(e), + [(a ? pointsToSegment : flatPointsToSegment)(e, i.curviness)])), + (e = o(nt(e, n, i))), + rt(t, n, r, e, "x", s), + a && rt(t, n, a, e, "y", l), + cacheRawPathMeasurements(e, i.resolution || (0 === i.curviness ? 20 : 12)) + ); + } + function ra(t) { + return t; + } + function ta(t, e, n) { + var r, + a = getGlobalMatrix(t), + o = 0, + i = 0; + return ( + "svg" === (t.tagName + "").toLowerCase() + ? (r = t.viewBox.baseVal).width || + (r = { + width: +t.getAttribute("width"), + height: +t.getAttribute("height"), + }) + : (r = e && t.getBBox && t.getBBox()), + e && + "auto" !== e && + ((o = e.push ? e[0] * (r ? r.width : t.offsetWidth || 0) : e.x), + (i = e.push ? e[1] * (r ? r.height : t.offsetHeight || 0) : e.y)), + n.apply(o || i ? a.apply({ x: o, y: i }) : { x: a.e, y: a.f }) + ); + } + function ua(t, e, n, r) { + var a, + o = getGlobalMatrix(t.parentNode, !0, !0), + i = o.clone().multiply(getGlobalMatrix(e)), + s = ta(t, n, o), + l = ta(e, r, o), + h = l.x, + u = l.y; + return ( + (i.e = i.f = 0), + "auto" === r && + e.getTotalLength && + "path" === e.tagName.toLowerCase() && + ((a = e.getAttribute("d").match(et) || []), + (h += (a = i.apply({ x: +a[0], y: +a[1] })).x), + (u += a.y)), + a && ((h -= (a = i.apply(e.getBBox())).x), (u -= a.y)), + (i.e = h - s.x), + (i.f = u - s.y), + i + ); + } + var j, + g, + Q, + W, + J, + o, + K = "x,translateX,left,marginLeft,xPercent".split(","), + tt = "y,translateY,top,marginTop,yPercent".split(","), + i = Math.PI / 180, + et = /[-+\.]*\d+\.?(?:e-|e\+)?\d*/g, + nt = function _align(t, e, n) { + var r, + a, + o, + i = n.align, + s = n.matrix, + l = n.offsetX, + h = n.offsetY, + u = n.alignOrigin, + g = t[0][0], + f = t[0][1], + c = oa(e, "x"), + p = oa(e, "y"); + return t && t.length + ? (i && + ("self" === i || (r = W(i)[0] || e) === e + ? transformRawPath(t, 1, 0, 0, 1, c - g, p - f) + : (u && !1 !== u[2] + ? j.set(e, { + transformOrigin: 100 * u[0] + "% " + 100 * u[1] + "%", + }) + : (u = [oa(e, "xPercent") / -100, oa(e, "yPercent") / -100]), + (o = (a = ua(e, r, u, "auto")).apply({ x: g, y: f })), + transformRawPath( + t, + a.a, + a.b, + a.c, + a.d, + c + a.e - (o.x - a.e), + p + a.f - (o.y - a.f) + ))), + s + ? transformRawPath(t, s.a, s.b, s.c, s.d, s.e, s.f) + : (l || h) && transformRawPath(t, 1, 0, 0, 1, l || 0, h || 0), + t) + : getRawPath("M0,0L0,0"); + }, + rt = function _addDimensionalPropTween(t, e, n, r, a, o) { + var i = e._gsap, + s = i.harness, + l = s && s.aliases && s.aliases[n], + h = l && l.indexOf(",") < 0 ? l : n, + u = (t._pt = new g(t._pt, e, h, 0, 0, ra, 0, i.set(e, h, t))); + (u.u = Q(i.get(e, h, o)) || 0), + (u.path = r), + (u.pp = a), + t._props.push(h); + }, + a = { + version: "3.12.2", + name: "motionPath", + register: function register(t, e, n) { + (Q = (j = t).utils.getUnit), + (W = j.utils.toArray), + (J = j.core.getStyleSaver), + (o = j.core.reverting || function () {}), + (g = n); + }, + init: function init(t, e, n) { + if (!j) + return ( + console.warn("Please gsap.registerPlugin(MotionPathPlugin)"), !1 + ); + ("object" == typeof e && !e.style && e.path) || (e = { path: e }); + var r, + a, + o = [], + i = e.path, + s = e.autoRotate, + l = e.unitX, + h = e.unitY, + u = e.x, + g = e.y, + f = i[0], + c = (function _sliceModifier(e, n) { + return function (t) { + return e || 1 !== n ? sliceRawPath(t, e, n) : t; + }; + })(e.start, "end" in e ? e.end : 1); + if ( + ((this.rawPaths = o), + (this.target = t), + (this.tween = n), + (this.styles = J && J(t, "transform")), + (this.rotate = s || 0 === s) && + ((this.rOffset = parseFloat(s) || 0), + (this.radians = !!e.useRadians), + (this.rProp = e.rotation || "rotation"), + (this.rSet = t._gsap.set(t, this.rProp, this)), + (this.ru = Q(t._gsap.get(t, this.rProp)) || 0)), + !Array.isArray(i) || "closed" in i || "number" == typeof f) + ) + cacheRawPathMeasurements( + (r = c(nt(getRawPath(e.path), t, e))), + e.resolution + ), + o.push(r), + rt(this, t, e.x || "x", r, "x", e.unitX || "px"), + rt(this, t, e.y || "y", r, "y", e.unitY || "px"); + else { + for (a in f) + !u && ~K.indexOf(a) ? (u = a) : !g && ~tt.indexOf(a) && (g = a); + for (a in (u && g + ? o.push( + qa( + this, + na(na([], i, u, 0), i, g, 1), + t, + u, + g, + c, + e, + l || Q(i[0][u]), + h || Q(i[0][g]) + ) + ) + : (u = g = 0), + f)) + a !== u && + a !== g && + o.push(qa(this, na([], i, a, 2), t, a, 0, c, e, Q(i[0][a]))); + } + }, + render: function render(t, e) { + var n = e.rawPaths, + r = n.length, + a = e._pt; + if (e.tween._time || !o()) { + for (1 < t ? (t = 1) : t < 0 && (t = 0); r--; ) + getPositionOnPath(n[r], t, !r && e.rotate, n[r]); + for (; a; ) + a.set(a.t, a.p, a.path[a.pp] + a.u, a.d, t), (a = a._next); + e.rotate && + e.rSet( + e.target, + e.rProp, + n[0].angle * (e.radians ? i : 1) + e.rOffset + e.ru, + e, + t + ); + } else e.styles.revert(); + }, + getLength: function getLength(t) { + return cacheRawPathMeasurements(getRawPath(t)).totalLength; + }, + sliceRawPath: sliceRawPath, + getRawPath: getRawPath, + pointsToSegment: pointsToSegment, + stringToRawPath: stringToRawPath, + rawPathToString: rawPathToString, + transformRawPath: transformRawPath, + getGlobalMatrix: getGlobalMatrix, + getPositionOnPath: getPositionOnPath, + cacheRawPathMeasurements: cacheRawPathMeasurements, + convertToPath: function convertToPath$1(t, e) { + return W(t).map(function (t) { + return convertToPath(t, !1 !== e); + }); + }, + convertCoordinates: function convertCoordinates(t, e, n) { + var r = getGlobalMatrix(e, !0, !0).multiply(getGlobalMatrix(t)); + return n ? r.apply(n) : r; + }, + getAlignMatrix: ua, + getRelativePosition: function getRelativePosition(t, e, n, r) { + var a = ua(t, e, n, r); + return { x: a.e, y: a.f }; + }, + arrayToRawPath: function arrayToRawPath(t, e) { + var n = na(na([], t, (e = e || {}).x || "x", 0), t, e.y || "y", 1); + return ( + e.relative && pa(n), + ["cubic" === e.type ? n : pointsToSegment(n, e.curviness)] + ); + }, + }; + !(function _getGSAP() { + return ( + j || + ("undefined" != typeof window && + (j = window.gsap) && + j.registerPlugin && + j) + ); + })() || j.registerPlugin(a), + (t.MotionPathPlugin = a), + (t.default = a); + if (typeof window === "undefined" || window !== t) { + Object.defineProperty(t, "__esModule", { value: !0 }); + } else { + delete t.default; + } +}); diff --git a/src/js/lib/aos.min.js b/src/js/lib/aos.min.js new file mode 100644 index 0000000..bda2b69 --- /dev/null +++ b/src/js/lib/aos.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AOS=t():e.AOS=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return e[o].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="dist/",t(0)}([function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var i=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]&&arguments[0];if(e&&(k=!0),k)return w=(0,y.default)(w,j),(0,b.default)(w,j.once),w},_=function(){w=(0,h.default)(),O()},S=function(){w.forEach(function(e,t){e.node.removeAttribute("data-aos"),e.node.removeAttribute("data-aos-easing"),e.node.removeAttribute("data-aos-duration"),e.node.removeAttribute("data-aos-delay")})},z=function(e){return e===!0||"mobile"===e&&p.default.mobile()||"phone"===e&&p.default.phone()||"tablet"===e&&p.default.tablet()||"function"==typeof e&&e()===!0},A=function(e){return j=i(j,e),w=(0,h.default)(),z(j.disable)||x?S():(document.querySelector("body").setAttribute("data-aos-easing",j.easing),document.querySelector("body").setAttribute("data-aos-duration",j.duration),document.querySelector("body").setAttribute("data-aos-delay",j.delay),"DOMContentLoaded"===j.startEvent&&["complete","interactive"].indexOf(document.readyState)>-1?O(!0):"load"===j.startEvent?window.addEventListener(j.startEvent,function(){O(!0)}):document.addEventListener(j.startEvent,function(){O(!0)}),window.addEventListener("resize",(0,f.default)(O,j.debounceDelay,!0)),window.addEventListener("orientationchange",(0,f.default)(O,j.debounceDelay,!0)),window.addEventListener("scroll",(0,u.default)(function(){(0,b.default)(w,j.once)},j.throttleDelay)),j.disableMutationObserver||(0,d.default)("[data-aos]",_),w)};e.exports={init:A,refresh:O,refreshHard:_}},function(e,t){},,,,,function(e,t){(function(t){"use strict";function n(e,t,n){function o(t){var n=b,o=v;return b=v=void 0,k=t,g=e.apply(o,n)}function r(e){return k=e,h=setTimeout(s,t),_?o(e):g}function a(e){var n=e-w,o=e-k,i=t-n;return S?j(i,y-o):i}function c(e){var n=e-w,o=e-k;return void 0===w||n>=t||n<0||S&&o>=y}function s(){var e=O();return c(e)?d(e):void(h=setTimeout(s,a(e)))}function d(e){return h=void 0,z&&b?o(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),k=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(O())}function m(){var e=O(),n=c(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(s,t),o(w)}return void 0===h&&(h=setTimeout(s,t)),g}var b,v,y,g,h,w,k=0,_=!1,S=!1,z=!0;if("function"!=typeof e)throw new TypeError(f);return t=u(t)||0,i(n)&&(_=!!n.leading,S="maxWait"in n,y=S?x(u(n.maxWait)||0,t):y,z="trailing"in n?!!n.trailing:z),m.cancel=l,m.flush=p,m}function o(e,t,o){var r=!0,a=!0;if("function"!=typeof e)throw new TypeError(f);return i(o)&&(r="leading"in o?!!o.leading:r,a="trailing"in o?!!o.trailing:a),n(e,t,{leading:r,maxWait:t,trailing:a})}function i(e){var t="undefined"==typeof e?"undefined":c(e);return!!e&&("object"==t||"function"==t)}function r(e){return!!e&&"object"==("undefined"==typeof e?"undefined":c(e))}function a(e){return"symbol"==("undefined"==typeof e?"undefined":c(e))||r(e)&&k.call(e)==d}function u(e){if("number"==typeof e)return e;if(a(e))return s;if(i(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=i(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(l,"");var n=m.test(e);return n||b.test(e)?v(e.slice(2),n?2:8):p.test(e)?s:+e}var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f="Expected a function",s=NaN,d="[object Symbol]",l=/^\s+|\s+$/g,p=/^[-+]0x[0-9a-f]+$/i,m=/^0b[01]+$/i,b=/^0o[0-7]+$/i,v=parseInt,y="object"==("undefined"==typeof t?"undefined":c(t))&&t&&t.Object===Object&&t,g="object"==("undefined"==typeof self?"undefined":c(self))&&self&&self.Object===Object&&self,h=y||g||Function("return this")(),w=Object.prototype,k=w.toString,x=Math.max,j=Math.min,O=function(){return h.Date.now()};e.exports=o}).call(t,function(){return this}())},function(e,t){(function(t){"use strict";function n(e,t,n){function i(t){var n=b,o=v;return b=v=void 0,O=t,g=e.apply(o,n)}function r(e){return O=e,h=setTimeout(s,t),_?i(e):g}function u(e){var n=e-w,o=e-O,i=t-n;return S?x(i,y-o):i}function f(e){var n=e-w,o=e-O;return void 0===w||n>=t||n<0||S&&o>=y}function s(){var e=j();return f(e)?d(e):void(h=setTimeout(s,u(e)))}function d(e){return h=void 0,z&&b?i(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),O=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(j())}function m(){var e=j(),n=f(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(s,t),i(w)}return void 0===h&&(h=setTimeout(s,t)),g}var b,v,y,g,h,w,O=0,_=!1,S=!1,z=!0;if("function"!=typeof e)throw new TypeError(c);return t=a(t)||0,o(n)&&(_=!!n.leading,S="maxWait"in n,y=S?k(a(n.maxWait)||0,t):y,z="trailing"in n?!!n.trailing:z),m.cancel=l,m.flush=p,m}function o(e){var t="undefined"==typeof e?"undefined":u(e);return!!e&&("object"==t||"function"==t)}function i(e){return!!e&&"object"==("undefined"==typeof e?"undefined":u(e))}function r(e){return"symbol"==("undefined"==typeof e?"undefined":u(e))||i(e)&&w.call(e)==s}function a(e){if("number"==typeof e)return e;if(r(e))return f;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(d,"");var n=p.test(e);return n||m.test(e)?b(e.slice(2),n?2:8):l.test(e)?f:+e}var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c="Expected a function",f=NaN,s="[object Symbol]",d=/^\s+|\s+$/g,l=/^[-+]0x[0-9a-f]+$/i,p=/^0b[01]+$/i,m=/^0o[0-7]+$/i,b=parseInt,v="object"==("undefined"==typeof t?"undefined":u(t))&&t&&t.Object===Object&&t,y="object"==("undefined"==typeof self?"undefined":u(self))&&self&&self.Object===Object&&self,g=v||y||Function("return this")(),h=Object.prototype,w=h.toString,k=Math.max,x=Math.min,j=function(){return g.Date.now()};e.exports=n}).call(t,function(){return this}())},function(e,t){"use strict";function n(e,t){var n=new r(o);a=t,n.observe(i.documentElement,{childList:!0,subtree:!0,removedNodes:!0})}function o(e){e&&e.forEach(function(e){var t=Array.prototype.slice.call(e.addedNodes),n=Array.prototype.slice.call(e.removedNodes),o=t.concat(n).filter(function(e){return e.hasAttribute&&e.hasAttribute("data-aos")}).length;o&&a()})}Object.defineProperty(t,"__esModule",{value:!0});var i=window.document,r=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,a=function(){};t.default=n},function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(){return navigator.userAgent||navigator.vendor||window.opera||""}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;ne.position?e.node.classList.add("aos-animate"):"undefined"!=typeof o&&("false"===o||!n&&"true"!==o)&&e.node.classList.remove("aos-animate")},o=function(e,t){var o=window.pageYOffset,i=window.innerHeight;e.forEach(function(e,r){n(e,i+o,t)})};t.default=o},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(12),r=o(i),a=function(e,t){return e.forEach(function(e,n){e.node.classList.add("aos-init"),e.position=(0,r.default)(e.node,t.offset)}),e};t.default=a},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(13),r=o(i),a=function(e,t){var n=0,o=0,i=window.innerHeight,a={offset:e.getAttribute("data-aos-offset"),anchor:e.getAttribute("data-aos-anchor"),anchorPlacement:e.getAttribute("data-aos-anchor-placement")};switch(a.offset&&!isNaN(a.offset)&&(o=parseInt(a.offset)),a.anchor&&document.querySelectorAll(a.anchor)&&(e=document.querySelectorAll(a.anchor)[0]),n=(0,r.default)(e).top,a.anchorPlacement){case"top-bottom":break;case"center-bottom":n+=e.offsetHeight/2;break;case"bottom-bottom":n+=e.offsetHeight;break;case"top-center":n+=i/2;break;case"bottom-center":n+=i/2+e.offsetHeight;break;case"center-center":n+=i/2+e.offsetHeight/2;break;case"top-top":n+=i;break;case"bottom-top":n+=e.offsetHeight+i;break;case"center-top":n+=e.offsetHeight/2+i}return a.anchorPlacement||a.offset||isNaN(t)||(o=t),n+o};t.default=a},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){for(var t=0,n=0;e&&!isNaN(e.offsetLeft)&&!isNaN(e.offsetTop);)t+=e.offsetLeft-("BODY"!=e.tagName?e.scrollLeft:0),n+=e.offsetTop-("BODY"!=e.tagName?e.scrollTop:0),e=e.offsetParent;return{top:n,left:t}};t.default=n},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){return e=e||document.querySelectorAll("[data-aos]"),Array.prototype.map.call(e,function(e){return{node:e}})};t.default=n}])}); \ No newline at end of file diff --git a/src/js/lib/gsap.min.js b/src/js/lib/gsap.min.js new file mode 100644 index 0000000..0acc08b --- /dev/null +++ b/src/js/lib/gsap.min.js @@ -0,0 +1,10 @@ +/*! + * GSAP 3.12.5 + * https://gsap.com + * + * @license Copyright 2024, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license or for Club GSAP members, the agreement issued with that membership. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function P(t){return(i=yt(t,ot))&&ze}function Q(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function R(t,e){return!e&&console.warn(t)}function S(t,e){return t&&(ot[t]=e)&&i&&(i[t]=e)||ot}function T(){return 0}function ea(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=gt.length;r--&&!gt[r].targetTest(i););e=gt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new Vt(t[r],e)))||t.splice(r,1);return t}function fa(t){return t._gsap||ea(Mt(t))[0]._gsap}function ga(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ha(t,e){return(t=t.split(",")).forEach(e)||t}function ia(t){return Math.round(1e5*t)/1e5||0}function ja(t){return Math.round(1e7*t)/1e7||0}function ka(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function la(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++ia;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function ya(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function za(t,e){t.parent&&(!e||t.parent.autoRemoveChildren)&&t.parent.remove&&t.parent.remove(t),t._act=0}function Aa(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Ca(t,e,r,i){return t._startAt&&(L?t._startAt.revert(ht):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ea(t){return t._repeat?Tt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ga(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ha(t){return t._end=ja(t._start+(t._tDur/Math.abs(t._ts||t._rts||X)||0))}function Ia(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=ja(r._time-(0X)&&e.render(r,!0)),Aa(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur(n=Math.abs(n))&&(a=i,o=n);return a}function tb(t){return za(t),t.scrollTrigger&&t.scrollTrigger.kill(!!L),t.progress()<1&&Ct(t,"onInterrupt"),t}function wb(t){if(t)if(t=!t.name&&t.default||t,x()||t.headless){var e=t.name,r=s(t),i=e&&!r&&t.init?function(){this._props=[]}:t,n={init:T,render:he,add:Wt,kill:ce,modifier:fe,rawVars:0},a={targetTest:0,get:0,getSetter:ne,aliases:{},register:0};if(Ft(),t!==i){if(pt[e])return;qa(i,qa(ua(t,n),a)),yt(i.prototype,yt(n,ua(t,a))),pt[i.prop=e]=i,t.targetTest&&(gt.push(i),ft[e]=1),e=("css"===e?"CSS":e.charAt(0).toUpperCase()+e.substr(1))+"Plugin"}S(e,i),t.register&&t.register(ze,i,_e)}else At.push(t)}function zb(t,e,r){return(6*(t+=t<0?1:1>16,e>>8&St,e&St]:0:zt.black;if(!p){if(","===e.substr(-1)&&(e=e.substr(0,e.length-1)),zt[e])p=zt[e];else if("#"===e.charAt(0)){if(e.length<6&&(e="#"+(n=e.charAt(1))+n+(a=e.charAt(2))+a+(s=e.charAt(3))+s+(5===e.length?e.charAt(4)+e.charAt(4):"")),9===e.length)return[(p=parseInt(e.substr(1,6),16))>>16,p>>8&St,p&St,parseInt(e.substr(7),16)/255];p=[(e=parseInt(e.substr(1),16))>>16,e>>8&St,e&St]}else if("hsl"===e.substr(0,3))if(p=c=e.match(tt),r){if(~e.indexOf("="))return p=e.match(et),i&&p.length<4&&(p[3]=1),p}else o=+p[0]%360/360,u=p[1]/100,n=2*(h=p[2]/100)-(a=h<=.5?h*(u+1):h+u-h*u),3=U?u.endTime(!1):t._dur;return r(e)&&(isNaN(e)||e in o)?(a=e.charAt(0),s="%"===e.substr(-1),n=e.indexOf("="),"<"===a||">"===a?(0<=n&&(e=e.replace(/=/,"")),("<"===a?u._start:u.endTime(0<=u._repeat))+(parseFloat(e.substr(1))||0)*(s?(n<0?u:i).totalDuration()/100:1)):n<0?(e in o||(o[e]=h),o[e]):(a=parseFloat(e.charAt(n-1)+e.substr(n+1)),s&&i&&(a=a/100*(Z(i)?i[0]:i).totalDuration()),1=r&&te)return i;i=i._next}else for(i=t._last;i&&i._start>=r;){if("isPause"===i.data&&i._start=n._start)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0=this.totalDuration()||!v&&_)&&(f!==this._start&&Math.abs(l)===Math.abs(this._ts)||this._lock||(!t&&g||!(v===m&&0=i&&(a instanceof $t?e&&n.push(a):(r&&n.push(a),t&&n.push.apply(n,a.getChildren(!0,e,r)))),a=a._next;return n},e.getById=function getById(t){for(var e=this.getChildren(1,1,1),r=e.length;r--;)if(e[r].vars.id===t)return e[r]},e.remove=function remove(t){return r(t)?this.removeLabel(t):s(t)?this.killTweensOf(t):(ya(this,t),t===this._recent&&(this._recent=this._last),Aa(this))},e.totalTime=function totalTime(t,e){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=ja(Rt.time-(0r:!r||s.isActive())&&n.push(s):(i=s.getTweensOf(a,r)).length&&n.push.apply(n,i),s=s._next;return n},e.tweenTo=function tweenTo(t,e){e=e||{};var r,i=this,n=xt(i,t),a=e.startAt,s=e.onStart,o=e.onStartParams,u=e.immediateRender,h=$t.to(i,qa({ease:e.ease||"none",lazy:!1,immediateRender:!1,time:n,overwrite:"auto",duration:e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale())||X,onStart:function onStart(){if(i.pause(),!r){var t=e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale());h._dur!==t&&Ra(h,t,0,1).render(h._time,!0,!0),r=1}s&&s.apply(h,o||[])}},e));return u?h.render(0):h},e.tweenFromTo=function tweenFromTo(t,e,r){return this.tweenTo(e,qa({startAt:{time:xt(this,t)}},r))},e.recent=function recent(){return this._recent},e.nextLabel=function nextLabel(t){return void 0===t&&(t=this._time),rb(this,xt(this,t))},e.previousLabel=function previousLabel(t){return void 0===t&&(t=this._time),rb(this,xt(this,t),1)},e.currentLabel=function currentLabel(t){return arguments.length?this.seek(t,!0):this.previousLabel(this._time+X)},e.shiftChildren=function shiftChildren(t,e,r){void 0===r&&(r=0);for(var i,n=this._first,a=this.labels;n;)n._start>=r&&(n._start+=t,n._end+=t),n=n._next;if(e)for(i in a)a[i]>=r&&(a[i]+=t);return Aa(this)},e.invalidate=function invalidate(t){var e=this._first;for(this._lock=0;e;)e.invalidate(t),e=e._next;return i.prototype.invalidate.call(this,t)},e.clear=function clear(t){void 0===t&&(t=!0);for(var e,r=this._first;r;)e=r._next,this.remove(r),r=e;return this._dp&&(this._time=this._tTime=this._pTime=0),t&&(this.labels={}),Aa(this)},e.totalDuration=function totalDuration(t){var e,r,i,n=0,a=this,s=a._last,o=U;if(arguments.length)return a.timeScale((a._repeat<0?a.duration():a.totalDuration())/(a.reversed()?-t:t));if(a._dirty){for(i=a.parent;s;)e=s._prev,s._dirty&&s.totalDuration(),o<(r=s._start)&&a._sort&&s._ts&&!a._lock?(a._lock=1,Ka(a,s,r-s._delay,1)._lock=0):o=r,r<0&&s._ts&&(n-=r,(!i&&!a._dp||i&&i.smoothChildTiming)&&(a._start+=r/a._ts,a._time-=r,a._tTime-=r),a.shiftChildren(-r,!1,-Infinity),o=0),s._end>n&&s._ts&&(n=s._end),s=e;Ra(a,a===I&&a._time>n?a._time:n,1,1),a._dirty=0}return a._tDur},Timeline.updateRoot=function updateRoot(t){if(I._ts&&(na(I,Ga(t,I)),f=Rt.frame),Rt.frame>=mt){mt+=q.autoSleep||120;var e=I._first;if((!e||!e._ts)&&q.autoSleep&&Rt._listeners.length<2){for(;e&&!e._ts;)e=e._next;e||Rt.sleep()}}},Timeline}(Ut);qa(Xt.prototype,{_lock:0,_hasPause:0,_forcing:0});function ac(t,e,i,n,a,o){var u,h,l,f;if(pt[t]&&!1!==(u=new pt[t]).init(a,u.rawVars?e[t]:function _processVars(t,e,i,n,a){if(s(t)&&(t=Kt(t,a,e,i,n)),!v(t)||t.style&&t.nodeType||Z(t)||$(t))return r(t)?Kt(t,a,e,i,n):t;var o,u={};for(o in t)u[o]=Kt(t[o],a,e,i,n);return u}(e[t],n,a,o,i),i,n,o)&&(i._pt=h=new _e(i._pt,a,t,0,1,u.render,u,0,u.priority),i!==d))for(l=i._ptLookup[i._targets.indexOf(a)],f=u._props.length;f--;)l[u._props[f]]=h;return u}function gc(t,r,e,i){var n,a,s=r.ease||i||"power1.inOut";if(Z(r))a=e[t]||(e[t]=[]),r.forEach(function(t,e){return a.push({t:e/(r.length-1)*100,v:t,e:s})});else for(n in r)a=e[n]||(e[n]=[]),"ease"===n||a.push({t:parseFloat(t),v:r[n],e:s})}var Nt,Gt,Wt=function _addPropTween(t,e,i,n,a,o,u,h,l,f){s(n)&&(n=n(a||0,t,o));var d,c=t[e],p="get"!==i?i:s(c)?l?t[e.indexOf("set")||!s(t["get"+e.substr(3)])?e:"get"+e.substr(3)](l):t[e]():c,_=s(c)?l?re:te:Zt;if(r(n)&&(~n.indexOf("random(")&&(n=ob(n)),"="===n.charAt(1)&&(!(d=ka(p,n)+(Ya(p)||0))&&0!==d||(n=d))),!f||p!==n||Gt)return isNaN(p*n)||""===n?(c||e in t||Q(e,n),function _addComplexStringPropTween(t,e,r,i,n,a,s){var o,u,h,l,f,d,c,p,_=new _e(this._pt,t,e,0,1,ue,null,n),m=0,g=0;for(_.b=r,_.e=i,r+="",(c=~(i+="").indexOf("random("))&&(i=ob(i)),a&&(a(p=[r,i],t,e),r=p[0],i=p[1]),u=r.match(it)||[];o=it.exec(i);)l=o[0],f=i.substring(m,o.index),h?h=(h+1)%5:"rgba("===f.substr(-5)&&(h=1),l!==u[g++]&&(d=parseFloat(u[g-1])||0,_._pt={_next:_._pt,p:f||1===g?f:",",s:d,c:"="===l.charAt(1)?ka(d,l)-d:parseFloat(l)-d,m:h&&h<4?Math.round:0},m=it.lastIndex);return _.c=m")}),s.duration();else{for(l in u={},x)"ease"===l||"easeEach"===l||gc(l,x[l],u,x.easeEach);for(l in u)for(A=u[l].sort(function(t,e){return t.t-e.t}),o=E=0;o=t._tDur||e<0)&&t.ratio===u&&(u&&za(t,1),r||L||(Ct(t,u?"onComplete":"onReverseComplete",!0),t._prom&&t._prom()))}else t._zTime||(t._zTime=e)}(this,t,e,r);return this},e.targets=function targets(){return this._targets},e.invalidate=function invalidate(t){return t&&this.vars.runBackwards||(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),D.prototype.invalidate.call(this,t)},e.resetTo=function resetTo(t,e,r,i,n){c||Rt.wake(),this._ts||this.play();var a,s=Math.min(this._dur,(this._dp._time-this._start)*this._ts);return this._initted||Qt(this,s),a=this._ease(s/this._dur),function _updatePropTweens(t,e,r,i,n,a,s,o){var u,h,l,f,d=(t._pt&&t._ptCache||(t._ptCache={}))[e];if(!d)for(d=t._ptCache[e]=[],l=t._ptLookup,f=t._targets.length;f--;){if((u=l[f][e])&&u.d&&u.d._pt)for(u=u.d._pt;u&&u.p!==e&&u.fp!==e;)u=u._next;if(!u)return Gt=1,t.vars[e]="+=0",Qt(t,s),Gt=0,o?R(e+" not eligible for reset"):1;d.push(u)}for(f=d.length;f--;)(u=(h=d[f])._pt||h).s=!i&&0!==i||n?u.s+(i||0)+a*u.c:i,u.c=r-u.s,h.e&&(h.e=ia(r)+Ya(h.e)),h.b&&(h.b=u.s+Ya(h.b))}(this,t,e,r,i,a,s,n)?this.resetTo(t,e,r,i,1):(Ia(this,0),this.parent||xa(this._dp,this,"_first","_last",this._dp._sort?"_start":0),this.render(0))},e.kill=function kill(t,e){if(void 0===e&&(e="all"),!(t||e&&"all"!==e))return this._lazy=this._pt=0,this.parent?tb(this):this;if(this.timeline){var i=this.timeline.totalDuration();return this.timeline.killTweensOf(t,e,Nt&&!0!==Nt.vars.overwrite)._first||tb(this),this.parent&&i!==this.timeline.totalDuration()&&Ra(this,this._dur*this.timeline._tDur/i,0,1),this}var n,a,s,o,u,h,l,f=this._targets,d=t?Mt(t):f,c=this._ptLookup,p=this._pt;if((!e||"all"===e)&&function _arraysMatch(t,e){for(var r=t.length,i=r===e.length;i&&r--&&t[r]===e[r];);return r<0}(f,d))return"all"===e&&(this._pt=0),tb(this);for(n=this._op=this._op||[],"all"!==e&&(r(e)&&(u={},ha(e,function(t){return u[t]=1}),e=u),e=function _addAliasesToVars(t,e){var r,i,n,a,s=t[0]?fa(t[0]).harness:0,o=s&&s.aliases;if(!o)return e;for(i in r=yt({},e),o)if(i in r)for(n=(a=o[i].split(",")).length;n--;)r[a[n]]=r[i];return r}(f,e)),l=f.length;l--;)if(~d.indexOf(f[l]))for(u in a=c[l],"all"===e?(n[l]=e,o=a,s={}):(s=n[l]=n[l]||{},o=e),o)(h=a&&a[u])&&("kill"in h.d&&!0!==h.d.kill(u)||ya(this,h,"_pt"),delete a[u]),"all"!==s&&(s[u]=1);return this._initted&&!this._pt&&p&&tb(this),this},Tween.to=function to(t,e,r){return new Tween(t,e,r)},Tween.from=function from(t,e){return Va(1,arguments)},Tween.delayedCall=function delayedCall(t,e,r,i){return new Tween(e,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:t,onComplete:e,onReverseComplete:e,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},Tween.fromTo=function fromTo(t,e,r){return Va(2,arguments)},Tween.set=function set(t,e){return e.duration=0,e.repeatDelay||(e.repeat=0),new Tween(t,e)},Tween.killTweensOf=function killTweensOf(t,e,r){return I.killTweensOf(t,e,r)},Tween}(Ut);qa($t.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),ha("staggerTo,staggerFrom,staggerFromTo",function(r){$t[r]=function(){var t=new Xt,e=kt.call(arguments,0);return e.splice("staggerFromTo"===r?5:4,0,0),t[r].apply(t,e)}});function oc(t,e,r){return t.setAttribute(e,r)}function wc(t,e,r,i){i.mSet(t,e,i.m.call(i.tween,r,i.mt),i)}var Zt=function _setterPlain(t,e,r){return t[e]=r},te=function _setterFunc(t,e,r){return t[e](r)},re=function _setterFuncWithParam(t,e,r,i){return t[e](i.fp,r)},ne=function _getSetter(t,e){return s(t[e])?te:u(t[e])&&t.setAttribute?oc:Zt},ae=function _renderPlain(t,e){return e.set(e.t,e.p,Math.round(1e6*(e.s+e.c*t))/1e6,e)},se=function _renderBoolean(t,e){return e.set(e.t,e.p,!!(e.s+e.c*t),e)},ue=function _renderComplexString(t,e){var r=e._pt,i="";if(!t&&e.b)i=e.b;else if(1===t&&e.e)i=e.e;else{for(;r;)i=r.p+(r.m?r.m(r.s+r.c*t):Math.round(1e4*(r.s+r.c*t))/1e4)+i,r=r._next;i+=e.c}e.set(e.t,e.p,i,e)},he=function _renderPropTweens(t,e){for(var r=e._pt;r;)r.r(t,r.d),r=r._next},fe=function _addPluginModifier(t,e,r,i){for(var n,a=this._pt;a;)n=a._next,a.p===i&&a.modifier(t,e,r),a=n},ce=function _killPropTweensOf(t){for(var e,r,i=this._pt;i;)r=i._next,i.p===t&&!i.op||i.op===t?ya(this,i,"_pt"):i.dep||(e=1),i=r;return!e},pe=function _sortPropTweensByPriority(t){for(var e,r,i,n,a=t._pt;a;){for(e=a._next,r=i;r&&r.pr>a.pr;)r=r._next;(a._prev=r?r._prev:n)?a._prev._next=a:i=a,(a._next=r)?r._prev=a:n=a,a=e}t._pt=i},_e=(PropTween.prototype.modifier=function modifier(t,e,r){this.mSet=this.mSet||this.set,this.set=wc,this.m=t,this.mt=r,this.tween=e},PropTween);function PropTween(t,e,r,i,n,a,s,o,u){this.t=e,this.s=i,this.c=n,this.p=r,this.r=a||ae,this.d=s||this,this.set=o||Zt,this.pr=u||0,(this._next=t)&&(t._prev=this)}ha(vt+"parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger",function(t){return ft[t]=1}),ot.TweenMax=ot.TweenLite=$t,ot.TimelineLite=ot.TimelineMax=Xt,I=new Xt({sortChildren:!1,defaults:V,autoRemoveChildren:!0,id:"root",smoothChildTiming:!0}),q.stringFilter=Fb;function Ec(t){return(ye[t]||Te).map(function(t){return t()})}function Fc(){var t=Date.now(),o=[];2+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}); +/* == malihu jquery custom scrollbar plugin == Version: 3.1.6, License: MIT License (MIT) */ +!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"undefined"!=typeof module&&module.exports?module.exports=e:e(jQuery,window,document)}(function(e){var t,o,a,n,i,r,l,s,c,d,u,f,m,h,p,g,v,x,S,_,w,C,b,y,B,T,k,M,O,I,D,E,W,R,A,L,z,P,H,U,F,q,j,Y,X,N,V,Q,G,J,K,Z,$,ee,te,oe,ae,ne;oe="function"==typeof define&&define.amd,ae="undefined"!=typeof module&&module.exports,ne="https:"==document.location.protocol?"https:":"http:",oe||(ae?require("jquery-mousewheel")(e):e.event.special.mousewheel||e("head").append(decodeURI("%3Cscript src="+ne+"//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js%3E%3C/script%3E"))),o="mCustomScrollbar",a={setTop:0,setLeft:0,axis:"y",scrollbarPosition:"inside",scrollInertia:950,autoDraggerLength:!0,alwaysShowScrollbar:0,snapOffset:0,mouseWheel:{enable:!0,scrollAmount:"auto",axis:"y",deltaFactor:"auto",disableOver:["select","option","keygen","datalist","textarea"]},scrollButtons:{scrollType:"stepless",scrollAmount:"auto"},keyboard:{enable:!0,scrollType:"stepless",scrollAmount:"auto"},contentTouchScroll:25,documentTouchScroll:!0,advanced:{autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",updateOnContentResize:!0,updateOnImageLoad:"auto",autoUpdateTimeout:60},theme:"light",callbacks:{onTotalScrollOffset:0,onTotalScrollBackOffset:0,alwaysTriggerOffsets:!0}},n=0,i={},r=window.attachEvent&&!window.addEventListener?1:0,l=!1,s=["mCSB_dragger_onDrag","mCSB_scrollTools_onDrag","mCS_img_loaded","mCS_disabled","mCS_destroyed","mCS_no_scrollbar","mCS-autoHide","mCS-dir-rtl","mCS_no_scrollbar_y","mCS_no_scrollbar_x","mCS_y_hidden","mCS_x_hidden","mCSB_draggerContainer","mCSB_buttonUp","mCSB_buttonDown","mCSB_buttonLeft","mCSB_buttonRight"],c={init:function(t){var t=e.extend(!0,{},a,t),o=d.call(this);if(t.live){var r=t.liveSelector||this.selector||".mCustomScrollbar",l=e(r);if("off"===t.live)return void f(r);i[r]=setTimeout(function(){l.mCustomScrollbar(t),"once"===t.live&&l.length&&f(r)},500)}else f(r);return t.setWidth=t.set_width?t.set_width:t.setWidth,t.setHeight=t.set_height?t.set_height:t.setHeight,t.axis=t.horizontalScroll?"x":m(t.axis),t.scrollInertia=t.scrollInertia>0&&t.scrollInertia<17?17:t.scrollInertia,"object"!=typeof t.mouseWheel&&1==t.mouseWheel&&(t.mouseWheel={enable:!0,scrollAmount:"auto",axis:"y",preventDefault:!1,deltaFactor:"auto",normalizeDelta:!1,invert:!1}),t.mouseWheel.scrollAmount=t.mouseWheelPixels?t.mouseWheelPixels:t.mouseWheel.scrollAmount,t.mouseWheel.normalizeDelta=t.advanced.normalizeMouseWheelDelta?t.advanced.normalizeMouseWheelDelta:t.mouseWheel.normalizeDelta,t.scrollButtons.scrollType=h(t.scrollButtons.scrollType),u(t),e(o).each(function(){var o=e(this);if(!o.data("mCS")){o.data("mCS",{idx:++n,opt:t,scrollRatio:{y:null,x:null},overflowed:null,contentReset:{y:null,x:null},bindEvents:!1,tweenRunning:!1,sequential:{},langDir:o.css("direction"),cbOffsets:null,trigger:null,poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}});var a=o.data("mCS"),i=a.opt,r=o.data("mcs-axis"),l=o.data("mcs-scrollbar-position"),d=o.data("mcs-theme");r&&(i.axis=r),l&&(i.scrollbarPosition=l),d&&(i.theme=d,u(i)),p.call(this),a&&i.callbacks.onCreate&&"function"==typeof i.callbacks.onCreate&&i.callbacks.onCreate.call(this),e("#mCSB_"+a.idx+"_container img:not(."+s[2]+")").addClass(s[2]),c.update.call(null,o)}})},update:function(t,o){var a=t||d.call(this);return e(a).each(function(){var t=e(this);if(t.data("mCS")){var a=t.data("mCS"),n=a.opt,i=e("#mCSB_"+a.idx+"_container"),r=e("#mCSB_"+a.idx),l=[e("#mCSB_"+a.idx+"_dragger_vertical"),e("#mCSB_"+a.idx+"_dragger_horizontal")];if(!i.length)return;a.tweenRunning&&X(t),o&&a&&n.callbacks.onBeforeUpdate&&"function"==typeof n.callbacks.onBeforeUpdate&&n.callbacks.onBeforeUpdate.call(this),t.hasClass(s[3])&&t.removeClass(s[3]),t.hasClass(s[4])&&t.removeClass(s[4]),r.css("max-height","none"),r.height()!==t.height()&&r.css("max-height",t.height()),v.call(this),"y"===n.axis||n.advanced.autoExpandHorizontalScroll||i.css("width",g(i)),a.overflowed=C.call(this),T.call(this),n.autoDraggerLength&&S.call(this),_.call(this),y.call(this);var c=[Math.abs(i[0].offsetTop),Math.abs(i[0].offsetLeft)];"x"!==n.axis&&(a.overflowed[0]?l[0].height()>l[0].parent().height()?b.call(this):(N(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}),a.contentReset.y=null):(b.call(this),"y"===n.axis?B.call(this):"yx"===n.axis&&a.overflowed[1]&&N(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}))),"y"!==n.axis&&(a.overflowed[1]?l[1].width()>l[1].parent().width()?b.call(this):(N(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}),a.contentReset.x=null):(b.call(this),"x"===n.axis?B.call(this):"yx"===n.axis&&a.overflowed[0]&&N(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}))),o&&a&&(2===o&&n.callbacks.onImageLoad&&"function"==typeof n.callbacks.onImageLoad?n.callbacks.onImageLoad.call(this):3===o&&n.callbacks.onSelectorChange&&"function"==typeof n.callbacks.onSelectorChange?n.callbacks.onSelectorChange.call(this):n.callbacks.onUpdate&&"function"==typeof n.callbacks.onUpdate&&n.callbacks.onUpdate.call(this)),Y.call(this)}})},scrollTo:function(t,o){if(void 0!==t&&null!=t){var a=d.call(this);return e(a).each(function(){var a=e(this);if(a.data("mCS")){var n=a.data("mCS"),i=n.opt,r={trigger:"external",scrollInertia:i.scrollInertia,scrollEasing:"mcsEaseInOut",moveDragger:!1,timeout:60,callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},l=e.extend(!0,{},r,o),s=q.call(this,t),c=l.scrollInertia>0&&l.scrollInertia<17?17:l.scrollInertia;s[0]=j.call(this,s[0],"y"),s[1]=j.call(this,s[1],"x"),l.moveDragger&&(s[0]*=n.scrollRatio.y,s[1]*=n.scrollRatio.x),l.dur=te()?0:c,setTimeout(function(){null!==s[0]&&void 0!==s[0]&&"x"!==i.axis&&n.overflowed[0]&&(l.dir="y",l.overwrite="all",N(a,s[0].toString(),l)),null!==s[1]&&void 0!==s[1]&&"y"!==i.axis&&n.overflowed[1]&&(l.dir="x",l.overwrite="none",N(a,s[1].toString(),l))},l.timeout)}})}},stop:function(){var t=d.call(this);return e(t).each(function(){var t=e(this);t.data("mCS")&&X(t)})},disable:function(t){var o=d.call(this);return e(o).each(function(){var o=e(this);o.data("mCS")&&(o.data("mCS"),Y.call(this,"remove"),B.call(this),t&&b.call(this),T.call(this,!0),o.addClass(s[3]))})},destroy:function(){var t=d.call(this);return e(t).each(function(){var a=e(this);if(a.data("mCS")){var n=a.data("mCS"),i=n.opt,r=e("#mCSB_"+n.idx),l=e("#mCSB_"+n.idx+"_container"),c=e(".mCSB_"+n.idx+"_scrollbar");i.live&&f(i.liveSelector||e(t).selector),Y.call(this,"remove"),B.call(this),b.call(this),a.removeData("mCS"),J(this,"mcs"),c.remove(),l.find("img."+s[2]).removeClass(s[2]),r.replaceWith(l.contents()),a.removeClass(o+" _mCS_"+n.idx+" "+s[6]+" "+s[7]+" "+s[5]+" "+s[3]).addClass(s[4])}})}},d=function(){return"object"!=typeof e(this)||e(this).length<1?".mCustomScrollbar":this},u=function(t){t.autoDraggerLength=!(e.inArray(t.theme,["rounded","rounded-dark","rounded-dots","rounded-dots-dark"])>-1)&&t.autoDraggerLength,t.autoExpandScrollbar=!(e.inArray(t.theme,["rounded-dots","rounded-dots-dark","3d","3d-dark","3d-thick","3d-thick-dark","inset","inset-dark","inset-2","inset-2-dark","inset-3","inset-3-dark"])>-1)&&t.autoExpandScrollbar,t.scrollButtons.enable=!(e.inArray(t.theme,["minimal","minimal-dark"])>-1)&&t.scrollButtons.enable,t.autoHideScrollbar=e.inArray(t.theme,["minimal","minimal-dark"])>-1||t.autoHideScrollbar,t.scrollbarPosition=e.inArray(t.theme,["minimal","minimal-dark"])>-1?"outside":t.scrollbarPosition},f=function(e){i[e]&&(clearTimeout(i[e]),J(i,e))},m=function(e){return"yx"===e||"xy"===e||"auto"===e?"yx":"x"===e||"horizontal"===e?"x":"y"},h=function(e){return"stepped"===e||"pixels"===e||"step"===e||"click"===e?"stepped":"stepless"},p=function(){var t=e(this),a=t.data("mCS"),n=a.opt,i=n.autoExpandScrollbar?" "+s[1]+"_expand":"",r=["
","
"],l="yx"===n.axis?"mCSB_vertical_horizontal":"x"===n.axis?"mCSB_horizontal":"mCSB_vertical",c="yx"===n.axis?r[0]+r[1]:"x"===n.axis?r[1]:r[0],d="yx"===n.axis?"
":"",u=n.autoHideScrollbar?" "+s[6]:"",f="x"!==n.axis&&"rtl"===a.langDir?" "+s[7]:"";n.setWidth&&t.css("width",n.setWidth),n.setHeight&&t.css("height",n.setHeight),n.setLeft="y"!==n.axis&&"rtl"===a.langDir?"989999px":n.setLeft,t.addClass(o+" _mCS_"+a.idx+u+f).wrapInner("
");var m=e("#mCSB_"+a.idx),h=e("#mCSB_"+a.idx+"_container");"y"===n.axis||n.advanced.autoExpandHorizontalScroll||h.css("width",g(h)),"outside"===n.scrollbarPosition?("static"===t.css("position")&&t.css("position","relative"),t.css("overflow","visible"),m.addClass("mCSB_outside").after(c)):(m.addClass("mCSB_inside").append(c),h.wrap(d)),x.call(this);var p=[e("#mCSB_"+a.idx+"_dragger_vertical"),e("#mCSB_"+a.idx+"_dragger_horizontal")];p[0].css("min-height",p[0].height()),p[1].css("min-width",p[1].width())},g=function(t){var o=[t[0].scrollWidth,Math.max.apply(Math,t.children().map(function(){return e(this).outerWidth(!0)}).get())],a=t.parent().width();return o[0]>a?o[0]:o[1]>a?o[1]:"100%"},v=function(){var t=e(this),o=t.data("mCS"),a=o.opt,n=e("#mCSB_"+o.idx+"_container");if(a.advanced.autoExpandHorizontalScroll&&"y"!==a.axis){n.css({width:"auto","min-width":0,"overflow-x":"scroll"});var i=Math.ceil(n[0].scrollWidth);3===a.advanced.autoExpandHorizontalScroll||2!==a.advanced.autoExpandHorizontalScroll&&i>n.parent().width()?n.css({width:i,"min-width":"100%","overflow-x":"inherit"}):n.css({"overflow-x":"inherit",position:"absolute"}).wrap("
").css({width:Math.ceil(n[0].getBoundingClientRect().right+.4)-Math.floor(n[0].getBoundingClientRect().left),"min-width":"100%",position:"relative"}).unwrap()}},x=function(){var t=e(this),o=t.data("mCS"),a=o.opt,n=e(".mCSB_"+o.idx+"_scrollbar:first"),i=$(a.scrollButtons.tabindex)?"tabindex='"+a.scrollButtons.tabindex+"'":"",r=["","","",""],l=["x"===a.axis?r[2]:r[0],"x"===a.axis?r[3]:r[1],r[2],r[3]];a.scrollButtons.enable&&n.prepend(l[0]).append(l[1]).next(".mCSB_scrollTools").prepend(l[2]).append(l[3])},S=function(){var t=e(this),o=t.data("mCS"),a=e("#mCSB_"+o.idx),n=e("#mCSB_"+o.idx+"_container"),i=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],l=[a.height()/n.outerHeight(!1),a.width()/n.outerWidth(!1)],s=[parseInt(i[0].css("min-height")),Math.round(l[0]*i[0].parent().height()),parseInt(i[1].css("min-width")),Math.round(l[1]*i[1].parent().width())],c=r&&s[1]i&&(i=l),s>r&&(r=s),[i>a.height(),r>a.width()]},b=function(){var t,o,a=e(this),n=a.data("mCS"),i=n.opt,r=e("#mCSB_"+n.idx),l=e("#mCSB_"+n.idx+"_container"),s=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];X(a),("x"!==i.axis&&!n.overflowed[0]||"y"===i.axis&&n.overflowed[0])&&(s[0].add(l).css("top",0),N(a,"_resetY")),("y"!==i.axis&&!n.overflowed[1]||"x"===i.axis&&n.overflowed[1])&&(t=o=0,"rtl"===n.langDir&&(t=r.width()-l.outerWidth(!1),o=Math.abs(t/n.scrollRatio.x)),l.css("left",t),s[1].css("left",o),N(a,"_resetX"))},y=function(){var t=e(this),o=t.data("mCS"),a=o.opt;if(!o.bindEvents){var n;if(M.call(this),a.contentTouchScroll&&O.call(this),I.call(this),a.mouseWheel.enable)!function o(){n=setTimeout(function(){e.event.special.mousewheel?(clearTimeout(n),D.call(t[0])):o()},100)}();L.call(this),P.call(this),a.advanced.autoScrollOnFocus&&z.call(this),a.scrollButtons.enable&&H.call(this),a.keyboard.enable&&U.call(this),o.bindEvents=!0}},B=function(){var t=e(this),o=t.data("mCS"),a=o.opt,n="mCS_"+o.idx,i=".mCSB_"+o.idx+"_scrollbar",r=e("#mCSB_"+o.idx+",#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,"+i+" ."+s[12]+",#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal,"+i+">a"),l=e("#mCSB_"+o.idx+"_container");if(a.advanced.releaseDraggableSelectors&&r.add(e(a.advanced.releaseDraggableSelectors)),a.advanced.extraDraggableSelectors&&r.add(e(a.advanced.extraDraggableSelectors)),o.bindEvents){var c=W()?top.document:document;e(document).add(e(c)).unbind("."+n),r.each(function(){e(this).unbind("."+n)}),clearTimeout(t[0]._focusTimeout),J(t[0],"_focusTimeout"),clearTimeout(o.sequential.step),J(o.sequential,"step"),clearTimeout(l[0].onCompleteTimeout),J(l[0],"onCompleteTimeout"),o.bindEvents=!1}},T=function(t){var o=e(this),a=o.data("mCS"),n=a.opt,i=e("#mCSB_"+a.idx+"_container_wrapper"),r=i.length?i:e("#mCSB_"+a.idx+"_container"),l=[e("#mCSB_"+a.idx+"_scrollbar_vertical"),e("#mCSB_"+a.idx+"_scrollbar_horizontal")],c=[l[0].find(".mCSB_dragger"),l[1].find(".mCSB_dragger")];"x"!==n.axis&&(a.overflowed[0]&&!t?(l[0].add(c[0]).add(l[0].children("a")).css("display","block"),r.removeClass(s[8]+" "+s[10])):(n.alwaysShowScrollbar?(2!==n.alwaysShowScrollbar&&c[0].css("display","none"),r.removeClass(s[10])):(l[0].css("display","none"),r.addClass(s[10])),r.addClass(s[8]))),"y"!==n.axis&&(a.overflowed[1]&&!t?(l[1].add(c[1]).add(l[1].children("a")).css("display","block"),r.removeClass(s[9]+" "+s[11])):(n.alwaysShowScrollbar?(2!==n.alwaysShowScrollbar&&c[1].css("display","none"),r.removeClass(s[11])):(l[1].css("display","none"),r.addClass(s[11])),r.addClass(s[9]))),a.overflowed[0]||a.overflowed[1]?o.removeClass(s[5]):o.addClass(s[5])},k=function(t){var o=t.type,a=t.target.ownerDocument!==document&&null!==frameElement?[e(frameElement).offset().top,e(frameElement).offset().left]:null,n=W()&&t.target.ownerDocument!==top.document&&null!==frameElement?[e(t.view.frameElement).offset().top,e(t.view.frameElement).offset().left]:[0,0];switch(o){case"pointerdown":case"MSPointerDown":case"pointermove":case"MSPointerMove":case"pointerup":case"MSPointerUp":return a?[t.originalEvent.pageY-a[0]+n[0],t.originalEvent.pageX-a[1]+n[1],!1]:[t.originalEvent.pageY,t.originalEvent.pageX,!1];case"touchstart":case"touchmove":case"touchend":var i=t.originalEvent.touches[0]||t.originalEvent.changedTouches[0],r=t.originalEvent.touches.length||t.originalEvent.changedTouches.length;return t.target.ownerDocument!==document?[i.screenY,i.screenX,r>1]:[i.pageY,i.pageX,r>1];default:return a?[t.pageY-a[0]+n[0],t.pageX-a[1]+n[1],!1]:[t.pageY,t.pageX,!1]}},M=function(){var t,o,a,n=e(this),i=n.data("mCS"),s=i.opt,c="mCS_"+i.idx,d=["mCSB_"+i.idx+"_dragger_vertical","mCSB_"+i.idx+"_dragger_horizontal"],u=e("#mCSB_"+i.idx+"_container"),f=e("#"+d[0]+",#"+d[1]),m=s.advanced.releaseDraggableSelectors?f.add(e(s.advanced.releaseDraggableSelectors)):f,h=W()?top.document:document,p=s.advanced.extraDraggableSelectors?e(h).add(e(s.advanced.extraDraggableSelectors)):e(h);function g(e,o,a,r){if(u[0].idleTimer=s.scrollInertia<233?250:0,t.attr("id")===d[1])var l="x",c=(t[0].offsetLeft-o+r)*i.scrollRatio.x;else var l="y",c=(t[0].offsetTop-e+a)*i.scrollRatio.y;N(n,c.toString(),{dir:l,drag:!0})}f.bind("contextmenu."+c,function(e){e.preventDefault()}).bind("mousedown."+c+" touchstart."+c+" pointerdown."+c+" MSPointerDown."+c,function(i){if(i.stopImmediatePropagation(),i.preventDefault(),K(i)){l=!0,r&&(document.onselectstart=function(){return!1}),R.call(u,!1),X(n);var c=(t=e(this)).offset(),d=k(i)[0]-c.top,f=k(i)[1]-c.left,m=t.height()+c.top,h=t.width()+c.left;d0&&f0&&(o=d,a=f),w(t,"active",s.autoExpandScrollbar)}}).bind("touchmove."+c,function(e){e.stopImmediatePropagation(),e.preventDefault();var n=t.offset(),i=k(e)[0]-n.top,r=k(e)[1]-n.left;g(o,a,i,r)}),e(document).add(p).bind("mousemove."+c+" pointermove."+c+" MSPointerMove."+c,function(e){if(t){var n=t.offset(),i=k(e)[0]-n.top,r=k(e)[1]-n.left;if(o===i&&a===r)return;g(o,a,i,r)}}).add(m).bind("mouseup."+c+" touchend."+c+" pointerup."+c+" MSPointerUp."+c,function(e){t&&(w(t,"active",s.autoExpandScrollbar),t=null),l=!1,r&&(document.onselectstart=null),R.call(u,!0)})},O=function(){var o,a,n,i,r,s,c,d,u,f,m,h,p,g,v=e(this),x=v.data("mCS"),S=x.opt,_="mCS_"+x.idx,w=e("#mCSB_"+x.idx),C=e("#mCSB_"+x.idx+"_container"),b=[e("#mCSB_"+x.idx+"_dragger_vertical"),e("#mCSB_"+x.idx+"_dragger_horizontal")],y=[],B=[],T=0,M="yx"===S.axis?"none":"all",O=[],I=C.find("iframe"),D=["touchstart."+_+" pointerdown."+_+" MSPointerDown."+_,"touchmove."+_+" pointermove."+_+" MSPointerMove."+_,"touchend."+_+" pointerup."+_+" MSPointerUp."+_],E=void 0!==document.body.style.touchAction&&""!==document.body.style.touchAction;function R(e){if(!Z(e)||l||k(e)[2])t=0;else{t=1,p=0,g=0,o=1,v.removeClass("mCS_touch_action");var i=C.offset();a=k(e)[0]-i.top,n=k(e)[1]-i.left,O=[k(e)[0],k(e)[1]]}}function A(e){if(Z(e)&&!l&&!k(e)[2]&&(S.documentTouchScroll||e.preventDefault(),e.stopImmediatePropagation(),(!g||p)&&o)){c=Q();var t=w.offset(),i=k(e)[0]-t.top,r=k(e)[1]-t.left;if(y.push(i),B.push(r),O[2]=Math.abs(k(e)[0]-O[0]),O[3]=Math.abs(k(e)[1]-O[1]),x.overflowed[0])var s=b[0].parent().height()-b[0].height(),d=a-i>0&&i-a>-s*x.scrollRatio.y&&(2*O[3]0&&r-n>-u*x.scrollRatio.x&&(2*O[2]30)){var v=(f=1e3/(d-s))<2.5,_=v?[y[y.length-2],B[B.length-2]]:[0,0];u=v?[a-_[0],n-_[1]]:[a-i,n-r];var b=[Math.abs(u[0]),Math.abs(u[1])];f=v?[Math.abs(u[0]/4),Math.abs(u[1]/4)]:[f,f];var T=[Math.abs(C[0].offsetTop)-u[0]*P(b[0]/f[0],f[0]),Math.abs(C[0].offsetLeft)-u[1]*P(b[1]/f[1],f[1])];m="yx"===S.axis?[T[0],T[1]]:"x"===S.axis?[null,T[1]]:[T[0],null],h=[4*b[0]+S.scrollInertia,4*b[1]+S.scrollInertia];var O=parseInt(S.contentTouchScroll)||0;m[0]=b[0]>O?m[0]:0,m[1]=b[1]>O?m[1]:0,x.overflowed[0]&&H(m[0],h[0],"mcsEaseOut","y",M,!1),x.overflowed[1]&&H(m[1],h[1],"mcsEaseOut","x",M,!1)}}}function P(e,t){var o=[1.5*t,2*t,t/1.5,t/2];return e>90?t>4?o[0]:o[3]:e>60?t>3?o[3]:o[2]:e>30?t>8?o[1]:t>6?o[0]:t>4?t:o[2]:t>8?t:o[3]}function H(e,t,o,a,n,i){e&&N(v,e.toString(),{dur:t,scrollEasing:o,dir:a,overwrite:n,drag:i})}C.bind(D[0],function(e){R(e)}).bind(D[1],function(e){A(e)}),w.bind(D[0],function(e){L(e)}).bind(D[2],function(e){z(e)}),I.length&&I.each(function(){e(this).bind("load",function(){W(this)&&e(this.contentDocument||this.contentWindow.document).bind(D[0],function(e){R(e),L(e)}).bind(D[1],function(e){A(e)}).bind(D[2],function(e){z(e)})})})},I=function(){var o,a=e(this),n=a.data("mCS"),i=n.opt,r=n.sequential,s="mCS_"+n.idx,c=e("#mCSB_"+n.idx+"_container"),d=c.parent();function u(e,t,n){r.type=n&&o?"stepped":"stepless",r.scrollAmount=10,F(a,e,t,"mcsLinearOut",n?60:null)}c.bind("mousedown."+s,function(e){t||o||(o=1,l=!0)}).add(document).bind("mousemove."+s,function(e){if(!t&&o&&(window.getSelection?window.getSelection().toString():document.selection&&"Control"!=document.selection.type&&document.selection.createRange().text)){var a=c.offset(),l=k(e)[0]-a.top+c[0].offsetTop,s=k(e)[1]-a.left+c[0].offsetLeft;l>0&&l0&&sd.height()&&u("on",40)),"y"!==i.axis&&n.overflowed[1]&&(s<0?u("on",37):s>d.width()&&u("on",39)))}}).bind("mouseup."+s+" dragend."+s,function(e){t||(o&&(o=0,u("off",null)),l=!1)})},D=function(){if(e(this).data("mCS")){var t=e(this),o=t.data("mCS"),a=o.opt,n="mCS_"+o.idx,i=e("#mCSB_"+o.idx),l=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],s=e("#mCSB_"+o.idx+"_container").find("iframe");s.length&&s.each(function(){e(this).bind("load",function(){W(this)&&e(this.contentDocument||this.contentWindow.document).bind("mousewheel."+n,function(e,t){c(e,t)})})}),i.bind("mousewheel."+n,function(e,t){c(e,t)})}function c(n,s){if(X(t),!A(t,n.target)){var c="auto"!==a.mouseWheel.deltaFactor?parseInt(a.mouseWheel.deltaFactor):r&&n.deltaFactor<100?100:n.deltaFactor||100,d=a.scrollInertia;if("x"===a.axis||"x"===a.mouseWheel.axis)var u="x",f=[Math.round(c*o.scrollRatio.x),parseInt(a.mouseWheel.scrollAmount)],m="auto"!==a.mouseWheel.scrollAmount?f[1]:f[0]>=i.width()?.9*i.width():f[0],h=Math.abs(e("#mCSB_"+o.idx+"_container")[0].offsetLeft),p=l[1][0].offsetLeft,g=l[1].parent().width()-l[1].width(),v="y"===a.mouseWheel.axis?n.deltaY||s:n.deltaX;else var u="y",f=[Math.round(c*o.scrollRatio.y),parseInt(a.mouseWheel.scrollAmount)],m="auto"!==a.mouseWheel.scrollAmount?f[1]:f[0]>=i.height()?.9*i.height():f[0],h=Math.abs(e("#mCSB_"+o.idx+"_container")[0].offsetTop),p=l[0][0].offsetTop,g=l[0].parent().height()-l[0].height(),v=n.deltaY||s;"y"===u&&!o.overflowed[0]||"x"===u&&!o.overflowed[1]||((a.mouseWheel.invert||n.webkitDirectionInvertedFromDevice)&&(v=-v),a.mouseWheel.normalizeDelta&&(v=v<0?-1:1),(v>0&&0!==p||v<0&&p!==g||a.mouseWheel.preventDefault)&&(n.stopImmediatePropagation(),n.preventDefault()),n.deltaFactor<5&&!a.mouseWheel.normalizeDelta&&(m=n.deltaFactor,d=17),N(t,(h-v*m).toString(),{dir:u,dur:d}))}}},E=new Object,W=function(t){var o=!1,a=!1,n=null;if(void 0===t?a="#empty":void 0!==e(t).attr("id")&&(a=e(t).attr("id")),!1!==a&&void 0!==E[a])return E[a];if(t){try{var i=t.contentDocument||t.contentWindow.document;n=i.body.innerHTML}catch(e){}o=null!==n}else{try{var i=top.document;n=i.body.innerHTML}catch(e){}o=null!==n}return!1!==a&&(E[a]=o),o},R=function(e){var t=this.find("iframe");if(t.length){var o=e?"auto":"none";t.css("pointer-events",o)}},A=function(t,o){var a=o.nodeName.toLowerCase(),n=t.data("mCS").opt.mouseWheel.disableOver;return e.inArray(a,n)>-1&&!(e.inArray(a,["select","textarea"])>-1&&!e(o).is(":focus"))},L=function(){var t,o=e(this),a=o.data("mCS"),n="mCS_"+a.idx,i=e("#mCSB_"+a.idx+"_container"),r=i.parent(),c=e(".mCSB_"+a.idx+"_scrollbar ."+s[12]);c.bind("mousedown."+n+" touchstart."+n+" pointerdown."+n+" MSPointerDown."+n,function(o){l=!0,e(o.target).hasClass("mCSB_dragger")||(t=1)}).bind("touchend."+n+" pointerup."+n+" MSPointerUp."+n,function(e){l=!1}).bind("click."+n,function(n){if(t&&(t=0,e(n.target).hasClass(s[12])||e(n.target).hasClass("mCSB_draggerRail"))){X(o);var l=e(this),c=l.find(".mCSB_dragger");if(l.parent(".mCSB_scrollTools_horizontal").length>0){if(!a.overflowed[1])return;var d="x",u=n.pageX>c.offset().left?-1:1,f=Math.abs(i[0].offsetLeft)-u*(.9*r.width())}else{if(!a.overflowed[0])return;var d="y",u=n.pageY>c.offset().top?-1:1,f=Math.abs(i[0].offsetTop)-u*(.9*r.height())}N(o,f.toString(),{dir:d,scrollEasing:"mcsEaseInOut"})}})},z=function(){var t=e(this),o=t.data("mCS"),a=o.opt,n="mCS_"+o.idx,i=e("#mCSB_"+o.idx+"_container"),r=i.parent();i.bind("focusin."+n,function(o){var n=e(document.activeElement),l=i.find(".mCustomScrollBox").length;n.is(a.advanced.autoScrollOnFocus)&&(X(t),clearTimeout(t[0]._focusTimeout),t[0]._focusTimer=l?17*l:0,t[0]._focusTimeout=setTimeout(function(){var e=[ee(n)[0],ee(n)[1]],o=[i[0].offsetTop,i[0].offsetLeft],l=[o[0]+e[0]>=0&&o[0]+e[0]=0&&o[0]+e[1]a");s.bind("contextmenu."+i,function(e){e.preventDefault()}).bind("mousedown."+i+" touchstart."+i+" pointerdown."+i+" MSPointerDown."+i+" mouseup."+i+" touchend."+i+" pointerup."+i+" MSPointerUp."+i+" mouseout."+i+" pointerout."+i+" MSPointerOut."+i+" click."+i,function(i){if(i.preventDefault(),K(i)){var r=e(this).attr("class");switch(n.type=a.scrollButtons.scrollType,i.type){case"mousedown":case"touchstart":case"pointerdown":case"MSPointerDown":if("stepped"===n.type)return;l=!0,o.tweenRunning=!1,s("on",r);break;case"mouseup":case"touchend":case"pointerup":case"MSPointerUp":case"mouseout":case"pointerout":case"MSPointerOut":if("stepped"===n.type)return;l=!1,n.dir&&s("off",r);break;case"click":if("stepped"!==n.type||o.tweenRunning)return;s("on",r)}}function s(e,o){n.scrollAmount=a.scrollButtons.scrollAmount,F(t,e,o)}})},U=function(){var t=e(this),o=t.data("mCS"),a=o.opt,n=o.sequential,i="mCS_"+o.idx,r=e("#mCSB_"+o.idx),l=e("#mCSB_"+o.idx+"_container"),s=l.parent(),c="input,textarea,select,datalist,keygen,[contenteditable='true']",d=l.find("iframe"),u=["blur."+i+" keydown."+i+" keyup."+i];function f(i){switch(i.type){case"blur":o.tweenRunning&&n.dir&&h("off",null);break;case"keydown":case"keyup":var r=i.keyCode?i.keyCode:i.which,d="on";if("x"!==a.axis&&(38===r||40===r)||"y"!==a.axis&&(37===r||39===r)){if((38===r||40===r)&&!o.overflowed[0]||(37===r||39===r)&&!o.overflowed[1])return;"keyup"===i.type&&(d="off"),e(document.activeElement).is(c)||(i.preventDefault(),i.stopImmediatePropagation(),h(d,r))}else if(33===r||34===r){if((o.overflowed[0]||o.overflowed[1])&&(i.preventDefault(),i.stopImmediatePropagation()),"keyup"===i.type){X(t);var u=34===r?-1:1;if("x"===a.axis||"yx"===a.axis&&o.overflowed[1]&&!o.overflowed[0])var f="x",m=Math.abs(l[0].offsetLeft)-u*(.9*s.width());else var f="y",m=Math.abs(l[0].offsetTop)-u*(.9*s.height());N(t,m.toString(),{dir:f,scrollEasing:"mcsEaseInOut"})}}else if((35===r||36===r)&&!e(document.activeElement).is(c)&&((o.overflowed[0]||o.overflowed[1])&&(i.preventDefault(),i.stopImmediatePropagation()),"keyup"===i.type)){if("x"===a.axis||"yx"===a.axis&&o.overflowed[1]&&!o.overflowed[0])var f="x",m=35===r?Math.abs(s.width()-l.outerWidth(!1)):0;else var f="y",m=35===r?Math.abs(s.height()-l.outerHeight(!1)):0;N(t,m.toString(),{dir:f,scrollEasing:"mcsEaseInOut"})}}function h(e,i){n.type=a.keyboard.scrollType,n.scrollAmount=a.keyboard.scrollAmount,"stepped"===n.type&&o.tweenRunning||F(t,e,i)}}d.length&&d.each(function(){e(this).bind("load",function(){W(this)&&e(this.contentDocument||this.contentWindow.document).bind(u[0],function(e){f(e)})})}),r.attr("tabindex","0").bind(u[0],function(e){f(e)})},F=function(t,o,a,n,i){var r=t.data("mCS"),l=r.opt,c=r.sequential,d=e("#mCSB_"+r.idx+"_container"),u="stepped"===c.type,f=l.scrollInertia<26?26:l.scrollInertia,m=l.scrollInertia<1?17:l.scrollInertia;switch(o){case"on":if(c.dir=[a===s[16]||a===s[15]||39===a||37===a?"x":"y",a===s[13]||a===s[15]||38===a||37===a?-1:1],X(t),$(a)&&"stepped"===c.type)return;h(u);break;case"off":clearTimeout(c.step),J(c,"step"),X(t),(u||r.tweenRunning&&c.dir)&&h(!0)}function h(e){l.snapAmount&&(c.scrollAmount=l.snapAmount instanceof Array?"x"===c.dir[0]?l.snapAmount[1]:l.snapAmount[0]:l.snapAmount);var o="stepped"!==c.type,a=i||(e?o?f/1.5:m:1e3/60),s=e?o?7.5:40:2.5,u=[Math.abs(d[0].offsetTop),Math.abs(d[0].offsetLeft)],p=[r.scrollRatio.y>10?10:r.scrollRatio.y,r.scrollRatio.x>10?10:r.scrollRatio.x],g="x"===c.dir[0]?u[1]+c.dir[1]*(p[1]*s):u[0]+c.dir[1]*(p[0]*s),v="x"===c.dir[0]?u[1]+c.dir[1]*parseInt(c.scrollAmount):u[0]+c.dir[1]*parseInt(c.scrollAmount),x="auto"!==c.scrollAmount?v:g,S=n||(e?o?"mcsLinearOut":"mcsEaseInOut":"mcsLinear"),_=!!e;e&&a<17&&(x="x"===c.dir[0]?u[1]:u[0]),N(t,x.toString(),{dir:c.dir[0],scrollEasing:S,dur:a,onComplete:_}),e?c.dir=!1:(clearTimeout(c.step),c.step=setTimeout(function(){h()},a))}},q=function(t){var o=e(this).data("mCS").opt,a=[];return"function"==typeof t&&(t=t()),t instanceof Array?a=t.length>1?[t[0],t[1]]:"x"===o.axis?[null,t[0]]:[t[0],null]:(a[0]=t.y?t.y:t.x||"x"===o.axis?null:t,a[1]=t.x?t.x:t.y||"y"===o.axis?null:t),"function"==typeof a[0]&&(a[0]=a[0]()),"function"==typeof a[1]&&(a[1]=a[1]()),a},j=function(t,o){if(null!=t&&void 0!==t){var a=e(this),n=a.data("mCS"),i=n.opt,r=e("#mCSB_"+n.idx+"_container"),l=r.parent(),s=typeof t;o||(o="x"===i.axis?"x":"y");var d="x"===o?r.outerWidth(!1)-l.width():r.outerHeight(!1)-l.height(),u="x"===o?r[0].offsetLeft:r[0].offsetTop,f="x"===o?"left":"top";switch(s){case"function":return t();case"object":var m=t.jquery?t:e(t);if(!m.length)return;return"x"===o?ee(m)[1]:ee(m)[0];case"string":case"number":if($(t))return Math.abs(t);if(-1!==t.indexOf("%"))return Math.abs(d*parseInt(t)/100);if(-1!==t.indexOf("-="))return Math.abs(u-parseInt(t.split("-=")[1]));if(-1!==t.indexOf("+=")){var h=u+parseInt(t.split("+=")[1]);return h>=0?0:Math.abs(h)}if(-1!==t.indexOf("px")&&$(t.split("px")[0]))return Math.abs(t.split("px")[0]);if("top"===t||"left"===t)return 0;if("bottom"===t)return Math.abs(l.height()-r.outerHeight(!1));if("right"===t)return Math.abs(l.width()-r.outerWidth(!1));if("first"===t||"last"===t){var m=r.find(":"+t);return"x"===o?ee(m)[1]:ee(m)[0]}return e(t).length?"x"===o?ee(e(t))[1]:ee(e(t))[0]:(r.css(f,t),void c.update.call(null,a[0]))}}},Y=function(t){var o=e(this),a=o.data("mCS"),n=a.opt,i=e("#mCSB_"+a.idx+"_container");if(t)return clearTimeout(i[0].autoUpdate),void J(i[0],"autoUpdate");function r(e){clearTimeout(i[0].autoUpdate),c.update.call(null,o[0],e)}!function t(){clearTimeout(i[0].autoUpdate),0!==o.parents("html").length?i[0].autoUpdate=setTimeout(function(){return n.advanced.updateOnSelectorChange&&(a.poll.change.n=function(){!0===n.advanced.updateOnSelectorChange&&(n.advanced.updateOnSelectorChange="*");var e=0,t=i.find(n.advanced.updateOnSelectorChange);return n.advanced.updateOnSelectorChange&&t.length>0&&t.each(function(){e+=this.offsetHeight+this.offsetWidth}),e}(),a.poll.change.n!==a.poll.change.o)?(a.poll.change.o=a.poll.change.n,void r(3)):n.advanced.updateOnContentResize&&(a.poll.size.n=o[0].scrollHeight+o[0].scrollWidth+i[0].offsetHeight+o[0].offsetHeight+o[0].offsetWidth,a.poll.size.n!==a.poll.size.o)?(a.poll.size.o=a.poll.size.n,void r(1)):!n.advanced.updateOnImageLoad||"auto"===n.advanced.updateOnImageLoad&&"y"===n.axis||(a.poll.img.n=i.find("img").length,a.poll.img.n===a.poll.img.o)?void((n.advanced.updateOnSelectorChange||n.advanced.updateOnContentResize||n.advanced.updateOnImageLoad)&&t()):(a.poll.img.o=a.poll.img.n,void i.find("img").each(function(){!function(t){if(e(t).hasClass(s[2]))r();else{var o,a,n=new Image;n.onload=(o=n,a=function(){this.onload=null,e(t).addClass(s[2]),r(2)},function(){return a.apply(o,arguments)}),n.src=t.src}}(this)}))},n.advanced.autoUpdateTimeout):o=null}()},X=function(t){var o=t.data("mCS"),a=e("#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal");a.each(function(){G.call(this)})},N=function(t,o,a){var n=t.data("mCS"),i=n.opt,r={trigger:"internal",dir:"y",scrollEasing:"mcsEaseOut",drag:!1,dur:i.scrollInertia,overwrite:"all",callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},a=e.extend(r,a),l=[a.dur,a.drag?0:a.dur],s=e("#mCSB_"+n.idx),c=e("#mCSB_"+n.idx+"_container"),d=c.parent(),u=i.callbacks.onTotalScrollOffset?q.call(t,i.callbacks.onTotalScrollOffset):[0,0],f=i.callbacks.onTotalScrollBackOffset?q.call(t,i.callbacks.onTotalScrollBackOffset):[0,0];if(n.trigger=a.trigger,0===d.scrollTop()&&0===d.scrollLeft()||(e(".mCSB_"+n.idx+"_scrollbar").css("visibility","visible"),d.scrollTop(0).scrollLeft(0)),"_resetY"!==o||n.contentReset.y||(y("onOverflowYNone")&&i.callbacks.onOverflowYNone.call(t[0]),n.contentReset.y=1),"_resetX"!==o||n.contentReset.x||(y("onOverflowXNone")&&i.callbacks.onOverflowXNone.call(t[0]),n.contentReset.x=1),"_resetY"!==o&&"_resetX"!==o){if(!n.contentReset.y&&t[0].mcs||!n.overflowed[0]||(y("onOverflowY")&&i.callbacks.onOverflowY.call(t[0]),n.contentReset.x=null),!n.contentReset.x&&t[0].mcs||!n.overflowed[1]||(y("onOverflowX")&&i.callbacks.onOverflowX.call(t[0]),n.contentReset.x=null),i.snapAmount){var m=i.snapAmount instanceof Array?"x"===a.dir?i.snapAmount[1]:i.snapAmount[0]:i.snapAmount;o=function(e,t,o){return Math.round(e/t)*t-o}(o,m,i.snapOffset)}switch(a.dir){case"x":var h=e("#mCSB_"+n.idx+"_dragger_horizontal"),p="left",g=c[0].offsetLeft,v=[s.width()-c.outerWidth(!1),h.parent().width()-h.width()],x=[o,0===o?0:o/n.scrollRatio.x],S=u[1],_=f[1],C=S>0?S/n.scrollRatio.x:0,b=_>0?_/n.scrollRatio.x:0;break;case"y":var h=e("#mCSB_"+n.idx+"_dragger_vertical"),p="top",g=c[0].offsetTop,v=[s.height()-c.outerHeight(!1),h.parent().height()-h.height()],x=[o,0===o?0:o/n.scrollRatio.y],S=u[0],_=f[0],C=S>0?S/n.scrollRatio.y:0,b=_>0?_/n.scrollRatio.y:0}x[1]<0||0===x[0]&&0===x[1]?x=[0,0]:x[1]>=v[1]?x=[v[0],v[1]]:x[0]=-x[0],t[0].mcs||(B(),y("onInit")&&i.callbacks.onInit.call(t[0])),clearTimeout(c[0].onCompleteTimeout),V(h[0],p,Math.round(x[1]),l[1],a.scrollEasing),!n.tweenRunning&&(0===g&&x[0]>=0||g===v[0]&&x[0]<=v[0])||V(c[0],p,Math.round(x[0]),l[0],a.scrollEasing,a.overwrite,{onStart:function(){a.callbacks&&a.onStart&&!n.tweenRunning&&(y("onScrollStart")&&(B(),i.callbacks.onScrollStart.call(t[0])),n.tweenRunning=!0,w(h),n.cbOffsets=[i.callbacks.alwaysTriggerOffsets||g>=v[0]+S,i.callbacks.alwaysTriggerOffsets||g<=-_])},onUpdate:function(){a.callbacks&&a.onUpdate&&y("whileScrolling")&&(B(),i.callbacks.whileScrolling.call(t[0]))},onComplete:function(){if(a.callbacks&&a.onComplete){"yx"===i.axis&&clearTimeout(c[0].onCompleteTimeout);var e=c[0].idleTimer||0;c[0].onCompleteTimeout=setTimeout(function(){y("onScroll")&&(B(),i.callbacks.onScroll.call(t[0])),y("onTotalScroll")&&x[1]>=v[1]-C&&n.cbOffsets[0]&&(B(),i.callbacks.onTotalScroll.call(t[0])),y("onTotalScrollBack")&&x[1]<=b&&n.cbOffsets[1]&&(B(),i.callbacks.onTotalScrollBack.call(t[0])),n.tweenRunning=!1,c[0].idleTimer=0,w(h,"hide")},e)}}})}function y(e){return n&&i.callbacks[e]&&"function"==typeof i.callbacks[e]}function B(){var e=[c[0].offsetTop,c[0].offsetLeft],o=[h[0].offsetTop,h[0].offsetLeft],n=[c.outerHeight(!1),c.outerWidth(!1)],i=[s.height(),s.width()];t[0].mcs={content:c,top:e[0],left:e[1],draggerTop:o[0],draggerLeft:o[1],topPct:Math.round(100*Math.abs(e[0])/(Math.abs(n[0])-i[0])),leftPct:Math.round(100*Math.abs(e[1])/(Math.abs(n[1])-i[1])),direction:a.dir}}},V=function(e,t,o,a,n,i,r){e._mTween||(e._mTween={top:{},left:{}});var l,s,r=r||{},c=r.onStart||function(){},d=r.onUpdate||function(){},u=r.onComplete||function(){},f=Q(),m=0,h=e.offsetTop,p=e.style,g=e._mTween[t];"left"===t&&(h=e.offsetLeft);var v=o-h;function x(){g.stop||(m||c.call(),m=Q()-f,S(),m>=g.time&&(g.time=m>g.time?m+l-(m-g.time):m+l-1,g.time0?(g.currVal=function(e,t,o,a,n){switch(n){case"linear":case"mcsLinear":return o*e/a+t;case"mcsLinearOut":return e/=a,e--,o*Math.sqrt(1-e*e)+t;case"easeInOutSmooth":return(e/=a/2)<1?o/2*e*e+t:-o/2*(--e*(e-2)-1)+t;case"easeInOutStrong":return(e/=a/2)<1?o/2*Math.pow(2,10*(e-1))+t:(e--,o/2*(2-Math.pow(2,-10*e))+t);case"easeInOut":case"mcsEaseInOut":return(e/=a/2)<1?o/2*e*e*e+t:o/2*((e-=2)*e*e+2)+t;case"easeOutSmooth":return e/=a,-o*(--e*e*e*e-1)+t;case"easeOutStrong":return o*(1-Math.pow(2,-10*e/a))+t;case"easeOut":case"mcsEaseOut":default:var i=(e/=a)*e,r=i*e;return t+o*(.499999999999997*r*i+-2.5*i*i+5.5*r+-6.5*i+4*e)}}(g.time,h,v,a,n),p[t]=Math.round(g.currVal)+"px"):p[t]=o+"px",d.call()}g.stop=0,"none"!==i&&null!=g.id&&(window.requestAnimationFrame?window.cancelAnimationFrame(g.id):clearTimeout(g.id),g.id=null),l=1e3/60,g.time=m+l,s=window.requestAnimationFrame?window.requestAnimationFrame:function(e){return S(),setTimeout(e,.01)},g.id=s(x)},Q=function(){return window.performance&&window.performance.now?window.performance.now():window.performance&&window.performance.webkitNow?window.performance.webkitNow():Date.now?Date.now():(new Date).getTime()},G=function(){var e=this;e._mTween||(e._mTween={top:{},left:{}});for(var t=["top","left"],o=0;o=0&&a[0]+ee(n)[0]=0&&a[1]+ee(n)[1]=0&&r[1]-i[1]*l[1][0]<0&&r[1]+n[1]-i[1]*l[1][1]>=0},mcsOverflow:e.expr[":"].mcsOverflow||function(t){var o=e(t).data("mCS");if(o)return o.overflowed[0]||o.overflowed[1]}})})}); \ No newline at end of file diff --git a/src/js/lib/lenis.min.js b/src/js/lib/lenis.min.js new file mode 100644 index 0000000..5091289 --- /dev/null +++ b/src/js/lib/lenis.min.js @@ -0,0 +1,2 @@ +!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).Lenis=i()}(this,(function(){"use strict";function clamp(t,i,e){return Math.max(t,Math.min(i,e))}class Animate{constructor(){this.isRunning=!1,this.value=0,this.from=0,this.to=0,this.duration=0,this.currentTime=0}advance(t){var i;if(!this.isRunning)return;let e=!1;if(this.duration&&this.easing){this.currentTime+=t;const i=clamp(0,this.currentTime/this.duration,1);e=i>=1;const s=e?1:this.easing(i);this.value=this.from+(this.to-this.from)*s}else this.lerp?(this.value=function damp(t,i,e,s){return function lerp(t,i,e){return(1-e)*t+e*i}(t,i,1-Math.exp(-e*s))}(this.value,this.to,60*this.lerp,t),Math.round(this.value)===this.to&&(this.value=this.to,e=!0)):(this.value=this.to,e=!0);e&&this.stop(),null===(i=this.onUpdate)||void 0===i||i.call(this,this.value,e)}stop(){this.isRunning=!1}fromTo(t,i,{lerp:e,duration:s,easing:o,onStart:n,onUpdate:l}){this.from=this.value=t,this.to=i,this.lerp=e,this.duration=s,this.easing=o,this.currentTime=0,this.isRunning=!0,null==n||n(),this.onUpdate=l}}class Dimensions{constructor({wrapper:t,content:i,autoResize:e=!0,debounce:s=250}={}){this.width=0,this.height=0,this.scrollWidth=0,this.scrollHeight=0,this.resize=()=>{this.onWrapperResize(),this.onContentResize()},this.onWrapperResize=()=>{this.wrapper===window?(this.width=window.innerWidth,this.height=window.innerHeight):this.wrapper instanceof HTMLElement&&(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)},this.onContentResize=()=>{this.wrapper===window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):this.wrapper instanceof HTMLElement&&(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)},this.wrapper=t,this.content=i,e&&(this.debouncedResize=function debounce(t,i){let e;return function(){let s=arguments,o=this;clearTimeout(e),e=setTimeout((function(){t.apply(o,s)}),i)}}(this.resize,s),this.wrapper===window?window.addEventListener("resize",this.debouncedResize,!1):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){var t,i;null===(t=this.wrapperResizeObserver)||void 0===t||t.disconnect(),null===(i=this.contentResizeObserver)||void 0===i||i.disconnect(),window.removeEventListener("resize",this.debouncedResize,!1)}get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}}class Emitter{constructor(){this.events={}}emit(t,...i){let e=this.events[t]||[];for(let t=0,s=e.length;t{var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}}off(t,i){var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}destroy(){this.events={}}}const t=100/6;class VirtualScroll{constructor(i,{wheelMultiplier:e=1,touchMultiplier:s=1}){this.lastDelta={x:0,y:0},this.windowWidth=0,this.windowHeight=0,this.onTouchStart=t=>{const{clientX:i,clientY:e}=t.targetTouches?t.targetTouches[0]:t;this.touchStart.x=i,this.touchStart.y=e,this.lastDelta={x:0,y:0},this.emitter.emit("scroll",{deltaX:0,deltaY:0,event:t})},this.onTouchMove=t=>{var i,e,s,o;const{clientX:n,clientY:l}=t.targetTouches?t.targetTouches[0]:t,r=-(n-(null!==(e=null===(i=this.touchStart)||void 0===i?void 0:i.x)&&void 0!==e?e:0))*this.touchMultiplier,h=-(l-(null!==(o=null===(s=this.touchStart)||void 0===s?void 0:s.y)&&void 0!==o?o:0))*this.touchMultiplier;this.touchStart.x=n,this.touchStart.y=l,this.lastDelta={x:r,y:h},this.emitter.emit("scroll",{deltaX:r,deltaY:h,event:t})},this.onTouchEnd=t=>{this.emitter.emit("scroll",{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:t})},this.onWheel=i=>{let{deltaX:e,deltaY:s,deltaMode:o}=i;e*=1===o?t:2===o?this.windowWidth:1,s*=1===o?t:2===o?this.windowHeight:1,e*=this.wheelMultiplier,s*=this.wheelMultiplier,this.emitter.emit("scroll",{deltaX:e,deltaY:s,event:i})},this.onWindowResize=()=>{this.windowWidth=window.innerWidth,this.windowHeight=window.innerHeight},this.element=i,this.wheelMultiplier=e,this.touchMultiplier=s,this.touchStart={x:null,y:null},this.emitter=new Emitter,window.addEventListener("resize",this.onWindowResize,!1),this.onWindowResize(),this.element.addEventListener("wheel",this.onWheel,{passive:!1}),this.element.addEventListener("touchstart",this.onTouchStart,{passive:!1}),this.element.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.element.addEventListener("touchend",this.onTouchEnd,{passive:!1})}on(t,i){return this.emitter.on(t,i)}destroy(){this.emitter.destroy(),window.removeEventListener("resize",this.onWindowResize,!1),this.element.removeEventListener("wheel",this.onWheel),this.element.removeEventListener("touchstart",this.onTouchStart),this.element.removeEventListener("touchmove",this.onTouchMove),this.element.removeEventListener("touchend",this.onTouchEnd)}}return class Lenis{constructor({wrapper:t=window,content:i=document.documentElement,wheelEventsTarget:e=t,eventsTarget:s=e,smoothWheel:o=!0,syncTouch:n=!1,syncTouchLerp:l=.075,touchInertiaMultiplier:r=35,duration:h,easing:a=(t=>Math.min(1,1.001-Math.pow(2,-10*t))),lerp:c=.1,infinite:d=!1,orientation:u="vertical",gestureOrientation:p="vertical",touchMultiplier:m=1,wheelMultiplier:v=1,autoResize:g=!0,prevent:w,virtualScroll:S,__experimental__naiveDimensions:f=!1}={}){this.__isScrolling=!1,this.__isStopped=!1,this.__isLocked=!1,this.userData={},this.lastVelocity=0,this.velocity=0,this.direction=0,this.onPointerDown=t=>{1===t.button&&this.reset()},this.onVirtualScroll=t=>{if("function"==typeof this.options.virtualScroll&&!1===this.options.virtualScroll(t))return;const{deltaX:i,deltaY:e,event:s}=t;if(this.emitter.emit("virtual-scroll",{deltaX:i,deltaY:e,event:s}),s.ctrlKey)return;const o=s.type.includes("touch"),n=s.type.includes("wheel");this.isTouching="touchstart"===s.type||"touchmove"===s.type;if(this.options.syncTouch&&o&&"touchstart"===s.type&&!this.isStopped&&!this.isLocked)return void this.reset();const l=0===i&&0===e,r="vertical"===this.options.gestureOrientation&&0===e||"horizontal"===this.options.gestureOrientation&&0===i;if(l||r)return;let h=s.composedPath();h=h.slice(0,h.indexOf(this.rootElement));const a=this.options.prevent;if(h.find((t=>{var i,e,s,l,r;return t instanceof Element&&("function"==typeof a&&(null==a?void 0:a(t))||(null===(i=t.hasAttribute)||void 0===i?void 0:i.call(t,"data-lenis-prevent"))||o&&(null===(e=t.hasAttribute)||void 0===e?void 0:e.call(t,"data-lenis-prevent-touch"))||n&&(null===(s=t.hasAttribute)||void 0===s?void 0:s.call(t,"data-lenis-prevent-wheel"))||(null===(l=t.classList)||void 0===l?void 0:l.contains("lenis"))&&!(null===(r=t.classList)||void 0===r?void 0:r.contains("lenis-stopped")))})))return;if(this.isStopped||this.isLocked)return void s.preventDefault();if(!(this.options.syncTouch&&o||this.options.smoothWheel&&n))return this.isScrolling="native",void this.animate.stop();s.preventDefault();let c=e;"both"===this.options.gestureOrientation?c=Math.abs(e)>Math.abs(i)?e:i:"horizontal"===this.options.gestureOrientation&&(c=i);const d=o&&this.options.syncTouch,u=o&&"touchend"===s.type&&Math.abs(c)>5;u&&(c=this.velocity*this.options.touchInertiaMultiplier),this.scrollTo(this.targetScroll+c,Object.assign({programmatic:!1},d?{lerp:u?this.options.syncTouchLerp:1}:{lerp:this.options.lerp,duration:this.options.duration,easing:this.options.easing}))},this.onNativeScroll=()=>{if(clearTimeout(this.__resetVelocityTimeout),delete this.__resetVelocityTimeout,this.__preventNextNativeScrollEvent)delete this.__preventNextNativeScrollEvent;else if(!1===this.isScrolling||"native"===this.isScrolling){const t=this.animatedScroll;this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity,this.velocity=this.animatedScroll-t,this.direction=Math.sign(this.animatedScroll-t),this.isScrolling="native",this.emit(),0!==this.velocity&&(this.__resetVelocityTimeout=setTimeout((()=>{this.lastVelocity=this.velocity,this.velocity=0,this.isScrolling=!1,this.emit()}),400))}},window.lenisVersion="1.1.9",t&&t!==document.documentElement&&t!==document.body||(t=window),this.options={wrapper:t,content:i,wheelEventsTarget:e,eventsTarget:s,smoothWheel:o,syncTouch:n,syncTouchLerp:l,touchInertiaMultiplier:r,duration:h,easing:a,lerp:c,infinite:d,gestureOrientation:p,orientation:u,touchMultiplier:m,wheelMultiplier:v,autoResize:g,prevent:w,virtualScroll:S,__experimental__naiveDimensions:f},this.animate=new Animate,this.emitter=new Emitter,this.dimensions=new Dimensions({wrapper:t,content:i,autoResize:g}),this.updateClassName(),this.userData={},this.time=0,this.velocity=this.lastVelocity=0,this.isLocked=!1,this.isStopped=!1,this.isScrolling=!1,this.targetScroll=this.animatedScroll=this.actualScroll,this.options.wrapper.addEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.addEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll=new VirtualScroll(s,{touchMultiplier:m,wheelMultiplier:v}),this.virtualScroll.on("scroll",this.onVirtualScroll)}destroy(){this.emitter.destroy(),this.options.wrapper.removeEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.removeEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll.destroy(),this.dimensions.destroy(),this.cleanUpClassName()}on(t,i){return this.emitter.on(t,i)}off(t,i){return this.emitter.off(t,i)}setScroll(t){this.isHorizontal?this.rootElement.scrollLeft=t:this.rootElement.scrollTop=t}resize(){this.dimensions.resize()}emit(){this.emitter.emit("scroll",this)}reset(){this.isLocked=!1,this.isScrolling=!1,this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity=0,this.animate.stop()}start(){this.isStopped&&(this.isStopped=!1,this.reset())}stop(){this.isStopped||(this.isStopped=!0,this.animate.stop(),this.reset())}raf(t){const i=t-(this.time||t);this.time=t,this.animate.advance(.001*i)}scrollTo(t,{offset:i=0,immediate:e=!1,lock:s=!1,duration:o=this.options.duration,easing:n=this.options.easing,lerp:l=this.options.lerp,onStart:r,onComplete:h,force:a=!1,programmatic:c=!0,userData:d={}}={}){if(!this.isStopped&&!this.isLocked||a){if("string"==typeof t&&["top","left","start"].includes(t))t=0;else if("string"==typeof t&&["bottom","right","end"].includes(t))t=this.limit;else{let e;if("string"==typeof t?e=document.querySelector(t):t instanceof HTMLElement&&(null==t?void 0:t.nodeType)&&(e=t),e){if(this.options.wrapper!==window){const t=this.rootElement.getBoundingClientRect();i-=this.isHorizontal?t.left:t.top}const s=e.getBoundingClientRect();t=(this.isHorizontal?s.left:s.top)+this.animatedScroll}}if("number"==typeof t&&(t+=i,t=Math.round(t),this.options.infinite?c&&(this.targetScroll=this.animatedScroll=this.scroll):t=clamp(0,t,this.limit),t!==this.targetScroll)){if(this.userData=d,e)return this.animatedScroll=this.targetScroll=t,this.setScroll(this.scroll),this.reset(),this.preventNextNativeScrollEvent(),this.emit(),null==h||h(this),void(this.userData={});c||(this.targetScroll=t),this.animate.fromTo(this.animatedScroll,t,{duration:o,easing:n,lerp:l,onStart:()=>{s&&(this.isLocked=!0),this.isScrolling="smooth",null==r||r(this)},onUpdate:(t,i)=>{this.isScrolling="smooth",this.lastVelocity=this.velocity,this.velocity=t-this.animatedScroll,this.direction=Math.sign(this.velocity),this.animatedScroll=t,this.setScroll(this.scroll),c&&(this.targetScroll=t),i||this.emit(),i&&(this.reset(),this.emit(),null==h||h(this),this.userData={},this.preventNextNativeScrollEvent())}})}}}preventNextNativeScrollEvent(){this.__preventNextNativeScrollEvent=!0,requestAnimationFrame((()=>{delete this.__preventNextNativeScrollEvent}))}get rootElement(){return this.options.wrapper===window?document.documentElement:this.options.wrapper}get limit(){return this.options.__experimental__naiveDimensions?this.isHorizontal?this.rootElement.scrollWidth-this.rootElement.clientWidth:this.rootElement.scrollHeight-this.rootElement.clientHeight:this.dimensions.limit[this.isHorizontal?"x":"y"]}get isHorizontal(){return"horizontal"===this.options.orientation}get actualScroll(){return this.isHorizontal?this.rootElement.scrollLeft:this.rootElement.scrollTop}get scroll(){return this.options.infinite?function modulo(t,i){return(t%i+i)%i}(this.animatedScroll,this.limit):this.animatedScroll}get progress(){return 0===this.limit?1:this.scroll/this.limit}get isScrolling(){return this.__isScrolling}set isScrolling(t){this.__isScrolling!==t&&(this.__isScrolling=t,this.updateClassName())}get isStopped(){return this.__isStopped}set isStopped(t){this.__isStopped!==t&&(this.__isStopped=t,this.updateClassName())}get isLocked(){return this.__isLocked}set isLocked(t){this.__isLocked!==t&&(this.__isLocked=t,this.updateClassName())}get isSmooth(){return"smooth"===this.isScrolling}get className(){let t="lenis";return this.isStopped&&(t+=" lenis-stopped"),this.isLocked&&(t+=" lenis-locked"),this.isScrolling&&(t+=" lenis-scrolling"),"smooth"===this.isScrolling&&(t+=" lenis-smooth"),t}updateClassName(){this.cleanUpClassName(),this.rootElement.className=`${this.rootElement.className} ${this.className}`.trim()}cleanUpClassName(){this.rootElement.className=this.rootElement.className.replace(/lenis(-\w+)?/g,"").trim()}}})); +//# sourceMappingURL=lenis.min.js.map \ No newline at end of file diff --git a/src/js/lib/lottie.min.js b/src/js/lib/lottie.min.js new file mode 100644 index 0000000..3f1d013 --- /dev/null +++ b/src/js/lib/lottie.min.js @@ -0,0 +1,15 @@ +(typeof navigator !== "undefined") && (function(root, factory) { + if (typeof define === "function" && define.amd) { + define(function() { + return factory(root); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(root); + } else { + root.lottie = factory(root); + root.bodymovin = root.lottie; + } +}((window || {}), function(window) { + "use strict";var svgNS="http://www.w3.org/2000/svg",locationHref="",initialDefaultFrame=-999999,subframeEnabled=!0,expressionsPlugin,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),cachedColors={},bm_rounder=Math.round,bm_rnd,bm_pow=Math.pow,bm_sqrt=Math.sqrt,bm_abs=Math.abs,bm_floor=Math.floor,bm_max=Math.max,bm_min=Math.min,blitter=10,BMMath={};function ProjectInterface(){return{}}!function(){var t,e=["abs","acos","acosh","asin","asinh","atan","atanh","atan2","ceil","cbrt","expm1","clz32","cos","cosh","exp","floor","fround","hypot","imul","log","log1p","log2","log10","max","min","pow","random","round","sign","sin","sinh","sqrt","tan","tanh","trunc","E","LN10","LN2","LOG10E","LOG2E","PI","SQRT1_2","SQRT2"],r=e.length;for(t=0;t>>=1;return(t+r)/e};return n.int32=function(){return 0|a.g(4)},n.quick=function(){return a.g(4)/4294967296},n.double=n,E(x(a.S),o),(e.pass||r||function(t,e,r,i){return i&&(i.S&&b(i,a),t.state=function(){return b(a,{})}),r?(h[c]=t,e):t})(n,s,"global"in e?e.global:this==h,e.state)},E(h.random(),o)}([],BMMath);var BezierFactory=function(){var t={getBezierEasing:function(t,e,r,i,s){var a=s||("bez_"+t+"_"+e+"_"+r+"_"+i).replace(/\./g,"p");if(o[a])return o[a];var n=new h([t,e,r,i]);return o[a]=n}},o={};var l=11,p=1/(l-1),e="function"==typeof Float32Array;function i(t,e){return 1-3*e+3*t}function s(t,e){return 3*e-6*t}function a(t){return 3*t}function f(t,e,r){return((i(e,r)*t+s(e,r))*t+a(e))*t}function m(t,e,r){return 3*i(e,r)*t*t+2*s(e,r)*t+a(e)}function h(t){this._p=t,this._mSampleValues=e?new Float32Array(l):new Array(l),this._precomputed=!1,this.get=this.get.bind(this)}return h.prototype={get:function(t){var e=this._p[0],r=this._p[1],i=this._p[2],s=this._p[3];return this._precomputed||this._precompute(),e===r&&i===s?t:0===t?0:1===t?1:f(this._getTForX(t),r,s)},_precompute:function(){var t=this._p[0],e=this._p[1],r=this._p[2],i=this._p[3];this._precomputed=!0,t===e&&r===i||this._calcSampleValues()},_calcSampleValues:function(){for(var t=this._p[0],e=this._p[2],r=0;rn?-1:1,l=!0;l;)if(i[a]<=n&&i[a+1]>n?(o=(n-i[a])/(i[a+1]-i[a]),l=!1):a+=h,a<0||s-1<=a){if(a===s-1)return r[a];l=!1}return r[a]+(r[a+1]-r[a])*o}var D=createTypedArray("float32",8);return{getSegmentsLength:function(t){var e,r=segments_length_pool.newElement(),i=t.c,s=t.v,a=t.o,n=t.i,o=t._length,h=r.lengths,l=0;for(e=0;er[0]||!(r[0]>t[0])&&(t[1]>r[1]||!(r[1]>t[1])&&(t[2]>r[2]||!(r[2]>t[2])&&void 0))}var h,r=function(){var i=[4,4,14];function s(t){var e,r,i,s=t.length;for(e=0;e=a.t-i){s.h&&(s=a),m=0;break}if(a.t-i>t){m=c;break}c=r&&r<=t||this._caching.lastFrame=t&&(this._caching._lastKeyframeIndex=-1,this._caching.lastIndex=0);var i=this.interpolateValue(t,this._caching);this.pv=i}return this._caching.lastFrame=t,this.pv}function d(t){var e;if("unidimensional"===this.propType)e=t*this.mult,1e-5=this.p.keyframes[this.p.keyframes.length-1].t?(e=this.p.getValueAtTime(this.p.keyframes[this.p.keyframes.length-1].t/i,0),this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length-1].t-.05)/i,0)):(e=this.p.pv,this.p.getValueAtTime((this.p._caching.lastFrame+this.p.offsetTime-.01)/i,this.p.offsetTime));else if(this.px&&this.px.keyframes&&this.py.keyframes&&this.px.getValueAtTime&&this.py.getValueAtTime){e=[],r=[];var s=this.px,a=this.py;s._caching.lastFrame+s.offsetTime<=s.keyframes[0].t?(e[0]=s.getValueAtTime((s.keyframes[0].t+.01)/i,0),e[1]=a.getValueAtTime((a.keyframes[0].t+.01)/i,0),r[0]=s.getValueAtTime(s.keyframes[0].t/i,0),r[1]=a.getValueAtTime(a.keyframes[0].t/i,0)):s._caching.lastFrame+s.offsetTime>=s.keyframes[s.keyframes.length-1].t?(e[0]=s.getValueAtTime(s.keyframes[s.keyframes.length-1].t/i,0),e[1]=a.getValueAtTime(a.keyframes[a.keyframes.length-1].t/i,0),r[0]=s.getValueAtTime((s.keyframes[s.keyframes.length-1].t-.01)/i,0),r[1]=a.getValueAtTime((a.keyframes[a.keyframes.length-1].t-.01)/i,0)):(e=[s.pv,a.pv],r[0]=s.getValueAtTime((s._caching.lastFrame+s.offsetTime-.01)/i,s.offsetTime),r[1]=a.getValueAtTime((a._caching.lastFrame+a.offsetTime-.01)/i,a.offsetTime))}else e=r=n;this.v.rotate(-Math.atan2(e[1]-r[1],e[0]-r[0]))}this.data.p&&this.data.p.s?this.data.p.z?this.v.translate(this.px.v,this.py.v,-this.pz.v):this.v.translate(this.px.v,this.py.v,0):this.v.translate(this.p.v[0],this.p.v[1],-this.p.v[2])}this.frameId=this.elem.globalData.frameId}},precalculateMatrix:function(){if(!this.a.k&&(this.pre.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations=1,!this.s.effectsSequence.length)){if(this.pre.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.appliedTransformations=2,this.sk){if(this.sk.effectsSequence.length||this.sa.effectsSequence.length)return;this.pre.skewFromAxis(-this.sk.v,this.sa.v),this.appliedTransformations=3}if(this.r){if(this.r.effectsSequence.length)return;this.pre.rotate(-this.r.v),this.appliedTransformations=4}else this.rz.effectsSequence.length||this.ry.effectsSequence.length||this.rx.effectsSequence.length||this.or.effectsSequence.length||(this.pre.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.appliedTransformations=4)}},autoOrient:function(){}},extendPrototype([DynamicPropertyContainer],i),i.prototype.addDynamicProperty=function(t){this._addDynamicProperty(t),this.elem.addDynamicProperty(t),this._isDirty=!0},i.prototype._addDynamicProperty=DynamicPropertyContainer.prototype.addDynamicProperty,{getTransformProperty:function(t,e,r){return new i(t,e,r)}}}();function ShapePath(){this.c=!1,this._length=0,this._maxLength=8,this.v=createSizedArray(this._maxLength),this.o=createSizedArray(this._maxLength),this.i=createSizedArray(this._maxLength)}ShapePath.prototype.setPathData=function(t,e){this.c=t,this.setLength(e);for(var r=0;r=this._maxLength&&this.doubleArrayLength(),r){case"v":a=this.v;break;case"i":a=this.i;break;case"o":a=this.o}(!a[i]||a[i]&&!s)&&(a[i]=point_pool.newElement()),a[i][0]=t,a[i][1]=e},ShapePath.prototype.setTripleAt=function(t,e,r,i,s,a,n,o){this.setXYAt(t,e,"v",n,o),this.setXYAt(r,i,"o",n,o),this.setXYAt(s,a,"i",n,o)},ShapePath.prototype.reverse=function(){var t=new ShapePath;t.setPathData(this.c,this._length);var e=this.v,r=this.o,i=this.i,s=0;this.c&&(t.setTripleAt(e[0][0],e[0][1],i[0][0],i[0][1],r[0][0],r[0][1],0,!1),s=1);var a,n=this._length-1,o=this._length;for(a=s;a=c[c.length-1].t-this.offsetTime)i=c[c.length-1].s?c[c.length-1].s[0]:c[c.length-2].e[0],a=!0;else{for(var d,u,y=m,g=c.length-1,v=!0;v&&(d=c[y],!((u=c[y+1]).t-this.offsetTime>t));)y=u.t-this.offsetTime)p=1;else if(ti+r);else p=o.s*s<=i?0:(o.s*s-i)/r,f=o.e*s>=i+r?1:(o.e*s-i)/r,h.push([p,f])}return h.length||h.push([0,0]),h},TrimModifier.prototype.releasePathsData=function(t){var e,r=t.length;for(e=0;ee.e){r.c=!1;break}e.s<=d&&e.e>=d+n.addedLength?(this.addSegment(m[i].v[s-1],m[i].o[s-1],m[i].i[s],m[i].v[s],r,o,y),y=!1):(l=bez.getNewSegment(m[i].v[s-1],m[i].v[s],m[i].o[s-1],m[i].i[s],(e.s-d)/n.addedLength,(e.e-d)/n.addedLength,h[s-1]),this.addSegmentFromArray(l,r,o,y),y=!1,r.c=!1),d+=n.addedLength,o+=1}if(m[i].c&&h.length){if(n=h[s-1],d<=e.e){var g=h[s-1].addedLength;e.s<=d&&e.e>=d+g?(this.addSegment(m[i].v[s-1],m[i].o[s-1],m[i].i[0],m[i].v[0],r,o,y),y=!1):(l=bez.getNewSegment(m[i].v[s-1],m[i].v[0],m[i].o[s-1],m[i].i[0],(e.s-d)/g,(e.e-d)/g,h[s-1]),this.addSegmentFromArray(l,r,o,y),y=!1,r.c=!1)}else r.c=!1;d+=n.addedLength,o+=1}if(r._length&&(r.setXYAt(r.v[p][0],r.v[p][1],"i",p),r.setXYAt(r.v[r._length-1][0],r.v[r._length-1][1],"o",r._length-1)),d>e.e)break;i=d.length&&(f=0,d=u[m+=1]?u[m].points:E.v.c?u[m=f=0].points:(l-=h.partialLength,null)),d&&(c=h,y=(h=d[f]).partialLength));L=T[s].an/2-T[s].add,_.translate(-L,0,0)}else L=T[s].an/2-T[s].add,_.translate(-L,0,0),_.translate(-x[0]*T[s].an/200,-x[1]*V/100,0);for(T[s].l/2,w=0;we));)r+=1;return this.keysIndex!==r&&(this.keysIndex=r),this.data.d.k[this.keysIndex].s},TextProperty.prototype.buildFinalText=function(t){for(var e,r=FontManager.getCombinedCharacterCodes(),i=[],s=0,a=t.length;sthis.minimumFontSize&&D=u(o)&&(n=c(0,d(t-o<0?d(h,1)-(o-t):h-t,1))),a(n));return n*this.a.v},getValue:function(t){this.iterateDynamicProperties(),this._mdf=t||this._mdf,this._currentTextLength=this.elem.textProperty.currentData.l.length||0,t&&2===this.data.r&&(this.e.v=this._currentTextLength);var e=2===this.data.r?1:100/this.data.totalChars,r=this.o.v/e,i=this.s.v/e+r,s=this.e.v/e+r;if(st-this.layers[e].st&&this.buildItem(e),this.completeLayers=!!this.elements[e]&&this.completeLayers;this.checkPendingElements()},BaseRenderer.prototype.createItem=function(t){switch(t.ty){case 2:return this.createImage(t);case 0:return this.createComp(t);case 1:return this.createSolid(t);case 3:return this.createNull(t);case 4:return this.createShape(t);case 5:return this.createText(t);case 13:return this.createCamera(t)}return this.createNull(t)},BaseRenderer.prototype.createCamera=function(){throw new Error("You're using a 3d camera. Try the html renderer.")},BaseRenderer.prototype.buildAllItems=function(){var t,e=this.layers.length;for(t=0;t=t)return this.threeDElements[e].perspectiveElem;e+=1}},HybridRenderer.prototype.createThreeDContainer=function(t,e){var r=createTag("div");styleDiv(r);var i=createTag("div");styleDiv(i),"3d"===e&&(r.style.width=this.globalData.compSize.w+"px",r.style.height=this.globalData.compSize.h+"px",r.style.transformOrigin=r.style.mozTransformOrigin=r.style.webkitTransformOrigin="50% 50%",i.style.transform=i.style.webkitTransform="matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)"),r.appendChild(i);var s={container:i,perspectiveElem:r,startPos:t,endPos:t,type:e};return this.threeDElements.push(s),s},HybridRenderer.prototype.build3dContainers=function(){var t,e,r=this.layers.length,i="";for(t=0;tt?!0!==this.isInRange&&(this.globalData._mdf=!0,this._mdf=!0,this.isInRange=!0,this.show()):!1!==this.isInRange&&(this.globalData._mdf=!0,this.isInRange=!1,this.hide())},renderRenderable:function(){var t,e=this.renderableComponents.length;for(t=0;t=t.x+t.width&&this.currentBBox.height+this.currentBBox.y>=t.y+t.height},HShapeElement.prototype.renderInnerContent=function(){if(this._renderShapeFrame(),!this.hidden&&(this._isFirstFrame||this._mdf)){var t=this.tempBoundingBox,e=999999;if(t.x=e,t.xMax=-e,t.y=e,t.yMax=-e,this.calculateBoundingBox(this.itemsData,t),t.width=t.xMaxthis.animationData.op&&(this.animationData.op=t.op,this.totalFrames=Math.floor(t.op-this.animationData.ip));var e,r,i=this.animationData.layers,s=i.length,a=t.layers,n=a.length;for(r=0;rthis.timeCompleted&&(this.currentFrame=this.timeCompleted),this.trigger("enterFrame"),this.renderFrame()},AnimationItem.prototype.renderFrame=function(){if(!1!==this.isLoaded)try{this.renderer.renderFrame(this.currentFrame+this.firstFrame)}catch(t){this.triggerRenderFrameError(t)}},AnimationItem.prototype.play=function(t){t&&this.name!=t||!0===this.isPaused&&(this.isPaused=!1,this._idle&&(this._idle=!1,this.trigger("_active")))},AnimationItem.prototype.pause=function(t){t&&this.name!=t||!1===this.isPaused&&(this.isPaused=!0,this._idle=!0,this.trigger("_idle"))},AnimationItem.prototype.togglePause=function(t){t&&this.name!=t||(!0===this.isPaused?this.play():this.pause())},AnimationItem.prototype.stop=function(t){t&&this.name!=t||(this.pause(),this.playCount=0,this._completedLoop=!1,this.setCurrentRawFrameValue(0))},AnimationItem.prototype.goToAndStop=function(t,e,r){r&&this.name!=r||(e?this.setCurrentRawFrameValue(t):this.setCurrentRawFrameValue(t*this.frameModifier),this.pause())},AnimationItem.prototype.goToAndPlay=function(t,e,r){this.goToAndStop(t,e,r),this.play()},AnimationItem.prototype.advanceTime=function(t){if(!0!==this.isPaused&&!1!==this.isLoaded){var e=this.currentRawFrame+t*this.frameModifier,r=!1;e>=this.totalFrames-1&&0=this.totalFrames?(this.playCount+=1,this.checkSegments(e%this.totalFrames)||(this.setCurrentRawFrameValue(e%this.totalFrames),this._completedLoop=!0,this.trigger("loopComplete"))):this.setCurrentRawFrameValue(e):this.checkSegments(e>this.totalFrames?e%this.totalFrames:0)||(r=!0,e=this.totalFrames-1):e<0?this.checkSegments(e%this.totalFrames)||(!this.loop||this.playCount--<=0&&!0!==this.loop?(r=!0,e=0):(this.setCurrentRawFrameValue(this.totalFrames+e%this.totalFrames),this._completedLoop?this.trigger("loopComplete"):this._completedLoop=!0)):this.setCurrentRawFrameValue(e),r&&(this.setCurrentRawFrameValue(e),this.pause(),this.trigger("complete"))}},AnimationItem.prototype.adjustSegment=function(t,e){this.playCount=0,t[1]t[0]&&(this.frameModifier<0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(1)),this.timeCompleted=this.totalFrames=t[1]-t[0],this.firstFrame=t[0],this.setCurrentRawFrameValue(.001+e)),this.trigger("segmentStart")},AnimationItem.prototype.setSegment=function(t,e){var r=-1;this.isPaused&&(this.currentRawFrame+this.firstFramee&&(r=e-t)),this.firstFrame=t,this.timeCompleted=this.totalFrames=e-t,-1!==r&&this.goToAndStop(r,!0)},AnimationItem.prototype.playSegments=function(t,e){if(e&&(this.segments.length=0),"object"==typeof t[0]){var r,i=t.length;for(r=0;rdata.k[e].t&&tdata.k[e+1].t-t?(r=e+2,data.k[e+1].t):(r=e+1,data.k[e].t);break}}-1===r&&(r=e+1,i=data.k[e].t)}else i=r=0;var a={};return a.index=r,a.time=i/elem.comp.globalData.frameRate,a}function key(t){var e,r,i;if(!data.k.length||"number"==typeof data.k[0])throw new Error("The property has no keyframe at index "+t);t-=1,e={time:data.k[t].t/elem.comp.globalData.frameRate,value:[]};var s=data.k[t].hasOwnProperty("s")?data.k[t].s:data.k[t-1].e;for(i=s.length,r=0;rl.length-1)&&(e=l.length-1),i=p-(s=l[l.length-1-e].t)),"pingpong"===t){if(Math.floor((h-s)/i)%2!=0)return this.getValueAtTime((i-(h-s)%i+s)/this.comp.globalData.frameRate,0)}else{if("offset"===t){var f=this.getValueAtTime(s/this.comp.globalData.frameRate,0),m=this.getValueAtTime(p/this.comp.globalData.frameRate,0),c=this.getValueAtTime(((h-s)%i+s)/this.comp.globalData.frameRate,0),d=Math.floor((h-s)/i);if(this.pv.length){for(n=(o=new Array(f.length)).length,a=0;al.length-1)&&(e=l.length-1),i=(s=l[e].t)-p),"pingpong"===t){if(Math.floor((p-h)/i)%2==0)return this.getValueAtTime(((p-h)%i+p)/this.comp.globalData.frameRate,0)}else{if("offset"===t){var f=this.getValueAtTime(p/this.comp.globalData.frameRate,0),m=this.getValueAtTime(s/this.comp.globalData.frameRate,0),c=this.getValueAtTime((i-(p-h)%i+p)/this.comp.globalData.frameRate,0),d=Math.floor((p-h)/i)+1;if(this.pv.length){for(n=(o=new Array(f.length)).length,a=0;an){var p=o,f=r.c&&o===h-1?0:o+1,m=(n-l)/a[o].addedLength;i=bez.getPointInSegment(r.v[p],r.v[f],r.o[p],r.i[f],m,a[o]);break}l+=a[o].addedLength,o+=1}return i||(i=r.c?[r.v[0][0],r.v[0][1]]:[r.v[r._length-1][0],r.v[r._length-1][1]]),i},vectorOnPath:function(t,e,r){t=1==t?this.v.c?0:.999:t;var i=this.pointOnPath(t,e),s=this.pointOnPath(t+.001,e),a=s[0]-i[0],n=s[1]-i[1],o=Math.sqrt(Math.pow(a,2)+Math.pow(n,2));return 0===o?[0,0]:"tangent"===r?[a/o,n/o]:[-n/o,a/o]},tangentOnPath:function(t,e){return this.vectorOnPath(t,e,"tangent")},normalOnPath:function(t,e){return this.vectorOnPath(t,e,"normal")},setGroupProperty:expressionHelpers.setGroupProperty,getValueAtTime:expressionHelpers.getStaticValueAtTime},extendPrototype([r],t),extendPrototype([r],e),e.prototype.getValueAtTime=function(t){return this._cachingAtTime||(this._cachingAtTime={shapeValue:shape_pool.clone(this.pv),lastIndex:0,lastTime:initialDefaultFrame}),t*=this.elem.globalData.frameRate,(t-=this.offsetTime)!==this._cachingAtTime.lastTime&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastTime=Math.abs(r)?t:r}function O(){(Ae=Ce.core.globals().ScrollTrigger)&&Ae.core&&function _integrate(){var e=Ae.core,r=e.bridge||{},t=e._scrollers,n=e._proxies;t.push.apply(t,Ie),n.push.apply(n,Le),Ie=t,Le=n,i=function _bridge(e,t){return r[e](t)}}()}function P(e){return Ce=e||r(),!Te&&Ce&&"undefined"!=typeof document&&document.body&&(Se=window,Pe=(ke=document).documentElement,Me=ke.body,t=[Se,ke,Pe,Me],Ce.utils.clamp,Be=Ce.core.context||function(){},Oe="onpointerenter"in Me?"pointer":"mouse",Ee=k.isTouch=Se.matchMedia&&Se.matchMedia("(hover: none), (pointer: coarse)").matches?1:"ontouchstart"in Se||0=o,n=Math.abs(t)>=o;S&&(r||n)&&S(se,e,t,me,ye),r&&(m&&0Math.abs(t)?"x":"y",ie=!0),"y"!==ae&&(me[2]+=e,se._vx.update(e,!0)),"x"!==ae&&(ye[2]+=t,se._vy.update(t,!0)),n?ee=ee||requestAnimationFrame(ff):ff()}function jf(e){if(!df(e,1)){var t=(e=M(e,s)).clientX,r=e.clientY,n=t-se.x,o=r-se.y,i=se.isDragging;se.x=t,se.y=r,(i||Math.abs(se.startX-t)>=a||Math.abs(se.startY-r)>=a)&&(h&&(re=!0),i||(se.isDragging=!0),hf(n,o),i||p&&p(se))}}function mf(e){return e.touches&&1=e)return a[n];return a[n-1]}for(n=a.length,e+=r;n--;)if(a[n]<=e)return a[n];return a[0]}:function(e,t,r){void 0===r&&(r=.001);var n=i(e);return!t||Math.abs(n-e)r&&(n*=t/100),e=e.substr(0,r-1)),e=n+(e in H?H[e]*t:~e.indexOf("%")?parseFloat(e)*t/100:parseFloat(e)||0)}return e}function Db(e,t,r,n,o,i,a,s){var l=o.startColor,c=o.endColor,u=o.fontSize,f=o.indent,d=o.fontWeight,p=Xe.createElement("div"),g=La(r)||"fixed"===z(r,"pinType"),h=-1!==e.indexOf("scroller"),v=g?We:r,b=-1!==e.indexOf("start"),m=b?l:c,y="border-color:"+m+";font-size:"+u+";color:"+m+";font-weight:"+d+";pointer-events:none;white-space:nowrap;font-family:sans-serif,Arial;z-index:1000;padding:4px 8px;border-width:0;border-style:solid;";return y+="position:"+((h||s)&&g?"fixed;":"absolute;"),!h&&!s&&g||(y+=(n===Fe?q:I)+":"+(i+parseFloat(f))+"px;"),a&&(y+="box-sizing:border-box;text-align:left;width:"+a.offsetWidth+"px;"),p._isStart=b,p.setAttribute("class","gsap-marker-"+e+(t?" marker-"+t:"")),p.style.cssText=y,p.innerText=t||0===t?e+"-"+t:e,v.children[0]?v.insertBefore(p,v.children[0]):v.appendChild(p),p._offset=p["offset"+n.op.d2],X(p,0,n,b),p}function Ib(){return 34We.clientWidth)||(Ie.cache++,v?D=D||requestAnimationFrame(Z):Z(),st||U("scrollStart"),st=at())}function Kb(){y=Ne.innerWidth,m=Ne.innerHeight}function Lb(){Ie.cache++,je||h||Xe.fullscreenElement||Xe.webkitFullscreenElement||b&&y===Ne.innerWidth&&!(Math.abs(Ne.innerHeight-m)>.25*Ne.innerHeight)||c.restart(!0)}function Ob(){return xb(ne,"scrollEnd",Ob)||Pt(!0)}function Rb(e){for(var t=0;tt,n=e._startClamp&&e.start>=t;(r||n)&&e.setPositions(n?t-1:e.start,r?Math.max(n?t:e.start+1,t):e.end,!0)}),Zb(!1),et=0,r.forEach(function(e){return e&&e.render&&e.render(-1)}),Ie.forEach(function(e){Ta(e)&&(e.smooth&&requestAnimationFrame(function(){return e.target.style.scrollBehavior="smooth"}),e.rec&&e(e.rec))}),Tb(w,1),c.pause(),kt++,Z(rt=2),Tt.forEach(function(e){return Ta(e.vars.onRefresh)&&e.vars.onRefresh(e)}),rt=ne.isRefreshing=!1,U("refresh")}else wb(ne,"scrollEnd",Ob)},Q=0,Mt=1,Z=function _updateAll(e){if(2===e||!rt&&!S){ne.isUpdating=!0,ot&&ot.update(0);var t=Tt.length,r=at(),n=50<=r-R,o=t&&Tt[0].scroll();if(Mt=o=Qa(be,he)){if(ie&&Ae()&&!de)for(i=ie.parentNode;i&&i!==We;)i._pinOffset&&(B-=i._pinOffset,q-=i._pinOffset),i=i.parentNode}else o=mb(ae),s=he===Fe,a=Ae(),G=parseFloat(j(he.a))+_,!y&&1=q})},Te.update=function(e,t,r){if(!de||r||e){var n,o,i,a,s,l,c,u=!0===rt?re:Te.scroll(),f=e?0:(u-B)/N,d=f<0?0:1u+(u-R)/(at()-Ke)*M&&(d=.9999)),d!==p&&Te.enabled){if(a=(s=(n=Te.isActive=!!d&&d<1)!=(!!p&&p<1))||!!d!=!!p,Te.direction=p=Qa(be,he),fe)if(e||!n&&!l)oc(ae,U);else{var g=wt(ae,!0),h=u-B;oc(ae,We,g.top+(he===Fe?h:0)+xt,g.left+(he===Fe?0:h)+xt)}Et(n||l?W:V),$&&d<1&&n||b(G+(1!==d||l?0:Q))}}else b(Ia(G+Q*d));!ue||A.tween||je||it||te.restart(!0),S&&(s||ce&&d&&(d<1||!tt))&&Ve(S.targets).forEach(function(e){return e.classList[n||ce?"add":"remove"](S.className)}),!T||ve||e||T(Te),a&&!je?(ve&&(c&&("complete"===i?O.pause().totalProgress(1):"reset"===i?O.restart(!0).pause():"restart"===i?O.restart(!0):O[i]()),T&&T(Te)),!s&&tt||(k&&s&&Xa(Te,k),xe[o]&&Xa(Te,xe[o]),ce&&(1===d?Te.kill(!1,1):xe[o]=0),s||xe[o=1===d?1:3]&&Xa(Te,xe[o])),pe&&!n&&Math.abs(Te.getVelocity())>(Ua(pe)?pe:2500)&&(Wa(Te.callbackAnimation),ee?ee.progress(1):Wa(O,"reverse"===i?1:!d,1))):ve&&T&&!je&&T(Te)}if(x){var v=de?u/de.duration()*(de._caScrollDist||0):u;y(v+(Y._isFlipped?1:0)),x(v)}C&&C(-u/de.duration()*(de._caScrollDist||0))}},Te.enable=function(e,t){Te.enabled||(Te.enabled=!0,wb(be,"resize",Lb),me||wb(be,"scroll",Jb),Se&&wb(ScrollTrigger,"refreshInit",Se),!1!==e&&(Te.progress=Oe=0,D=R=Me=Ae()),!1!==t&&Te.refresh())},Te.getTween=function(e){return e&&A?A.tween:ee},Te.setPositions=function(e,t,r,n){if(de){var o=de.scrollTrigger,i=de.duration(),a=o.end-o.start;e=o.start+a*e/i,t=o.start+a*t/i}Te.refresh(!1,!1,{start:Da(e,r&&!!Te._startClamp),end:Da(t,r&&!!Te._endClamp)},n),Te.update()},Te.adjustPinSpacing=function(e){if(Z&&e){var t=Z.indexOf(he.d)+1;Z[t]=parseFloat(Z[t])+e+xt,Z[1]=parseFloat(Z[1])+e+xt,Et(Z)}},Te.disable=function(e,t){if(Te.enabled&&(!1!==e&&Te.revert(!0,!0),Te.enabled=Te.isActive=!1,t||ee&&ee.pause(),re=0,n&&(n.uncache=1),Se&&xb(ScrollTrigger,"refreshInit",Se),te&&(te.pause(),A.tween&&A.tween.kill()&&(A.tween=0)),!me)){for(var r=Tt.length;r--;)if(Tt[r].scroller===be&&Tt[r]!==Te)return;xb(be,"resize",Lb),me||xb(be,"scroll",Jb)}},Te.kill=function(e,t){Te.disable(e,t),ee&&!t&&ee.kill(),a&&delete St[a];var r=Tt.indexOf(Te);0<=r&&Tt.splice(r,1),r===Qe&&0i&&(b()>i?a.progress(1)&&b(i):a.resetTo("scrollY",i))}Va(e)||(e={}),e.preventDefault=e.isNormalizer=e.allowClicks=!0,e.type||(e.type="wheel,touch"),e.debounce=!!e.debounce,e.id=e.id||"normalizer";var n,i,l,o,a,c,u,s,f=e.normalizeScrollX,t=e.momentum,r=e.allowNestedScroll,d=e.onRelease,p=J(e.target)||Je,g=He.core.globals().ScrollSmoother,h=g&&g.get(),v=E&&(e.content&&J(e.content)||h&&!1!==e.content&&!h.smooth()&&h.content()),b=K(p,Fe),m=K(p,Ye),y=1,x=(k.isTouch&&Ne.visualViewport?Ne.visualViewport.scale*Ne.visualViewport.width:Ne.outerWidth)/Ne.innerWidth,w=0,_=Ta(t)?function(){return t(n)}:function(){return t||2.8},C=xc(p,e.type,!0,r),T=Ha,S=Ha;return v&&He.set(v,{y:"+=0"}),e.ignoreCheck=function(e){return E&&"touchmove"===e.type&&function ignoreDrag(){if(o){requestAnimationFrame(zq);var e=Ia(n.deltaY/2),t=S(b.v-e);if(v&&t!==b.v+b.offset){b.offset=t-b.v;var r=Ia((parseFloat(v&&v._gsap.y)||0)-b.offset);v.style.transform="matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, "+r+", 0, 1)",v._gsap.y=r+"px",b.cacheID=Ie.cache,Z()}return!0}b.offset&&Dq(),o=!0}()||1.05=i||i-1<=r)&&He.to({},{onUpdate:Jq,duration:o})}else s.restart(!0);d&&d(e)},e.onWheel=function(){a._ts&&a.pause(),1e3i.indexOf(e)<0)).forEach((i=>{void 0===s[i]?s[i]=a[i]:e(a[i])&&e(s[i])&&Object.keys(a[i]).length>0&&t(s[i],a[i])}))}const s={body:{},addEventListener(){},removeEventListener(){},activeElement:{blur(){},nodeName:""},querySelector:()=>null,querySelectorAll:()=>[],getElementById:()=>null,createEvent:()=>({initEvent(){}}),createElement:()=>({children:[],childNodes:[],style:{},setAttribute(){},getElementsByTagName:()=>[]}),createElementNS:()=>({}),importNode:()=>null,location:{hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",protocol:"",search:""}};function a(){const e="undefined"!=typeof document?document:{};return t(e,s),e}const i={document:s,navigator:{userAgent:""},location:{hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",protocol:"",search:""},history:{replaceState(){},pushState(){},go(){},back(){}},CustomEvent:function(){return this},addEventListener(){},removeEventListener(){},getComputedStyle:()=>({getPropertyValue:()=>""}),Image(){},Date(){},screen:{},setTimeout(){},clearTimeout(){},matchMedia:()=>({}),requestAnimationFrame:e=>"undefined"==typeof setTimeout?(e(),null):setTimeout(e,0),cancelAnimationFrame(e){"undefined"!=typeof setTimeout&&clearTimeout(e)}};function r(){const e="undefined"!=typeof window?window:{};return t(e,i),e}function n(e){return void 0===e&&(e=""),e.trim().split(" ").filter((e=>!!e.trim()))}function l(e,t){return void 0===t&&(t=0),setTimeout(e,t)}function o(){return Date.now()}function d(e,t){void 0===t&&(t="x");const s=r();let a,i,n;const l=function(e){const t=r();let s;return t.getComputedStyle&&(s=t.getComputedStyle(e,null)),!s&&e.currentStyle&&(s=e.currentStyle),s||(s=e.style),s}(e);return s.WebKitCSSMatrix?(i=l.transform||l.webkitTransform,i.split(",").length>6&&(i=i.split(", ").map((e=>e.replace(",","."))).join(", ")),n=new s.WebKitCSSMatrix("none"===i?"":i)):(n=l.MozTransform||l.OTransform||l.MsTransform||l.msTransform||l.transform||l.getPropertyValue("transform").replace("translate(","matrix(1, 0, 0, 1,"),a=n.toString().split(",")),"x"===t&&(i=s.WebKitCSSMatrix?n.m41:16===a.length?parseFloat(a[12]):parseFloat(a[4])),"y"===t&&(i=s.WebKitCSSMatrix?n.m42:16===a.length?parseFloat(a[13]):parseFloat(a[5])),i||0}function c(e){return"object"==typeof e&&null!==e&&e.constructor&&"Object"===Object.prototype.toString.call(e).slice(8,-1)}function p(){const e=Object(arguments.length<=0?void 0:arguments[0]),t=["__proto__","constructor","prototype"];for(let a=1;at.indexOf(e)<0));for(let t=0,a=s.length;tn?"next":"prev",p=(e,t)=>"next"===c&&e>=t||"prev"===c&&e<=t,u=()=>{l=(new Date).getTime(),null===o&&(o=l);const e=Math.max(Math.min((l-o)/d,1),0),r=.5-Math.cos(e*Math.PI)/2;let c=n+r*(s-n);if(p(c,s)&&(c=s),t.wrapperEl.scrollTo({[a]:c}),p(c,s))return t.wrapperEl.style.overflow="hidden",t.wrapperEl.style.scrollSnapType="",setTimeout((()=>{t.wrapperEl.style.overflow="",t.wrapperEl.scrollTo({[a]:c})})),void i.cancelAnimationFrame(t.cssModeFrameID);t.cssModeFrameID=i.requestAnimationFrame(u)};u()}function h(e){return e.querySelector(".swiper-slide-transform")||e.shadowRoot&&e.shadowRoot.querySelector(".swiper-slide-transform")||e}function f(e,t){void 0===t&&(t="");const s=r(),a=[...e.children];return s.HTMLSlotElement&&e instanceof HTMLSlotElement&&a.push(...e.assignedElements()),t?a.filter((e=>e.matches(t))):a}function g(e){try{return void console.warn(e)}catch(e){}}function v(e,t){void 0===t&&(t=[]);const s=document.createElement(e);return s.classList.add(...Array.isArray(t)?t:n(t)),s}function w(e){const t=r(),s=a(),i=e.getBoundingClientRect(),n=s.body,l=e.clientTop||n.clientTop||0,o=e.clientLeft||n.clientLeft||0,d=e===t?t.scrollY:e.scrollTop,c=e===t?t.scrollX:e.scrollLeft;return{top:i.top+d-l,left:i.left+c-o}}function b(e,t){return r().getComputedStyle(e,null).getPropertyValue(t)}function y(e){let t,s=e;if(s){for(t=0;null!==(s=s.previousSibling);)1===s.nodeType&&(t+=1);return t}}function E(e,t){const s=[];let a=e.parentElement;for(;a;)t?a.matches(t)&&s.push(a):s.push(a),a=a.parentElement;return s}function x(e,t){t&&e.addEventListener("transitionend",(function s(a){a.target===e&&(t.call(e,a),e.removeEventListener("transitionend",s))}))}function S(e,t,s){const a=r();return s?e["width"===t?"offsetWidth":"offsetHeight"]+parseFloat(a.getComputedStyle(e,null).getPropertyValue("width"===t?"margin-right":"margin-top"))+parseFloat(a.getComputedStyle(e,null).getPropertyValue("width"===t?"margin-left":"margin-bottom")):e.offsetWidth}function T(e){return(Array.isArray(e)?e:[e]).filter((e=>!!e))}function M(e){return t=>Math.abs(t)>0&&e.browser&&e.browser.need3dFix&&Math.abs(t)%90==0?t+.001:t}let C,P,L;function I(){return C||(C=function(){const e=r(),t=a();return{smoothScroll:t.documentElement&&t.documentElement.style&&"scrollBehavior"in t.documentElement.style,touch:!!("ontouchstart"in e||e.DocumentTouch&&t instanceof e.DocumentTouch)}}()),C}function z(e){return void 0===e&&(e={}),P||(P=function(e){let{userAgent:t}=void 0===e?{}:e;const s=I(),a=r(),i=a.navigator.platform,n=t||a.navigator.userAgent,l={ios:!1,android:!1},o=a.screen.width,d=a.screen.height,c=n.match(/(Android);?[\s\/]+([\d.]+)?/);let p=n.match(/(iPad).*OS\s([\d_]+)/);const u=n.match(/(iPod)(.*OS\s([\d_]+))?/),m=!p&&n.match(/(iPhone\sOS|iOS)\s([\d_]+)/),h="Win32"===i;let f="MacIntel"===i;return!p&&f&&s.touch&&["1024x1366","1366x1024","834x1194","1194x834","834x1112","1112x834","768x1024","1024x768","820x1180","1180x820","810x1080","1080x810"].indexOf(`${o}x${d}`)>=0&&(p=n.match(/(Version)\/([\d.]+)/),p||(p=[0,1,"13_0_0"]),f=!1),c&&!h&&(l.os="android",l.android=!0),(p||m||u)&&(l.os="ios",l.ios=!0),l}(e)),P}function A(){return L||(L=function(){const e=r(),t=z();let s=!1;function a(){const t=e.navigator.userAgent.toLowerCase();return t.indexOf("safari")>=0&&t.indexOf("chrome")<0&&t.indexOf("android")<0}if(a()){const t=String(e.navigator.userAgent);if(t.includes("Version/")){const[e,a]=t.split("Version/")[1].split(" ")[0].split(".").map((e=>Number(e)));s=e<16||16===e&&a<2}}const i=/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(e.navigator.userAgent),n=a();return{isSafari:s||n,needPerspectiveFix:s,need3dFix:n||i&&t.ios,isWebView:i}}()),L}var $={on(e,t,s){const a=this;if(!a.eventsListeners||a.destroyed)return a;if("function"!=typeof t)return a;const i=s?"unshift":"push";return e.split(" ").forEach((e=>{a.eventsListeners[e]||(a.eventsListeners[e]=[]),a.eventsListeners[e][i](t)})),a},once(e,t,s){const a=this;if(!a.eventsListeners||a.destroyed)return a;if("function"!=typeof t)return a;function i(){a.off(e,i),i.__emitterProxy&&delete i.__emitterProxy;for(var s=arguments.length,r=new Array(s),n=0;n=0&&t.eventsAnyListeners.splice(s,1),t},off(e,t){const s=this;return!s.eventsListeners||s.destroyed?s:s.eventsListeners?(e.split(" ").forEach((e=>{void 0===t?s.eventsListeners[e]=[]:s.eventsListeners[e]&&s.eventsListeners[e].forEach(((a,i)=>{(a===t||a.__emitterProxy&&a.__emitterProxy===t)&&s.eventsListeners[e].splice(i,1)}))})),s):s},emit(){const e=this;if(!e.eventsListeners||e.destroyed)return e;if(!e.eventsListeners)return e;let t,s,a;for(var i=arguments.length,r=new Array(i),n=0;n{e.eventsAnyListeners&&e.eventsAnyListeners.length&&e.eventsAnyListeners.forEach((e=>{e.apply(a,[t,...s])})),e.eventsListeners&&e.eventsListeners[t]&&e.eventsListeners[t].forEach((e=>{e.apply(a,s)}))})),e}};const k=(e,t,s)=>{t&&!e.classList.contains(s)?e.classList.add(s):!t&&e.classList.contains(s)&&e.classList.remove(s)};const O=(e,t,s)=>{t&&!e.classList.contains(s)?e.classList.add(s):!t&&e.classList.contains(s)&&e.classList.remove(s)};const D=(e,t)=>{if(!e||e.destroyed||!e.params)return;const s=t.closest(e.isElement?"swiper-slide":`.${e.params.slideClass}`);if(s){let t=s.querySelector(`.${e.params.lazyPreloaderClass}`);!t&&e.isElement&&(s.shadowRoot?t=s.shadowRoot.querySelector(`.${e.params.lazyPreloaderClass}`):requestAnimationFrame((()=>{s.shadowRoot&&(t=s.shadowRoot.querySelector(`.${e.params.lazyPreloaderClass}`),t&&t.remove())}))),t&&t.remove()}},G=(e,t)=>{if(!e.slides[t])return;const s=e.slides[t].querySelector('[loading="lazy"]');s&&s.removeAttribute("loading")},X=e=>{if(!e||e.destroyed||!e.params)return;let t=e.params.lazyPreloadPrevNext;const s=e.slides.length;if(!s||!t||t<0)return;t=Math.min(t,s);const a="auto"===e.params.slidesPerView?e.slidesPerViewDynamic():Math.ceil(e.params.slidesPerView),i=e.activeIndex;if(e.params.grid&&e.params.grid.rows>1){const s=i,r=[s-t];return r.push(...Array.from({length:t}).map(((e,t)=>s+a+t))),void e.slides.forEach(((t,s)=>{r.includes(t.column)&&G(e,s)}))}const r=i+a-1;if(e.params.rewind||e.params.loop)for(let a=i-t;a<=r+t;a+=1){const t=(a%s+s)%s;(tr)&&G(e,t)}else for(let a=Math.max(i-t,0);a<=Math.min(r+t,s-1);a+=1)a!==i&&(a>r||a=0?x=parseFloat(x.replace("%",""))/100*r:"string"==typeof x&&(x=parseFloat(x)),e.virtualSize=-x,c.forEach((e=>{n?e.style.marginLeft="":e.style.marginRight="",e.style.marginBottom="",e.style.marginTop=""})),s.centeredSlides&&s.cssMode&&(u(a,"--swiper-centered-offset-before",""),u(a,"--swiper-centered-offset-after",""));const P=s.grid&&s.grid.rows>1&&e.grid;let L;P?e.grid.initSlides(c):e.grid&&e.grid.unsetSlides();const I="auto"===s.slidesPerView&&s.breakpoints&&Object.keys(s.breakpoints).filter((e=>void 0!==s.breakpoints[e].slidesPerView)).length>0;for(let a=0;a1&&m.push(e.virtualSize-r)}if(o&&s.loop){const t=g[0]+x;if(s.slidesPerGroup>1){const a=Math.ceil((e.virtual.slidesBefore+e.virtual.slidesAfter)/s.slidesPerGroup),i=t*s.slidesPerGroup;for(let e=0;e!(s.cssMode&&!s.loop)||t!==c.length-1)).forEach((e=>{e.style[t]=`${x}px`}))}if(s.centeredSlides&&s.centeredSlidesBounds){let e=0;g.forEach((t=>{e+=t+(x||0)})),e-=x;const t=e>r?e-r:0;m=m.map((e=>e<=0?-v:e>t?t+w:e))}if(s.centerInsufficientSlides){let e=0;g.forEach((t=>{e+=t+(x||0)})),e-=x;const t=(s.slidesOffsetBefore||0)+(s.slidesOffsetAfter||0);if(e+t{m[t]=e-s})),h.forEach(((e,t)=>{h[t]=e+s}))}}if(Object.assign(e,{slides:c,snapGrid:m,slidesGrid:h,slidesSizesGrid:g}),s.centeredSlides&&s.cssMode&&!s.centeredSlidesBounds){u(a,"--swiper-centered-offset-before",-m[0]+"px"),u(a,"--swiper-centered-offset-after",e.size/2-g[g.length-1]/2+"px");const t=-e.snapGrid[0],s=-e.slidesGrid[0];e.snapGrid=e.snapGrid.map((e=>e+t)),e.slidesGrid=e.slidesGrid.map((e=>e+s))}if(p!==d&&e.emit("slidesLengthChange"),m.length!==y&&(e.params.watchOverflow&&e.checkOverflow(),e.emit("snapGridLengthChange")),h.length!==E&&e.emit("slidesGridLengthChange"),s.watchSlidesProgress&&e.updateSlidesOffset(),e.emit("slidesUpdated"),!(o||s.cssMode||"slide"!==s.effect&&"fade"!==s.effect)){const t=`${s.containerModifierClass}backface-hidden`,a=e.el.classList.contains(t);p<=s.maxBackfaceHiddenSlides?a||e.el.classList.add(t):a&&e.el.classList.remove(t)}},updateAutoHeight:function(e){const t=this,s=[],a=t.virtual&&t.params.virtual.enabled;let i,r=0;"number"==typeof e?t.setTransition(e):!0===e&&t.setTransition(t.params.speed);const n=e=>a?t.slides[t.getSlideIndexByData(e)]:t.slides[e];if("auto"!==t.params.slidesPerView&&t.params.slidesPerView>1)if(t.params.centeredSlides)(t.visibleSlides||[]).forEach((e=>{s.push(e)}));else for(i=0;it.slides.length&&!a)break;s.push(n(e))}else s.push(n(t.activeIndex));for(i=0;ir?e:r}(r||0===r)&&(t.wrapperEl.style.height=`${r}px`)},updateSlidesOffset:function(){const e=this,t=e.slides,s=e.isElement?e.isHorizontal()?e.wrapperEl.offsetLeft:e.wrapperEl.offsetTop:0;for(let a=0;a=0?l=parseFloat(l.replace("%",""))/100*t.size:"string"==typeof l&&(l=parseFloat(l));for(let e=0;e=0&&u<=t.size-t.slidesSizesGrid[e],f=u>=0&&u1&&m<=t.size||u<=0&&m>=t.size;f&&(t.visibleSlides.push(o),t.visibleSlidesIndexes.push(e)),k(o,f,s.slideVisibleClass),k(o,h,s.slideFullyVisibleClass),o.progress=i?-c:c,o.originalProgress=i?-p:p}},updateProgress:function(e){const t=this;if(void 0===e){const s=t.rtlTranslate?-1:1;e=t&&t.translate&&t.translate*s||0}const s=t.params,a=t.maxTranslate()-t.minTranslate();let{progress:i,isBeginning:r,isEnd:n,progressLoop:l}=t;const o=r,d=n;if(0===a)i=0,r=!0,n=!0;else{i=(e-t.minTranslate())/a;const s=Math.abs(e-t.minTranslate())<1,l=Math.abs(e-t.maxTranslate())<1;r=s||i<=0,n=l||i>=1,s&&(i=0),l&&(i=1)}if(s.loop){const s=t.getSlideIndexByData(0),a=t.getSlideIndexByData(t.slides.length-1),i=t.slidesGrid[s],r=t.slidesGrid[a],n=t.slidesGrid[t.slidesGrid.length-1],o=Math.abs(e);l=o>=i?(o-i)/n:(o+n-r)/n,l>1&&(l-=1)}Object.assign(t,{progress:i,progressLoop:l,isBeginning:r,isEnd:n}),(s.watchSlidesProgress||s.centeredSlides&&s.autoHeight)&&t.updateSlidesProgress(e),r&&!o&&t.emit("reachBeginning toEdge"),n&&!d&&t.emit("reachEnd toEdge"),(o&&!r||d&&!n)&&t.emit("fromEdge"),t.emit("progress",i)},updateSlidesClasses:function(){const e=this,{slides:t,params:s,slidesEl:a,activeIndex:i}=e,r=e.virtual&&s.virtual.enabled,n=e.grid&&s.grid&&s.grid.rows>1,l=e=>f(a,`.${s.slideClass}${e}, swiper-slide${e}`)[0];let o,d,c;if(r)if(s.loop){let t=i-e.virtual.slidesBefore;t<0&&(t=e.virtual.slides.length+t),t>=e.virtual.slides.length&&(t-=e.virtual.slides.length),o=l(`[data-swiper-slide-index="${t}"]`)}else o=l(`[data-swiper-slide-index="${i}"]`);else n?(o=t.find((e=>e.column===i)),c=t.find((e=>e.column===i+1)),d=t.find((e=>e.column===i-1))):o=t[i];o&&(n||(c=function(e,t){const s=[];for(;e.nextElementSibling;){const a=e.nextElementSibling;t?a.matches(t)&&s.push(a):s.push(a),e=a}return s}(o,`.${s.slideClass}, swiper-slide`)[0],s.loop&&!c&&(c=t[0]),d=function(e,t){const s=[];for(;e.previousElementSibling;){const a=e.previousElementSibling;t?a.matches(t)&&s.push(a):s.push(a),e=a}return s}(o,`.${s.slideClass}, swiper-slide`)[0],s.loop&&0===!d&&(d=t[t.length-1]))),t.forEach((e=>{O(e,e===o,s.slideActiveClass),O(e,e===c,s.slideNextClass),O(e,e===d,s.slidePrevClass)})),e.emitSlidesClasses()},updateActiveIndex:function(e){const t=this,s=t.rtlTranslate?t.translate:-t.translate,{snapGrid:a,params:i,activeIndex:r,realIndex:n,snapIndex:l}=t;let o,d=e;const c=e=>{let s=e-t.virtual.slidesBefore;return s<0&&(s=t.virtual.slides.length+s),s>=t.virtual.slides.length&&(s-=t.virtual.slides.length),s};if(void 0===d&&(d=function(e){const{slidesGrid:t,params:s}=e,a=e.rtlTranslate?e.translate:-e.translate;let i;for(let e=0;e=t[e]&&a=t[e]&&a=t[e]&&(i=e);return s.normalizeSlideIndex&&(i<0||void 0===i)&&(i=0),i}(t)),a.indexOf(s)>=0)o=a.indexOf(s);else{const e=Math.min(i.slidesPerGroupSkip,d);o=e+Math.floor((d-e)/i.slidesPerGroup)}if(o>=a.length&&(o=a.length-1),d===r&&!t.params.loop)return void(o!==l&&(t.snapIndex=o,t.emit("snapIndexChange")));if(d===r&&t.params.loop&&t.virtual&&t.params.virtual.enabled)return void(t.realIndex=c(d));const p=t.grid&&i.grid&&i.grid.rows>1;let u;if(t.virtual&&i.virtual.enabled&&i.loop)u=c(d);else if(p){const e=t.slides.find((e=>e.column===d));let s=parseInt(e.getAttribute("data-swiper-slide-index"),10);Number.isNaN(s)&&(s=Math.max(t.slides.indexOf(e),0)),u=Math.floor(s/i.grid.rows)}else if(t.slides[d]){const e=t.slides[d].getAttribute("data-swiper-slide-index");u=e?parseInt(e,10):d}else u=d;Object.assign(t,{previousSnapIndex:l,snapIndex:o,previousRealIndex:n,realIndex:u,previousIndex:r,activeIndex:d}),t.initialized&&X(t),t.emit("activeIndexChange"),t.emit("snapIndexChange"),(t.initialized||t.params.runCallbacksOnInit)&&(n!==u&&t.emit("realIndexChange"),t.emit("slideChange"))},updateClickedSlide:function(e,t){const s=this,a=s.params;let i=e.closest(`.${a.slideClass}, swiper-slide`);!i&&s.isElement&&t&&t.length>1&&t.includes(e)&&[...t.slice(t.indexOf(e)+1,t.length)].forEach((e=>{!i&&e.matches&&e.matches(`.${a.slideClass}, swiper-slide`)&&(i=e)}));let r,n=!1;if(i)for(let e=0;eo?o:a&&en?"next":r=o.length&&(v=o.length-1);const w=-o[v];if(l.normalizeSlideIndex)for(let e=0;e=s&&t=s&&t=s&&(n=e)}if(r.initialized&&n!==p){if(!r.allowSlideNext&&(u?w>r.translate&&w>r.minTranslate():wr.translate&&w>r.maxTranslate()&&(p||0)!==n)return!1}let b;n!==(c||0)&&s&&r.emit("beforeSlideChangeStart"),r.updateProgress(w),b=n>p?"next":n0?(r._cssModeVirtualInitialSet=!0,requestAnimationFrame((()=>{h[e?"scrollLeft":"scrollTop"]=s}))):h[e?"scrollLeft":"scrollTop"]=s,y&&requestAnimationFrame((()=>{r.wrapperEl.style.scrollSnapType="",r._immediateVirtual=!1}));else{if(!r.support.smoothScroll)return m({swiper:r,targetPosition:s,side:e?"left":"top"}),!0;h.scrollTo({[e?"left":"top"]:s,behavior:"smooth"})}return!0}const E=A().isSafari;return y&&!i&&E&&r.isElement&&r.virtual.update(!1,!1,n),r.setTransition(t),r.setTranslate(w),r.updateActiveIndex(n),r.updateSlidesClasses(),r.emit("beforeTransitionStart",t,a),r.transitionStart(s,b),0===t?r.transitionEnd(s,b):r.animating||(r.animating=!0,r.onSlideToWrapperTransitionEnd||(r.onSlideToWrapperTransitionEnd=function(e){r&&!r.destroyed&&e.target===this&&(r.wrapperEl.removeEventListener("transitionend",r.onSlideToWrapperTransitionEnd),r.onSlideToWrapperTransitionEnd=null,delete r.onSlideToWrapperTransitionEnd,r.transitionEnd(s,b))}),r.wrapperEl.addEventListener("transitionend",r.onSlideToWrapperTransitionEnd)),!0},slideToLoop:function(e,t,s,a){if(void 0===e&&(e=0),void 0===s&&(s=!0),"string"==typeof e){e=parseInt(e,10)}const i=this;if(i.destroyed)return;void 0===t&&(t=i.params.speed);const r=i.grid&&i.params.grid&&i.params.grid.rows>1;let n=e;if(i.params.loop)if(i.virtual&&i.params.virtual.enabled)n+=i.virtual.slidesBefore;else{let e;if(r){const t=n*i.params.grid.rows;e=i.slides.find((e=>1*e.getAttribute("data-swiper-slide-index")===t)).column}else e=i.getSlideIndexByData(n);const t=r?Math.ceil(i.slides.length/i.params.grid.rows):i.slides.length,{centeredSlides:s}=i.params;let l=i.params.slidesPerView;"auto"===l?l=i.slidesPerViewDynamic():(l=Math.ceil(parseFloat(i.params.slidesPerView,10)),s&&l%2==0&&(l+=1));let o=t-e1*t.getAttribute("data-swiper-slide-index")===e)).column}else n=i.getSlideIndexByData(n)}return requestAnimationFrame((()=>{i.slideTo(n,t,s,a)})),i},slideNext:function(e,t,s){void 0===t&&(t=!0);const a=this,{enabled:i,params:r,animating:n}=a;if(!i||a.destroyed)return a;void 0===e&&(e=a.params.speed);let l=r.slidesPerGroup;"auto"===r.slidesPerView&&1===r.slidesPerGroup&&r.slidesPerGroupAuto&&(l=Math.max(a.slidesPerViewDynamic("current",!0),1));const o=a.activeIndex{a.slideTo(a.activeIndex+o,e,t,s)})),!0}return r.rewind&&a.isEnd?a.slideTo(0,e,t,s):a.slideTo(a.activeIndex+o,e,t,s)},slidePrev:function(e,t,s){void 0===t&&(t=!0);const a=this,{params:i,snapGrid:r,slidesGrid:n,rtlTranslate:l,enabled:o,animating:d}=a;if(!o||a.destroyed)return a;void 0===e&&(e=a.params.speed);const c=a.virtual&&i.virtual.enabled;if(i.loop){if(d&&!c&&i.loopPreventsSliding)return!1;a.loopFix({direction:"prev"}),a._clientLeft=a.wrapperEl.clientLeft}function p(e){return e<0?-Math.floor(Math.abs(e)):Math.floor(e)}const u=p(l?a.translate:-a.translate),m=r.map((e=>p(e))),h=i.freeMode&&i.freeMode.enabled;let f=r[m.indexOf(u)-1];if(void 0===f&&(i.cssMode||h)){let e;r.forEach(((t,s)=>{u>=t&&(e=s)})),void 0!==e&&(f=h?r[e]:r[e>0?e-1:e])}let g=0;if(void 0!==f&&(g=n.indexOf(f),g<0&&(g=a.activeIndex-1),"auto"===i.slidesPerView&&1===i.slidesPerGroup&&i.slidesPerGroupAuto&&(g=g-a.slidesPerViewDynamic("previous",!0)+1,g=Math.max(g,0))),i.rewind&&a.isBeginning){const i=a.params.virtual&&a.params.virtual.enabled&&a.virtual?a.virtual.slides.length-1:a.slides.length-1;return a.slideTo(i,e,t,s)}return i.loop&&0===a.activeIndex&&i.cssMode?(requestAnimationFrame((()=>{a.slideTo(g,e,t,s)})),!0):a.slideTo(g,e,t,s)},slideReset:function(e,t,s){void 0===t&&(t=!0);const a=this;if(!a.destroyed)return void 0===e&&(e=a.params.speed),a.slideTo(a.activeIndex,e,t,s)},slideToClosest:function(e,t,s,a){void 0===t&&(t=!0),void 0===a&&(a=.5);const i=this;if(i.destroyed)return;void 0===e&&(e=i.params.speed);let r=i.activeIndex;const n=Math.min(i.params.slidesPerGroupSkip,r),l=n+Math.floor((r-n)/i.params.slidesPerGroup),o=i.rtlTranslate?i.translate:-i.translate;if(o>=i.snapGrid[l]){const e=i.snapGrid[l];o-e>(i.snapGrid[l+1]-e)*a&&(r+=i.params.slidesPerGroup)}else{const e=i.snapGrid[l-1];o-e<=(i.snapGrid[l]-e)*a&&(r-=i.params.slidesPerGroup)}return r=Math.max(r,0),r=Math.min(r,i.slidesGrid.length-1),i.slideTo(r,e,t,s)},slideToClickedSlide:function(){const e=this;if(e.destroyed)return;const{params:t,slidesEl:s}=e,a="auto"===t.slidesPerView?e.slidesPerViewDynamic():t.slidesPerView;let i,r=e.clickedIndex;const n=e.isElement?"swiper-slide":`.${t.slideClass}`;if(t.loop){if(e.animating)return;i=parseInt(e.clickedSlide.getAttribute("data-swiper-slide-index"),10),t.centeredSlides?re.slides.length-e.loopedSlides+a/2?(e.loopFix(),r=e.getSlideIndex(f(s,`${n}[data-swiper-slide-index="${i}"]`)[0]),l((()=>{e.slideTo(r)}))):e.slideTo(r):r>e.slides.length-a?(e.loopFix(),r=e.getSlideIndex(f(s,`${n}[data-swiper-slide-index="${i}"]`)[0]),l((()=>{e.slideTo(r)}))):e.slideTo(r)}else e.slideTo(r)}};var R={loopCreate:function(e){const t=this,{params:s,slidesEl:a}=t;if(!s.loop||t.virtual&&t.params.virtual.enabled)return;const i=()=>{f(a,`.${s.slideClass}, swiper-slide`).forEach(((e,t)=>{e.setAttribute("data-swiper-slide-index",t)}))},r=t.grid&&s.grid&&s.grid.rows>1,n=s.slidesPerGroup*(r?s.grid.rows:1),l=t.slides.length%n!=0,o=r&&t.slides.length%s.grid.rows!=0,d=e=>{for(let a=0;a1;d.lengthe.classList.contains(m.slideActiveClass)))):x=r;const S="next"===a||!a,T="prev"===a||!a;let M=0,C=0;const P=b?Math.ceil(d.length/m.grid.rows):d.length,L=(b?d[r].column:r)+(h&&void 0===i?-f/2+.5:0);if(L=0;t-=1)d[t].column===e&&y.push(t)}else y.push(P-t-1)}}else if(L+f>P-w){C=Math.max(L-(P-2*w),v);for(let e=0;e{e.column===t&&E.push(s)})):E.push(t)}}if(o.__preventObserver__=!0,requestAnimationFrame((()=>{o.__preventObserver__=!1})),T&&y.forEach((e=>{d[e].swiperLoopMoveDOM=!0,u.prepend(d[e]),d[e].swiperLoopMoveDOM=!1})),S&&E.forEach((e=>{d[e].swiperLoopMoveDOM=!0,u.append(d[e]),d[e].swiperLoopMoveDOM=!1})),o.recalcSlides(),"auto"===m.slidesPerView?o.updateSlides():b&&(y.length>0&&T||E.length>0&&S)&&o.slides.forEach(((e,t)=>{o.grid.updateSlide(t,e,o.slides)})),m.watchSlidesProgress&&o.updateSlidesOffset(),s)if(y.length>0&&T){if(void 0===t){const e=o.slidesGrid[x],t=o.slidesGrid[x+M]-e;l?o.setTranslate(o.translate-t):(o.slideTo(x+Math.ceil(M),0,!1,!0),i&&(o.touchEventsData.startTranslate=o.touchEventsData.startTranslate-t,o.touchEventsData.currentTranslate=o.touchEventsData.currentTranslate-t))}else if(i){const e=b?y.length/m.grid.rows:y.length;o.slideTo(o.activeIndex+e,0,!1,!0),o.touchEventsData.currentTranslate=o.translate}}else if(E.length>0&&S)if(void 0===t){const e=o.slidesGrid[x],t=o.slidesGrid[x-C]-e;l?o.setTranslate(o.translate-t):(o.slideTo(x-C,0,!1,!0),i&&(o.touchEventsData.startTranslate=o.touchEventsData.startTranslate-t,o.touchEventsData.currentTranslate=o.touchEventsData.currentTranslate-t))}else{const e=b?E.length/m.grid.rows:E.length;o.slideTo(o.activeIndex-e,0,!1,!0)}if(o.allowSlidePrev=c,o.allowSlideNext=p,o.controller&&o.controller.control&&!n){const e={slideRealIndex:t,direction:a,setTranslate:i,activeSlideIndex:r,byController:!0};Array.isArray(o.controller.control)?o.controller.control.forEach((t=>{!t.destroyed&&t.params.loop&&t.loopFix({...e,slideTo:t.params.slidesPerView===m.slidesPerView&&s})})):o.controller.control instanceof o.constructor&&o.controller.control.params.loop&&o.controller.control.loopFix({...e,slideTo:o.controller.control.params.slidesPerView===m.slidesPerView&&s})}o.emit("loopFix")},loopDestroy:function(){const e=this,{params:t,slidesEl:s}=e;if(!t.loop||!s||e.virtual&&e.params.virtual.enabled)return;e.recalcSlides();const a=[];e.slides.forEach((e=>{const t=void 0===e.swiperSlideIndex?1*e.getAttribute("data-swiper-slide-index"):e.swiperSlideIndex;a[t]=e})),e.slides.forEach((e=>{e.removeAttribute("data-swiper-slide-index")})),a.forEach((e=>{s.append(e)})),e.recalcSlides(),e.slideTo(e.realIndex,0)}};function q(e,t,s){const a=r(),{params:i}=e,n=i.edgeSwipeDetection,l=i.edgeSwipeThreshold;return!n||!(s<=l||s>=a.innerWidth-l)||"prevent"===n&&(t.preventDefault(),!0)}function _(e){const t=this,s=a();let i=e;i.originalEvent&&(i=i.originalEvent);const n=t.touchEventsData;if("pointerdown"===i.type){if(null!==n.pointerId&&n.pointerId!==i.pointerId)return;n.pointerId=i.pointerId}else"touchstart"===i.type&&1===i.targetTouches.length&&(n.touchId=i.targetTouches[0].identifier);if("touchstart"===i.type)return void q(t,i,i.targetTouches[0].pageX);const{params:l,touches:d,enabled:c}=t;if(!c)return;if(!l.simulateTouch&&"mouse"===i.pointerType)return;if(t.animating&&l.preventInteractionOnTransition)return;!t.animating&&l.cssMode&&l.loop&&t.loopFix();let p=i.target;if("wrapper"===l.touchEventsTarget&&!function(e,t){const s=r();let a=t.contains(e);!a&&s.HTMLSlotElement&&t instanceof HTMLSlotElement&&(a=[...t.assignedElements()].includes(e),a||(a=function(e,t){const s=[t];for(;s.length>0;){const t=s.shift();if(e===t)return!0;s.push(...t.children,...t.shadowRoot?t.shadowRoot.children:[],...t.assignedElements?t.assignedElements():[])}}(e,t)));return a}(p,t.wrapperEl))return;if("which"in i&&3===i.which)return;if("button"in i&&i.button>0)return;if(n.isTouched&&n.isMoved)return;const u=!!l.noSwipingClass&&""!==l.noSwipingClass,m=i.composedPath?i.composedPath():i.path;u&&i.target&&i.target.shadowRoot&&m&&(p=m[0]);const h=l.noSwipingSelector?l.noSwipingSelector:`.${l.noSwipingClass}`,f=!(!i.target||!i.target.shadowRoot);if(l.noSwiping&&(f?function(e,t){return void 0===t&&(t=this),function t(s){if(!s||s===a()||s===r())return null;s.assignedSlot&&(s=s.assignedSlot);const i=s.closest(e);return i||s.getRootNode?i||t(s.getRootNode().host):null}(t)}(h,p):p.closest(h)))return void(t.allowClick=!0);if(l.swipeHandler&&!p.closest(l.swipeHandler))return;d.currentX=i.pageX,d.currentY=i.pageY;const g=d.currentX,v=d.currentY;if(!q(t,i,g))return;Object.assign(n,{isTouched:!0,isMoved:!1,allowTouchCallbacks:!0,isScrolling:void 0,startMoving:void 0}),d.startX=g,d.startY=v,n.touchStartTime=o(),t.allowClick=!0,t.updateSize(),t.swipeDirection=void 0,l.threshold>0&&(n.allowThresholdMove=!1);let w=!0;p.matches(n.focusableElements)&&(w=!1,"SELECT"===p.nodeName&&(n.isTouched=!1)),s.activeElement&&s.activeElement.matches(n.focusableElements)&&s.activeElement!==p&&("mouse"===i.pointerType||"mouse"!==i.pointerType&&!p.matches(n.focusableElements))&&s.activeElement.blur();const b=w&&t.allowTouchMove&&l.touchStartPreventDefault;!l.touchStartForcePreventDefault&&!b||p.isContentEditable||i.preventDefault(),l.freeMode&&l.freeMode.enabled&&t.freeMode&&t.animating&&!l.cssMode&&t.freeMode.onTouchStart(),t.emit("touchStart",i)}function F(e){const t=a(),s=this,i=s.touchEventsData,{params:r,touches:n,rtlTranslate:l,enabled:d}=s;if(!d)return;if(!r.simulateTouch&&"mouse"===e.pointerType)return;let c,p=e;if(p.originalEvent&&(p=p.originalEvent),"pointermove"===p.type){if(null!==i.touchId)return;if(p.pointerId!==i.pointerId)return}if("touchmove"===p.type){if(c=[...p.changedTouches].find((e=>e.identifier===i.touchId)),!c||c.identifier!==i.touchId)return}else c=p;if(!i.isTouched)return void(i.startMoving&&i.isScrolling&&s.emit("touchMoveOpposite",p));const u=c.pageX,m=c.pageY;if(p.preventedByNestedSwiper)return n.startX=u,void(n.startY=m);if(!s.allowTouchMove)return p.target.matches(i.focusableElements)||(s.allowClick=!1),void(i.isTouched&&(Object.assign(n,{startX:u,startY:m,currentX:u,currentY:m}),i.touchStartTime=o()));if(r.touchReleaseOnEdges&&!r.loop)if(s.isVertical()){if(mn.startY&&s.translate>=s.minTranslate())return i.isTouched=!1,void(i.isMoved=!1)}else if(un.startX&&s.translate>=s.minTranslate())return;if(t.activeElement&&t.activeElement.matches(i.focusableElements)&&t.activeElement!==p.target&&"mouse"!==p.pointerType&&t.activeElement.blur(),t.activeElement&&p.target===t.activeElement&&p.target.matches(i.focusableElements))return i.isMoved=!0,void(s.allowClick=!1);i.allowTouchCallbacks&&s.emit("touchMove",p),n.previousX=n.currentX,n.previousY=n.currentY,n.currentX=u,n.currentY=m;const h=n.currentX-n.startX,f=n.currentY-n.startY;if(s.params.threshold&&Math.sqrt(h**2+f**2)=25&&(e=180*Math.atan2(Math.abs(f),Math.abs(h))/Math.PI,i.isScrolling=s.isHorizontal()?e>r.touchAngle:90-e>r.touchAngle)}if(i.isScrolling&&s.emit("touchMoveOpposite",p),void 0===i.startMoving&&(n.currentX===n.startX&&n.currentY===n.startY||(i.startMoving=!0)),i.isScrolling||"touchmove"===p.type&&i.preventTouchMoveFromPointerMove)return void(i.isTouched=!1);if(!i.startMoving)return;s.allowClick=!1,!r.cssMode&&p.cancelable&&p.preventDefault(),r.touchMoveStopPropagation&&!r.nested&&p.stopPropagation();let g=s.isHorizontal()?h:f,v=s.isHorizontal()?n.currentX-n.previousX:n.currentY-n.previousY;r.oneWayMovement&&(g=Math.abs(g)*(l?1:-1),v=Math.abs(v)*(l?1:-1)),n.diff=g,g*=r.touchRatio,l&&(g=-g,v=-v);const w=s.touchesDirection;s.swipeDirection=g>0?"prev":"next",s.touchesDirection=v>0?"prev":"next";const b=s.params.loop&&!r.cssMode,y="next"===s.touchesDirection&&s.allowSlideNext||"prev"===s.touchesDirection&&s.allowSlidePrev;if(!i.isMoved){if(b&&y&&s.loopFix({direction:s.swipeDirection}),i.startTranslate=s.getTranslate(),s.setTransition(0),s.animating){const e=new window.CustomEvent("transitionend",{bubbles:!0,cancelable:!0,detail:{bySwiperTouchMove:!0}});s.wrapperEl.dispatchEvent(e)}i.allowMomentumBounce=!1,!r.grabCursor||!0!==s.allowSlideNext&&!0!==s.allowSlidePrev||s.setGrabCursor(!0),s.emit("sliderFirstMove",p)}if((new Date).getTime(),!1!==r._loopSwapReset&&i.isMoved&&i.allowThresholdMove&&w!==s.touchesDirection&&b&&y&&Math.abs(g)>=1)return Object.assign(n,{startX:u,startY:m,currentX:u,currentY:m,startTranslate:i.currentTranslate}),i.loopSwapReset=!0,void(i.startTranslate=i.currentTranslate);s.emit("sliderMove",p),i.isMoved=!0,i.currentTranslate=g+i.startTranslate;let E=!0,x=r.resistanceRatio;if(r.touchReleaseOnEdges&&(x=0),g>0?(b&&y&&i.allowThresholdMove&&i.currentTranslate>(r.centeredSlides?s.minTranslate()-s.slidesSizesGrid[s.activeIndex+1]-("auto"!==r.slidesPerView&&s.slides.length-r.slidesPerView>=2?s.slidesSizesGrid[s.activeIndex+1]+s.params.spaceBetween:0)-s.params.spaceBetween:s.minTranslate())&&s.loopFix({direction:"prev",setTranslate:!0,activeSlideIndex:0}),i.currentTranslate>s.minTranslate()&&(E=!1,r.resistance&&(i.currentTranslate=s.minTranslate()-1+(-s.minTranslate()+i.startTranslate+g)**x))):g<0&&(b&&y&&i.allowThresholdMove&&i.currentTranslate<(r.centeredSlides?s.maxTranslate()+s.slidesSizesGrid[s.slidesSizesGrid.length-1]+s.params.spaceBetween+("auto"!==r.slidesPerView&&s.slides.length-r.slidesPerView>=2?s.slidesSizesGrid[s.slidesSizesGrid.length-1]+s.params.spaceBetween:0):s.maxTranslate())&&s.loopFix({direction:"next",setTranslate:!0,activeSlideIndex:s.slides.length-("auto"===r.slidesPerView?s.slidesPerViewDynamic():Math.ceil(parseFloat(r.slidesPerView,10)))}),i.currentTranslatei.startTranslate&&(i.currentTranslate=i.startTranslate),s.allowSlidePrev||s.allowSlideNext||(i.currentTranslate=i.startTranslate),r.threshold>0){if(!(Math.abs(g)>r.threshold||i.allowThresholdMove))return void(i.currentTranslate=i.startTranslate);if(!i.allowThresholdMove)return i.allowThresholdMove=!0,n.startX=n.currentX,n.startY=n.currentY,i.currentTranslate=i.startTranslate,void(n.diff=s.isHorizontal()?n.currentX-n.startX:n.currentY-n.startY)}r.followFinger&&!r.cssMode&&((r.freeMode&&r.freeMode.enabled&&s.freeMode||r.watchSlidesProgress)&&(s.updateActiveIndex(),s.updateSlidesClasses()),r.freeMode&&r.freeMode.enabled&&s.freeMode&&s.freeMode.onTouchMove(),s.updateProgress(i.currentTranslate),s.setTranslate(i.currentTranslate))}function V(e){const t=this,s=t.touchEventsData;let a,i=e;i.originalEvent&&(i=i.originalEvent);if("touchend"===i.type||"touchcancel"===i.type){if(a=[...i.changedTouches].find((e=>e.identifier===s.touchId)),!a||a.identifier!==s.touchId)return}else{if(null!==s.touchId)return;if(i.pointerId!==s.pointerId)return;a=i}if(["pointercancel","pointerout","pointerleave","contextmenu"].includes(i.type)){if(!(["pointercancel","contextmenu"].includes(i.type)&&(t.browser.isSafari||t.browser.isWebView)))return}s.pointerId=null,s.touchId=null;const{params:r,touches:n,rtlTranslate:d,slidesGrid:c,enabled:p}=t;if(!p)return;if(!r.simulateTouch&&"mouse"===i.pointerType)return;if(s.allowTouchCallbacks&&t.emit("touchEnd",i),s.allowTouchCallbacks=!1,!s.isTouched)return s.isMoved&&r.grabCursor&&t.setGrabCursor(!1),s.isMoved=!1,void(s.startMoving=!1);r.grabCursor&&s.isMoved&&s.isTouched&&(!0===t.allowSlideNext||!0===t.allowSlidePrev)&&t.setGrabCursor(!1);const u=o(),m=u-s.touchStartTime;if(t.allowClick){const e=i.path||i.composedPath&&i.composedPath();t.updateClickedSlide(e&&e[0]||i.target,e),t.emit("tap click",i),m<300&&u-s.lastClickTime<300&&t.emit("doubleTap doubleClick",i)}if(s.lastClickTime=o(),l((()=>{t.destroyed||(t.allowClick=!0)})),!s.isTouched||!s.isMoved||!t.swipeDirection||0===n.diff&&!s.loopSwapReset||s.currentTranslate===s.startTranslate&&!s.loopSwapReset)return s.isTouched=!1,s.isMoved=!1,void(s.startMoving=!1);let h;if(s.isTouched=!1,s.isMoved=!1,s.startMoving=!1,h=r.followFinger?d?t.translate:-t.translate:-s.currentTranslate,r.cssMode)return;if(r.freeMode&&r.freeMode.enabled)return void t.freeMode.onTouchEnd({currentPos:h});const f=h>=-t.maxTranslate()&&!t.params.loop;let g=0,v=t.slidesSizesGrid[0];for(let e=0;e=c[e]&&h=c[e])&&(g=e,v=c[c.length-1]-c[c.length-2])}let w=null,b=null;r.rewind&&(t.isBeginning?b=r.virtual&&r.virtual.enabled&&t.virtual?t.virtual.slides.length-1:t.slides.length-1:t.isEnd&&(w=0));const y=(h-c[g])/v,E=gr.longSwipesMs){if(!r.longSwipes)return void t.slideTo(t.activeIndex);"next"===t.swipeDirection&&(y>=r.longSwipesRatio?t.slideTo(r.rewind&&t.isEnd?w:g+E):t.slideTo(g)),"prev"===t.swipeDirection&&(y>1-r.longSwipesRatio?t.slideTo(g+E):null!==b&&y<0&&Math.abs(y)>r.longSwipesRatio?t.slideTo(b):t.slideTo(g))}else{if(!r.shortSwipes)return void t.slideTo(t.activeIndex);t.navigation&&(i.target===t.navigation.nextEl||i.target===t.navigation.prevEl)?i.target===t.navigation.nextEl?t.slideTo(g+E):t.slideTo(g):("next"===t.swipeDirection&&t.slideTo(null!==w?w:g+E),"prev"===t.swipeDirection&&t.slideTo(null!==b?b:g))}}function W(){const e=this,{params:t,el:s}=e;if(s&&0===s.offsetWidth)return;t.breakpoints&&e.setBreakpoint();const{allowSlideNext:a,allowSlidePrev:i,snapGrid:r}=e,n=e.virtual&&e.params.virtual.enabled;e.allowSlideNext=!0,e.allowSlidePrev=!0,e.updateSize(),e.updateSlides(),e.updateSlidesClasses();const l=n&&t.loop;!("auto"===t.slidesPerView||t.slidesPerView>1)||!e.isEnd||e.isBeginning||e.params.centeredSlides||l?e.params.loop&&!n?e.slideToLoop(e.realIndex,0,!1,!0):e.slideTo(e.activeIndex,0,!1,!0):e.slideTo(e.slides.length-1,0,!1,!0),e.autoplay&&e.autoplay.running&&e.autoplay.paused&&(clearTimeout(e.autoplay.resizeTimeout),e.autoplay.resizeTimeout=setTimeout((()=>{e.autoplay&&e.autoplay.running&&e.autoplay.paused&&e.autoplay.resume()}),500)),e.allowSlidePrev=i,e.allowSlideNext=a,e.params.watchOverflow&&r!==e.snapGrid&&e.checkOverflow()}function j(e){const t=this;t.enabled&&(t.allowClick||(t.params.preventClicks&&e.preventDefault(),t.params.preventClicksPropagation&&t.animating&&(e.stopPropagation(),e.stopImmediatePropagation())))}function U(){const e=this,{wrapperEl:t,rtlTranslate:s,enabled:a}=e;if(!a)return;let i;e.previousTranslate=e.translate,e.isHorizontal()?e.translate=-t.scrollLeft:e.translate=-t.scrollTop,0===e.translate&&(e.translate=0),e.updateActiveIndex(),e.updateSlidesClasses();const r=e.maxTranslate()-e.minTranslate();i=0===r?0:(e.translate-e.minTranslate())/r,i!==e.progress&&e.updateProgress(s?-e.translate:e.translate),e.emit("setTranslate",e.translate,!1)}function K(e){const t=this;D(t,e.target),t.params.cssMode||"auto"!==t.params.slidesPerView&&!t.params.autoHeight||t.update()}function Z(){const e=this;e.documentTouchHandlerProceeded||(e.documentTouchHandlerProceeded=!0,e.params.touchReleaseOnEdges&&(e.el.style.touchAction="auto"))}const Q=(e,t)=>{const s=a(),{params:i,el:r,wrapperEl:n,device:l}=e,o=!!i.nested,d="on"===t?"addEventListener":"removeEventListener",c=t;r&&"string"!=typeof r&&(s[d]("touchstart",e.onDocumentTouchStart,{passive:!1,capture:o}),r[d]("touchstart",e.onTouchStart,{passive:!1}),r[d]("pointerdown",e.onTouchStart,{passive:!1}),s[d]("touchmove",e.onTouchMove,{passive:!1,capture:o}),s[d]("pointermove",e.onTouchMove,{passive:!1,capture:o}),s[d]("touchend",e.onTouchEnd,{passive:!0}),s[d]("pointerup",e.onTouchEnd,{passive:!0}),s[d]("pointercancel",e.onTouchEnd,{passive:!0}),s[d]("touchcancel",e.onTouchEnd,{passive:!0}),s[d]("pointerout",e.onTouchEnd,{passive:!0}),s[d]("pointerleave",e.onTouchEnd,{passive:!0}),s[d]("contextmenu",e.onTouchEnd,{passive:!0}),(i.preventClicks||i.preventClicksPropagation)&&r[d]("click",e.onClick,!0),i.cssMode&&n[d]("scroll",e.onScroll),i.updateOnWindowResize?e[c](l.ios||l.android?"resize orientationchange observerUpdate":"resize observerUpdate",W,!0):e[c]("observerUpdate",W,!0),r[d]("load",e.onLoad,{capture:!0}))};const J=(e,t)=>e.grid&&t.grid&&t.grid.rows>1;var ee={init:!0,direction:"horizontal",oneWayMovement:!1,swiperElementNodeName:"SWIPER-CONTAINER",touchEventsTarget:"wrapper",initialSlide:0,speed:300,cssMode:!1,updateOnWindowResize:!0,resizeObserver:!0,nested:!1,createElements:!1,eventsPrefix:"swiper",enabled:!0,focusableElements:"input, select, option, textarea, button, video, label",width:null,height:null,preventInteractionOnTransition:!1,userAgent:null,url:null,edgeSwipeDetection:!1,edgeSwipeThreshold:20,autoHeight:!1,setWrapperSize:!1,virtualTranslate:!1,effect:"slide",breakpoints:void 0,breakpointsBase:"window",spaceBetween:0,slidesPerView:1,slidesPerGroup:1,slidesPerGroupSkip:0,slidesPerGroupAuto:!1,centeredSlides:!1,centeredSlidesBounds:!1,slidesOffsetBefore:0,slidesOffsetAfter:0,normalizeSlideIndex:!0,centerInsufficientSlides:!1,watchOverflow:!0,roundLengths:!1,touchRatio:1,touchAngle:45,simulateTouch:!0,shortSwipes:!0,longSwipes:!0,longSwipesRatio:.5,longSwipesMs:300,followFinger:!0,allowTouchMove:!0,threshold:5,touchMoveStopPropagation:!1,touchStartPreventDefault:!0,touchStartForcePreventDefault:!1,touchReleaseOnEdges:!1,uniqueNavElements:!0,resistance:!0,resistanceRatio:.85,watchSlidesProgress:!1,grabCursor:!1,preventClicks:!0,preventClicksPropagation:!0,slideToClickedSlide:!1,loop:!1,loopAddBlankSlides:!0,loopAdditionalSlides:0,loopPreventsSliding:!0,rewind:!1,allowSlidePrev:!0,allowSlideNext:!0,swipeHandler:null,noSwiping:!0,noSwipingClass:"swiper-no-swiping",noSwipingSelector:null,passiveListeners:!0,maxBackfaceHiddenSlides:10,containerModifierClass:"swiper-",slideClass:"swiper-slide",slideBlankClass:"swiper-slide-blank",slideActiveClass:"swiper-slide-active",slideVisibleClass:"swiper-slide-visible",slideFullyVisibleClass:"swiper-slide-fully-visible",slideNextClass:"swiper-slide-next",slidePrevClass:"swiper-slide-prev",wrapperClass:"swiper-wrapper",lazyPreloaderClass:"swiper-lazy-preloader",lazyPreloadPrevNext:0,runCallbacksOnInit:!0,_emitClasses:!1};function te(e,t){return function(s){void 0===s&&(s={});const a=Object.keys(s)[0],i=s[a];"object"==typeof i&&null!==i?(!0===e[a]&&(e[a]={enabled:!0}),"navigation"===a&&e[a]&&e[a].enabled&&!e[a].prevEl&&!e[a].nextEl&&(e[a].auto=!0),["pagination","scrollbar"].indexOf(a)>=0&&e[a]&&e[a].enabled&&!e[a].el&&(e[a].auto=!0),a in e&&"enabled"in i?("object"!=typeof e[a]||"enabled"in e[a]||(e[a].enabled=!0),e[a]||(e[a]={enabled:!1}),p(t,s)):p(t,s)):p(t,s)}}const se={eventsEmitter:$,update:H,translate:Y,transition:{setTransition:function(e,t){const s=this;s.params.cssMode||(s.wrapperEl.style.transitionDuration=`${e}ms`,s.wrapperEl.style.transitionDelay=0===e?"0ms":""),s.emit("setTransition",e,t)},transitionStart:function(e,t){void 0===e&&(e=!0);const s=this,{params:a}=s;a.cssMode||(a.autoHeight&&s.updateAutoHeight(),B({swiper:s,runCallbacks:e,direction:t,step:"Start"}))},transitionEnd:function(e,t){void 0===e&&(e=!0);const s=this,{params:a}=s;s.animating=!1,a.cssMode||(s.setTransition(0),B({swiper:s,runCallbacks:e,direction:t,step:"End"}))}},slide:N,loop:R,grabCursor:{setGrabCursor:function(e){const t=this;if(!t.params.simulateTouch||t.params.watchOverflow&&t.isLocked||t.params.cssMode)return;const s="container"===t.params.touchEventsTarget?t.el:t.wrapperEl;t.isElement&&(t.__preventObserver__=!0),s.style.cursor="move",s.style.cursor=e?"grabbing":"grab",t.isElement&&requestAnimationFrame((()=>{t.__preventObserver__=!1}))},unsetGrabCursor:function(){const e=this;e.params.watchOverflow&&e.isLocked||e.params.cssMode||(e.isElement&&(e.__preventObserver__=!0),e["container"===e.params.touchEventsTarget?"el":"wrapperEl"].style.cursor="",e.isElement&&requestAnimationFrame((()=>{e.__preventObserver__=!1})))}},events:{attachEvents:function(){const e=this,{params:t}=e;e.onTouchStart=_.bind(e),e.onTouchMove=F.bind(e),e.onTouchEnd=V.bind(e),e.onDocumentTouchStart=Z.bind(e),t.cssMode&&(e.onScroll=U.bind(e)),e.onClick=j.bind(e),e.onLoad=K.bind(e),Q(e,"on")},detachEvents:function(){Q(this,"off")}},breakpoints:{setBreakpoint:function(){const e=this,{realIndex:t,initialized:s,params:i,el:r}=e,n=i.breakpoints;if(!n||n&&0===Object.keys(n).length)return;const l=a(),o="window"!==i.breakpointsBase&&i.breakpointsBase?"container":i.breakpointsBase,d=["window","container"].includes(i.breakpointsBase)||!i.breakpointsBase?e.el:l.querySelector(i.breakpointsBase),c=e.getBreakpoint(n,o,d);if(!c||e.currentBreakpoint===c)return;const u=(c in n?n[c]:void 0)||e.originalParams,m=J(e,i),h=J(e,u),f=e.params.grabCursor,g=u.grabCursor,v=i.enabled;m&&!h?(r.classList.remove(`${i.containerModifierClass}grid`,`${i.containerModifierClass}grid-column`),e.emitContainerClasses()):!m&&h&&(r.classList.add(`${i.containerModifierClass}grid`),(u.grid.fill&&"column"===u.grid.fill||!u.grid.fill&&"column"===i.grid.fill)&&r.classList.add(`${i.containerModifierClass}grid-column`),e.emitContainerClasses()),f&&!g?e.unsetGrabCursor():!f&&g&&e.setGrabCursor(),["navigation","pagination","scrollbar"].forEach((t=>{if(void 0===u[t])return;const s=i[t]&&i[t].enabled,a=u[t]&&u[t].enabled;s&&!a&&e[t].disable(),!s&&a&&e[t].enable()}));const w=u.direction&&u.direction!==i.direction,b=i.loop&&(u.slidesPerView!==i.slidesPerView||w),y=i.loop;w&&s&&e.changeDirection(),p(e.params,u);const E=e.params.enabled,x=e.params.loop;Object.assign(e,{allowTouchMove:e.params.allowTouchMove,allowSlideNext:e.params.allowSlideNext,allowSlidePrev:e.params.allowSlidePrev}),v&&!E?e.disable():!v&&E&&e.enable(),e.currentBreakpoint=c,e.emit("_beforeBreakpoint",u),s&&(b?(e.loopDestroy(),e.loopCreate(t),e.updateSlides()):!y&&x?(e.loopCreate(t),e.updateSlides()):y&&!x&&e.loopDestroy()),e.emit("breakpoint",u)},getBreakpoint:function(e,t,s){if(void 0===t&&(t="window"),!e||"container"===t&&!s)return;let a=!1;const i=r(),n="window"===t?i.innerHeight:s.clientHeight,l=Object.keys(e).map((e=>{if("string"==typeof e&&0===e.indexOf("@")){const t=parseFloat(e.substr(1));return{value:n*t,point:e}}return{value:e,point:e}}));l.sort(((e,t)=>parseInt(e.value,10)-parseInt(t.value,10)));for(let e=0;es}else e.isLocked=1===e.snapGrid.length;!0===s.allowSlideNext&&(e.allowSlideNext=!e.isLocked),!0===s.allowSlidePrev&&(e.allowSlidePrev=!e.isLocked),t&&t!==e.isLocked&&(e.isEnd=!1),t!==e.isLocked&&e.emit(e.isLocked?"lock":"unlock")}},classes:{addClasses:function(){const e=this,{classNames:t,params:s,rtl:a,el:i,device:r}=e,n=function(e,t){const s=[];return e.forEach((e=>{"object"==typeof e?Object.keys(e).forEach((a=>{e[a]&&s.push(t+a)})):"string"==typeof e&&s.push(t+e)})),s}(["initialized",s.direction,{"free-mode":e.params.freeMode&&s.freeMode.enabled},{autoheight:s.autoHeight},{rtl:a},{grid:s.grid&&s.grid.rows>1},{"grid-column":s.grid&&s.grid.rows>1&&"column"===s.grid.fill},{android:r.android},{ios:r.ios},{"css-mode":s.cssMode},{centered:s.cssMode&&s.centeredSlides},{"watch-progress":s.watchSlidesProgress}],s.containerModifierClass);t.push(...n),i.classList.add(...t),e.emitContainerClasses()},removeClasses:function(){const{el:e,classNames:t}=this;e&&"string"!=typeof e&&(e.classList.remove(...t),this.emitContainerClasses())}}},ae={};class ie{constructor(){let e,t;for(var s=arguments.length,i=new Array(s),r=0;r1){const e=[];return n.querySelectorAll(t.el).forEach((s=>{const a=p({},t,{el:s});e.push(new ie(a))})),e}const l=this;l.__swiper__=!0,l.support=I(),l.device=z({userAgent:t.userAgent}),l.browser=A(),l.eventsListeners={},l.eventsAnyListeners=[],l.modules=[...l.__modules__],t.modules&&Array.isArray(t.modules)&&l.modules.push(...t.modules);const o={};l.modules.forEach((e=>{e({params:t,swiper:l,extendParams:te(t,o),on:l.on.bind(l),once:l.once.bind(l),off:l.off.bind(l),emit:l.emit.bind(l)})}));const d=p({},ee,o);return l.params=p({},d,ae,t),l.originalParams=p({},l.params),l.passedParams=p({},t),l.params&&l.params.on&&Object.keys(l.params.on).forEach((e=>{l.on(e,l.params.on[e])})),l.params&&l.params.onAny&&l.onAny(l.params.onAny),Object.assign(l,{enabled:l.params.enabled,el:e,classNames:[],slides:[],slidesGrid:[],snapGrid:[],slidesSizesGrid:[],isHorizontal:()=>"horizontal"===l.params.direction,isVertical:()=>"vertical"===l.params.direction,activeIndex:0,realIndex:0,isBeginning:!0,isEnd:!1,translate:0,previousTranslate:0,progress:0,velocity:0,animating:!1,cssOverflowAdjustment(){return Math.trunc(this.translate/2**23)*2**23},allowSlideNext:l.params.allowSlideNext,allowSlidePrev:l.params.allowSlidePrev,touchEventsData:{isTouched:void 0,isMoved:void 0,allowTouchCallbacks:void 0,touchStartTime:void 0,isScrolling:void 0,currentTranslate:void 0,startTranslate:void 0,allowThresholdMove:void 0,focusableElements:l.params.focusableElements,lastClickTime:0,clickTimeout:void 0,velocities:[],allowMomentumBounce:void 0,startMoving:void 0,pointerId:null,touchId:null},allowClick:!0,allowTouchMove:l.params.allowTouchMove,touches:{startX:0,startY:0,currentX:0,currentY:0,diff:0},imagesToLoad:[],imagesLoaded:0}),l.emit("_swiper"),l.params.init&&l.init(),l}getDirectionLabel(e){return this.isHorizontal()?e:{width:"height","margin-top":"margin-left","margin-bottom ":"margin-right","margin-left":"margin-top","margin-right":"margin-bottom","padding-left":"padding-top","padding-right":"padding-bottom",marginRight:"marginBottom"}[e]}getSlideIndex(e){const{slidesEl:t,params:s}=this,a=y(f(t,`.${s.slideClass}, swiper-slide`)[0]);return y(e)-a}getSlideIndexByData(e){return this.getSlideIndex(this.slides.find((t=>1*t.getAttribute("data-swiper-slide-index")===e)))}recalcSlides(){const{slidesEl:e,params:t}=this;this.slides=f(e,`.${t.slideClass}, swiper-slide`)}enable(){const e=this;e.enabled||(e.enabled=!0,e.params.grabCursor&&e.setGrabCursor(),e.emit("enable"))}disable(){const e=this;e.enabled&&(e.enabled=!1,e.params.grabCursor&&e.unsetGrabCursor(),e.emit("disable"))}setProgress(e,t){const s=this;e=Math.min(Math.max(e,0),1);const a=s.minTranslate(),i=(s.maxTranslate()-a)*e+a;s.translateTo(i,void 0===t?0:t),s.updateActiveIndex(),s.updateSlidesClasses()}emitContainerClasses(){const e=this;if(!e.params._emitClasses||!e.el)return;const t=e.el.className.split(" ").filter((t=>0===t.indexOf("swiper")||0===t.indexOf(e.params.containerModifierClass)));e.emit("_containerClasses",t.join(" "))}getSlideClasses(e){const t=this;return t.destroyed?"":e.className.split(" ").filter((e=>0===e.indexOf("swiper-slide")||0===e.indexOf(t.params.slideClass))).join(" ")}emitSlidesClasses(){const e=this;if(!e.params._emitClasses||!e.el)return;const t=[];e.slides.forEach((s=>{const a=e.getSlideClasses(s);t.push({slideEl:s,classNames:a}),e.emit("_slideClass",s,a)})),e.emit("_slideClasses",t)}slidesPerViewDynamic(e,t){void 0===e&&(e="current"),void 0===t&&(t=!1);const{params:s,slides:a,slidesGrid:i,slidesSizesGrid:r,size:n,activeIndex:l}=this;let o=1;if("number"==typeof s.slidesPerView)return s.slidesPerView;if(s.centeredSlides){let e,t=a[l]?Math.ceil(a[l].swiperSlideSize):0;for(let s=l+1;sn&&(e=!0));for(let s=l-1;s>=0;s-=1)a[s]&&!e&&(t+=a[s].swiperSlideSize,o+=1,t>n&&(e=!0))}else if("current"===e)for(let e=l+1;e=0;e-=1){i[l]-i[e]{t.complete&&D(e,t)})),e.updateSize(),e.updateSlides(),e.updateProgress(),e.updateSlidesClasses(),s.freeMode&&s.freeMode.enabled&&!s.cssMode)a(),s.autoHeight&&e.updateAutoHeight();else{if(("auto"===s.slidesPerView||s.slidesPerView>1)&&e.isEnd&&!s.centeredSlides){const t=e.virtual&&s.virtual.enabled?e.virtual.slides:e.slides;i=e.slideTo(t.length-1,0,!1,!0)}else i=e.slideTo(e.activeIndex,0,!1,!0);i||a()}s.watchOverflow&&t!==e.snapGrid&&e.checkOverflow(),e.emit("update")}changeDirection(e,t){void 0===t&&(t=!0);const s=this,a=s.params.direction;return e||(e="horizontal"===a?"vertical":"horizontal"),e===a||"horizontal"!==e&&"vertical"!==e||(s.el.classList.remove(`${s.params.containerModifierClass}${a}`),s.el.classList.add(`${s.params.containerModifierClass}${e}`),s.emitContainerClasses(),s.params.direction=e,s.slides.forEach((t=>{"vertical"===e?t.style.width="":t.style.height=""})),s.emit("changeDirection"),t&&s.update()),s}changeLanguageDirection(e){const t=this;t.rtl&&"rtl"===e||!t.rtl&&"ltr"===e||(t.rtl="rtl"===e,t.rtlTranslate="horizontal"===t.params.direction&&t.rtl,t.rtl?(t.el.classList.add(`${t.params.containerModifierClass}rtl`),t.el.dir="rtl"):(t.el.classList.remove(`${t.params.containerModifierClass}rtl`),t.el.dir="ltr"),t.update())}mount(e){const t=this;if(t.mounted)return!0;let s=e||t.params.el;if("string"==typeof s&&(s=document.querySelector(s)),!s)return!1;s.swiper=t,s.parentNode&&s.parentNode.host&&s.parentNode.host.nodeName===t.params.swiperElementNodeName.toUpperCase()&&(t.isElement=!0);const a=()=>`.${(t.params.wrapperClass||"").trim().split(" ").join(".")}`;let i=(()=>{if(s&&s.shadowRoot&&s.shadowRoot.querySelector){return s.shadowRoot.querySelector(a())}return f(s,a())[0]})();return!i&&t.params.createElements&&(i=v("div",t.params.wrapperClass),s.append(i),f(s,`.${t.params.slideClass}`).forEach((e=>{i.append(e)}))),Object.assign(t,{el:s,wrapperEl:i,slidesEl:t.isElement&&!s.parentNode.host.slideSlots?s.parentNode.host:i,hostEl:t.isElement?s.parentNode.host:s,mounted:!0,rtl:"rtl"===s.dir.toLowerCase()||"rtl"===b(s,"direction"),rtlTranslate:"horizontal"===t.params.direction&&("rtl"===s.dir.toLowerCase()||"rtl"===b(s,"direction")),wrongRTL:"-webkit-box"===b(i,"display")}),!0}init(e){const t=this;if(t.initialized)return t;if(!1===t.mount(e))return t;t.emit("beforeInit"),t.params.breakpoints&&t.setBreakpoint(),t.addClasses(),t.updateSize(),t.updateSlides(),t.params.watchOverflow&&t.checkOverflow(),t.params.grabCursor&&t.enabled&&t.setGrabCursor(),t.params.loop&&t.virtual&&t.params.virtual.enabled?t.slideTo(t.params.initialSlide+t.virtual.slidesBefore,0,t.params.runCallbacksOnInit,!1,!0):t.slideTo(t.params.initialSlide,0,t.params.runCallbacksOnInit,!1,!0),t.params.loop&&t.loopCreate(),t.attachEvents();const s=[...t.el.querySelectorAll('[loading="lazy"]')];return t.isElement&&s.push(...t.hostEl.querySelectorAll('[loading="lazy"]')),s.forEach((e=>{e.complete?D(t,e):e.addEventListener("load",(e=>{D(t,e.target)}))})),X(t),t.initialized=!0,X(t),t.emit("init"),t.emit("afterInit"),t}destroy(e,t){void 0===e&&(e=!0),void 0===t&&(t=!0);const s=this,{params:a,el:i,wrapperEl:r,slides:n}=s;return void 0===s.params||s.destroyed||(s.emit("beforeDestroy"),s.initialized=!1,s.detachEvents(),a.loop&&s.loopDestroy(),t&&(s.removeClasses(),i&&"string"!=typeof i&&i.removeAttribute("style"),r&&r.removeAttribute("style"),n&&n.length&&n.forEach((e=>{e.classList.remove(a.slideVisibleClass,a.slideFullyVisibleClass,a.slideActiveClass,a.slideNextClass,a.slidePrevClass),e.removeAttribute("style"),e.removeAttribute("data-swiper-slide-index")}))),s.emit("destroy"),Object.keys(s.eventsListeners).forEach((e=>{s.off(e)})),!1!==e&&(s.el&&"string"!=typeof s.el&&(s.el.swiper=null),function(e){const t=e;Object.keys(t).forEach((e=>{try{t[e]=null}catch(e){}try{delete t[e]}catch(e){}}))}(s)),s.destroyed=!0),null}static extendDefaults(e){p(ae,e)}static get extendedDefaults(){return ae}static get defaults(){return ee}static installModule(e){ie.prototype.__modules__||(ie.prototype.__modules__=[]);const t=ie.prototype.__modules__;"function"==typeof e&&t.indexOf(e)<0&&t.push(e)}static use(e){return Array.isArray(e)?(e.forEach((e=>ie.installModule(e))),ie):(ie.installModule(e),ie)}}function re(e,t,s,a){return e.params.createElements&&Object.keys(a).forEach((i=>{if(!s[i]&&!0===s.auto){let r=f(e.el,`.${a[i]}`)[0];r||(r=v("div",a[i]),r.className=a[i],e.el.append(r)),s[i]=r,t[i]=r}})),s}function ne(e){return void 0===e&&(e=""),`.${e.trim().replace(/([\.:!+\/])/g,"\\$1").replace(/ /g,".")}`}function le(e){const t=this,{params:s,slidesEl:a}=t;s.loop&&t.loopDestroy();const i=e=>{if("string"==typeof e){const t=document.createElement("div");t.innerHTML=e,a.append(t.children[0]),t.innerHTML=""}else a.append(e)};if("object"==typeof e&&"length"in e)for(let t=0;t{if("string"==typeof e){const t=document.createElement("div");t.innerHTML=e,i.prepend(t.children[0]),t.innerHTML=""}else i.prepend(e)};if("object"==typeof e&&"length"in e){for(let t=0;t=l)return void s.appendSlide(t);let o=n>e?n+1:n;const d=[];for(let t=l-1;t>=e;t-=1){const e=s.slides[t];e.remove(),d.unshift(e)}if("object"==typeof t&&"length"in t){for(let e=0;ee?n+t.length:n}else r.append(t);for(let e=0;e{if(s.params.effect!==t)return;s.classNames.push(`${s.params.containerModifierClass}${t}`),l&&l()&&s.classNames.push(`${s.params.containerModifierClass}3d`);const e=n?n():{};Object.assign(s.params,e),Object.assign(s.originalParams,e)})),a("setTranslate",(()=>{s.params.effect===t&&i()})),a("setTransition",((e,a)=>{s.params.effect===t&&r(a)})),a("transitionEnd",(()=>{if(s.params.effect===t&&o){if(!d||!d().slideShadows)return;s.slides.forEach((e=>{e.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach((e=>e.remove()))})),o()}})),a("virtualUpdate",(()=>{s.params.effect===t&&(s.slides.length||(c=!0),requestAnimationFrame((()=>{c&&s.slides&&s.slides.length&&(i(),c=!1)})))}))}function me(e,t){const s=h(t);return s!==t&&(s.style.backfaceVisibility="hidden",s.style["-webkit-backface-visibility"]="hidden"),s}function he(e){let{swiper:t,duration:s,transformElements:a,allSlides:i}=e;const{activeIndex:r}=t;if(t.params.virtualTranslate&&0!==s){let e,s=!1;e=i?a:a.filter((e=>{const s=e.classList.contains("swiper-slide-transform")?(e=>{if(!e.parentElement)return t.slides.find((t=>t.shadowRoot&&t.shadowRoot===e.parentNode));return e.parentElement})(e):e;return t.getSlideIndex(s)===r})),e.forEach((e=>{x(e,(()=>{if(s)return;if(!t||t.destroyed)return;s=!0,t.animating=!1;const e=new window.CustomEvent("transitionend",{bubbles:!0,cancelable:!0});t.wrapperEl.dispatchEvent(e)}))}))}}function fe(e,t,s){const a=`swiper-slide-shadow${s?`-${s}`:""}${e?` swiper-slide-shadow-${e}`:""}`,i=h(t);let r=i.querySelector(`.${a.split(" ").join(".")}`);return r||(r=v("div",a.split(" ")),i.append(r)),r}Object.keys(se).forEach((e=>{Object.keys(se[e]).forEach((t=>{ie.prototype[t]=se[e][t]}))})),ie.use([function(e){let{swiper:t,on:s,emit:a}=e;const i=r();let n=null,l=null;const o=()=>{t&&!t.destroyed&&t.initialized&&(a("beforeResize"),a("resize"))},d=()=>{t&&!t.destroyed&&t.initialized&&a("orientationchange")};s("init",(()=>{t.params.resizeObserver&&void 0!==i.ResizeObserver?t&&!t.destroyed&&t.initialized&&(n=new ResizeObserver((e=>{l=i.requestAnimationFrame((()=>{const{width:s,height:a}=t;let i=s,r=a;e.forEach((e=>{let{contentBoxSize:s,contentRect:a,target:n}=e;n&&n!==t.el||(i=a?a.width:(s[0]||s).inlineSize,r=a?a.height:(s[0]||s).blockSize)})),i===s&&r===a||o()}))})),n.observe(t.el)):(i.addEventListener("resize",o),i.addEventListener("orientationchange",d))})),s("destroy",(()=>{l&&i.cancelAnimationFrame(l),n&&n.unobserve&&t.el&&(n.unobserve(t.el),n=null),i.removeEventListener("resize",o),i.removeEventListener("orientationchange",d)}))},function(e){let{swiper:t,extendParams:s,on:a,emit:i}=e;const n=[],l=r(),o=function(e,s){void 0===s&&(s={});const a=new(l.MutationObserver||l.WebkitMutationObserver)((e=>{if(t.__preventObserver__)return;if(1===e.length)return void i("observerUpdate",e[0]);const s=function(){i("observerUpdate",e[0])};l.requestAnimationFrame?l.requestAnimationFrame(s):l.setTimeout(s,0)}));a.observe(e,{attributes:void 0===s.attributes||s.attributes,childList:t.isElement||(void 0===s.childList||s).childList,characterData:void 0===s.characterData||s.characterData}),n.push(a)};s({observer:!1,observeParents:!1,observeSlideChildren:!1}),a("init",(()=>{if(t.params.observer){if(t.params.observeParents){const e=E(t.hostEl);for(let t=0;t{n.forEach((e=>{e.disconnect()})),n.splice(0,n.length)}))}]);const ge=[function(e){let t,{swiper:s,extendParams:i,on:r,emit:n}=e;i({virtual:{enabled:!1,slides:[],cache:!0,renderSlide:null,renderExternal:null,renderExternalUpdate:!0,addSlidesBefore:0,addSlidesAfter:0}});const l=a();s.virtual={cache:{},from:void 0,to:void 0,slides:[],offset:0,slidesGrid:[]};const o=l.createElement("div");function d(e,t){const a=s.params.virtual;if(a.cache&&s.virtual.cache[t])return s.virtual.cache[t];let i;return a.renderSlide?(i=a.renderSlide.call(s,e,t),"string"==typeof i&&(o.innerHTML=i,i=o.children[0])):i=s.isElement?v("swiper-slide"):v("div",s.params.slideClass),i.setAttribute("data-swiper-slide-index",t),a.renderSlide||(i.innerHTML=e),a.cache&&(s.virtual.cache[t]=i),i}function c(e,t,a){const{slidesPerView:i,slidesPerGroup:r,centeredSlides:l,loop:o,initialSlide:c}=s.params;if(t&&!o&&c>0)return;const{addSlidesBefore:p,addSlidesAfter:u}=s.params.virtual,{from:m,to:h,slides:g,slidesGrid:v,offset:w}=s.virtual;s.params.cssMode||s.updateActiveIndex();const b=void 0===a?s.activeIndex||0:a;let y,E,x;y=s.rtlTranslate?"right":s.isHorizontal()?"left":"top",l?(E=Math.floor(i/2)+r+u,x=Math.floor(i/2)+r+p):(E=i+(r-1)+u,x=(o?i:r)+p);let S=b-x,T=b+E;o||(S=Math.max(S,0),T=Math.min(T,g.length-1));let M=(s.slidesGrid[S]||0)-(s.slidesGrid[0]||0);function C(){s.updateSlides(),s.updateProgress(),s.updateSlidesClasses(),n("virtualUpdate")}if(o&&b>=x?(S-=x,l||(M+=s.slidesGrid[0])):o&&b{e.style[y]=M-Math.abs(s.cssOverflowAdjustment())+"px"})),s.updateProgress(),void n("virtualUpdate");if(s.params.virtual.renderExternal)return s.params.virtual.renderExternal.call(s,{offset:M,from:S,to:T,slides:function(){const e=[];for(let t=S;t<=T;t+=1)e.push(g[t]);return e}()}),void(s.params.virtual.renderExternalUpdate?C():n("virtualUpdate"));const P=[],L=[],I=e=>{let t=e;return e<0?t=g.length+e:t>=g.length&&(t-=g.length),t};if(e)s.slides.filter((e=>e.matches(`.${s.params.slideClass}, swiper-slide`))).forEach((e=>{e.remove()}));else for(let e=m;e<=h;e+=1)if(eT){const t=I(e);s.slides.filter((e=>e.matches(`.${s.params.slideClass}[data-swiper-slide-index="${t}"], swiper-slide[data-swiper-slide-index="${t}"]`))).forEach((e=>{e.remove()}))}const z=o?-g.length:0,A=o?2*g.length:g.length;for(let t=z;t=S&&t<=T){const s=I(t);void 0===h||e?L.push(s):(t>h&&L.push(s),t{s.slidesEl.append(d(g[e],e))})),o)for(let e=P.length-1;e>=0;e-=1){const t=P[e];s.slidesEl.prepend(d(g[t],t))}else P.sort(((e,t)=>t-e)),P.forEach((e=>{s.slidesEl.prepend(d(g[e],e))}));f(s.slidesEl,".swiper-slide, swiper-slide").forEach((e=>{e.style[y]=M-Math.abs(s.cssOverflowAdjustment())+"px"})),C()}r("beforeInit",(()=>{if(!s.params.virtual.enabled)return;let e;if(void 0===s.passedParams.virtual.slides){const t=[...s.slidesEl.children].filter((e=>e.matches(`.${s.params.slideClass}, swiper-slide`)));t&&t.length&&(s.virtual.slides=[...t],e=!0,t.forEach(((e,t)=>{e.setAttribute("data-swiper-slide-index",t),s.virtual.cache[t]=e,e.remove()})))}e||(s.virtual.slides=s.params.virtual.slides),s.classNames.push(`${s.params.containerModifierClass}virtual`),s.params.watchSlidesProgress=!0,s.originalParams.watchSlidesProgress=!0,c(!1,!0)})),r("setTranslate",(()=>{s.params.virtual.enabled&&(s.params.cssMode&&!s._immediateVirtual?(clearTimeout(t),t=setTimeout((()=>{c()}),100)):c())})),r("init update resize",(()=>{s.params.virtual.enabled&&s.params.cssMode&&u(s.wrapperEl,"--swiper-virtual-size",`${s.virtualSize}px`)})),Object.assign(s.virtual,{appendSlide:function(e){if("object"==typeof e&&"length"in e)for(let t=0;t{const a=e[s],r=a.getAttribute("data-swiper-slide-index");r&&a.setAttribute("data-swiper-slide-index",parseInt(r,10)+i),t[parseInt(s,10)+i]=a})),s.virtual.cache=t}c(!0),s.slideTo(a,0)},removeSlide:function(e){if(null==e)return;let t=s.activeIndex;if(Array.isArray(e))for(let a=e.length-1;a>=0;a-=1)s.params.virtual.cache&&(delete s.virtual.cache[e[a]],Object.keys(s.virtual.cache).forEach((t=>{t>e&&(s.virtual.cache[t-1]=s.virtual.cache[t],s.virtual.cache[t-1].setAttribute("data-swiper-slide-index",t-1),delete s.virtual.cache[t])}))),s.virtual.slides.splice(e[a],1),e[a]{t>e&&(s.virtual.cache[t-1]=s.virtual.cache[t],s.virtual.cache[t-1].setAttribute("data-swiper-slide-index",t-1),delete s.virtual.cache[t])}))),s.virtual.slides.splice(e,1),e0&&0===E(t.el,`.${t.params.slideActiveClass}`).length)return;const a=t.el,i=a.clientWidth,r=a.clientHeight,n=o.innerWidth,l=o.innerHeight,d=w(a);s&&(d.left-=a.scrollLeft);const c=[[d.left,d.top],[d.left+i,d.top],[d.left,d.top+r],[d.left+i,d.top+r]];for(let t=0;t=0&&s[0]<=n&&s[1]>=0&&s[1]<=l){if(0===s[0]&&0===s[1])continue;e=!0}}if(!e)return}t.isHorizontal()?((d||c||p||u)&&(a.preventDefault?a.preventDefault():a.returnValue=!1),((c||u)&&!s||(d||p)&&s)&&t.slideNext(),((d||p)&&!s||(c||u)&&s)&&t.slidePrev()):((d||c||m||h)&&(a.preventDefault?a.preventDefault():a.returnValue=!1),(c||h)&&t.slideNext(),(d||m)&&t.slidePrev()),n("keyPress",i)}}function c(){t.keyboard.enabled||(l.addEventListener("keydown",d),t.keyboard.enabled=!0)}function p(){t.keyboard.enabled&&(l.removeEventListener("keydown",d),t.keyboard.enabled=!1)}t.keyboard={enabled:!1},s({keyboard:{enabled:!1,onlyInViewport:!0,pageUpDown:!0}}),i("init",(()=>{t.params.keyboard.enabled&&c()})),i("destroy",(()=>{t.keyboard.enabled&&p()})),Object.assign(t.keyboard,{enable:c,disable:p})},function(e){let{swiper:t,extendParams:s,on:a,emit:i}=e;const n=r();let d;s({mousewheel:{enabled:!1,releaseOnEdges:!1,invert:!1,forceToAxis:!1,sensitivity:1,eventsTarget:"container",thresholdDelta:null,thresholdTime:null,noMousewheelClass:"swiper-no-mousewheel"}}),t.mousewheel={enabled:!1};let c,p=o();const u=[];function m(){t.enabled&&(t.mouseEntered=!0)}function h(){t.enabled&&(t.mouseEntered=!1)}function f(e){return!(t.params.mousewheel.thresholdDelta&&e.delta=6&&o()-p<60||(e.direction<0?t.isEnd&&!t.params.loop||t.animating||(t.slideNext(),i("scroll",e.raw)):t.isBeginning&&!t.params.loop||t.animating||(t.slidePrev(),i("scroll",e.raw)),p=(new n.Date).getTime(),!1)))}function g(e){let s=e,a=!0;if(!t.enabled)return;if(e.target.closest(`.${t.params.mousewheel.noMousewheelClass}`))return;const r=t.params.mousewheel;t.params.cssMode&&s.preventDefault();let n=t.el;"container"!==t.params.mousewheel.eventsTarget&&(n=document.querySelector(t.params.mousewheel.eventsTarget));const p=n&&n.contains(s.target);if(!t.mouseEntered&&!p&&!r.releaseOnEdges)return!0;s.originalEvent&&(s=s.originalEvent);let m=0;const h=t.rtlTranslate?-1:1,g=function(e){let t=0,s=0,a=0,i=0;return"detail"in e&&(s=e.detail),"wheelDelta"in e&&(s=-e.wheelDelta/120),"wheelDeltaY"in e&&(s=-e.wheelDeltaY/120),"wheelDeltaX"in e&&(t=-e.wheelDeltaX/120),"axis"in e&&e.axis===e.HORIZONTAL_AXIS&&(t=s,s=0),a=10*t,i=10*s,"deltaY"in e&&(i=e.deltaY),"deltaX"in e&&(a=e.deltaX),e.shiftKey&&!a&&(a=i,i=0),(a||i)&&e.deltaMode&&(1===e.deltaMode?(a*=40,i*=40):(a*=800,i*=800)),a&&!t&&(t=a<1?-1:1),i&&!s&&(s=i<1?-1:1),{spinX:t,spinY:s,pixelX:a,pixelY:i}}(s);if(r.forceToAxis)if(t.isHorizontal()){if(!(Math.abs(g.pixelX)>Math.abs(g.pixelY)))return!0;m=-g.pixelX*h}else{if(!(Math.abs(g.pixelY)>Math.abs(g.pixelX)))return!0;m=-g.pixelY}else m=Math.abs(g.pixelX)>Math.abs(g.pixelY)?-g.pixelX*h:-g.pixelY;if(0===m)return!0;r.invert&&(m=-m);let v=t.getTranslate()+m*r.sensitivity;if(v>=t.minTranslate()&&(v=t.minTranslate()),v<=t.maxTranslate()&&(v=t.maxTranslate()),a=!!t.params.loop||!(v===t.minTranslate()||v===t.maxTranslate()),a&&t.params.nested&&s.stopPropagation(),t.params.freeMode&&t.params.freeMode.enabled){const e={time:o(),delta:Math.abs(m),direction:Math.sign(m)},a=c&&e.time=t.minTranslate()&&(n=t.minTranslate()),n<=t.maxTranslate()&&(n=t.maxTranslate()),t.setTransition(0),t.setTranslate(n),t.updateProgress(),t.updateActiveIndex(),t.updateSlidesClasses(),(!o&&t.isBeginning||!p&&t.isEnd)&&t.updateSlidesClasses(),t.params.loop&&t.loopFix({direction:e.direction<0?"next":"prev",byMousewheel:!0}),t.params.freeMode.sticky){clearTimeout(d),d=void 0,u.length>=15&&u.shift();const s=u.length?u[u.length-1]:void 0,a=u[0];if(u.push(e),s&&(e.delta>s.delta||e.direction!==s.direction))u.splice(0);else if(u.length>=15&&e.time-a.time<500&&a.delta-e.delta>=1&&e.delta<=6){const s=m>0?.8:.2;c=e,u.splice(0),d=l((()=>{!t.destroyed&&t.params&&t.slideToClosest(t.params.speed,!0,void 0,s)}),0)}d||(d=l((()=>{if(t.destroyed||!t.params)return;c=e,u.splice(0),t.slideToClosest(t.params.speed,!0,void 0,.5)}),500))}if(a||i("scroll",s),t.params.autoplay&&t.params.autoplay.disableOnInteraction&&t.autoplay.stop(),r.releaseOnEdges&&(n===t.minTranslate()||n===t.maxTranslate()))return!0}}else{const s={time:o(),delta:Math.abs(m),direction:Math.sign(m),raw:e};u.length>=2&&u.shift();const a=u.length?u[u.length-1]:void 0;if(u.push(s),a?(s.direction!==a.direction||s.delta>a.delta||s.time>a.time+150)&&f(s):f(s),function(e){const s=t.params.mousewheel;if(e.direction<0){if(t.isEnd&&!t.params.loop&&s.releaseOnEdges)return!0}else if(t.isBeginning&&!t.params.loop&&s.releaseOnEdges)return!0;return!1}(s))return!0}return s.preventDefault?s.preventDefault():s.returnValue=!1,!1}function v(e){let s=t.el;"container"!==t.params.mousewheel.eventsTarget&&(s=document.querySelector(t.params.mousewheel.eventsTarget)),s[e]("mouseenter",m),s[e]("mouseleave",h),s[e]("wheel",g)}function w(){return t.params.cssMode?(t.wrapperEl.removeEventListener("wheel",g),!0):!t.mousewheel.enabled&&(v("addEventListener"),t.mousewheel.enabled=!0,!0)}function b(){return t.params.cssMode?(t.wrapperEl.addEventListener(event,g),!0):!!t.mousewheel.enabled&&(v("removeEventListener"),t.mousewheel.enabled=!1,!0)}a("init",(()=>{!t.params.mousewheel.enabled&&t.params.cssMode&&b(),t.params.mousewheel.enabled&&w()})),a("destroy",(()=>{t.params.cssMode&&w(),t.mousewheel.enabled&&b()})),Object.assign(t.mousewheel,{enable:w,disable:b})},function(e){let{swiper:t,extendParams:s,on:a,emit:i}=e;function r(e){let s;return e&&"string"==typeof e&&t.isElement&&(s=t.el.querySelector(e)||t.hostEl.querySelector(e),s)?s:(e&&("string"==typeof e&&(s=[...document.querySelectorAll(e)]),t.params.uniqueNavElements&&"string"==typeof e&&s&&s.length>1&&1===t.el.querySelectorAll(e).length?s=t.el.querySelector(e):s&&1===s.length&&(s=s[0])),e&&!s?e:s)}function n(e,s){const a=t.params.navigation;(e=T(e)).forEach((e=>{e&&(e.classList[s?"add":"remove"](...a.disabledClass.split(" ")),"BUTTON"===e.tagName&&(e.disabled=s),t.params.watchOverflow&&t.enabled&&e.classList[t.isLocked?"add":"remove"](a.lockClass))}))}function l(){const{nextEl:e,prevEl:s}=t.navigation;if(t.params.loop)return n(s,!1),void n(e,!1);n(s,t.isBeginning&&!t.params.rewind),n(e,t.isEnd&&!t.params.rewind)}function o(e){e.preventDefault(),(!t.isBeginning||t.params.loop||t.params.rewind)&&(t.slidePrev(),i("navigationPrev"))}function d(e){e.preventDefault(),(!t.isEnd||t.params.loop||t.params.rewind)&&(t.slideNext(),i("navigationNext"))}function c(){const e=t.params.navigation;if(t.params.navigation=re(t,t.originalParams.navigation,t.params.navigation,{nextEl:"swiper-button-next",prevEl:"swiper-button-prev"}),!e.nextEl&&!e.prevEl)return;let s=r(e.nextEl),a=r(e.prevEl);Object.assign(t.navigation,{nextEl:s,prevEl:a}),s=T(s),a=T(a);const i=(s,a)=>{s&&s.addEventListener("click","next"===a?d:o),!t.enabled&&s&&s.classList.add(...e.lockClass.split(" "))};s.forEach((e=>i(e,"next"))),a.forEach((e=>i(e,"prev")))}function p(){let{nextEl:e,prevEl:s}=t.navigation;e=T(e),s=T(s);const a=(e,s)=>{e.removeEventListener("click","next"===s?d:o),e.classList.remove(...t.params.navigation.disabledClass.split(" "))};e.forEach((e=>a(e,"next"))),s.forEach((e=>a(e,"prev")))}s({navigation:{nextEl:null,prevEl:null,hideOnClick:!1,disabledClass:"swiper-button-disabled",hiddenClass:"swiper-button-hidden",lockClass:"swiper-button-lock",navigationDisabledClass:"swiper-navigation-disabled"}}),t.navigation={nextEl:null,prevEl:null},a("init",(()=>{!1===t.params.navigation.enabled?u():(c(),l())})),a("toEdge fromEdge lock unlock",(()=>{l()})),a("destroy",(()=>{p()})),a("enable disable",(()=>{let{nextEl:e,prevEl:s}=t.navigation;e=T(e),s=T(s),t.enabled?l():[...e,...s].filter((e=>!!e)).forEach((e=>e.classList.add(t.params.navigation.lockClass)))})),a("click",((e,s)=>{let{nextEl:a,prevEl:r}=t.navigation;a=T(a),r=T(r);const n=s.target;let l=r.includes(n)||a.includes(n);if(t.isElement&&!l){const e=s.path||s.composedPath&&s.composedPath();e&&(l=e.find((e=>a.includes(e)||r.includes(e))))}if(t.params.navigation.hideOnClick&&!l){if(t.pagination&&t.params.pagination&&t.params.pagination.clickable&&(t.pagination.el===n||t.pagination.el.contains(n)))return;let e;a.length?e=a[0].classList.contains(t.params.navigation.hiddenClass):r.length&&(e=r[0].classList.contains(t.params.navigation.hiddenClass)),i(!0===e?"navigationShow":"navigationHide"),[...a,...r].filter((e=>!!e)).forEach((e=>e.classList.toggle(t.params.navigation.hiddenClass)))}}));const u=()=>{t.el.classList.add(...t.params.navigation.navigationDisabledClass.split(" ")),p()};Object.assign(t.navigation,{enable:()=>{t.el.classList.remove(...t.params.navigation.navigationDisabledClass.split(" ")),c(),l()},disable:u,update:l,init:c,destroy:p})},function(e){let{swiper:t,extendParams:s,on:a,emit:i}=e;const r="swiper-pagination";let n;s({pagination:{el:null,bulletElement:"span",clickable:!1,hideOnClick:!1,renderBullet:null,renderProgressbar:null,renderFraction:null,renderCustom:null,progressbarOpposite:!1,type:"bullets",dynamicBullets:!1,dynamicMainBullets:1,formatFractionCurrent:e=>e,formatFractionTotal:e=>e,bulletClass:`${r}-bullet`,bulletActiveClass:`${r}-bullet-active`,modifierClass:`${r}-`,currentClass:`${r}-current`,totalClass:`${r}-total`,hiddenClass:`${r}-hidden`,progressbarFillClass:`${r}-progressbar-fill`,progressbarOppositeClass:`${r}-progressbar-opposite`,clickableClass:`${r}-clickable`,lockClass:`${r}-lock`,horizontalClass:`${r}-horizontal`,verticalClass:`${r}-vertical`,paginationDisabledClass:`${r}-disabled`}}),t.pagination={el:null,bullets:[]};let l=0;function o(){return!t.params.pagination.el||!t.pagination.el||Array.isArray(t.pagination.el)&&0===t.pagination.el.length}function d(e,s){const{bulletActiveClass:a}=t.params.pagination;e&&(e=e[("prev"===s?"previous":"next")+"ElementSibling"])&&(e.classList.add(`${a}-${s}`),(e=e[("prev"===s?"previous":"next")+"ElementSibling"])&&e.classList.add(`${a}-${s}-${s}`))}function c(e){const s=e.target.closest(ne(t.params.pagination.bulletClass));if(!s)return;e.preventDefault();const a=y(s)*t.params.slidesPerGroup;if(t.params.loop){if(t.realIndex===a)return;const e=(i=t.realIndex,r=a,n=t.slides.length,(r%=n)==1+(i%=n)?"next":r===i-1?"previous":void 0);"next"===e?t.slideNext():"previous"===e?t.slidePrev():t.slideToLoop(a)}else t.slideTo(a);var i,r,n}function p(){const e=t.rtl,s=t.params.pagination;if(o())return;let a,r,c=t.pagination.el;c=T(c);const p=t.virtual&&t.params.virtual.enabled?t.virtual.slides.length:t.slides.length,u=t.params.loop?Math.ceil(p/t.params.slidesPerGroup):t.snapGrid.length;if(t.params.loop?(r=t.previousRealIndex||0,a=t.params.slidesPerGroup>1?Math.floor(t.realIndex/t.params.slidesPerGroup):t.realIndex):void 0!==t.snapIndex?(a=t.snapIndex,r=t.previousSnapIndex):(r=t.previousIndex||0,a=t.activeIndex||0),"bullets"===s.type&&t.pagination.bullets&&t.pagination.bullets.length>0){const i=t.pagination.bullets;let o,p,u;if(s.dynamicBullets&&(n=S(i[0],t.isHorizontal()?"width":"height",!0),c.forEach((e=>{e.style[t.isHorizontal()?"width":"height"]=n*(s.dynamicMainBullets+4)+"px"})),s.dynamicMainBullets>1&&void 0!==r&&(l+=a-(r||0),l>s.dynamicMainBullets-1?l=s.dynamicMainBullets-1:l<0&&(l=0)),o=Math.max(a-l,0),p=o+(Math.min(i.length,s.dynamicMainBullets)-1),u=(p+o)/2),i.forEach((e=>{const t=[...["","-next","-next-next","-prev","-prev-prev","-main"].map((e=>`${s.bulletActiveClass}${e}`))].map((e=>"string"==typeof e&&e.includes(" ")?e.split(" "):e)).flat();e.classList.remove(...t)})),c.length>1)i.forEach((e=>{const i=y(e);i===a?e.classList.add(...s.bulletActiveClass.split(" ")):t.isElement&&e.setAttribute("part","bullet"),s.dynamicBullets&&(i>=o&&i<=p&&e.classList.add(...`${s.bulletActiveClass}-main`.split(" ")),i===o&&d(e,"prev"),i===p&&d(e,"next"))}));else{const e=i[a];if(e&&e.classList.add(...s.bulletActiveClass.split(" ")),t.isElement&&i.forEach(((e,t)=>{e.setAttribute("part",t===a?"bullet-active":"bullet")})),s.dynamicBullets){const e=i[o],t=i[p];for(let e=o;e<=p;e+=1)i[e]&&i[e].classList.add(...`${s.bulletActiveClass}-main`.split(" "));d(e,"prev"),d(t,"next")}}if(s.dynamicBullets){const a=Math.min(i.length,s.dynamicMainBullets+4),r=(n*a-n)/2-u*n,l=e?"right":"left";i.forEach((e=>{e.style[t.isHorizontal()?l:"top"]=`${r}px`}))}}c.forEach(((e,r)=>{if("fraction"===s.type&&(e.querySelectorAll(ne(s.currentClass)).forEach((e=>{e.textContent=s.formatFractionCurrent(a+1)})),e.querySelectorAll(ne(s.totalClass)).forEach((e=>{e.textContent=s.formatFractionTotal(u)}))),"progressbar"===s.type){let i;i=s.progressbarOpposite?t.isHorizontal()?"vertical":"horizontal":t.isHorizontal()?"horizontal":"vertical";const r=(a+1)/u;let n=1,l=1;"horizontal"===i?n=r:l=r,e.querySelectorAll(ne(s.progressbarFillClass)).forEach((e=>{e.style.transform=`translate3d(0,0,0) scaleX(${n}) scaleY(${l})`,e.style.transitionDuration=`${t.params.speed}ms`}))}"custom"===s.type&&s.renderCustom?(e.innerHTML=s.renderCustom(t,a+1,u),0===r&&i("paginationRender",e)):(0===r&&i("paginationRender",e),i("paginationUpdate",e)),t.params.watchOverflow&&t.enabled&&e.classList[t.isLocked?"add":"remove"](s.lockClass)}))}function u(){const e=t.params.pagination;if(o())return;const s=t.virtual&&t.params.virtual.enabled?t.virtual.slides.length:t.grid&&t.params.grid.rows>1?t.slides.length/Math.ceil(t.params.grid.rows):t.slides.length;let a=t.pagination.el;a=T(a);let r="";if("bullets"===e.type){let a=t.params.loop?Math.ceil(s/t.params.slidesPerGroup):t.snapGrid.length;t.params.freeMode&&t.params.freeMode.enabled&&a>s&&(a=s);for(let s=0;s`}"fraction"===e.type&&(r=e.renderFraction?e.renderFraction.call(t,e.currentClass,e.totalClass):` / `),"progressbar"===e.type&&(r=e.renderProgressbar?e.renderProgressbar.call(t,e.progressbarFillClass):``),t.pagination.bullets=[],a.forEach((s=>{"custom"!==e.type&&(s.innerHTML=r||""),"bullets"===e.type&&t.pagination.bullets.push(...s.querySelectorAll(ne(e.bulletClass)))})),"custom"!==e.type&&i("paginationRender",a[0])}function m(){t.params.pagination=re(t,t.originalParams.pagination,t.params.pagination,{el:"swiper-pagination"});const e=t.params.pagination;if(!e.el)return;let s;"string"==typeof e.el&&t.isElement&&(s=t.el.querySelector(e.el)),s||"string"!=typeof e.el||(s=[...document.querySelectorAll(e.el)]),s||(s=e.el),s&&0!==s.length&&(t.params.uniqueNavElements&&"string"==typeof e.el&&Array.isArray(s)&&s.length>1&&(s=[...t.el.querySelectorAll(e.el)],s.length>1&&(s=s.find((e=>E(e,".swiper")[0]===t.el)))),Array.isArray(s)&&1===s.length&&(s=s[0]),Object.assign(t.pagination,{el:s}),s=T(s),s.forEach((s=>{"bullets"===e.type&&e.clickable&&s.classList.add(...(e.clickableClass||"").split(" ")),s.classList.add(e.modifierClass+e.type),s.classList.add(t.isHorizontal()?e.horizontalClass:e.verticalClass),"bullets"===e.type&&e.dynamicBullets&&(s.classList.add(`${e.modifierClass}${e.type}-dynamic`),l=0,e.dynamicMainBullets<1&&(e.dynamicMainBullets=1)),"progressbar"===e.type&&e.progressbarOpposite&&s.classList.add(e.progressbarOppositeClass),e.clickable&&s.addEventListener("click",c),t.enabled||s.classList.add(e.lockClass)})))}function h(){const e=t.params.pagination;if(o())return;let s=t.pagination.el;s&&(s=T(s),s.forEach((s=>{s.classList.remove(e.hiddenClass),s.classList.remove(e.modifierClass+e.type),s.classList.remove(t.isHorizontal()?e.horizontalClass:e.verticalClass),e.clickable&&(s.classList.remove(...(e.clickableClass||"").split(" ")),s.removeEventListener("click",c))}))),t.pagination.bullets&&t.pagination.bullets.forEach((t=>t.classList.remove(...e.bulletActiveClass.split(" "))))}a("changeDirection",(()=>{if(!t.pagination||!t.pagination.el)return;const e=t.params.pagination;let{el:s}=t.pagination;s=T(s),s.forEach((s=>{s.classList.remove(e.horizontalClass,e.verticalClass),s.classList.add(t.isHorizontal()?e.horizontalClass:e.verticalClass)}))})),a("init",(()=>{!1===t.params.pagination.enabled?f():(m(),u(),p())})),a("activeIndexChange",(()=>{void 0===t.snapIndex&&p()})),a("snapIndexChange",(()=>{p()})),a("snapGridLengthChange",(()=>{u(),p()})),a("destroy",(()=>{h()})),a("enable disable",(()=>{let{el:e}=t.pagination;e&&(e=T(e),e.forEach((e=>e.classList[t.enabled?"remove":"add"](t.params.pagination.lockClass))))})),a("lock unlock",(()=>{p()})),a("click",((e,s)=>{const a=s.target,r=T(t.pagination.el);if(t.params.pagination.el&&t.params.pagination.hideOnClick&&r&&r.length>0&&!a.classList.contains(t.params.pagination.bulletClass)){if(t.navigation&&(t.navigation.nextEl&&a===t.navigation.nextEl||t.navigation.prevEl&&a===t.navigation.prevEl))return;const e=r[0].classList.contains(t.params.pagination.hiddenClass);i(!0===e?"paginationShow":"paginationHide"),r.forEach((e=>e.classList.toggle(t.params.pagination.hiddenClass)))}}));const f=()=>{t.el.classList.add(t.params.pagination.paginationDisabledClass);let{el:e}=t.pagination;e&&(e=T(e),e.forEach((e=>e.classList.add(t.params.pagination.paginationDisabledClass)))),h()};Object.assign(t.pagination,{enable:()=>{t.el.classList.remove(t.params.pagination.paginationDisabledClass);let{el:e}=t.pagination;e&&(e=T(e),e.forEach((e=>e.classList.remove(t.params.pagination.paginationDisabledClass)))),m(),u(),p()},disable:f,render:u,update:p,init:m,destroy:h})},function(e){let{swiper:t,extendParams:s,on:i,emit:r}=e;const o=a();let d,c,p,u,m=!1,h=null,f=null;function g(){if(!t.params.scrollbar.el||!t.scrollbar.el)return;const{scrollbar:e,rtlTranslate:s}=t,{dragEl:a,el:i}=e,r=t.params.scrollbar,n=t.params.loop?t.progressLoop:t.progress;let l=c,o=(p-c)*n;s?(o=-o,o>0?(l=c-o,o=0):-o+c>p&&(l=p+o)):o<0?(l=c+o,o=0):o+c>p&&(l=p-o),t.isHorizontal()?(a.style.transform=`translate3d(${o}px, 0, 0)`,a.style.width=`${l}px`):(a.style.transform=`translate3d(0px, ${o}px, 0)`,a.style.height=`${l}px`),r.hide&&(clearTimeout(h),i.style.opacity=1,h=setTimeout((()=>{i.style.opacity=0,i.style.transitionDuration="400ms"}),1e3))}function b(){if(!t.params.scrollbar.el||!t.scrollbar.el)return;const{scrollbar:e}=t,{dragEl:s,el:a}=e;s.style.width="",s.style.height="",p=t.isHorizontal()?a.offsetWidth:a.offsetHeight,u=t.size/(t.virtualSize+t.params.slidesOffsetBefore-(t.params.centeredSlides?t.snapGrid[0]:0)),c="auto"===t.params.scrollbar.dragSize?p*u:parseInt(t.params.scrollbar.dragSize,10),t.isHorizontal()?s.style.width=`${c}px`:s.style.height=`${c}px`,a.style.display=u>=1?"none":"",t.params.scrollbar.hide&&(a.style.opacity=0),t.params.watchOverflow&&t.enabled&&e.el.classList[t.isLocked?"add":"remove"](t.params.scrollbar.lockClass)}function y(e){return t.isHorizontal()?e.clientX:e.clientY}function E(e){const{scrollbar:s,rtlTranslate:a}=t,{el:i}=s;let r;r=(y(e)-w(i)[t.isHorizontal()?"left":"top"]-(null!==d?d:c/2))/(p-c),r=Math.max(Math.min(r,1),0),a&&(r=1-r);const n=t.minTranslate()+(t.maxTranslate()-t.minTranslate())*r;t.updateProgress(n),t.setTranslate(n),t.updateActiveIndex(),t.updateSlidesClasses()}function x(e){const s=t.params.scrollbar,{scrollbar:a,wrapperEl:i}=t,{el:n,dragEl:l}=a;m=!0,d=e.target===l?y(e)-e.target.getBoundingClientRect()[t.isHorizontal()?"left":"top"]:null,e.preventDefault(),e.stopPropagation(),i.style.transitionDuration="100ms",l.style.transitionDuration="100ms",E(e),clearTimeout(f),n.style.transitionDuration="0ms",s.hide&&(n.style.opacity=1),t.params.cssMode&&(t.wrapperEl.style["scroll-snap-type"]="none"),r("scrollbarDragStart",e)}function S(e){const{scrollbar:s,wrapperEl:a}=t,{el:i,dragEl:n}=s;m&&(e.preventDefault&&e.cancelable?e.preventDefault():e.returnValue=!1,E(e),a.style.transitionDuration="0ms",i.style.transitionDuration="0ms",n.style.transitionDuration="0ms",r("scrollbarDragMove",e))}function M(e){const s=t.params.scrollbar,{scrollbar:a,wrapperEl:i}=t,{el:n}=a;m&&(m=!1,t.params.cssMode&&(t.wrapperEl.style["scroll-snap-type"]="",i.style.transitionDuration=""),s.hide&&(clearTimeout(f),f=l((()=>{n.style.opacity=0,n.style.transitionDuration="400ms"}),1e3)),r("scrollbarDragEnd",e),s.snapOnRelease&&t.slideToClosest())}function C(e){const{scrollbar:s,params:a}=t,i=s.el;if(!i)return;const r=i,n=!!a.passiveListeners&&{passive:!1,capture:!1},l=!!a.passiveListeners&&{passive:!0,capture:!1};if(!r)return;const d="on"===e?"addEventListener":"removeEventListener";r[d]("pointerdown",x,n),o[d]("pointermove",S,n),o[d]("pointerup",M,l)}function P(){const{scrollbar:e,el:s}=t;t.params.scrollbar=re(t,t.originalParams.scrollbar,t.params.scrollbar,{el:"swiper-scrollbar"});const a=t.params.scrollbar;if(!a.el)return;let i,r;if("string"==typeof a.el&&t.isElement&&(i=t.el.querySelector(a.el)),i||"string"!=typeof a.el)i||(i=a.el);else if(i=o.querySelectorAll(a.el),!i.length)return;t.params.uniqueNavElements&&"string"==typeof a.el&&i.length>1&&1===s.querySelectorAll(a.el).length&&(i=s.querySelector(a.el)),i.length>0&&(i=i[0]),i.classList.add(t.isHorizontal()?a.horizontalClass:a.verticalClass),i&&(r=i.querySelector(ne(t.params.scrollbar.dragClass)),r||(r=v("div",t.params.scrollbar.dragClass),i.append(r))),Object.assign(e,{el:i,dragEl:r}),a.draggable&&t.params.scrollbar.el&&t.scrollbar.el&&C("on"),i&&i.classList[t.enabled?"remove":"add"](...n(t.params.scrollbar.lockClass))}function L(){const e=t.params.scrollbar,s=t.scrollbar.el;s&&s.classList.remove(...n(t.isHorizontal()?e.horizontalClass:e.verticalClass)),t.params.scrollbar.el&&t.scrollbar.el&&C("off")}s({scrollbar:{el:null,dragSize:"auto",hide:!1,draggable:!1,snapOnRelease:!0,lockClass:"swiper-scrollbar-lock",dragClass:"swiper-scrollbar-drag",scrollbarDisabledClass:"swiper-scrollbar-disabled",horizontalClass:"swiper-scrollbar-horizontal",verticalClass:"swiper-scrollbar-vertical"}}),t.scrollbar={el:null,dragEl:null},i("changeDirection",(()=>{if(!t.scrollbar||!t.scrollbar.el)return;const e=t.params.scrollbar;let{el:s}=t.scrollbar;s=T(s),s.forEach((s=>{s.classList.remove(e.horizontalClass,e.verticalClass),s.classList.add(t.isHorizontal()?e.horizontalClass:e.verticalClass)}))})),i("init",(()=>{!1===t.params.scrollbar.enabled?I():(P(),b(),g())})),i("update resize observerUpdate lock unlock changeDirection",(()=>{b()})),i("setTranslate",(()=>{g()})),i("setTransition",((e,s)=>{!function(e){t.params.scrollbar.el&&t.scrollbar.el&&(t.scrollbar.dragEl.style.transitionDuration=`${e}ms`)}(s)})),i("enable disable",(()=>{const{el:e}=t.scrollbar;e&&e.classList[t.enabled?"remove":"add"](...n(t.params.scrollbar.lockClass))})),i("destroy",(()=>{L()}));const I=()=>{t.el.classList.add(...n(t.params.scrollbar.scrollbarDisabledClass)),t.scrollbar.el&&t.scrollbar.el.classList.add(...n(t.params.scrollbar.scrollbarDisabledClass)),L()};Object.assign(t.scrollbar,{enable:()=>{t.el.classList.remove(...n(t.params.scrollbar.scrollbarDisabledClass)),t.scrollbar.el&&t.scrollbar.el.classList.remove(...n(t.params.scrollbar.scrollbarDisabledClass)),P(),b(),g()},disable:I,updateSize:b,setTranslate:g,init:P,destroy:L})},function(e){let{swiper:t,extendParams:s,on:a}=e;s({parallax:{enabled:!1}});const i="[data-swiper-parallax], [data-swiper-parallax-x], [data-swiper-parallax-y], [data-swiper-parallax-opacity], [data-swiper-parallax-scale]",r=(e,s)=>{const{rtl:a}=t,i=a?-1:1,r=e.getAttribute("data-swiper-parallax")||"0";let n=e.getAttribute("data-swiper-parallax-x"),l=e.getAttribute("data-swiper-parallax-y");const o=e.getAttribute("data-swiper-parallax-scale"),d=e.getAttribute("data-swiper-parallax-opacity"),c=e.getAttribute("data-swiper-parallax-rotate");if(n||l?(n=n||"0",l=l||"0"):t.isHorizontal()?(n=r,l="0"):(l=r,n="0"),n=n.indexOf("%")>=0?parseInt(n,10)*s*i+"%":n*s*i+"px",l=l.indexOf("%")>=0?parseInt(l,10)*s+"%":l*s+"px",null!=d){const t=d-(d-1)*(1-Math.abs(s));e.style.opacity=t}let p=`translate3d(${n}, ${l}, 0px)`;if(null!=o){p+=` scale(${o-(o-1)*(1-Math.abs(s))})`}if(c&&null!=c){p+=` rotate(${c*s*-1}deg)`}e.style.transform=p},n=()=>{const{el:e,slides:s,progress:a,snapGrid:n,isElement:l}=t,o=f(e,i);t.isElement&&o.push(...f(t.hostEl,i)),o.forEach((e=>{r(e,a)})),s.forEach(((e,s)=>{let l=e.progress;t.params.slidesPerGroup>1&&"auto"!==t.params.slidesPerView&&(l+=Math.ceil(s/2)-a*(n.length-1)),l=Math.min(Math.max(l,-1),1),e.querySelectorAll(`${i}, [data-swiper-parallax-rotate]`).forEach((e=>{r(e,l)}))}))};a("beforeInit",(()=>{t.params.parallax.enabled&&(t.params.watchSlidesProgress=!0,t.originalParams.watchSlidesProgress=!0)})),a("init",(()=>{t.params.parallax.enabled&&n()})),a("setTranslate",(()=>{t.params.parallax.enabled&&n()})),a("setTransition",((e,s)=>{t.params.parallax.enabled&&function(e){void 0===e&&(e=t.params.speed);const{el:s,hostEl:a}=t,r=[...s.querySelectorAll(i)];t.isElement&&r.push(...a.querySelectorAll(i)),r.forEach((t=>{let s=parseInt(t.getAttribute("data-swiper-parallax-duration"),10)||e;0===e&&(s=0),t.style.transitionDuration=`${s}ms`}))}(s)}))},function(e){let{swiper:t,extendParams:s,on:a,emit:i}=e;const n=r();s({zoom:{enabled:!1,limitToOriginalSize:!1,maxRatio:3,minRatio:1,panOnMouseMove:!1,toggle:!0,containerClass:"swiper-zoom-container",zoomedSlideClass:"swiper-slide-zoomed"}}),t.zoom={enabled:!1};let l=1,o=!1,c=!1,p={x:0,y:0};const u=-3;let m,h;const g=[],v={originX:0,originY:0,slideEl:void 0,slideWidth:void 0,slideHeight:void 0,imageEl:void 0,imageWrapEl:void 0,maxRatio:3},b={isTouched:void 0,isMoved:void 0,currentX:void 0,currentY:void 0,minX:void 0,minY:void 0,maxX:void 0,maxY:void 0,width:void 0,height:void 0,startX:void 0,startY:void 0,touchesStart:{},touchesCurrent:{}},y={x:void 0,y:void 0,prevPositionX:void 0,prevPositionY:void 0,prevTime:void 0};let x,S=1;function T(){if(g.length<2)return 1;const e=g[0].pageX,t=g[0].pageY,s=g[1].pageX,a=g[1].pageY;return Math.sqrt((s-e)**2+(a-t)**2)}function M(){const e=t.params.zoom,s=v.imageWrapEl.getAttribute("data-swiper-zoom")||e.maxRatio;if(e.limitToOriginalSize&&v.imageEl&&v.imageEl.naturalWidth){const e=v.imageEl.naturalWidth/v.imageEl.offsetWidth;return Math.min(e,s)}return s}function C(e){const s=t.isElement?"swiper-slide":`.${t.params.slideClass}`;return!!e.target.matches(s)||t.slides.filter((t=>t.contains(e.target))).length>0}function P(e){const s=`.${t.params.zoom.containerClass}`;return!!e.target.matches(s)||[...t.hostEl.querySelectorAll(s)].filter((t=>t.contains(e.target))).length>0}function L(e){if("mouse"===e.pointerType&&g.splice(0,g.length),!C(e))return;const s=t.params.zoom;if(m=!1,h=!1,g.push(e),!(g.length<2)){if(m=!0,v.scaleStart=T(),!v.slideEl){v.slideEl=e.target.closest(`.${t.params.slideClass}, swiper-slide`),v.slideEl||(v.slideEl=t.slides[t.activeIndex]);let a=v.slideEl.querySelector(`.${s.containerClass}`);if(a&&(a=a.querySelectorAll("picture, img, svg, canvas, .swiper-zoom-target")[0]),v.imageEl=a,v.imageWrapEl=a?E(v.imageEl,`.${s.containerClass}`)[0]:void 0,!v.imageWrapEl)return void(v.imageEl=void 0);v.maxRatio=M()}if(v.imageEl){const[e,t]=function(){if(g.length<2)return{x:null,y:null};const e=v.imageEl.getBoundingClientRect();return[(g[0].pageX+(g[1].pageX-g[0].pageX)/2-e.x-n.scrollX)/l,(g[0].pageY+(g[1].pageY-g[0].pageY)/2-e.y-n.scrollY)/l]}();v.originX=e,v.originY=t,v.imageEl.style.transitionDuration="0ms"}o=!0}}function I(e){if(!C(e))return;const s=t.params.zoom,a=t.zoom,i=g.findIndex((t=>t.pointerId===e.pointerId));i>=0&&(g[i]=e),g.length<2||(h=!0,v.scaleMove=T(),v.imageEl&&(a.scale=v.scaleMove/v.scaleStart*l,a.scale>v.maxRatio&&(a.scale=v.maxRatio-1+(a.scale-v.maxRatio+1)**.5),a.scalet.pointerId===e.pointerId));i>=0&&g.splice(i,1),m&&h&&(m=!1,h=!1,v.imageEl&&(a.scale=Math.max(Math.min(a.scale,v.maxRatio),s.minRatio),v.imageEl.style.transitionDuration=`${t.params.speed}ms`,v.imageEl.style.transform=`translate3d(0,0,0) scale(${a.scale})`,l=a.scale,o=!1,a.scale>1&&v.slideEl?v.slideEl.classList.add(`${s.zoomedSlideClass}`):a.scale<=1&&v.slideEl&&v.slideEl.classList.remove(`${s.zoomedSlideClass}`),1===a.scale&&(v.originX=0,v.originY=0,v.slideEl=void 0)))}function A(){t.touchEventsData.preventTouchMoveFromPointerMove=!1}function $(e){const s="mouse"===e.pointerType&&t.params.zoom.panOnMouseMove;if(!C(e)||!P(e))return;const a=t.zoom;if(!v.imageEl)return;if(!b.isTouched||!v.slideEl)return void(s&&O(e));if(s)return void O(e);b.isMoved||(b.width=v.imageEl.offsetWidth||v.imageEl.clientWidth,b.height=v.imageEl.offsetHeight||v.imageEl.clientHeight,b.startX=d(v.imageWrapEl,"x")||0,b.startY=d(v.imageWrapEl,"y")||0,v.slideWidth=v.slideEl.offsetWidth,v.slideHeight=v.slideEl.offsetHeight,v.imageWrapEl.style.transitionDuration="0ms");const i=b.width*a.scale,r=b.height*a.scale;b.minX=Math.min(v.slideWidth/2-i/2,0),b.maxX=-b.minX,b.minY=Math.min(v.slideHeight/2-r/2,0),b.maxY=-b.minY,b.touchesCurrent.x=g.length>0?g[0].pageX:e.pageX,b.touchesCurrent.y=g.length>0?g[0].pageY:e.pageY;if(Math.max(Math.abs(b.touchesCurrent.x-b.touchesStart.x),Math.abs(b.touchesCurrent.y-b.touchesStart.y))>5&&(t.allowClick=!1),!b.isMoved&&!o){if(t.isHorizontal()&&(Math.floor(b.minX)===Math.floor(b.startX)&&b.touchesCurrent.xb.touchesStart.x))return b.isTouched=!1,void A();if(!t.isHorizontal()&&(Math.floor(b.minY)===Math.floor(b.startY)&&b.touchesCurrent.yb.touchesStart.y))return b.isTouched=!1,void A()}e.cancelable&&e.preventDefault(),e.stopPropagation(),clearTimeout(x),t.touchEventsData.preventTouchMoveFromPointerMove=!0,x=setTimeout((()=>{t.destroyed||A()})),b.isMoved=!0;const n=(a.scale-l)/(v.maxRatio-t.params.zoom.minRatio),{originX:c,originY:p}=v;b.currentX=b.touchesCurrent.x-b.touchesStart.x+b.startX+n*(b.width-2*c),b.currentY=b.touchesCurrent.y-b.touchesStart.y+b.startY+n*(b.height-2*p),b.currentXb.maxX&&(b.currentX=b.maxX-1+(b.currentX-b.maxX+1)**.8),b.currentYb.maxY&&(b.currentY=b.maxY-1+(b.currentY-b.maxY+1)**.8),y.prevPositionX||(y.prevPositionX=b.touchesCurrent.x),y.prevPositionY||(y.prevPositionY=b.touchesCurrent.y),y.prevTime||(y.prevTime=Date.now()),y.x=(b.touchesCurrent.x-y.prevPositionX)/(Date.now()-y.prevTime)/2,y.y=(b.touchesCurrent.y-y.prevPositionY)/(Date.now()-y.prevTime)/2,Math.abs(b.touchesCurrent.x-y.prevPositionX)<2&&(y.x=0),Math.abs(b.touchesCurrent.y-y.prevPositionY)<2&&(y.y=0),y.prevPositionX=b.touchesCurrent.x,y.prevPositionY=b.touchesCurrent.y,y.prevTime=Date.now(),v.imageWrapEl.style.transform=`translate3d(${b.currentX}px, ${b.currentY}px,0)`}function k(){const e=t.zoom;v.slideEl&&t.activeIndex!==t.slides.indexOf(v.slideEl)&&(v.imageEl&&(v.imageEl.style.transform="translate3d(0,0,0) scale(1)"),v.imageWrapEl&&(v.imageWrapEl.style.transform="translate3d(0,0,0)"),v.slideEl.classList.remove(`${t.params.zoom.zoomedSlideClass}`),e.scale=1,l=1,v.slideEl=void 0,v.imageEl=void 0,v.imageWrapEl=void 0,v.originX=0,v.originY=0)}function O(e){if(l<=1||!v.imageWrapEl)return;if(!C(e)||!P(e))return;const t=n.getComputedStyle(v.imageWrapEl).transform,s=new n.DOMMatrix(t);if(!c)return c=!0,p.x=e.clientX,p.y=e.clientY,b.startX=s.e,b.startY=s.f,b.width=v.imageEl.offsetWidth||v.imageEl.clientWidth,b.height=v.imageEl.offsetHeight||v.imageEl.clientHeight,v.slideWidth=v.slideEl.offsetWidth,void(v.slideHeight=v.slideEl.offsetHeight);const a=(e.clientX-p.x)*u,i=(e.clientY-p.y)*u,r=b.width*l,o=b.height*l,d=v.slideWidth,m=v.slideHeight,h=Math.min(d/2-r/2,0),f=-h,g=Math.min(m/2-o/2,0),w=-g,y=Math.max(Math.min(b.startX+a,f),h),E=Math.max(Math.min(b.startY+i,w),g);v.imageWrapEl.style.transitionDuration="0ms",v.imageWrapEl.style.transform=`translate3d(${y}px, ${E}px, 0)`,p.x=e.clientX,p.y=e.clientY,b.startX=y,b.startY=E,b.currentX=y,b.currentY=E}function D(e){const s=t.zoom,a=t.params.zoom;if(!v.slideEl){e&&e.target&&(v.slideEl=e.target.closest(`.${t.params.slideClass}, swiper-slide`)),v.slideEl||(t.params.virtual&&t.params.virtual.enabled&&t.virtual?v.slideEl=f(t.slidesEl,`.${t.params.slideActiveClass}`)[0]:v.slideEl=t.slides[t.activeIndex]);let s=v.slideEl.querySelector(`.${a.containerClass}`);s&&(s=s.querySelectorAll("picture, img, svg, canvas, .swiper-zoom-target")[0]),v.imageEl=s,v.imageWrapEl=s?E(v.imageEl,`.${a.containerClass}`)[0]:void 0}if(!v.imageEl||!v.imageWrapEl)return;let i,r,o,d,c,p,u,m,h,g,y,x,S,T,C,P,L,I;t.params.cssMode&&(t.wrapperEl.style.overflow="hidden",t.wrapperEl.style.touchAction="none"),v.slideEl.classList.add(`${a.zoomedSlideClass}`),void 0===b.touchesStart.x&&e?(i=e.pageX,r=e.pageY):(i=b.touchesStart.x,r=b.touchesStart.y);const z=l,A="number"==typeof e?e:null;1===l&&A&&(i=void 0,r=void 0,b.touchesStart.x=void 0,b.touchesStart.y=void 0);const $=M();s.scale=A||$,l=A||$,!e||1===l&&A?(u=0,m=0):(L=v.slideEl.offsetWidth,I=v.slideEl.offsetHeight,o=w(v.slideEl).left+n.scrollX,d=w(v.slideEl).top+n.scrollY,c=o+L/2-i,p=d+I/2-r,h=v.imageEl.offsetWidth||v.imageEl.clientWidth,g=v.imageEl.offsetHeight||v.imageEl.clientHeight,y=h*s.scale,x=g*s.scale,S=Math.min(L/2-y/2,0),T=Math.min(I/2-x/2,0),C=-S,P=-T,z>0&&A&&"number"==typeof b.currentX&&"number"==typeof b.currentY?(u=b.currentX*s.scale/z,m=b.currentY*s.scale/z):(u=c*s.scale,m=p*s.scale),uC&&(u=C),mP&&(m=P)),A&&1===s.scale&&(v.originX=0,v.originY=0),b.currentX=u,b.currentY=m,v.imageWrapEl.style.transitionDuration="300ms",v.imageWrapEl.style.transform=`translate3d(${u}px, ${m}px,0)`,v.imageEl.style.transitionDuration="300ms",v.imageEl.style.transform=`translate3d(0,0,0) scale(${s.scale})`}function G(){const e=t.zoom,s=t.params.zoom;if(!v.slideEl){t.params.virtual&&t.params.virtual.enabled&&t.virtual?v.slideEl=f(t.slidesEl,`.${t.params.slideActiveClass}`)[0]:v.slideEl=t.slides[t.activeIndex];let e=v.slideEl.querySelector(`.${s.containerClass}`);e&&(e=e.querySelectorAll("picture, img, svg, canvas, .swiper-zoom-target")[0]),v.imageEl=e,v.imageWrapEl=e?E(v.imageEl,`.${s.containerClass}`)[0]:void 0}v.imageEl&&v.imageWrapEl&&(t.params.cssMode&&(t.wrapperEl.style.overflow="",t.wrapperEl.style.touchAction=""),e.scale=1,l=1,b.currentX=void 0,b.currentY=void 0,b.touchesStart.x=void 0,b.touchesStart.y=void 0,v.imageWrapEl.style.transitionDuration="300ms",v.imageWrapEl.style.transform="translate3d(0,0,0)",v.imageEl.style.transitionDuration="300ms",v.imageEl.style.transform="translate3d(0,0,0) scale(1)",v.slideEl.classList.remove(`${s.zoomedSlideClass}`),v.slideEl=void 0,v.originX=0,v.originY=0,t.params.zoom.panOnMouseMove&&(p={x:0,y:0},c&&(c=!1,b.startX=0,b.startY=0)))}function X(e){const s=t.zoom;s.scale&&1!==s.scale?G():D(e)}function H(){return{passiveListener:!!t.params.passiveListeners&&{passive:!0,capture:!1},activeListenerWithCapture:!t.params.passiveListeners||{passive:!1,capture:!0}}}function Y(){const e=t.zoom;if(e.enabled)return;e.enabled=!0;const{passiveListener:s,activeListenerWithCapture:a}=H();t.wrapperEl.addEventListener("pointerdown",L,s),t.wrapperEl.addEventListener("pointermove",I,a),["pointerup","pointercancel","pointerout"].forEach((e=>{t.wrapperEl.addEventListener(e,z,s)})),t.wrapperEl.addEventListener("pointermove",$,a)}function B(){const e=t.zoom;if(!e.enabled)return;e.enabled=!1;const{passiveListener:s,activeListenerWithCapture:a}=H();t.wrapperEl.removeEventListener("pointerdown",L,s),t.wrapperEl.removeEventListener("pointermove",I,a),["pointerup","pointercancel","pointerout"].forEach((e=>{t.wrapperEl.removeEventListener(e,z,s)})),t.wrapperEl.removeEventListener("pointermove",$,a)}Object.defineProperty(t.zoom,"scale",{get:()=>S,set(e){if(S!==e){const t=v.imageEl,s=v.slideEl;i("zoomChange",e,t,s)}S=e}}),a("init",(()=>{t.params.zoom.enabled&&Y()})),a("destroy",(()=>{B()})),a("touchStart",((e,s)=>{t.zoom.enabled&&function(e){const s=t.device;if(!v.imageEl)return;if(b.isTouched)return;s.android&&e.cancelable&&e.preventDefault(),b.isTouched=!0;const a=g.length>0?g[0]:e;b.touchesStart.x=a.pageX,b.touchesStart.y=a.pageY}(s)})),a("touchEnd",((e,s)=>{t.zoom.enabled&&function(){const e=t.zoom;if(g.length=0,!v.imageEl)return;if(!b.isTouched||!b.isMoved)return b.isTouched=!1,void(b.isMoved=!1);b.isTouched=!1,b.isMoved=!1;let s=300,a=300;const i=y.x*s,r=b.currentX+i,n=y.y*a,l=b.currentY+n;0!==y.x&&(s=Math.abs((r-b.currentX)/y.x)),0!==y.y&&(a=Math.abs((l-b.currentY)/y.y));const o=Math.max(s,a);b.currentX=r,b.currentY=l;const d=b.width*e.scale,c=b.height*e.scale;b.minX=Math.min(v.slideWidth/2-d/2,0),b.maxX=-b.minX,b.minY=Math.min(v.slideHeight/2-c/2,0),b.maxY=-b.minY,b.currentX=Math.max(Math.min(b.currentX,b.maxX),b.minX),b.currentY=Math.max(Math.min(b.currentY,b.maxY),b.minY),v.imageWrapEl.style.transitionDuration=`${o}ms`,v.imageWrapEl.style.transform=`translate3d(${b.currentX}px, ${b.currentY}px,0)`}()})),a("doubleTap",((e,s)=>{!t.animating&&t.params.zoom.enabled&&t.zoom.enabled&&t.params.zoom.toggle&&X(s)})),a("transitionEnd",(()=>{t.zoom.enabled&&t.params.zoom.enabled&&k()})),a("slideChange",(()=>{t.zoom.enabled&&t.params.zoom.enabled&&t.params.cssMode&&k()})),Object.assign(t.zoom,{enable:Y,disable:B,in:D,out:G,toggle:X})},function(e){let{swiper:t,extendParams:s,on:a}=e;function i(e,t){const s=function(){let e,t,s;return(a,i)=>{for(t=-1,e=a.length;e-t>1;)s=e+t>>1,a[s]<=i?t=s:e=s;return e}}();let a,i;return this.x=e,this.y=t,this.lastIndex=e.length-1,this.interpolate=function(e){return e?(i=s(this.x,e),a=i-1,(e-this.x[a])*(this.y[i]-this.y[a])/(this.x[i]-this.x[a])+this.y[a]):0},this}function r(){t.controller.control&&t.controller.spline&&(t.controller.spline=void 0,delete t.controller.spline)}s({controller:{control:void 0,inverse:!1,by:"slide"}}),t.controller={control:void 0},a("beforeInit",(()=>{if("undefined"!=typeof window&&("string"==typeof t.params.controller.control||t.params.controller.control instanceof HTMLElement)){("string"==typeof t.params.controller.control?[...document.querySelectorAll(t.params.controller.control)]:[t.params.controller.control]).forEach((e=>{if(t.controller.control||(t.controller.control=[]),e&&e.swiper)t.controller.control.push(e.swiper);else if(e){const s=`${t.params.eventsPrefix}init`,a=i=>{t.controller.control.push(i.detail[0]),t.update(),e.removeEventListener(s,a)};e.addEventListener(s,a)}}))}else t.controller.control=t.params.controller.control})),a("update",(()=>{r()})),a("resize",(()=>{r()})),a("observerUpdate",(()=>{r()})),a("setTranslate",((e,s,a)=>{t.controller.control&&!t.controller.control.destroyed&&t.controller.setTranslate(s,a)})),a("setTransition",((e,s,a)=>{t.controller.control&&!t.controller.control.destroyed&&t.controller.setTransition(s,a)})),Object.assign(t.controller,{setTranslate:function(e,s){const a=t.controller.control;let r,n;const l=t.constructor;function o(e){if(e.destroyed)return;const s=t.rtlTranslate?-t.translate:t.translate;"slide"===t.params.controller.by&&(!function(e){t.controller.spline=t.params.loop?new i(t.slidesGrid,e.slidesGrid):new i(t.snapGrid,e.snapGrid)}(e),n=-t.controller.spline.interpolate(-s)),n&&"container"!==t.params.controller.by||(r=(e.maxTranslate()-e.minTranslate())/(t.maxTranslate()-t.minTranslate()),!Number.isNaN(r)&&Number.isFinite(r)||(r=1),n=(s-t.minTranslate())*r+e.minTranslate()),t.params.controller.inverse&&(n=e.maxTranslate()-n),e.updateProgress(n),e.setTranslate(n,t),e.updateActiveIndex(),e.updateSlidesClasses()}if(Array.isArray(a))for(let e=0;e{s.updateAutoHeight()})),x(s.wrapperEl,(()=>{i&&s.transitionEnd()}))))}if(Array.isArray(i))for(r=0;r{e.setAttribute("tabIndex","0")}))}function p(e){(e=T(e)).forEach((e=>{e.setAttribute("tabIndex","-1")}))}function u(e,t){(e=T(e)).forEach((e=>{e.setAttribute("role",t)}))}function m(e,t){(e=T(e)).forEach((e=>{e.setAttribute("aria-roledescription",t)}))}function h(e,t){(e=T(e)).forEach((e=>{e.setAttribute("aria-label",t)}))}function f(e){(e=T(e)).forEach((e=>{e.setAttribute("aria-disabled",!0)}))}function g(e){(e=T(e)).forEach((e=>{e.setAttribute("aria-disabled",!1)}))}function w(e){if(13!==e.keyCode&&32!==e.keyCode)return;const s=t.params.a11y,a=e.target;if(!t.pagination||!t.pagination.el||a!==t.pagination.el&&!t.pagination.el.contains(e.target)||e.target.matches(ne(t.params.pagination.bulletClass))){if(t.navigation&&t.navigation.prevEl&&t.navigation.nextEl){const e=T(t.navigation.prevEl);T(t.navigation.nextEl).includes(a)&&(t.isEnd&&!t.params.loop||t.slideNext(),t.isEnd?d(s.lastSlideMessage):d(s.nextSlideMessage)),e.includes(a)&&(t.isBeginning&&!t.params.loop||t.slidePrev(),t.isBeginning?d(s.firstSlideMessage):d(s.prevSlideMessage))}t.pagination&&a.matches(ne(t.params.pagination.bulletClass))&&a.click()}}function b(){return t.pagination&&t.pagination.bullets&&t.pagination.bullets.length}function E(){return b()&&t.params.pagination.clickable}const x=(e,t,s)=>{c(e),"BUTTON"!==e.tagName&&(u(e,"button"),e.addEventListener("keydown",w)),h(e,s),function(e,t){(e=T(e)).forEach((e=>{e.setAttribute("aria-controls",t)}))}(e,t)},S=e=>{n&&n!==e.target&&!n.contains(e.target)&&(r=!0),t.a11y.clicked=!0},M=()=>{r=!1,requestAnimationFrame((()=>{requestAnimationFrame((()=>{t.destroyed||(t.a11y.clicked=!1)}))}))},C=e=>{o=(new Date).getTime()},P=e=>{if(t.a11y.clicked||!t.params.a11y.scrollOnFocus)return;if((new Date).getTime()-o<100)return;const s=e.target.closest(`.${t.params.slideClass}, swiper-slide`);if(!s||!t.slides.includes(s))return;n=s;const a=t.slides.indexOf(s)===t.activeIndex,i=t.params.watchSlidesProgress&&t.visibleSlides&&t.visibleSlides.includes(s);a||i||e.sourceCapabilities&&e.sourceCapabilities.firesTouchEvents||(t.isHorizontal()?t.el.scrollLeft=0:t.el.scrollTop=0,requestAnimationFrame((()=>{r||(t.params.loop?t.slideToLoop(parseInt(s.getAttribute("data-swiper-slide-index")),0):t.slideTo(t.slides.indexOf(s),0),r=!1)})))},L=()=>{const e=t.params.a11y;e.itemRoleDescriptionMessage&&m(t.slides,e.itemRoleDescriptionMessage),e.slideRole&&u(t.slides,e.slideRole);const s=t.slides.length;e.slideLabelMessage&&t.slides.forEach(((a,i)=>{const r=t.params.loop?parseInt(a.getAttribute("data-swiper-slide-index"),10):i;h(a,e.slideLabelMessage.replace(/\{\{index\}\}/,r+1).replace(/\{\{slidesLength\}\}/,s))}))},I=()=>{const e=t.params.a11y;t.el.append(l);const s=t.el;e.containerRoleDescriptionMessage&&m(s,e.containerRoleDescriptionMessage),e.containerMessage&&h(s,e.containerMessage),e.containerRole&&u(s,e.containerRole);const i=t.wrapperEl,r=e.id||i.getAttribute("id")||`swiper-wrapper-${n=16,void 0===n&&(n=16),"x".repeat(n).replace(/x/g,(()=>Math.round(16*Math.random()).toString(16)))}`;var n;const o=t.params.autoplay&&t.params.autoplay.enabled?"off":"polite";var d;d=r,T(i).forEach((e=>{e.setAttribute("id",d)})),function(e,t){(e=T(e)).forEach((e=>{e.setAttribute("aria-live",t)}))}(i,o),L();let{nextEl:c,prevEl:p}=t.navigation?t.navigation:{};if(c=T(c),p=T(p),c&&c.forEach((t=>x(t,r,e.nextSlideMessage))),p&&p.forEach((t=>x(t,r,e.prevSlideMessage))),E()){T(t.pagination.el).forEach((e=>{e.addEventListener("keydown",w)}))}a().addEventListener("visibilitychange",C),t.el.addEventListener("focus",P,!0),t.el.addEventListener("focus",P,!0),t.el.addEventListener("pointerdown",S,!0),t.el.addEventListener("pointerup",M,!0)};i("beforeInit",(()=>{l=v("span",t.params.a11y.notificationClass),l.setAttribute("aria-live","assertive"),l.setAttribute("aria-atomic","true")})),i("afterInit",(()=>{t.params.a11y.enabled&&I()})),i("slidesLengthChange snapGridLengthChange slidesGridLengthChange",(()=>{t.params.a11y.enabled&&L()})),i("fromEdge toEdge afterInit lock unlock",(()=>{t.params.a11y.enabled&&function(){if(t.params.loop||t.params.rewind||!t.navigation)return;const{nextEl:e,prevEl:s}=t.navigation;s&&(t.isBeginning?(f(s),p(s)):(g(s),c(s))),e&&(t.isEnd?(f(e),p(e)):(g(e),c(e)))}()})),i("paginationUpdate",(()=>{t.params.a11y.enabled&&function(){const e=t.params.a11y;b()&&t.pagination.bullets.forEach((s=>{t.params.pagination.clickable&&(c(s),t.params.pagination.renderBullet||(u(s,"button"),h(s,e.paginationBulletMessage.replace(/\{\{index\}\}/,y(s)+1)))),s.matches(ne(t.params.pagination.bulletActiveClass))?s.setAttribute("aria-current","true"):s.removeAttribute("aria-current")}))}()})),i("destroy",(()=>{t.params.a11y.enabled&&function(){l&&l.remove();let{nextEl:e,prevEl:s}=t.navigation?t.navigation:{};e=T(e),s=T(s),e&&e.forEach((e=>e.removeEventListener("keydown",w))),s&&s.forEach((e=>e.removeEventListener("keydown",w))),E()&&T(t.pagination.el).forEach((e=>{e.removeEventListener("keydown",w)}));a().removeEventListener("visibilitychange",C),t.el&&"string"!=typeof t.el&&(t.el.removeEventListener("focus",P,!0),t.el.removeEventListener("pointerdown",S,!0),t.el.removeEventListener("pointerup",M,!0))}()}))},function(e){let{swiper:t,extendParams:s,on:a}=e;s({history:{enabled:!1,root:"",replaceState:!1,key:"slides",keepQuery:!1}});let i=!1,n={};const l=e=>e.toString().replace(/\s+/g,"-").replace(/[^\w-]+/g,"").replace(/--+/g,"-").replace(/^-+/,"").replace(/-+$/,""),o=e=>{const t=r();let s;s=e?new URL(e):t.location;const a=s.pathname.slice(1).split("/").filter((e=>""!==e)),i=a.length;return{key:a[i-2],value:a[i-1]}},d=(e,s)=>{const a=r();if(!i||!t.params.history.enabled)return;let n;n=t.params.url?new URL(t.params.url):a.location;const o=t.virtual&&t.params.virtual.enabled?t.slidesEl.querySelector(`[data-swiper-slide-index="${s}"]`):t.slides[s];let d=l(o.getAttribute("data-history"));if(t.params.history.root.length>0){let s=t.params.history.root;"/"===s[s.length-1]&&(s=s.slice(0,s.length-1)),d=`${s}/${e?`${e}/`:""}${d}`}else n.pathname.includes(e)||(d=`${e?`${e}/`:""}${d}`);t.params.history.keepQuery&&(d+=n.search);const c=a.history.state;c&&c.value===d||(t.params.history.replaceState?a.history.replaceState({value:d},null,d):a.history.pushState({value:d},null,d))},c=(e,s,a)=>{if(s)for(let i=0,r=t.slides.length;i{n=o(t.params.url),c(t.params.speed,n.value,!1)};a("init",(()=>{t.params.history.enabled&&(()=>{const e=r();if(t.params.history){if(!e.history||!e.history.pushState)return t.params.history.enabled=!1,void(t.params.hashNavigation.enabled=!0);i=!0,n=o(t.params.url),n.key||n.value?(c(0,n.value,t.params.runCallbacksOnInit),t.params.history.replaceState||e.addEventListener("popstate",p)):t.params.history.replaceState||e.addEventListener("popstate",p)}})()})),a("destroy",(()=>{t.params.history.enabled&&(()=>{const e=r();t.params.history.replaceState||e.removeEventListener("popstate",p)})()})),a("transitionEnd _freeModeNoMomentumRelease",(()=>{i&&d(t.params.history.key,t.activeIndex)})),a("slideChange",(()=>{i&&t.params.cssMode&&d(t.params.history.key,t.activeIndex)}))},function(e){let{swiper:t,extendParams:s,emit:i,on:n}=e,l=!1;const o=a(),d=r();s({hashNavigation:{enabled:!1,replaceState:!1,watchState:!1,getSlideIndex(e,s){if(t.virtual&&t.params.virtual.enabled){const e=t.slides.find((e=>e.getAttribute("data-hash")===s));if(!e)return 0;return parseInt(e.getAttribute("data-swiper-slide-index"),10)}return t.getSlideIndex(f(t.slidesEl,`.${t.params.slideClass}[data-hash="${s}"], swiper-slide[data-hash="${s}"]`)[0])}}});const c=()=>{i("hashChange");const e=o.location.hash.replace("#",""),s=t.virtual&&t.params.virtual.enabled?t.slidesEl.querySelector(`[data-swiper-slide-index="${t.activeIndex}"]`):t.slides[t.activeIndex];if(e!==(s?s.getAttribute("data-hash"):"")){const s=t.params.hashNavigation.getSlideIndex(t,e);if(void 0===s||Number.isNaN(s))return;t.slideTo(s)}},p=()=>{if(!l||!t.params.hashNavigation.enabled)return;const e=t.virtual&&t.params.virtual.enabled?t.slidesEl.querySelector(`[data-swiper-slide-index="${t.activeIndex}"]`):t.slides[t.activeIndex],s=e?e.getAttribute("data-hash")||e.getAttribute("data-history"):"";t.params.hashNavigation.replaceState&&d.history&&d.history.replaceState?(d.history.replaceState(null,null,`#${s}`||""),i("hashSet")):(o.location.hash=s||"",i("hashSet"))};n("init",(()=>{t.params.hashNavigation.enabled&&(()=>{if(!t.params.hashNavigation.enabled||t.params.history&&t.params.history.enabled)return;l=!0;const e=o.location.hash.replace("#","");if(e){const s=0,a=t.params.hashNavigation.getSlideIndex(t,e);t.slideTo(a||0,s,t.params.runCallbacksOnInit,!0)}t.params.hashNavigation.watchState&&d.addEventListener("hashchange",c)})()})),n("destroy",(()=>{t.params.hashNavigation.enabled&&t.params.hashNavigation.watchState&&d.removeEventListener("hashchange",c)})),n("transitionEnd _freeModeNoMomentumRelease",(()=>{l&&p()})),n("slideChange",(()=>{l&&t.params.cssMode&&p()}))},function(e){let t,s,{swiper:i,extendParams:r,on:n,emit:l,params:o}=e;i.autoplay={running:!1,paused:!1,timeLeft:0},r({autoplay:{enabled:!1,delay:3e3,waitForTransition:!0,disableOnInteraction:!1,stopOnLastSlide:!1,reverseDirection:!1,pauseOnMouseEnter:!1}});let d,c,p,u,m,h,f,g,v=o&&o.autoplay?o.autoplay.delay:3e3,w=o&&o.autoplay?o.autoplay.delay:3e3,b=(new Date).getTime();function y(e){i&&!i.destroyed&&i.wrapperEl&&e.target===i.wrapperEl&&(i.wrapperEl.removeEventListener("transitionend",y),g||e.detail&&e.detail.bySwiperTouchMove||C())}const E=()=>{if(i.destroyed||!i.autoplay.running)return;i.autoplay.paused?c=!0:c&&(w=d,c=!1);const e=i.autoplay.paused?d:b+w-(new Date).getTime();i.autoplay.timeLeft=e,l("autoplayTimeLeft",e,e/v),s=requestAnimationFrame((()=>{E()}))},x=e=>{if(i.destroyed||!i.autoplay.running)return;cancelAnimationFrame(s),E();let a=void 0===e?i.params.autoplay.delay:e;v=i.params.autoplay.delay,w=i.params.autoplay.delay;const r=(()=>{let e;if(e=i.virtual&&i.params.virtual.enabled?i.slides.find((e=>e.classList.contains("swiper-slide-active"))):i.slides[i.activeIndex],!e)return;return parseInt(e.getAttribute("data-swiper-autoplay"),10)})();!Number.isNaN(r)&&r>0&&void 0===e&&(a=r,v=r,w=r),d=a;const n=i.params.speed,o=()=>{i&&!i.destroyed&&(i.params.autoplay.reverseDirection?!i.isBeginning||i.params.loop||i.params.rewind?(i.slidePrev(n,!0,!0),l("autoplay")):i.params.autoplay.stopOnLastSlide||(i.slideTo(i.slides.length-1,n,!0,!0),l("autoplay")):!i.isEnd||i.params.loop||i.params.rewind?(i.slideNext(n,!0,!0),l("autoplay")):i.params.autoplay.stopOnLastSlide||(i.slideTo(0,n,!0,!0),l("autoplay")),i.params.cssMode&&(b=(new Date).getTime(),requestAnimationFrame((()=>{x()}))))};return a>0?(clearTimeout(t),t=setTimeout((()=>{o()}),a)):requestAnimationFrame((()=>{o()})),a},S=()=>{b=(new Date).getTime(),i.autoplay.running=!0,x(),l("autoplayStart")},T=()=>{i.autoplay.running=!1,clearTimeout(t),cancelAnimationFrame(s),l("autoplayStop")},M=(e,s)=>{if(i.destroyed||!i.autoplay.running)return;clearTimeout(t),e||(f=!0);const a=()=>{l("autoplayPause"),i.params.autoplay.waitForTransition?i.wrapperEl.addEventListener("transitionend",y):C()};if(i.autoplay.paused=!0,s)return h&&(d=i.params.autoplay.delay),h=!1,void a();const r=d||i.params.autoplay.delay;d=r-((new Date).getTime()-b),i.isEnd&&d<0&&!i.params.loop||(d<0&&(d=0),a())},C=()=>{i.isEnd&&d<0&&!i.params.loop||i.destroyed||!i.autoplay.running||(b=(new Date).getTime(),f?(f=!1,x(d)):x(),i.autoplay.paused=!1,l("autoplayResume"))},P=()=>{if(i.destroyed||!i.autoplay.running)return;const e=a();"hidden"===e.visibilityState&&(f=!0,M(!0)),"visible"===e.visibilityState&&C()},L=e=>{"mouse"===e.pointerType&&(f=!0,g=!0,i.animating||i.autoplay.paused||M(!0))},I=e=>{"mouse"===e.pointerType&&(g=!1,i.autoplay.paused&&C())};n("init",(()=>{i.params.autoplay.enabled&&(i.params.autoplay.pauseOnMouseEnter&&(i.el.addEventListener("pointerenter",L),i.el.addEventListener("pointerleave",I)),a().addEventListener("visibilitychange",P),S())})),n("destroy",(()=>{i.el&&"string"!=typeof i.el&&(i.el.removeEventListener("pointerenter",L),i.el.removeEventListener("pointerleave",I)),a().removeEventListener("visibilitychange",P),i.autoplay.running&&T()})),n("_freeModeStaticRelease",(()=>{(u||f)&&C()})),n("_freeModeNoMomentumRelease",(()=>{i.params.autoplay.disableOnInteraction?T():M(!0,!0)})),n("beforeTransitionStart",((e,t,s)=>{!i.destroyed&&i.autoplay.running&&(s||!i.params.autoplay.disableOnInteraction?M(!0,!0):T())})),n("sliderFirstMove",(()=>{!i.destroyed&&i.autoplay.running&&(i.params.autoplay.disableOnInteraction?T():(p=!0,u=!1,f=!1,m=setTimeout((()=>{f=!0,u=!0,M(!0)}),200)))})),n("touchEnd",(()=>{if(!i.destroyed&&i.autoplay.running&&p){if(clearTimeout(m),clearTimeout(t),i.params.autoplay.disableOnInteraction)return u=!1,void(p=!1);u&&i.params.cssMode&&C(),u=!1,p=!1}})),n("slideChange",(()=>{!i.destroyed&&i.autoplay.running&&(h=!0)})),Object.assign(i.autoplay,{start:S,stop:T,pause:M,resume:C})},function(e){let{swiper:t,extendParams:s,on:i}=e;s({thumbs:{swiper:null,multipleActiveThumbs:!0,autoScrollOffset:0,slideThumbActiveClass:"swiper-slide-thumb-active",thumbsContainerClass:"swiper-thumbs"}});let r=!1,n=!1;function l(){const e=t.thumbs.swiper;if(!e||e.destroyed)return;const s=e.clickedIndex,a=e.clickedSlide;if(a&&a.classList.contains(t.params.thumbs.slideThumbActiveClass))return;if(null==s)return;let i;i=e.params.loop?parseInt(e.clickedSlide.getAttribute("data-swiper-slide-index"),10):s,t.params.loop?t.slideToLoop(i):t.slideTo(i)}function o(){const{thumbs:e}=t.params;if(r)return!1;r=!0;const s=t.constructor;if(e.swiper instanceof s){if(e.swiper.destroyed)return r=!1,!1;t.thumbs.swiper=e.swiper,Object.assign(t.thumbs.swiper.originalParams,{watchSlidesProgress:!0,slideToClickedSlide:!1}),Object.assign(t.thumbs.swiper.params,{watchSlidesProgress:!0,slideToClickedSlide:!1}),t.thumbs.swiper.update()}else if(c(e.swiper)){const a=Object.assign({},e.swiper);Object.assign(a,{watchSlidesProgress:!0,slideToClickedSlide:!1}),t.thumbs.swiper=new s(a),n=!0}return t.thumbs.swiper.el.classList.add(t.params.thumbs.thumbsContainerClass),t.thumbs.swiper.on("tap",l),!0}function d(e){const s=t.thumbs.swiper;if(!s||s.destroyed)return;const a="auto"===s.params.slidesPerView?s.slidesPerViewDynamic():s.params.slidesPerView;let i=1;const r=t.params.thumbs.slideThumbActiveClass;if(t.params.slidesPerView>1&&!t.params.centeredSlides&&(i=t.params.slidesPerView),t.params.thumbs.multipleActiveThumbs||(i=1),i=Math.floor(i),s.slides.forEach((e=>e.classList.remove(r))),s.params.loop||s.params.virtual&&s.params.virtual.enabled)for(let e=0;e{e.classList.add(r)}));else for(let e=0;ee.getAttribute("data-swiper-slide-index")===`${t.realIndex}`));r=s.slides.indexOf(e),o=t.activeIndex>t.previousIndex?"next":"prev"}else r=t.realIndex,o=r>t.previousIndex?"next":"prev";l&&(r+="next"===o?n:-1*n),s.visibleSlidesIndexes&&s.visibleSlidesIndexes.indexOf(r)<0&&(s.params.centeredSlides?r=r>i?r-Math.floor(a/2)+1:r+Math.floor(a/2)-1:r>i&&s.params.slidesPerGroup,s.slideTo(r,e?0:void 0))}}t.thumbs={swiper:null},i("beforeInit",(()=>{const{thumbs:e}=t.params;if(e&&e.swiper)if("string"==typeof e.swiper||e.swiper instanceof HTMLElement){const s=a(),i=()=>{const a="string"==typeof e.swiper?s.querySelector(e.swiper):e.swiper;if(a&&a.swiper)e.swiper=a.swiper,o(),d(!0);else if(a){const s=`${t.params.eventsPrefix}init`,i=r=>{e.swiper=r.detail[0],a.removeEventListener(s,i),o(),d(!0),e.swiper.update(),t.update()};a.addEventListener(s,i)}return a},r=()=>{if(t.destroyed)return;i()||requestAnimationFrame(r)};requestAnimationFrame(r)}else o(),d(!0)})),i("slideChange update resize observerUpdate",(()=>{d()})),i("setTransition",((e,s)=>{const a=t.thumbs.swiper;a&&!a.destroyed&&a.setTransition(s)})),i("beforeDestroy",(()=>{const e=t.thumbs.swiper;e&&!e.destroyed&&n&&e.destroy()})),Object.assign(t.thumbs,{init:o,update:d})},function(e){let{swiper:t,extendParams:s,emit:a,once:i}=e;s({freeMode:{enabled:!1,momentum:!0,momentumRatio:1,momentumBounce:!0,momentumBounceRatio:1,momentumVelocityRatio:1,sticky:!1,minimumVelocity:.02}}),Object.assign(t,{freeMode:{onTouchStart:function(){if(t.params.cssMode)return;const e=t.getTranslate();t.setTranslate(e),t.setTransition(0),t.touchEventsData.velocities.length=0,t.freeMode.onTouchEnd({currentPos:t.rtl?t.translate:-t.translate})},onTouchMove:function(){if(t.params.cssMode)return;const{touchEventsData:e,touches:s}=t;0===e.velocities.length&&e.velocities.push({position:s[t.isHorizontal()?"startX":"startY"],time:e.touchStartTime}),e.velocities.push({position:s[t.isHorizontal()?"currentX":"currentY"],time:o()})},onTouchEnd:function(e){let{currentPos:s}=e;if(t.params.cssMode)return;const{params:r,wrapperEl:n,rtlTranslate:l,snapGrid:d,touchEventsData:c}=t,p=o()-c.touchStartTime;if(s<-t.minTranslate())t.slideTo(t.activeIndex);else if(s>-t.maxTranslate())t.slides.length1){const e=c.velocities.pop(),s=c.velocities.pop(),a=e.position-s.position,i=e.time-s.time;t.velocity=a/i,t.velocity/=2,Math.abs(t.velocity)150||o()-e.time>300)&&(t.velocity=0)}else t.velocity=0;t.velocity*=r.freeMode.momentumVelocityRatio,c.velocities.length=0;let e=1e3*r.freeMode.momentumRatio;const s=t.velocity*e;let p=t.translate+s;l&&(p=-p);let u,m=!1;const h=20*Math.abs(t.velocity)*r.freeMode.momentumBounceRatio;let f;if(pt.minTranslate())r.freeMode.momentumBounce?(p-t.minTranslate()>h&&(p=t.minTranslate()+h),u=t.minTranslate(),m=!0,c.allowMomentumBounce=!0):p=t.minTranslate(),r.loop&&r.centeredSlides&&(f=!0);else if(r.freeMode.sticky){let e;for(let t=0;t-p){e=t;break}p=Math.abs(d[e]-p){t.loopFix()})),0!==t.velocity){if(e=l?Math.abs((-p-t.translate)/t.velocity):Math.abs((p-t.translate)/t.velocity),r.freeMode.sticky){const s=Math.abs((l?-p:p)-t.translate),a=t.slidesSizesGrid[t.activeIndex];e=s{t&&!t.destroyed&&c.allowMomentumBounce&&(a("momentumBounce"),t.setTransition(r.speed),setTimeout((()=>{t.setTranslate(u),x(n,(()=>{t&&!t.destroyed&&t.transitionEnd()}))}),0))}))):t.velocity?(a("_freeModeNoMomentumRelease"),t.updateProgress(p),t.setTransition(e),t.setTranslate(p),t.transitionStart(!0,t.swipeDirection),t.animating||(t.animating=!0,x(n,(()=>{t&&!t.destroyed&&t.transitionEnd()})))):t.updateProgress(p),t.updateActiveIndex(),t.updateSlidesClasses()}else{if(r.freeMode.sticky)return void t.slideToClosest();r.freeMode&&a("_freeModeNoMomentumRelease")}(!r.freeMode.momentum||p>=r.longSwipesMs)&&(a("_freeModeStaticRelease"),t.updateProgress(),t.updateActiveIndex(),t.updateSlidesClasses())}}}})},function(e){let t,s,a,i,{swiper:r,extendParams:n,on:l}=e;n({grid:{rows:1,fill:"column"}});const o=()=>{let e=r.params.spaceBetween;return"string"==typeof e&&e.indexOf("%")>=0?e=parseFloat(e.replace("%",""))/100*r.size:"string"==typeof e&&(e=parseFloat(e)),e};l("init",(()=>{i=r.params.grid&&r.params.grid.rows>1})),l("update",(()=>{const{params:e,el:t}=r,s=e.grid&&e.grid.rows>1;i&&!s?(t.classList.remove(`${e.containerModifierClass}grid`,`${e.containerModifierClass}grid-column`),a=1,r.emitContainerClasses()):!i&&s&&(t.classList.add(`${e.containerModifierClass}grid`),"column"===e.grid.fill&&t.classList.add(`${e.containerModifierClass}grid-column`),r.emitContainerClasses()),i=s})),r.grid={initSlides:e=>{const{slidesPerView:i}=r.params,{rows:n,fill:l}=r.params.grid,o=r.virtual&&r.params.virtual.enabled?r.virtual.slides.length:e.length;a=Math.floor(o/n),t=Math.floor(o/n)===o/n?o:Math.ceil(o/n)*n,"auto"!==i&&"row"===l&&(t=Math.max(t,i*n)),s=t/n},unsetSlides:()=>{r.slides&&r.slides.forEach((e=>{e.swiperSlideGridSet&&(e.style.height="",e.style[r.getDirectionLabel("margin-top")]="")}))},updateSlide:(e,i,n)=>{const{slidesPerGroup:l}=r.params,d=o(),{rows:c,fill:p}=r.params.grid,u=r.virtual&&r.params.virtual.enabled?r.virtual.slides.length:n.length;let m,h,f;if("row"===p&&l>1){const s=Math.floor(e/(l*c)),a=e-c*l*s,r=0===s?l:Math.min(Math.ceil((u-s*c*l)/c),l);f=Math.floor(a/r),h=a-f*r+s*l,m=h+f*t/c,i.style.order=m}else"column"===p?(h=Math.floor(e/c),f=e-h*c,(h>a||h===a&&f===c-1)&&(f+=1,f>=c&&(f=0,h+=1))):(f=Math.floor(e/s),h=e-f*s);i.row=f,i.column=h,i.style.height=`calc((100% - ${(c-1)*d}px) / ${c})`,i.style[r.getDirectionLabel("margin-top")]=0!==f?d&&`${d}px`:"",i.swiperSlideGridSet=!0},updateWrapperSize:(e,s)=>{const{centeredSlides:a,roundLengths:i}=r.params,n=o(),{rows:l}=r.params.grid;if(r.virtualSize=(e+n)*t,r.virtualSize=Math.ceil(r.virtualSize/l)-n,r.params.cssMode||(r.wrapperEl.style[r.getDirectionLabel("width")]=`${r.virtualSize+n}px`),a){const e=[];for(let t=0;t{const{slides:e}=t;t.params.fadeEffect;for(let s=0;s{const s=t.slides.map((e=>h(e)));s.forEach((t=>{t.style.transitionDuration=`${e}ms`})),he({swiper:t,duration:e,transformElements:s,allSlides:!0})},overwriteParams:()=>({slidesPerView:1,slidesPerGroup:1,watchSlidesProgress:!0,spaceBetween:0,virtualTranslate:!t.params.cssMode})})},function(e){let{swiper:t,extendParams:s,on:a}=e;s({cubeEffect:{slideShadows:!0,shadow:!0,shadowOffset:20,shadowScale:.94}});const i=(e,t,s)=>{let a=s?e.querySelector(".swiper-slide-shadow-left"):e.querySelector(".swiper-slide-shadow-top"),i=s?e.querySelector(".swiper-slide-shadow-right"):e.querySelector(".swiper-slide-shadow-bottom");a||(a=v("div",("swiper-slide-shadow-cube swiper-slide-shadow-"+(s?"left":"top")).split(" ")),e.append(a)),i||(i=v("div",("swiper-slide-shadow-cube swiper-slide-shadow-"+(s?"right":"bottom")).split(" ")),e.append(i)),a&&(a.style.opacity=Math.max(-t,0)),i&&(i.style.opacity=Math.max(t,0))};ue({effect:"cube",swiper:t,on:a,setTranslate:()=>{const{el:e,wrapperEl:s,slides:a,width:r,height:n,rtlTranslate:l,size:o,browser:d}=t,c=M(t),p=t.params.cubeEffect,u=t.isHorizontal(),m=t.virtual&&t.params.virtual.enabled;let h,f=0;p.shadow&&(u?(h=t.wrapperEl.querySelector(".swiper-cube-shadow"),h||(h=v("div","swiper-cube-shadow"),t.wrapperEl.append(h)),h.style.height=`${r}px`):(h=e.querySelector(".swiper-cube-shadow"),h||(h=v("div","swiper-cube-shadow"),e.append(h))));for(let e=0;e-1&&(f=90*s+90*d,l&&(f=90*-s-90*d)),t.style.transform=w,p.slideShadows&&i(t,d,u)}if(s.style.transformOrigin=`50% 50% -${o/2}px`,s.style["-webkit-transform-origin"]=`50% 50% -${o/2}px`,p.shadow)if(u)h.style.transform=`translate3d(0px, ${r/2+p.shadowOffset}px, ${-r/2}px) rotateX(89.99deg) rotateZ(0deg) scale(${p.shadowScale})`;else{const e=Math.abs(f)-90*Math.floor(Math.abs(f)/90),t=1.5-(Math.sin(2*e*Math.PI/360)/2+Math.cos(2*e*Math.PI/360)/2),s=p.shadowScale,a=p.shadowScale/t,i=p.shadowOffset;h.style.transform=`scale3d(${s}, 1, ${a}) translate3d(0px, ${n/2+i}px, ${-n/2/a}px) rotateX(-89.99deg)`}const g=(d.isSafari||d.isWebView)&&d.needPerspectiveFix?-o/2:0;s.style.transform=`translate3d(0px,0,${g}px) rotateX(${c(t.isHorizontal()?0:f)}deg) rotateY(${c(t.isHorizontal()?-f:0)}deg)`,s.style.setProperty("--swiper-cube-translate-z",`${g}px`)},setTransition:e=>{const{el:s,slides:a}=t;if(a.forEach((t=>{t.style.transitionDuration=`${e}ms`,t.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach((t=>{t.style.transitionDuration=`${e}ms`}))})),t.params.cubeEffect.shadow&&!t.isHorizontal()){const t=s.querySelector(".swiper-cube-shadow");t&&(t.style.transitionDuration=`${e}ms`)}},recreateShadows:()=>{const e=t.isHorizontal();t.slides.forEach((t=>{const s=Math.max(Math.min(t.progress,1),-1);i(t,s,e)}))},getEffectParams:()=>t.params.cubeEffect,perspective:()=>!0,overwriteParams:()=>({slidesPerView:1,slidesPerGroup:1,watchSlidesProgress:!0,resistanceRatio:0,spaceBetween:0,centeredSlides:!1,virtualTranslate:!0})})},function(e){let{swiper:t,extendParams:s,on:a}=e;s({flipEffect:{slideShadows:!0,limitRotation:!0}});const i=(e,s)=>{let a=t.isHorizontal()?e.querySelector(".swiper-slide-shadow-left"):e.querySelector(".swiper-slide-shadow-top"),i=t.isHorizontal()?e.querySelector(".swiper-slide-shadow-right"):e.querySelector(".swiper-slide-shadow-bottom");a||(a=fe("flip",e,t.isHorizontal()?"left":"top")),i||(i=fe("flip",e,t.isHorizontal()?"right":"bottom")),a&&(a.style.opacity=Math.max(-s,0)),i&&(i.style.opacity=Math.max(s,0))};ue({effect:"flip",swiper:t,on:a,setTranslate:()=>{const{slides:e,rtlTranslate:s}=t,a=t.params.flipEffect,r=M(t);for(let n=0;n{const s=t.slides.map((e=>h(e)));s.forEach((t=>{t.style.transitionDuration=`${e}ms`,t.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach((t=>{t.style.transitionDuration=`${e}ms`}))})),he({swiper:t,duration:e,transformElements:s})},recreateShadows:()=>{t.params.flipEffect,t.slides.forEach((e=>{let s=e.progress;t.params.flipEffect.limitRotation&&(s=Math.max(Math.min(e.progress,1),-1)),i(e,s)}))},getEffectParams:()=>t.params.flipEffect,perspective:()=>!0,overwriteParams:()=>({slidesPerView:1,slidesPerGroup:1,watchSlidesProgress:!0,spaceBetween:0,virtualTranslate:!t.params.cssMode})})},function(e){let{swiper:t,extendParams:s,on:a}=e;s({coverflowEffect:{rotate:50,stretch:0,depth:100,scale:1,modifier:1,slideShadows:!0}}),ue({effect:"coverflow",swiper:t,on:a,setTranslate:()=>{const{width:e,height:s,slides:a,slidesSizesGrid:i}=t,r=t.params.coverflowEffect,n=t.isHorizontal(),l=t.translate,o=n?e/2-l:s/2-l,d=n?r.rotate:-r.rotate,c=r.depth,p=M(t);for(let e=0,t=a.length;e0?u:0),s&&(s.style.opacity=-u>0?-u:0)}}},setTransition:e=>{t.slides.map((e=>h(e))).forEach((t=>{t.style.transitionDuration=`${e}ms`,t.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach((t=>{t.style.transitionDuration=`${e}ms`}))}))},perspective:()=>!0,overwriteParams:()=>({watchSlidesProgress:!0})})},function(e){let{swiper:t,extendParams:s,on:a}=e;s({creativeEffect:{limitProgress:1,shadowPerProgress:!1,progressMultiplier:1,perspective:!0,prev:{translate:[0,0,0],rotate:[0,0,0],opacity:1,scale:1},next:{translate:[0,0,0],rotate:[0,0,0],opacity:1,scale:1}}});const i=e=>"string"==typeof e?e:`${e}px`;ue({effect:"creative",swiper:t,on:a,setTranslate:()=>{const{slides:e,wrapperEl:s,slidesSizesGrid:a}=t,r=t.params.creativeEffect,{progressMultiplier:n}=r,l=t.params.centeredSlides,o=M(t);if(l){const e=a[0]/2-t.params.slidesOffsetBefore||0;s.style.transform=`translateX(calc(50% - ${e}px))`}for(let s=0;s0&&(g=r.prev,f=!0),m.forEach(((e,t)=>{m[t]=`calc(${e}px + (${i(g.translate[t])} * ${Math.abs(c*n)}))`})),h.forEach(((e,t)=>{let s=g.rotate[t]*Math.abs(c*n);h[t]=s})),a.style.zIndex=-Math.abs(Math.round(d))+e.length;const v=m.join(", "),w=`rotateX(${o(h[0])}deg) rotateY(${o(h[1])}deg) rotateZ(${o(h[2])}deg)`,b=p<0?`scale(${1+(1-g.scale)*p*n})`:`scale(${1-(1-g.scale)*p*n})`,y=p<0?1+(1-g.opacity)*p*n:1-(1-g.opacity)*p*n,E=`translate3d(${v}) ${w} ${b}`;if(f&&g.shadow||!f){let e=a.querySelector(".swiper-slide-shadow");if(!e&&g.shadow&&(e=fe("creative",a)),e){const t=r.shadowPerProgress?c*(1/r.limitProgress):c;e.style.opacity=Math.min(Math.max(Math.abs(t),0),1)}}const x=me(0,a);x.style.transform=E,x.style.opacity=y,g.origin&&(x.style.transformOrigin=g.origin)}},setTransition:e=>{const s=t.slides.map((e=>h(e)));s.forEach((t=>{t.style.transitionDuration=`${e}ms`,t.querySelectorAll(".swiper-slide-shadow").forEach((t=>{t.style.transitionDuration=`${e}ms`}))})),he({swiper:t,duration:e,transformElements:s,allSlides:!0})},perspective:()=>t.params.creativeEffect.perspective,overwriteParams:()=>({watchSlidesProgress:!0,virtualTranslate:!t.params.cssMode})})},function(e){let{swiper:t,extendParams:s,on:a}=e;s({cardsEffect:{slideShadows:!0,rotate:!0,perSlideRotate:2,perSlideOffset:8}}),ue({effect:"cards",swiper:t,on:a,setTranslate:()=>{const{slides:e,activeIndex:s,rtlTranslate:a}=t,i=t.params.cardsEffect,{startTranslate:r,isTouched:n}=t.touchEventsData,l=a?-t.translate:t.translate;for(let o=0;o0&&p<1&&(n||t.params.cssMode)&&l-1&&(n||t.params.cssMode)&&l>r;if(y||E){const e=(1-Math.abs((Math.abs(p)-.5)/.5))**.5;v+=-28*p*e,g+=-.5*e,w+=96*e,h=-25*e*Math.abs(p)+"%"}if(m=p<0?`calc(${m}px ${a?"-":"+"} (${w*Math.abs(p)}%))`:p>0?`calc(${m}px ${a?"-":"+"} (-${w*Math.abs(p)}%))`:`${m}px`,!t.isHorizontal()){const e=h;h=m,m=e}const x=p<0?""+(1+(1-g)*p):""+(1-(1-g)*p),S=`\n translate3d(${m}, ${h}, ${f}px)\n rotateZ(${i.rotate?a?-v:v:0}deg)\n scale(${x})\n `;if(i.slideShadows){let e=d.querySelector(".swiper-slide-shadow");e||(e=fe("cards",d)),e&&(e.style.opacity=Math.min(Math.max((Math.abs(p)-.5)/.5,0),1))}d.style.zIndex=-Math.abs(Math.round(c))+e.length;me(0,d).style.transform=S}},setTransition:e=>{const s=t.slides.map((e=>h(e)));s.forEach((t=>{t.style.transitionDuration=`${e}ms`,t.querySelectorAll(".swiper-slide-shadow").forEach((t=>{t.style.transitionDuration=`${e}ms`}))})),he({swiper:t,duration:e,transformElements:s})},perspective:()=>!0,overwriteParams:()=>({_loopSwapReset:!1,watchSlidesProgress:!0,loopAdditionalSlides:3,centeredSlides:!0,virtualTranslate:!t.params.cssMode})})}];return ie.use(ge),ie}(); +//# sourceMappingURL=swiper-bundle.min.js.map \ No newline at end of file diff --git a/src/js/main/Gaugechart.js b/src/js/main/Gaugechart.js new file mode 100644 index 0000000..506c7e5 --- /dev/null +++ b/src/js/main/Gaugechart.js @@ -0,0 +1,741 @@ +// ============================================ +// 게이지 차트 모듈 (이미지 아이콘 버전) +// 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils) +// ============================================ + +class GaugeChart { + // 상수 정의 + static CONSTANTS = { + MIN_PERCENT: 0.14, // 최소 표시 퍼센트 (14%) + ANIMATION_DURATION: 1400, // 애니메이션 시간 (ms) + PHASE2_DURATION: 800, // 초과 시 2단계 애니메이션 시간 + ICON_HIDE_THRESHOLD: 0.7, // 아이콘 숨김 임계값 (70%) + }; + + static ICON_CONFIG = { + rocket: { width: 18, height: 48, offsetX: 9, offsetY: 24, outerOffset: 30 }, + snail: { + width: 75, // 50 → 75 (1.5배) + height: 64.5, // 43 → 64.5 (1.5배) + offsetX: 37.5, // 25 → 37.5 (1.5배) + offsetY: 32.25, // 21.5 → 32.25 (1.5배) + outerOffset: 30, // 유지 + }, + }; + + constructor(config, dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + if (!config || typeof config !== 'object') { + this._handleError(new Error('config가 유효하지 않습니다.'), 'constructor'); + config = {}; + } + + this.config = { + size: config.size || 832, + strokeWidth: config.strokeWidth || 31, + maxValue: config.maxValue || 50, + padding: config.padding || 20, + outerTextOffset: config.outerTextOffset || 6, + innerTextOffset: config.innerTextOffset || 35, + dotRadius: config.dotRadius || 7, + // 아이콘 이미지 경로 설정 + rocketIconPath: + config.rocketIconPath || "./assets/images/ico/icon-rocket.svg", + snailIconPath: + config.snailIconPath || "./assets/images/ico/icon-snail.svg", + }; + + this.svg = null; + this.center = 0; + this.radius = 0; + this.animated = false; + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `GaugeChart.${context}`, + component: 'GaugeChart', + ...additionalInfo + }, false); + } else { + console.error(`[GaugeChart] ${context}:`, error, additionalInfo); + } + } + + // ============================================ + // 초기화 + // ============================================ + + init() { + try { + this.svg = this.domUtils?.$("#gauge") || document.getElementById("gauge"); + if (!this.svg) { + this._handleError(new Error('게이지 SVG 요소를 찾을 수 없습니다.'), 'init'); + return; + } + + this._calculateDimensions(); + this._setupViewBox(); + } catch (error) { + this._handleError(error, 'init'); + } + } + + _calculateDimensions() { + try { + const { size, strokeWidth, padding } = this.config; + if (typeof size !== 'number' || typeof strokeWidth !== 'number' || typeof padding !== 'number') { + this._handleError(new Error('config 값이 유효하지 않습니다.'), '_calculateDimensions'); + return; + } + + this.center = size / 2; + this.radius = size / 2 - strokeWidth / 2 - padding; + } catch (error) { + this._handleError(error, '_calculateDimensions'); + } + } + + _setupViewBox() { + try { + if (!this.svg) { + this._handleError(new Error('svg 요소가 없습니다.'), '_setupViewBox'); + return; + } + + const { size } = this.config; + if (typeof size !== 'number') { + this._handleError(new Error('size가 유효하지 않습니다.'), '_setupViewBox'); + return; + } + + const extraSpace = 100; + this.svg.setAttribute( + "viewBox", + `-50 -50 ${size + 100} ${size + extraSpace}` + ); + this.svg.setAttribute("preserveAspectRatio", "xMidYMid meet"); + } catch (error) { + this._handleError(error, '_setupViewBox'); + } + } + + // ============================================ + // 게이지 업데이트 + // ============================================ + + update(value) { + try { + if (!this.svg) { + console.warn("[GaugeChart] svg 요소가 없습니다."); + return; + } + + // 입력값 유효성 검증 + if (typeof value !== 'number' || isNaN(value) || value < 0) { + this._handleError(new Error(`유효하지 않은 value: ${value}`), 'update'); + return; + } + + const shouldAnimate = !this.animated; + this.svg.innerHTML = ""; + + const { percent, angle } = this._calculateAngle(value); + const isOverMax = value > this.config.maxValue; + + this._createTextPaths(); + this._createBackgroundArc(isOverMax); + this._createFilledArc(angle, isOverMax, shouldAnimate); + this._addMyLearningText(value, percent, angle, shouldAnimate); + this._addEndIcon(angle, value, shouldAnimate); + this._addAverageLearningText(value, percent); + + if (shouldAnimate) { + this.animated = true; + } + } catch (error) { + this._handleError(error, 'update', { value }); + } + } + + _calculateAngle(value) { + const { maxValue } = this.config; + const { MIN_PERCENT } = GaugeChart.CONSTANTS; + + let percent; + + if (value <= maxValue) { + percent = value / maxValue; + percent = Math.max(percent, MIN_PERCENT); + } else { + percent = 2 - value / maxValue; + percent = Math.max(percent, 0.1); + } + + const angle = -180 + percent * 180; + + return { percent, angle }; + } + + // ============================================ + // SVG 생성 메서드 + // ============================================ + + _createTextPaths() { + const defs = this._createSVGElement("defs"); + + // 외곽 텍스트 경로 + const outerArc = this._createPathElement( + "outerArc", + this._createArcPath(this.radius - this.config.outerTextOffset, -180, 0) + ); + defs.appendChild(outerArc); + + // 내부 텍스트 경로 + const innerArc = this._createPathElement( + "innerArc", + this._createArcPath(this.radius - this.config.innerTextOffset, -180, 0) + ); + defs.appendChild(innerArc); + + this.svg.appendChild(defs); + } + + _createBackgroundArc(isOverMax) { + const bg = this._createPathElement( + null, + this._createArcPath(this.radius, -180, 0) + ); + + if (isOverMax) { + bg.setAttribute("stroke", "#D8823B"); + } else { + bg.setAttribute("class", "bg-arc"); + bg.setAttribute("stroke", "#72451F"); + bg.setAttribute("stroke-opacity", "0.1"); + } + + bg.setAttribute("stroke-width", this.config.strokeWidth); + bg.setAttribute("stroke-linecap", "round"); + bg.setAttribute("fill", "none"); + + this.svg.appendChild(bg); + } + + _createFilledArc(angle, isOverMax, shouldAnimate) { + const gradient = this._createGradient(angle); + const fg = this._createPathElement(null, ""); + + fg.setAttribute("stroke", isOverMax ? "url(#arcSweepGradient)" : "#e89555"); + fg.setAttribute("stroke-width", this.config.strokeWidth); + fg.setAttribute("stroke-linecap", "round"); + fg.setAttribute("fill", "none"); + + this.svg.appendChild(fg); + + if (shouldAnimate) { + this._animateFilledArc(fg, angle, isOverMax); + } else { + fg.setAttribute("d", this._createArcPath(this.radius, -180, angle)); + } + } + + _createGradient(angle) { + try { + if (!this.svg) { + this._handleError(new Error('svg 요소가 없습니다.'), '_createGradient'); + return null; + } + + const defs = this.domUtils?.$("defs", this.svg) || this.svg.querySelector("defs"); + if (!defs) { + this._handleError(new Error('defs 요소를 찾을 수 없습니다.'), '_createGradient'); + return null; + } + + let gradient = this.domUtils?.$("#arcSweepGradient", defs) || defs.querySelector("#arcSweepGradient"); + + if (!gradient) { + gradient = this._createSVGElement("linearGradient"); + gradient.setAttribute("id", "arcSweepGradient"); + gradient.setAttribute("x1", "0%"); + gradient.setAttribute("y1", "0%"); + gradient.setAttribute("x2", "100%"); + gradient.setAttribute("y2", "0%"); + + const colors = [ + { offset: "0%", color: "#72451F" }, + { offset: "100%", color: "#D8823B" }, + ]; + + colors.forEach(({ offset, color }) => { + const stop = this._createSVGElement("stop"); + stop.setAttribute("offset", offset); + stop.setAttribute("stop-color", color); + gradient.appendChild(stop); + }); + + defs.appendChild(gradient); + } + + gradient.setAttribute( + "gradientTransform", + `rotate(${angle - 90}, 0.5, 0.5)` + ); + + return gradient; + } catch (error) { + this._handleError(error, '_createGradient', { angle }); + return null; + } + } + + // ============================================ + // 애니메이션 + // ============================================ + + _animateFilledArc(element, targetAngle, isOverMax) { + const { MIN_PERCENT, ANIMATION_DURATION, PHASE2_DURATION } = + GaugeChart.CONSTANTS; + const startAngle = -180 + MIN_PERCENT * 180; + const fullAngle = 0; + + element.setAttribute( + "d", + this._createArcPath(this.radius, -180, startAngle) + ); + + if (isOverMax) { + this._animateTwoPhase( + element, + startAngle, + fullAngle, + targetAngle, + ANIMATION_DURATION, + PHASE2_DURATION + ); + } else { + this._animateSinglePhase( + element, + startAngle, + targetAngle, + ANIMATION_DURATION + ); + } + } + + _animateSinglePhase(element, startAngle, endAngle, duration) { + const startTime = performance.now(); + + const animate = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easeOut = this._easeOutCubic(progress); + + const currentAngle = startAngle + (endAngle - startAngle) * easeOut; + element.setAttribute( + "d", + this._createArcPath(this.radius, -180, currentAngle) + ); + + if (progress < 1) { + requestAnimationFrame(animate); + } + }; + + requestAnimationFrame(animate); + } + + _animateTwoPhase( + element, + startAngle, + midAngle, + endAngle, + phase1Duration, + phase2Duration + ) { + const startTime = performance.now(); + + const animate = (currentTime) => { + const elapsed = currentTime - startTime; + + if (elapsed < phase1Duration) { + // Phase 1: 15% → 100% + const progress = elapsed / phase1Duration; + const easeOut = this._easeOutCubic(progress); + const currentAngle = startAngle + (midAngle - startAngle) * easeOut; + element.setAttribute( + "d", + this._createArcPath(this.radius, -180, currentAngle) + ); + requestAnimationFrame(animate); + } else if (elapsed < phase1Duration + phase2Duration) { + // Phase 2: 100% → 최종값 + const progress = (elapsed - phase1Duration) / phase2Duration; + const easeOut = this._easeOutCubic(progress); + const currentAngle = midAngle + (endAngle - midAngle) * easeOut; + element.setAttribute( + "d", + this._createArcPath(this.radius, -180, currentAngle) + ); + requestAnimationFrame(animate); + } else { + // 완료 + element.setAttribute( + "d", + this._createArcPath(this.radius, -180, endAngle) + ); + } + }; + + requestAnimationFrame(animate); + } + + // ============================================ + // 텍스트 및 아이콘 + // ============================================ + + _addMyLearningText(value, percent, angle, shouldAnimate) { + try { + if (!this.svg) { + this._handleError(new Error('svg 요소가 없습니다.'), '_addMyLearningText'); + return; + } + + const text = this._createTextElement( + "나의 학습", + value, + percent, + this.config.maxValue + ); + + if (!text) { + this._handleError(new Error('text 요소를 생성할 수 없습니다.'), '_addMyLearningText'); + return; + } + + this.svg.appendChild(text); + + const textPath = this.domUtils?.$("textPath", text) || text.querySelector("textPath"); + if (!textPath) { + this._handleError(new Error('textPath 요소를 찾을 수 없습니다.'), '_addMyLearningText'); + return; + } + + const finalOffset = this._calculateTextOffset(value, percent); + + if (shouldAnimate) { + this._animateText( + textPath, + 15, + finalOffset, + GaugeChart.CONSTANTS.ANIMATION_DURATION + ); + } else { + textPath.setAttribute("startOffset", `${finalOffset}%`); + } + + // 클릭 이벤트 추가: mypage.html로 이동 + if (this.domUtils) { + this.domUtils.setStyles(text, { cursor: "pointer" }); + } else { + text.style.cursor = "pointer"; + } + + const clickHandler = () => { + try { + window.location.href = "mypage.html"; + } catch (error) { + this._handleError(error, '_addMyLearningText.clickHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(text, "click", clickHandler); + this.listenerIds.push({ element: text, id: listenerId, type: 'click' }); + } else { + text.addEventListener("click", clickHandler); + } + } catch (error) { + this._handleError(error, '_addMyLearningText', { value, percent, angle, shouldAnimate }); + } + } + + _createTextElement(label, value, percent, maxValue) { + const text = this._createSVGElement("text"); + text.setAttribute("class", "my-learning-text"); + text.setAttribute("font-size", "16"); + text.setAttribute("font-weight", "700"); + text.setAttribute("fill", "#FFF"); + text.setAttribute("stroke", "rgba(0, 0, 0, 0.30)"); + text.setAttribute("stroke-width", "2"); + text.setAttribute("paint-order", "stroke fill"); + + const textPath = this._createSVGElement("textPath"); + textPath.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + "#outerArc" + ); + textPath.setAttribute("text-anchor", "middle"); + textPath.textContent = `${label} : ${value}분 >`; + + text.appendChild(textPath); + return text; + } + + _calculateTextOffset(value, percent) { + if (value > this.config.maxValue) { + return 95; + } + return Math.max(10, percent * 100 - 4.5); + } + + _animateText(element, startOffset, endOffset, duration) { + const startTime = performance.now(); + + const animate = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easeOut = this._easeOutCubic(progress); + const currentOffset = startOffset + (endOffset - startOffset) * easeOut; + + element.setAttribute("startOffset", `${currentOffset}%`); + + if (progress < 1) { + requestAnimationFrame(animate); + } + }; + + requestAnimationFrame(animate); + } + + _addEndIcon(angle, value, shouldAnimate) { + const { ICON_HIDE_THRESHOLD } = GaugeChart.CONSTANTS; + + // 70%~100% 사이에는 아이콘 표시하지 않음 + if ( + value >= this.config.maxValue * ICON_HIDE_THRESHOLD && + value <= this.config.maxValue + ) { + return; + } + + const isOverMax = value > this.config.maxValue; + const iconType = isOverMax ? "rocket" : "snail"; + const iconConfig = GaugeChart.ICON_CONFIG[iconType]; + + const icon = this._createIconElement(iconType); + this.svg.appendChild(icon); + + if (shouldAnimate) { + this._animateIcon(icon, angle, iconConfig, isOverMax); + } else { + this._positionIcon(icon, angle, iconConfig, 0); + } + } + + _createIconElement(type) { + const icon = this._createSVGElement("g"); + + // image 요소 생성 + const image = this._createSVGElement("image"); + const iconConfig = GaugeChart.ICON_CONFIG[type]; + + // 이미지 경로 설정 + const imagePath = + type === "rocket" + ? this.config.rocketIconPath + : this.config.snailIconPath; + + image.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + imagePath + ); + image.setAttribute("width", iconConfig.width); + image.setAttribute("height", iconConfig.height); + + icon.appendChild(image); + return icon; + } + + _animateIcon(icon, targetAngle, iconConfig, isOverMax) { + const { MIN_PERCENT, ANIMATION_DURATION } = GaugeChart.CONSTANTS; + const startAngle = -180 + MIN_PERCENT * 180; + const fullAngle = 0; + const angleOffset = -3; + + this._positionIcon(icon, startAngle, iconConfig, angleOffset); + + if (isOverMax) { + // 초과 시: 100%까지만 + this._animateIconPosition( + icon, + startAngle, + fullAngle, + iconConfig, + angleOffset, + ANIMATION_DURATION + ); + } else { + // 정상 범위: 최종 각도까지 + this._animateIconPosition( + icon, + startAngle, + targetAngle, + iconConfig, + angleOffset, + ANIMATION_DURATION + ); + } + } + + _animateIconPosition( + icon, + startAngle, + endAngle, + iconConfig, + angleOffset, + duration + ) { + const startTime = performance.now(); + + const animate = (currentTime) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const easeOut = this._easeOutCubic(progress); + + const currentAngle = startAngle + (endAngle - startAngle) * easeOut; + this._positionIcon(icon, currentAngle, iconConfig, angleOffset); + + if (progress < 1) { + requestAnimationFrame(animate); + } + }; + + requestAnimationFrame(animate); + } + + _positionIcon(icon, angle, iconConfig, angleOffset = 0) { + const { offsetX, offsetY, outerOffset } = iconConfig; + const position = this._polarToCartesian( + this.center, + this.center, + this.radius + outerOffset, + angle + angleOffset + ); + + const rotation = + angle + (iconConfig === GaugeChart.ICON_CONFIG.snail ? 90 : 0); + + icon.setAttribute( + "transform", + `translate(${position.x - offsetX}, ${position.y - offsetY}) rotate(${rotation}, ${offsetX}, ${offsetY})` + ); + } + + _addAverageLearningText(value, percent) { + const text = this._createSVGElement("text"); + text.setAttribute("font-size", "13"); + text.setAttribute("font-weight", "400"); + text.setAttribute("fill", "#4A4947"); + + const textPath = this._createSVGElement("textPath"); + textPath.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + "#innerArc" + ); + + const offset = + value > this.config.maxValue ? Math.max(10, percent * 100) : 100; + + textPath.setAttribute("startOffset", `${offset}%`); + textPath.setAttribute("text-anchor", "end"); + textPath.innerHTML = `전체 평균 학습`; + + text.appendChild(textPath); + this.svg.appendChild(text); + } + + // ============================================ + // 유틸리티 메서드 + // ============================================ + + _createSVGElement(type) { + return document.createElementNS("http://www.w3.org/2000/svg", type); + } + + _createPathElement(id, d) { + const path = this._createSVGElement("path"); + if (id) path.setAttribute("id", id); + if (d) path.setAttribute("d", d); + return path; + } + + _polarToCartesian(cx, cy, r, angle) { + const rad = (angle * Math.PI) / 180; + return { + x: cx + r * Math.cos(rad), + y: cy + r * Math.sin(rad), + }; + } + + _createArcPath(r, startAngle, endAngle) { + const start = this._polarToCartesian( + this.center, + this.center, + r, + startAngle + ); + const end = this._polarToCartesian(this.center, this.center, r, endAngle); + return `M ${start.x} ${start.y} A ${r} ${r} 0 0 1 ${end.x} ${end.y}`; + } + + _easeOutCubic(t) { + return 1 - Math.pow(1 - t, 3); + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 참조 정리 + this.svg = null; + this.config = null; + this.center = 0; + this.radius = 0; + this.animated = false; + } catch (error) { + this._handleError(error, 'destroy'); + } + } +} diff --git a/src/js/main/MultiGuide.js b/src/js/main/MultiGuide.js new file mode 100644 index 0000000..944bf37 --- /dev/null +++ b/src/js/main/MultiGuide.js @@ -0,0 +1,1212 @@ +// multiGuide.js +// 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils) +class MultiGuide { + constructor(targets, dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + this.resizeTimer = null; // resize 타이머 저장 + + try { + // 입력값 유효성 검증 + if (!targets || !Array.isArray(targets)) { + this._handleError(new Error('targets가 배열이 아닙니다.'), 'constructor'); + this.targets = []; + } else { + this.targets = targets; + } + + this.isActive = false; + this.init(); + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `MultiGuide.${context}`, + component: 'MultiGuide', + ...additionalInfo + }, false); + } else { + console.error(`[MultiGuide] ${context}:`, error, additionalInfo); + } + } + + init() { + try { + console.log("[MultiGuide.init] 초기화 시작"); + + this.guideWrap = this.domUtils?.$(".guide-wrap") || document.querySelector(".guide-wrap"); + console.log("[MultiGuide.init] guideWrap:", this.guideWrap); + + if (!this.guideWrap) { + console.error("[MultiGuide.init] ❌ .guide-wrap 요소를 찾을 수 없습니다!"); + setTimeout(() => { + try { + this.guideWrap = this.domUtils?.$(".guide-wrap") || document.querySelector(".guide-wrap"); + if (this.guideWrap) { + console.log("[MultiGuide.init] ✅ 재시도 후 guideWrap 찾음"); + this.continueInit(); + } else { + console.error("[MultiGuide.init] ❌ 재시도 실패"); + this._handleError(new Error('.guide-wrap 요소를 찾을 수 없습니다.'), 'init.retry'); + } + } catch (error) { + this._handleError(error, 'init.retry'); + } + }, 100); + return; + } + + this.continueInit(); + } catch (error) { + this._handleError(error, 'init'); + } + } + + continueInit() { + try { + if (!this.guideWrap) { + this._handleError(new Error('guideWrap이 없습니다.'), 'continueInit'); + return; + } + + this.cutoutPath = this.domUtils?.$("#guide-cutout-path") || document.getElementById("guide-cutout-path"); + this.strokePath = this.domUtils?.$("#guide-stroke-path") || document.getElementById("guide-stroke-path"); + this.arcEllipseStrokePath = this.domUtils?.$("#guide-arc-ellipse-stroke-path") || document.getElementById("guide-arc-ellipse-stroke-path"); + this.arcStrokeMaskPath = this.domUtils?.$("#guide-arc-stroke-mask-path") || document.getElementById("guide-arc-stroke-mask-path"); + this.connectionLinesGroup = this.domUtils?.$("#guide-connection-lines") || document.getElementById("guide-connection-lines"); + this.elementConnectionLinesGroup = this.domUtils?.$("#guide-element-connection-lines") || document.getElementById("guide-element-connection-lines"); + + this.bordersContainer = this.domUtils?.$("#guideBorders") || document.getElementById("guideBorders"); + this.labelsContainer = this.domUtils?.$("#guideLabels") || document.getElementById("guideLabels"); + + if (!this.bordersContainer) { + this.bordersContainer = this.domUtils?.createElement('div', { + id: 'guideBorders', + class: 'guide-borders' + }) || document.createElement("div"); + + if (!this.domUtils) { + this.bordersContainer.id = "guideBorders"; + this.bordersContainer.className = "guide-borders"; + } + + this.guideWrap.appendChild(this.bordersContainer); + } + + if (!this.labelsContainer) { + this.labelsContainer = this.domUtils?.createElement('div', { + id: 'guideLabels', + class: 'guide-labels', + style: { + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + pointerEvents: 'none', + zIndex: '10000' + } + }) || document.createElement("div"); + + if (!this.domUtils) { + this.labelsContainer.id = "guideLabels"; + this.labelsContainer.className = "guide-labels"; + this.labelsContainer.style.cssText = + "position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10000;"; + } + + this.guideWrap.appendChild(this.labelsContainer); + } + + this.attachEvents(); + console.log("[MultiGuide.init] ✅ 초기화 완료"); + } catch (error) { + this._handleError(error, 'continueInit'); + } + } + + getStartButton() { + try { + return ( + this.domUtils?.$(".guide-start-btn") || + this.domUtils?.$("#guideStart") || + this.domUtils?.$("[data-guide-start]") || + document.querySelector(".guide-start-btn") || + document.getElementById("guideStart") || + document.querySelector("[data-guide-start]") + ); + } catch (error) { + this._handleError(error, 'getStartButton'); + return null; + } + } + + attachEvents() { + try { + const startBtn = this.getStartButton(); + if (startBtn) { + const startBtnHandler = (e) => { + try { + e.preventDefault(); + e.stopPropagation(); + this.show(); + } catch (error) { + this._handleError(error, 'attachEvents.startBtnHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(startBtn, "click", startBtnHandler); + this.listenerIds.push({ element: startBtn, id: listenerId, type: 'click' }); + } else { + startBtn.addEventListener("click", startBtnHandler); + } + } + + const svg = this.domUtils?.$(".guide-svg") || document.querySelector(".guide-svg"); + if (svg) { + const svgHandler = (e) => { + try { + if (e.target === svg || e.target.tagName.toLowerCase() === "rect") { + this.hide(); + } + } catch (error) { + this._handleError(error, 'attachEvents.svgHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(svg, "click", svgHandler); + this.listenerIds.push({ element: svg, id: listenerId, type: 'click' }); + } else { + svg.addEventListener("click", svgHandler); + } + } + + const keydownHandler = (e) => { + try { + if (e.key === "Escape" && this.isActive) { + this.hide(); + } + } catch (error) { + this._handleError(error, 'attachEvents.keydownHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(document, "keydown", keydownHandler); + this.listenerIds.push({ element: document, id: listenerId, type: 'keydown' }); + } else { + document.addEventListener("keydown", keydownHandler); + } + + // 화면 리사이즈 시 재측정 + const resizeHandler = () => { + try { + if (!this.isActive) return; + + // 디바운싱: 100ms 후에 재측정 + if (this.resizeTimer) { + clearTimeout(this.resizeTimer); + } + + // Utils.debounce 사용 (있는 경우) + if (this.utils && this.utils.debounce) { + const debouncedResize = this.utils.debounce(() => { + console.log("[MultiGuide] 화면 리사이즈 감지 - 재측정 중..."); + this.createCutouts(); + this.createBorders(); + this.createLabels(); + }, 100); + this.resizeTimer = setTimeout(debouncedResize, 100); + } else { + this.resizeTimer = setTimeout(() => { + console.log("[MultiGuide] 화면 리사이즈 감지 - 재측정 중..."); + this.createCutouts(); + this.createBorders(); + this.createLabels(); + }, 100); + } + } catch (error) { + this._handleError(error, 'attachEvents.resizeHandler'); + } + }; + + if (this.eventManager) { + const listenerId = this.eventManager.on(window, "resize", resizeHandler); + this.listenerIds.push({ element: window, id: listenerId, type: 'resize' }); + } else { + window.addEventListener("resize", resizeHandler); + } + } catch (error) { + this._handleError(error, 'attachEvents'); + } + } + + show() { + try { + console.log("[MultiGuide.show] 가이드 표시 시작"); + + if (!this.guideWrap) { + this._handleError(new Error('guideWrap을 찾을 수 없습니다.'), 'show'); + return; + } + + this.isActive = true; + if (this.domUtils) { + this.domUtils.addClasses(this.guideWrap, 'active'); + } else { + this.guideWrap.classList.add("active"); + } + + const startBtn = this.getStartButton(); + if (startBtn) { + if (this.domUtils) { + this.domUtils.setStyles(startBtn, { display: 'none' }); + } else { + startBtn.style.display = "none"; + } + } + + if (this.domUtils) { + this.domUtils.setStyles(document.body, { overflow: 'hidden' }); + } else { + document.body.style.overflow = "hidden"; + } + + this.createCutouts(); + this.createBorders(); + this.createLabels(); + + // 🔥 createLabels 이후에 키워드 연결선 생성 + setTimeout(() => { + try { + this.createKeywordElementConnection(); + } catch (error) { + this._handleError(error, 'show.createKeywordElementConnection'); + } + }, 100); + + console.log("[MultiGuide.show] ✅ 가이드 표시 완료"); + } catch (error) { + this._handleError(error, 'show'); + } + } + + hide() { + try { + this.isActive = false; + + if (this.guideWrap) { + if (this.domUtils) { + this.domUtils.removeClasses(this.guideWrap, 'active'); + } else { + this.guideWrap.classList.remove("active"); + } + } + + const startBtn = this.getStartButton(); + if (startBtn) { + if (this.domUtils) { + this.domUtils.setStyles(startBtn, { display: 'block' }); + } else { + startBtn.style.display = "block"; + } + } + + if (this.domUtils) { + this.domUtils.setStyles(document.body, { overflow: '' }); + } else { + document.body.style.overflow = ""; + } + + if (this.bordersContainer) this.bordersContainer.innerHTML = ""; + if (this.labelsContainer) this.labelsContainer.innerHTML = ""; + if (this.connectionLinesGroup) this.connectionLinesGroup.innerHTML = ""; + if (this.elementConnectionLinesGroup) this.elementConnectionLinesGroup.innerHTML = ""; + } catch (error) { + this._handleError(error, 'hide'); + } + } + + createCutouts() { + try { + if (!this.cutoutPath) { + this._handleError(new Error('cutoutPath가 없습니다.'), 'createCutouts'); + return; + } + + let pathData = ""; + let strokePathData = ""; + let arcEllipseStrokePathData = ""; + let arcStrokeMaskPathData = ""; + + if (!this.targets || !Array.isArray(this.targets)) { + this._handleError(new Error('targets가 배열이 아닙니다.'), 'createCutouts'); + return; + } + + this.targets.forEach((target, index) => { + try { + if (!target || !target.selector) { + console.warn(`[MultiGuide] target ${index}가 유효하지 않습니다.`); + return; + } + + const element = this.domUtils?.$(target.selector) || document.querySelector(target.selector); + if (!element) { + console.warn("[MultiGuide] 요소를 찾을 수 없음:", target.selector); + return; + } + + console.log("[createCutouts]", { + selector: target.selector, + useGaugeArc: target.useGaugeArc, + shape: target.shape + }); + + // 🔥 gauge arc 처리 + if (target.useGaugeArc) { + console.log("[createCutouts] 게이지 아크 생성 중..."); + + const maskPath = this.createGaugeArcStrokeMaskPath(element, target); + console.log("[createCutouts] maskPath 길이:", maskPath.length); + arcStrokeMaskPathData += maskPath + " "; + + const strokePath = this.createGaugeArcEllipseStrokeBoundaryPath(element, target); + console.log("[createCutouts] strokePath 길이:", strokePath.length); + arcEllipseStrokePathData += strokePath + " "; + } + // 🔥 기본 사각형 처리 + else { + const rect = element.getBoundingClientRect(); + const padding = target.padding || 0; + const radius = target.borderRadius || 8; + + pathData += this.createRoundedRectPath( + rect.left - padding, + rect.top - padding, + rect.width + padding * 2, + rect.height + padding * 2, + radius + ) + " "; + + strokePathData += this.createRoundedRectStrokePath( + rect.left - padding, + rect.top - padding, + rect.width + padding * 2, + rect.height + padding * 2, + radius + ) + " "; + } + } catch (error) { + this._handleError(error, 'createCutouts.target', { index, target }); + } + }); + + this.cutoutPath.setAttribute("d", pathData.trim()); + + if (this.strokePath) { + this.strokePath.setAttribute("d", strokePathData.trim()); + } + + if (this.arcStrokeMaskPath) { + console.log("[createCutouts] arcStrokeMaskPath 설정:", arcStrokeMaskPathData.trim() ? "있음" : "없음"); + this.arcStrokeMaskPath.setAttribute("d", arcStrokeMaskPathData.trim()); + } + + if (this.arcEllipseStrokePath) { + console.log("[createCutouts] arcEllipseStrokePath 설정:", arcEllipseStrokePathData.trim() ? "있음" : "없음"); + this.arcEllipseStrokePath.setAttribute("d", arcEllipseStrokePathData.trim()); + } + } catch (error) { + this._handleError(error, 'createCutouts'); + } + } + + createGaugeArcStrokeMaskPath(containerElement, target) { + try { + const gaugeSvg = this.domUtils?.$("#gauge") || document.getElementById("gauge"); + if (!gaugeSvg) { + console.warn("[MultiGuide] gauge SVG를 찾을 수 없습니다"); + return ""; + } + + const size = target.gaugeSize || 832; + const strokeWidth = target.gaugeStrokeWidth || 31; + const padding = target.gaugePadding || 20; + + const center = size / 2; + const radius = size / 2 - strokeWidth / 2 - padding; + + const svgRect = gaugeSvg.getBoundingClientRect(); + const viewBox = gaugeSvg.viewBox.baseVal; + const scaleX = svgRect.width / viewBox.width; + const scaleY = svgRect.height / viewBox.height; + + const screenCenterX = svgRect.left + (center - viewBox.x) * scaleX; + const screenCenterY = svgRect.top + (center - viewBox.y) * scaleY; + const screenRadius = radius * scaleX; + const screenStrokeWidth = strokeWidth * scaleX; + + const startAngle = target.startAngle || 180; + const endAngle = target.endAngle || 360; + + const startRad = (startAngle * Math.PI) / 180; + const endRad = (endAngle * Math.PI) / 180; + + const outerRadius = screenRadius + screenStrokeWidth / 2; + const innerRadius = screenRadius - screenStrokeWidth / 2; + + const outerStartX = screenCenterX + outerRadius * Math.cos(startRad); + const outerStartY = screenCenterY + outerRadius * Math.sin(startRad); + const outerEndX = screenCenterX + outerRadius * Math.cos(endRad); + const outerEndY = screenCenterY + outerRadius * Math.sin(endRad); + + const innerStartX = screenCenterX + innerRadius * Math.cos(startRad); + const innerStartY = screenCenterY + innerRadius * Math.sin(startRad); + const innerEndX = screenCenterX + innerRadius * Math.cos(endRad); + const innerEndY = screenCenterY + innerRadius * Math.sin(endRad); + + const largeArc = endAngle - startAngle > 180 ? 1 : 0; + + let pathData = ` + M ${outerStartX},${outerStartY} + A ${outerRadius},${outerRadius} 0 ${largeArc} 1 ${outerEndX},${outerEndY} + L ${innerEndX},${innerEndY} + A ${innerRadius},${innerRadius} 0 ${largeArc} 0 ${innerStartX},${innerStartY} + Z + `; + + const startCapCenterX = screenCenterX + screenRadius * Math.cos(startRad); + const startCapCenterY = screenCenterY + screenRadius * Math.sin(startRad); + const capRadius = screenStrokeWidth / 2; + + pathData += ` + M ${startCapCenterX + capRadius},${startCapCenterY} + A ${capRadius},${capRadius} 0 1 1 ${startCapCenterX - capRadius},${startCapCenterY} + A ${capRadius},${capRadius} 0 1 1 ${startCapCenterX + capRadius},${startCapCenterY} + Z + `; + + const endCapCenterX = screenCenterX + screenRadius * Math.cos(endRad); + const endCapCenterY = screenCenterY + screenRadius * Math.sin(endRad); + + pathData += ` + M ${endCapCenterX + capRadius},${endCapCenterY} + A ${capRadius},${capRadius} 0 1 1 ${endCapCenterX - capRadius},${endCapCenterY} + A ${capRadius},${capRadius} 0 1 1 ${endCapCenterX + capRadius},${endCapCenterY} + Z + `; + + return pathData; + } catch (error) { + this._handleError(error, 'createGaugeArcStrokeMaskPath', { target }); + return ""; + } + } + + createGaugeArcEllipseStrokeBoundaryPath(containerElement, target) { + try { + const gaugeSvg = this.domUtils?.$("#gauge") || document.getElementById("gauge"); + if (!gaugeSvg) { + console.warn("[MultiGuide] gauge SVG를 찾을 수 없습니다"); + return ""; + } + + const size = target.gaugeSize || 832; + const strokeWidth = target.gaugeStrokeWidth || 31; + const padding = target.gaugePadding || 20; + + const center = size / 2; + const radius = size / 2 - strokeWidth / 2 - padding; + + const svgRect = gaugeSvg.getBoundingClientRect(); + const viewBox = gaugeSvg.viewBox.baseVal; + const scaleX = svgRect.width / viewBox.width; + const scaleY = svgRect.height / viewBox.height; + + const screenCenterX = svgRect.left + (center - viewBox.x) * scaleX; + const screenCenterY = svgRect.top + (center - viewBox.y) * scaleY; + const screenRadius = radius * scaleX; + const screenStrokeWidth = strokeWidth * scaleX; + + const startAngle = target.startAngle || 180; + const endAngle = target.endAngle || 360; + + const startRad = (startAngle * Math.PI) / 180; + const endRad = (endAngle * Math.PI) / 180; + + const outerRadius = screenRadius + screenStrokeWidth / 2; + const innerRadius = screenRadius - screenStrokeWidth / 2; + + const outerStartX = screenCenterX + outerRadius * Math.cos(startRad); + const outerStartY = screenCenterY + outerRadius * Math.sin(startRad); + const outerEndX = screenCenterX + outerRadius * Math.cos(endRad); + const outerEndY = screenCenterY + outerRadius * Math.sin(endRad); + + const innerStartX = screenCenterX + innerRadius * Math.cos(startRad); + const innerStartY = screenCenterY + innerRadius * Math.sin(startRad); + const innerEndX = screenCenterX + innerRadius * Math.cos(endRad); + const innerEndY = screenCenterY + innerRadius * Math.sin(endRad); + + const largeArc = endAngle - startAngle > 180 ? 1 : 0; + const capRadius = screenStrokeWidth / 2; + + const startCapCenterX = screenCenterX + screenRadius * Math.cos(startRad); + const startCapCenterY = screenCenterY + screenRadius * Math.sin(startRad); + const endCapCenterX = screenCenterX + screenRadius * Math.cos(endRad); + const endCapCenterY = screenCenterY + screenRadius * Math.sin(endRad); + + let pathData = ""; + + pathData += `M ${outerStartX},${outerStartY} A ${outerRadius},${outerRadius} 0 ${largeArc} 1 ${outerEndX},${outerEndY} `; + pathData += `M ${innerStartX},${innerStartY} A ${innerRadius},${innerRadius} 0 ${largeArc} 1 ${innerEndX},${innerEndY} `; + + const startCapLeftX = startCapCenterX - capRadius; + const startCapRightX = startCapCenterX + capRadius; + pathData += `M ${startCapLeftX},${startCapCenterY} A ${capRadius},${capRadius} 0 0 0 ${startCapRightX},${startCapCenterY} `; + + const endCapLeftX = endCapCenterX - capRadius; + const endCapRightX = endCapCenterX + capRadius; + pathData += `M ${endCapLeftX},${endCapCenterY} A ${capRadius},${capRadius} 0 0 0 ${endCapRightX},${endCapCenterY}`; + + return pathData.trim(); + } catch (error) { + this._handleError(error, 'createGaugeArcEllipseStrokeBoundaryPath', { target }); + return ""; + } + } + + createRoundedRectPath(x, y, width, height, radius) { + return ` + M ${x + radius},${y} + L ${x + width - radius},${y} + Q ${x + width},${y} ${x + width},${y + radius} + L ${x + width},${y + height - radius} + Q ${x + width},${y + height} ${x + width - radius},${y + height} + L ${x + radius},${y + height} + Q ${x},${y + height} ${x},${y + height - radius} + L ${x},${y + radius} + Q ${x},${y} ${x + radius},${y} + Z + `; + } + + createRoundedRectStrokePath(x, y, width, height, radius) { + return ` + M ${x + radius},${y} + L ${x + width - radius},${y} + Q ${x + width},${y} ${x + width},${y + radius} + L ${x + width},${y + height - radius} + Q ${x + width},${y + height} ${x + width - radius},${y + height} + L ${x + radius},${y + height} + Q ${x},${y + height} ${x},${y + height - radius} + L ${x},${y + radius} + Q ${x},${y} ${x + radius},${y} + Z + `; + } + + createBorders() { + try { + if (!this.bordersContainer) { + console.warn("[MultiGuide] bordersContainer가 없습니다."); + return; + } + + if (!this.targets || !Array.isArray(this.targets)) { + this._handleError(new Error('targets가 배열이 아닙니다.'), 'createBorders'); + return; + } + + this.targets.forEach((target, index) => { + try { + if (!target || !target.selector) { + console.warn(`[MultiGuide] target ${index}가 유효하지 않습니다.`); + return; + } + + const element = this.domUtils?.$(target.selector) || document.querySelector(target.selector); + if (!element) { + console.warn(`[MultiGuide] 요소를 찾을 수 없음: ${target.selector}`); + return; + } + + if (target.useGaugeArc || target.shape === "arc") { + return; + } + + const rect = element.getBoundingClientRect(); + const padding = target.padding || 15; + + const border = this.domUtils?.createElement('div', { + class: 'guide-border', + style: { + left: `${rect.left - padding}px`, + top: `${rect.top - padding}px`, + width: `${rect.width + padding * 2}px`, + height: `${rect.height + padding * 2}px`, + animationDelay: `${index * 0.1}s` + } + }) || document.createElement("div"); + + if (!this.domUtils) { + border.className = "guide-border"; + border.style.cssText = ` + left: ${rect.left - padding}px; + top: ${rect.top - padding}px; + width: ${rect.width + padding * 2}px; + height: ${rect.height + padding * 2}px; + animation-delay: ${index * 0.1}s; + `; + } + + this.bordersContainer.appendChild(border); + } catch (error) { + this._handleError(error, 'createBorders.target', { index, target }); + } + }); + } catch (error) { + this._handleError(error, 'createBorders'); + } + } + + createConnectionLine(elementRect, maskRect, tooltipBox, target, horizontalPos, verticalPos) { + if (!this.connectionLinesGroup) return; + + // tooltip의 실제 위치 계산 + const tooltipRect = tooltipBox.getBoundingClientRect(); + const tooltipCenterX = tooltipRect.left + tooltipRect.width / 2; + const tooltipCenterY = tooltipRect.top + tooltipRect.height / 2; + + // 요소의 연결점 계산 (요소의 가장 가까운 모서리 또는 중앙) + let elementX, elementY; + + if (horizontalPos === "left") { + // tooltip이 왼쪽에 있으면 요소의 왼쪽 중앙 + elementX = elementRect.left; + elementY = elementRect.top + elementRect.height / 2; + } else if (horizontalPos === "right") { + // tooltip이 오른쪽에 있으면 요소의 오른쪽 중앙 + elementX = elementRect.right; + elementY = elementRect.top + elementRect.height / 2; + } else { + // center일 때 + if (verticalPos === "top") { + // tooltip이 위에 있으면 요소의 위쪽 중앙 + elementX = elementRect.left + elementRect.width / 2; + elementY = elementRect.top; + } else if (verticalPos === "bottom") { + // tooltip이 아래에 있으면 요소의 아래쪽 중앙 + elementX = elementRect.left + elementRect.width / 2; + elementY = elementRect.bottom; + } else { + // center center일 때는 요소의 중앙 + elementX = elementRect.left + elementRect.width / 2; + elementY = elementRect.top + elementRect.height / 2; + } + } + + // tooltip의 연결점 계산 + let tooltipX, tooltipY; + + if (horizontalPos === "left") { + // tooltip이 왼쪽에 있으면 tooltip의 오른쪽 중앙 + tooltipX = tooltipRect.right; + tooltipY = tooltipRect.top + tooltipRect.height / 2; + } else if (horizontalPos === "right") { + // tooltip이 오른쪽에 있으면 tooltip의 왼쪽 중앙 + tooltipX = tooltipRect.left; + tooltipY = tooltipRect.top + tooltipRect.height / 2; + } else { + // center일 때 + if (verticalPos === "top") { + // tooltip이 위에 있으면 tooltip의 아래쪽 중앙 + tooltipX = tooltipRect.left + tooltipRect.width / 2; + tooltipY = tooltipRect.bottom; + } else if (verticalPos === "bottom") { + // tooltip이 아래에 있으면 tooltip의 위쪽 중앙 + tooltipX = tooltipRect.left + tooltipRect.width / 2; + tooltipY = tooltipRect.top; + } else { + // center center일 때는 tooltip의 중앙 + tooltipX = tooltipRect.left + tooltipRect.width / 2; + tooltipY = tooltipRect.top + tooltipRect.height / 2; + } + } + + // SVG path 생성 (부드러운 곡선) + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + const midX = (elementX + tooltipX) / 2; + const midY = (elementY + tooltipY) / 2; + + // 곡선을 위한 제어점 계산 + let controlX1, controlY1, controlX2, controlY2; + + if (horizontalPos === "left" || horizontalPos === "right") { + // 수평 연결: 수직 방향으로 곡선 + controlX1 = elementX + (tooltipX - elementX) * 0.3; + controlY1 = elementY; + controlX2 = tooltipX - (tooltipX - elementX) * 0.3; + controlY2 = tooltipY; + } else { + // 수직 연결: 수평 방향으로 곡선 + controlX1 = elementX; + controlY1 = elementY + (tooltipY - elementY) * 0.3; + controlX2 = tooltipX; + controlY2 = tooltipY - (tooltipY - elementY) * 0.3; + } + + const pathData = `M ${elementX} ${elementY} C ${controlX1} ${controlY1}, ${controlX2} ${controlY2}, ${tooltipX} ${tooltipY}`; + path.setAttribute("d", pathData); + path.setAttribute("stroke", "#FFF"); + path.setAttribute("stroke-width", "1"); + //path.setAttribute("stroke-dasharray", "2,2"); + path.setAttribute("fill", "none"); + //path.setAttribute("opacity", "0.6"); + path.setAttribute("data-guide-selector", target.selector); + + this.connectionLinesGroup.appendChild(path); + } + + createKeywordElementConnection() { + try { + console.log("[createKeywordElementConnection] 시작"); + + if (!this.elementConnectionLinesGroup) { + console.warn("[MultiGuide] ❌ elementConnectionLinesGroup 없음"); + return; + } + + const element1 = this.domUtils?.$(".video-card:nth-child(3) .key-badge:first-child") || document.querySelector(".video-card:nth-child(3) .key-badge:first-child"); + const element2 = this.domUtils?.$(".video-card:nth-child(5) .key-badge:first-child") || document.querySelector(".video-card:nth-child(5) .key-badge:first-child"); + + console.log("element1:", element1); + console.log("element2:", element2); + + if (!element1 || !element2) { + console.warn("❌ 키워드 요소 없음"); + return; + } + + // 🔥 interest 툴팁의 index 찾기 + const interestTarget = this.targets.find(t => t.class === "interest"); + const interestIndex = this.targets.indexOf(interestTarget); + const animationDelay = interestIndex >= 0 ? interestIndex * 0.1 + 0.3 : 0.3; + + const rect1 = element1.getBoundingClientRect(); + const rect2 = element2.getBoundingClientRect(); + + const x1 = rect1.right; + const y1 = rect1.top + rect1.height / 2; + const x2 = rect2.left + rect2.width / 2; + const y2 = rect2.top; + + const offset = 20; + const midX1 = x1 + offset; + const midY1 = y1; + const midX2 = x2; + const midY2 = y1; + + // 두 키워드 요소를 연결하는 실선 + const elementPathData = `M ${x1} ${y1} L ${midX1} ${midY1} L ${midX2} ${midY2} L ${x2} ${y2}`; + const elementPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); + elementPath.setAttribute("d", elementPathData); + elementPath.setAttribute("stroke", "#FFF"); + elementPath.setAttribute("stroke-width", "1.5"); + elementPath.setAttribute("fill", "none"); + elementPath.setAttribute("stroke-linecap", "round"); + elementPath.setAttribute("stroke-linejoin", "round"); + // 🔥 애니메이션 추가 + elementPath.style.opacity = "0"; + elementPath.style.animation = `connectionFadeIn 0.4s ease-out ${animationDelay}s forwards`; + + this.elementConnectionLinesGroup.appendChild(elementPath); + console.log("✅ 키워드 요소 연결선 추가"); + + // interest 툴팁박스에서 연결선까지 실선으로 연결 + const interestTooltip = this.labelsContainer.querySelector('.guide-tooltip-box.interest'); + console.log("interestTooltip:", interestTooltip); + + if (interestTooltip) { + const interestRect = interestTooltip.getBoundingClientRect(); + + const connectionMidX = midX2; + const connectionMidY = (midY2 + y2) / 2; + + const interestX = interestRect.right; + const interestY = interestRect.top + interestRect.height / 2; + + const interestPathData = `M ${interestX} ${interestY} L ${connectionMidX} ${interestY}`; + + const interestPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); + interestPath.setAttribute("d", interestPathData); + interestPath.setAttribute("stroke", "#FFF"); + interestPath.setAttribute("stroke-width", "1.5"); + interestPath.setAttribute("fill", "none"); + interestPath.setAttribute("stroke-linecap", "round"); + interestPath.setAttribute("stroke-linejoin", "round"); + // 🔥 애니메이션 추가 + interestPath.style.opacity = "0"; + interestPath.style.animation = `connectionFadeIn 0.4s ease-out ${animationDelay}s forwards`; + + this.elementConnectionLinesGroup.appendChild(interestPath); + console.log("✅ interest 툴팁 연결선 추가"); + } else { + console.warn("⚠️ interest 툴팁 없음"); + } + + // 🔥 애니메이션 CSS 추가 + if (!(this.domUtils?.$("#guide-connection-animation-styles") || document.getElementById("guide-connection-animation-styles"))) { + const style = this.domUtils?.createElement('style', { id: 'guide-connection-animation-styles' }) || document.createElement("style"); + style.id = "guide-connection-animation-styles"; + style.textContent = ` + @keyframes connectionFadeIn { + from { + opacity: 0; + stroke-dasharray: 1000; + stroke-dashoffset: 1000; + } + to { + opacity: 0.8; + stroke-dasharray: 1000; + stroke-dashoffset: 0; + } + } + `; + document.head.appendChild(style); + } + } catch (error) { + this._handleError(error, 'createKeywordElementConnection'); + } + } + + createLabels() { + try { + if (!this.labelsContainer) { + console.warn("[MultiGuide] labelsContainer가 없습니다."); + return; + } + + if (!this.targets || !Array.isArray(this.targets)) { + this._handleError(new Error('targets가 배열이 아닙니다.'), 'createLabels'); + return; + } + + this.targets.forEach((target, index) => { + try { + if (!target || !target.label) { + return; + } + + const element = this.domUtils?.$(target.selector) || document.querySelector(target.selector); + if (!element) { + console.warn(`[MultiGuide] 요소를 찾을 수 없음: ${target.selector}`); + return; + } + + const elementRect = element.getBoundingClientRect(); + // 마스크된 영역의 rect 계산 (padding 포함) + const padding = target.padding || 0; + const maskRect = { + left: elementRect.left - padding, + top: elementRect.top - padding, + right: elementRect.right + padding, + bottom: elementRect.bottom + padding, + width: elementRect.width + padding * 2, + height: elementRect.height + padding * 2, + centerX: elementRect.left + elementRect.width / 2, + centerY: elementRect.top + elementRect.height / 2 + }; + + // 기존 tooltipBox가 있는지 확인 (data-selector 속성으로 찾기) + let tooltipBox = this.labelsContainer.querySelector( + `[data-guide-selector="${CSS.escape(target.selector)}"]` + ); + + // 기존 것이 없으면 새로 생성 + if (!tooltipBox) { + tooltipBox = document.createElement("div"); + // 기본 클래스와 target의 class 속성 추가 + tooltipBox.className = "guide-tooltip-box" + (target.class ? ` ${target.class}` : ""); + tooltipBox.setAttribute("data-guide-selector", target.selector); + tooltipBox.style.cssText = ` + animation: tooltipFadeIn 0.4s ease-out ${index * 0.1 + 0.3}s forwards; + opacity: 0; + `; + + const title = document.createElement("div"); + title.className = "guide-tooltip-title"; + title.textContent = target.label; + tooltipBox.appendChild(title); + + if (target.description) { + const description = document.createElement("div"); + description.className = "guide-tooltip-description"; + const lines = target.description.split('\n'); + lines.forEach((line) => { + if (line.trim()) { + const p = document.createElement("p"); + p.textContent = line; + description.appendChild(p); + } + }); + tooltipBox.appendChild(description); + } + + this.labelsContainer.appendChild(tooltipBox); + } + + // 위치 계산 및 업데이트 (기존 것이든 새 것이든 위치는 업데이트) + // position 형식: "bottom", "top", "left", "right", "center", "center center", "center bottom" 등 + const positionParts = (target.position || "").toLowerCase().split(/\s+/); + const verticalPos = positionParts.find(p => p === "top" || p === "bottom" || p === "center") || + (elementRect.top < 150 ? "bottom" : "top"); + const horizontalPos = positionParts.find(p => p === "left" || p === "right" || p === "center") || "center"; + + let left, top; + + // 가로 위치 계산 + if (horizontalPos === "left") { + left = maskRect.left - 10; + tooltipBox.setAttribute("data-position", "left"); + tooltipBox.style.transform = "translateX(-100%)"; + } else if (horizontalPos === "right") { + left = maskRect.right + 10; + tooltipBox.setAttribute("data-position", "right"); + tooltipBox.style.transform = "translateX(0)"; + } else { + // center: position이 "center" 또는 "center center"일 때는 요소의 중앙, 그 외는 마스크된 영역의 중앙 + if (verticalPos === "center" || (positionParts.length === 1 && positionParts[0] === "center")) { + // 요소의 중앙에 tooltip 중앙이 맞도록 + left = elementRect.left + elementRect.width / 2; + } else { + // 마스크된 영역의 중앙에 tooltip 중앙이 맞도록 + left = maskRect.centerX; + } + tooltipBox.setAttribute("data-position", "center"); + tooltipBox.style.transform = "translateX(-50%)"; + } + + // 세로 위치 계산 + // learning 클래스인 경우 guide-arc-ellipse-stroke-path의 top 위치 사용 + if (target.class === "learning" && this.arcEllipseStrokePath) { + const arcPathRect = this.arcEllipseStrokePath.getBoundingClientRect(); + top = arcPathRect.top; + } else if (verticalPos === "center") { + // 요소의 중앙에 tooltip 중앙이 맞도록 + const boxHeight = tooltipBox.offsetHeight || 100; + top = elementRect.top + elementRect.height / 2 - boxHeight / 2; + } else if (verticalPos === "bottom" || (elementRect.top < 150 && verticalPos !== "top")) { + top = maskRect.bottom + 15; + } else { + const boxHeight = tooltipBox.offsetHeight || 100; + top = maskRect.top - boxHeight - 15; + if (top < 10) { + top = maskRect.bottom + 15; + } + } + + tooltipBox.style.left = `${left}px`; + tooltipBox.style.top = `${top}px`; + + // 연결 라인 그리기 + this.createConnectionLine(elementRect, maskRect, tooltipBox, target, horizontalPos, verticalPos); + } catch (error) { + this._handleError(error, 'createLabels.target', { index, target }); + } + }); + + if (!(this.domUtils?.$("#guide-tooltip-styles") || document.getElementById("guide-tooltip-styles"))) { + const style = document.createElement("style"); + style.id = "guide-tooltip-styles"; + style.textContent = ` + @keyframes tooltipFadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + .guide-tooltip-box[data-position="left"] { + animation-name: tooltipFadeInLeft; + } + .guide-tooltip-box[data-position="right"] { + animation-name: tooltipFadeInRight; + } + @keyframes tooltipFadeInLeft { + from { + opacity: 0; + transform: translateX(-100%) translateY(-10px); + } + to { + opacity: 1; + transform: translateX(-100%) translateY(0); + } + } + @keyframes tooltipFadeInRight { + from { + opacity: 0; + transform: translateX(0) translateY(-10px); + } + to { + opacity: 1; + transform: translateX(0) translateY(0); + } + } + `; + document.head.appendChild(style); + } + } catch (error) { + this._handleError(error, 'createLabels'); + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // resize 타이머 정리 + if (this.resizeTimer) { + clearTimeout(this.resizeTimer); + this.resizeTimer = null; + } + + // 가이드 숨기기 + this.hide(); + + // 참조 정리 + this.guideWrap = null; + this.cutoutPath = null; + this.strokePath = null; + this.arcEllipseStrokePath = null; + this.arcStrokeMaskPath = null; + this.connectionLinesGroup = null; + this.elementConnectionLinesGroup = null; + this.bordersContainer = null; + this.labelsContainer = null; + this.targets = []; + this.isActive = false; + } catch (error) { + this._handleError(error, 'destroy'); + } + } + } + + // 초기화 함수 + window.initMultiGuide = function(targets) { + // targets가 없으면 기본값 사용 또는 에러 처리 + if (!targets || !Array.isArray(targets) || targets.length === 0) { + console.warn("[MultiGuide] targets가 제공되지 않았습니다. 기본 targets를 사용합니다."); + // 기본 targets를 사용하거나 에러를 발생시킬 수 있습니다. + // 여기서는 에러를 발생시키는 것이 더 안전합니다. + if (!targets) { + console.error("[MultiGuide] initMultiGuide는 targets 파라미터가 필요합니다."); + return; + } + } + + if (window.multiGuide) { + console.log("MultiGuide가 이미 초기화되어 있습니다."); + return; + } + + window.multiGuide = new MultiGuide(targets); + console.log("멀티 가이드 초기화 완료!"); + + let retryCount = 0; + const maxRetries = 50; + + function autoShowGuide() { + retryCount++; + + const requiredElements = targets.map(target => { + const el = document.querySelector(target.selector); + return { target, element: el, found: el !== null }; + }); + + const foundCount = requiredElements.filter(r => r.found).length; + + console.log(`[MultiGuide] 요소 확인 (${retryCount}회차): ${foundCount}/${targets.length}`); + + const minRequiredElements = Math.max(3, Math.floor(targets.length * 0.5)); + + if (foundCount >= minRequiredElements) { + const gaugeTarget = targets.find(t => t.useGaugeArc); + if (gaugeTarget) { + const gaugeElement = document.getElementById("gauge"); + if (gaugeElement) { + const hasGaugeContent = gaugeElement.children.length > 0 || + gaugeElement.querySelector('path') !== null || + gaugeElement.innerHTML.trim() !== ''; + + if (!hasGaugeContent && retryCount < 20) { + console.log("[MultiGuide] 게이지가 아직 초기화되지 않았습니다. 대기 중..."); + if (retryCount < maxRetries) { + setTimeout(autoShowGuide, 200); + } + return; + } + } + } + + try { + console.log("[MultiGuide] 가이드 실행 시도..."); + window.multiGuide.show(); + console.log("[MultiGuide] ✅ 멀티 가이드 자동 실행 완료!"); + } catch (error) { + console.error("[MultiGuide] ❌ 실행 중 오류:", error); + } + } else { + if (retryCount < maxRetries) { + console.log(`[MultiGuide] 요소가 부족합니다. 재시도 중... (${retryCount}/${maxRetries})`); + setTimeout(autoShowGuide, 200); + } else { + console.warn("[MultiGuide] ⚠️ 자동 실행 시간 초과"); + if (foundCount > 0) { + try { + window.multiGuide.show(); + } catch (error) { + console.error("[MultiGuide] 실행 실패:", error); + } + } + } + } + } + + if (document.readyState === "complete") { + setTimeout(autoShowGuide, 100); + } else { + window.addEventListener("load", () => { + setTimeout(autoShowGuide, 100); + }); + } + }; \ No newline at end of file diff --git a/src/js/main/VideoCardRenderer.js b/src/js/main/VideoCardRenderer.js new file mode 100644 index 0000000..f5dc9aa --- /dev/null +++ b/src/js/main/VideoCardRenderer.js @@ -0,0 +1,259 @@ +// ============================================ +// 비디오 카드 렌더링 모듈 +// 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils, AnimationUtils) +// ============================================ + +class VideoCardRenderer { + constructor(config, dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + // 입력값 유효성 검증 + if (!config || typeof config !== 'object') { + this._handleError(new Error('config가 유효하지 않습니다.'), 'constructor'); + config = {}; + } + + this.config = config; + this.animationDelay = config.animationDelay || 50; + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `VideoCardRenderer.${context}`, + component: 'VideoCardRenderer', + ...additionalInfo + }, false); + } else { + console.error(`[VideoCardRenderer] ${context}:`, error, additionalInfo); + } + } + + // 비디오 카드들 렌더링 (AnimationUtils 활용) + async renderCards(videos, containerId = "videoCardsContainer") { + try { + // 입력값 유효성 검증 + if (!videos || !Array.isArray(videos)) { + this._handleError(new Error('videos가 배열이 아닙니다.'), 'renderCards', { containerId }); + return; + } + + if (!containerId || typeof containerId !== 'string') { + this._handleError(new Error('containerId가 유효하지 않습니다.'), 'renderCards', { videos }); + return; + } + + const container = this.domUtils?.$(`#${containerId}`) || document.querySelector(`#${containerId}`); + if (!container) { + this._handleError(new Error(`컨테이너를 찾을 수 없습니다: #${containerId}`), 'renderCards', { containerId }); + return; + } + + if (this.domUtils) { + this.domUtils.empty(container); + } else { + container.innerHTML = ''; + } + + const cards = []; + videos.forEach((video, index) => { + try { + const card = this.createVideoCard(video); + if (card) { + cards.push(card); + container.appendChild(card); + } + } catch (error) { + this._handleError(error, 'renderCards.createCard', { index, video }); + } + }); + + // 순차적 애니메이션 (AnimationUtils 활용) + if (this.animationUtils && cards.length > 0) { + await this.animationUtils.sequentialAnimate(cards, "show", this.animationDelay); + } else if (typeof AnimationUtils !== 'undefined' && cards.length > 0) { + await AnimationUtils.sequentialAnimate(cards, "show", this.animationDelay); + } + } catch (error) { + this._handleError(error, 'renderCards', { videos, containerId }); + } + } + + // 비디오 카드 생성 (DOMUtils, VideoBase 활용) + createVideoCard(video) { + try { + // 입력값 유효성 검증 + if (!video || typeof video !== 'object') { + this._handleError(new Error('video가 유효하지 않습니다.'), 'createVideoCard'); + return null; + } + + // VideoModel 사용 (있는 경우) + let videoModel; + if (typeof VideoModel !== 'undefined' && video instanceof VideoModel) { + videoModel = video; + } else if (typeof VideoModel !== 'undefined') { + try { + videoModel = new VideoModel(video); + } catch (error) { + this._handleError(error, 'createVideoCard.VideoModel', { video }); + // VideoModel 생성 실패 시 원본 video 사용 + videoModel = video; + } + } else { + videoModel = video; + } + + const keywords = videoModel.keywords || []; + const keywordTags = Array.isArray(keywords) + ? keywords.map((kw) => { + const escapedKw = this._escapeHtml(String(kw || '')); + return `${escapedKw}`; + }).join(" ") + : ""; + + const categoryClass = this.getCategoryClass(videoModel.category); + const pickerName = videoModel.picker && String(videoModel.picker).trim(); + const pickBadge = pickerName + ? `
${this._escapeHtml(pickerName)}님Pick!
` + : ""; + + const gauge = videoModel.gauge; + const gaugeBar = (typeof gauge === 'number' && gauge >= 0 && gauge <= 100) + ? `
` + : ""; + + // VideoBase를 사용하여 썸네일 URL 생성 + let thumbnailUrl = ''; + try { + if (videoModel.getThumbnailUrl && typeof videoModel.getThumbnailUrl === 'function') { + thumbnailUrl = videoModel.getThumbnailUrl("sd"); + } else if (typeof VideoBase !== 'undefined' && VideoBase.getYouTubeThumbnail) { + const videoId = videoModel.url || videoModel.getVideoId?.() || videoModel.id; + thumbnailUrl = VideoBase.getYouTubeThumbnail(videoId, "sd"); + } else { + // 기본 썸네일 URL 생성 + const videoId = videoModel.url || videoModel.id; + thumbnailUrl = `https://img.youtube.com/vi/${videoId}/sddefault.jpg`; + } + } catch (error) { + this._handleError(error, 'createVideoCard.thumbnailUrl', { videoModel }); + thumbnailUrl = ''; + } + + const videoId = videoModel.id || videoModel.url || ''; + const title = this._escapeHtml(String(videoModel.title || '')); + const category = this._escapeHtml(String(videoModel.category || '')); + const isBookmarked = !!videoModel.bookmark; + const checkboxChecked = isBookmarked ? ' checked' : ''; + + const cardContent = ` +
+
+ ${title} +
+
+ +
${category}
+
${title}
+
${keywordTags}
+
+ ${pickBadge} +
+ ${gaugeBar} + `; + + // DOMUtils.createElement 사용 + if (this.domUtils && this.domUtils.createElement) { + return this.domUtils.createElement("div", { class: "video-card" }, cardContent); + } else if (typeof DOMUtils !== 'undefined' && DOMUtils.createElement) { + return DOMUtils.createElement("div", { class: "video-card" }, cardContent); + } else { + // 폴백: 직접 DOM 요소 생성 + const div = document.createElement("div"); + div.className = "video-card"; + div.innerHTML = cardContent; + return div; + } + } catch (error) { + this._handleError(error, 'createVideoCard', { video }); + return null; + } + } + + /** + * HTML 이스케이프 (XSS 방지) + * @private + */ + _escapeHtml(text) { + if (typeof text !== 'string') { + text = String(text || ''); + } + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, (m) => map[m]); + } + + // 유틸리티: 카테고리 클래스 + getCategoryClass(category) { + try { + if (!category || typeof category !== 'string') { + return "default"; + } + + const map = { + 리더십: "leader", + 인사이트: "insight", + 비즈트렌드: "biz", + }; + return map[category] || "default"; + } catch (error) { + this._handleError(error, 'getCategoryClass', { category }); + return "default"; + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 참조 정리 + this.config = null; + this.animationDelay = 50; + } catch (error) { + this._handleError(error, 'destroy'); + } + } +} diff --git a/src/js/main/VideoSlider.js b/src/js/main/VideoSlider.js new file mode 100644 index 0000000..a855b97 --- /dev/null +++ b/src/js/main/VideoSlider.js @@ -0,0 +1,302 @@ +// ============================================ +// 비디오 슬라이드 모듈 (페이지네이션) +// 공통 모듈 활용 (ErrorHandler, DOMUtils, EventManager, Utils, AnimationUtils) +// ============================================ + +class VideoSlider { + constructor(config, dependencies = {}) { + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + // 입력값 유효성 검증 + if (!config || typeof config !== 'object') { + this._handleError(new Error('config가 유효하지 않습니다.'), 'constructor'); + config = {}; + } + + this.config = config; + this.videos = Array.isArray(config.videos) ? config.videos : []; + this.currentPage = 0; + this.videosPerPage = config.videosPerPage || 6; + + // config에 videosPerPage가 없으면 설정 + if (!this.config.videosPerPage) { + this.config.videosPerPage = this.videosPerPage; + } + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `VideoSlider.${context}`, + component: 'VideoSlider', + ...additionalInfo + }, false); + } else { + console.error(`[VideoSlider] ${context}:`, error, additionalInfo); + } + } + + // 초기화 + init() { + try { + this.setupEventListeners(); + this.updatePagination(); + } catch (error) { + this._handleError(error, 'init'); + } + } + + // 이벤트 리스너 설정 (EventManager 활용) + setupEventListeners() { + try { + const prevBtn = this.domUtils?.$("#prevBtn") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#prevBtn") : null) || + document.querySelector("#prevBtn"); + const nextBtn = this.domUtils?.$("#nextBtn") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#nextBtn") : null) || + document.querySelector("#nextBtn"); + + if (!prevBtn || !nextBtn) { + console.warn("[VideoSlider] 이전/다음 버튼을 찾을 수 없습니다."); + return; + } + + // 이전 버튼 클릭 핸들러 + const prevHandler = async (e) => { + try { + e.preventDefault(); + e.stopPropagation(); + if (this.currentPage > 0) { + this.currentPage--; + await this.changePage(); + } + } catch (error) { + this._handleError(error, 'setupEventListeners.prevHandler'); + } + }; + + // 다음 버튼 클릭 핸들러 + const nextHandler = async (e) => { + try { + e.preventDefault(); + e.stopPropagation(); + const totalPages = this.getTotalPages(); + if (this.currentPage < totalPages - 1) { + this.currentPage++; + await this.changePage(); + } + } catch (error) { + this._handleError(error, 'setupEventListeners.nextHandler'); + } + }; + + // EventManager 사용 (있는 경우) + if (this.eventManager) { + const prevId = this.eventManager.on(prevBtn, "click", prevHandler); + const nextId = this.eventManager.on(nextBtn, "click", nextHandler); + this.listenerIds.push( + { element: prevBtn, id: prevId, type: 'click' }, + { element: nextBtn, id: nextId, type: 'click' } + ); + } else { + // 폴백: 직접 이벤트 리스너 등록 + prevBtn.addEventListener("click", prevHandler); + nextBtn.addEventListener("click", nextHandler); + } + } catch (error) { + this._handleError(error, 'setupEventListeners'); + } + } + + // 현재 페이지 영상 가져오기 + getCurrentPageVideos() { + try { + if (!Array.isArray(this.videos)) { + this._handleError(new Error('videos가 배열이 아닙니다.'), 'getCurrentPageVideos'); + return []; + } + + const videosPerPage = this.config.videosPerPage || this.videosPerPage || 6; + const start = this.currentPage * videosPerPage; + const end = start + videosPerPage; + return this.videos.slice(start, end); + } catch (error) { + this._handleError(error, 'getCurrentPageVideos'); + return []; + } + } + + // 페이지 변경 (AnimationUtils 활용) + async changePage() { + try { + const container = this.domUtils?.$("#videoCardsContainer") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#videoCardsContainer") : null) || + document.querySelector("#videoCardsContainer"); + + if (!container) { + this._handleError(new Error('videoCardsContainer를 찾을 수 없습니다.'), 'changePage'); + return; + } + + // 페이드 아웃 효과 + if (this.animationUtils && this.animationUtils.fade) { + await this.animationUtils.fade(container, "out", 400); + } else if (typeof AnimationUtils !== 'undefined' && AnimationUtils.fade) { + await AnimationUtils.fade(container, "out", 400); + } else { + // 폴백: 직접 페이드 효과 + container.style.opacity = '0'; + await (this.utils?.delay(400) || new Promise(resolve => setTimeout(resolve, 400))); + } + + // 외부 렌더링 함수 호출 + if (this.config.onPageChange && typeof this.config.onPageChange === 'function') { + try { + this.config.onPageChange(this.getCurrentPageVideos()); + } catch (error) { + this._handleError(error, 'changePage.onPageChange'); + } + } + + // 페이지네이션 업데이트 + this.updatePagination(); + + // 페이드 인 효과 + if (this.animationUtils && this.animationUtils.fade) { + await this.animationUtils.fade(container, "in", 400); + } else if (typeof AnimationUtils !== 'undefined' && AnimationUtils.fade) { + await AnimationUtils.fade(container, "in", 400); + } else { + // 폴백: 직접 페이드 효과 + container.style.opacity = '1'; + } + } catch (error) { + this._handleError(error, 'changePage'); + } + } + + // 페이지네이션 업데이트 + updatePagination() { + try { + const pagination = this.domUtils?.$("#pagination") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#pagination") : null) || + document.querySelector("#pagination"); + const prevBtn = this.domUtils?.$("#prevBtn") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#prevBtn") : null) || + document.querySelector("#prevBtn"); + const nextBtn = this.domUtils?.$("#nextBtn") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#nextBtn") : null) || + document.querySelector("#nextBtn"); + + if (!pagination) { + console.warn("[VideoSlider] pagination 요소를 찾을 수 없습니다."); + return; + } + + const totalPages = this.getTotalPages(); + const currentPageNum = Math.max(0, Math.min(this.currentPage + 1, totalPages)); + + // XSS 방지를 위해 텍스트로 설정 + pagination.innerHTML = ''; + const currentSpan = document.createElement('span'); + currentSpan.className = 'current'; + currentSpan.textContent = currentPageNum; + pagination.appendChild(currentSpan); + pagination.appendChild(document.createTextNode(` / ${totalPages}`)); + + // 이전 버튼 상태 + if (prevBtn) { + const isDisabled = this.currentPage === 0; + if (this.domUtils && this.domUtils.toggleClass) { + this.domUtils.toggleClass(prevBtn, "disabled", isDisabled); + } else if (typeof DOMUtils !== 'undefined' && DOMUtils.toggleClass) { + DOMUtils.toggleClass(prevBtn, "disabled", isDisabled); + } else { + // 폴백 + if (isDisabled) { + prevBtn.classList.add("disabled"); + } else { + prevBtn.classList.remove("disabled"); + } + } + } + + // 다음 버튼 상태 + if (nextBtn) { + const isDisabled = this.currentPage >= totalPages - 1; + if (this.domUtils && this.domUtils.toggleClass) { + this.domUtils.toggleClass(nextBtn, "disabled", isDisabled); + } else if (typeof DOMUtils !== 'undefined' && DOMUtils.toggleClass) { + DOMUtils.toggleClass(nextBtn, "disabled", isDisabled); + } else { + // 폴백 + if (isDisabled) { + nextBtn.classList.add("disabled"); + } else { + nextBtn.classList.remove("disabled"); + } + } + } + } catch (error) { + this._handleError(error, 'updatePagination'); + } + } + + // 전체 페이지 수 + getTotalPages() { + try { + if (!Array.isArray(this.videos)) { + return 0; + } + + const videosPerPage = this.config.videosPerPage || this.videosPerPage || 6; + if (videosPerPage <= 0) { + this._handleError(new Error('videosPerPage가 0 이하입니다.'), 'getTotalPages'); + return 1; + } + + return Math.max(1, Math.ceil(this.videos.length / videosPerPage)); + } catch (error) { + this._handleError(error, 'getTotalPages'); + return 1; + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + + // 참조 정리 + this.config = null; + this.videos = []; + this.currentPage = 0; + } catch (error) { + this._handleError(error, 'destroy'); + } + } +} diff --git a/src/js/main/Videomodalmanager.js b/src/js/main/Videomodalmanager.js new file mode 100644 index 0000000..d3f7674 --- /dev/null +++ b/src/js/main/Videomodalmanager.js @@ -0,0 +1,317 @@ +// ============================================ +// 비디오 모달 관리 모듈 (Videomodalmanager.js) +// ============================================ +// +// [역할] 비디오 카드 클릭 시 모달을 열고, YouTube 영상을 재생합니다. +// +// [파일명 참고] "Videomodalmanager"는 legacy 표기입니다. +// 클래스명은 VideoModalManager (PascalCase)를 사용합니다. +// +// [의존성] VideoModalBase(부모), DOMUtils, EventManager, ErrorHandler, Utils +// +// [사용 예] index.html 등에서: +// const modalManager = new VideoModalManager({ videos: [...] }); +// modalManager.init(); +// +// ============================================ + +class VideoModalManager extends VideoModalBase { + constructor(config, dependencies = {}) { + // 입력값 유효성 검증 (super 호출 전에 가능한 작업만) + if (!config || typeof config !== 'object') { + config = {}; + } + + // VideoModalBase에 전달할 config 준비 + const baseConfig = { + videos: config.videos || [], + modalPath: config.modalPath || "./_modal/video.html", + modalPathTemplate: config.modalPathTemplate || "./_modal/video-{type}.html", + enableHeightAdjustment: config.enableHeightAdjustment !== false, + enableCommentResizer: config.enableCommentResizer !== false, + enableCommentBox: config.enableCommentBox !== false, + ...config, + }; + + // 부모 클래스 생성자 호출 (반드시 먼저 호출) + super(baseConfig); + + // 의존성 주입 (폴백 포함) - super() 호출 후 + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + // 추가 초기화 작업 + } catch (error) { + this._handleError(error, 'constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context: `VideoModalManager.${context}`, + component: 'VideoModalManager', + ...additionalInfo + }, false); + } else { + console.error(`[VideoModalManager] ${context}:`, error, additionalInfo); + } + } + + // 초기화 + init() { + try { + this.setupCardClickEvents(); + } catch (error) { + this._handleError(error, 'init'); + } + } + + // 카드 클릭 이벤트 설정 (DOMUtils, EventManager 활용) + setupCardClickEvents() { + try { + const container = this.domUtils?.$("#videoCardsContainer") || + (typeof DOMUtils !== 'undefined' ? DOMUtils.$("#videoCardsContainer") : null) || + document.querySelector("#videoCardsContainer"); + + if (!container) { + console.warn("[VideoModalManager] videoCardsContainer를 찾을 수 없습니다."); + return; + } + + // VideoModalManager 인스턴스를 참조하기 위해 변수에 저장 + const self = this; + + const clickHandler = function(e) { + try { + e.preventDefault(); + e.stopPropagation(); + + // this는 .card 요소, self는 VideoModalManager 인스턴스 + const videoIdAttr = this.getAttribute("data-video-id"); + if (!videoIdAttr) { + console.warn("[VideoModalManager] data-video-id 속성이 없습니다."); + return; + } + + const videoId = parseInt(videoIdAttr, 10); + if (isNaN(videoId) || videoId <= 0) { + self._handleError(new Error(`유효하지 않은 videoId: ${videoIdAttr}`), 'setupCardClickEvents.clickHandler'); + return; + } + + self.openVideo(videoId); + } catch (error) { + self._handleError(error, 'setupCardClickEvents.clickHandler'); + } + }; + + // EventManager 또는 DOMUtils.delegate 사용 + if (this.eventManager) { + const delegateId = this.eventManager.delegate(container, "click", ".card", clickHandler); + this.listenerIds.push({ element: container, id: delegateId, type: 'delegate', selector: '.card' }); + } else if (this.domUtils && this.domUtils.delegate) { + this.domUtils.delegate(container, "click", ".card", clickHandler); + } else if (typeof DOMUtils !== 'undefined' && DOMUtils.delegate) { + DOMUtils.delegate(container, "click", ".card", clickHandler); + } else { + // 폴백: 직접 이벤트 위임 구현 + container.addEventListener("click", function(e) { + const card = e.target.closest(".card"); + if (card) { + clickHandler.call(card, e); + } + }); + } + } catch (error) { + this._handleError(error, 'setupCardClickEvents'); + } + } + + // 기존 메서드 호환성을 위한 래퍼 + async loadVideoModal(videoId) { + try { + // 입력값 유효성 검증 + if (!videoId || (typeof videoId !== 'number' && typeof videoId !== 'string')) { + this._handleError(new Error(`유효하지 않은 videoId: ${videoId}`), 'loadVideoModal'); + return null; + } + + const id = typeof videoId === 'string' ? parseInt(videoId, 10) : videoId; + if (isNaN(id) || id <= 0) { + this._handleError(new Error(`유효하지 않은 videoId: ${videoId}`), 'loadVideoModal'); + return null; + } + + return await this.openVideo(id); + } catch (error) { + this._handleError(error, 'loadVideoModal', { videoId }); + return null; + } + } + + // 기존 코드 호환성을 위한 래퍼 메서드들 + get currentModal() { + try { + return this.currentModalElement; + } catch (error) { + this._handleError(error, 'currentModal.get'); + return null; + } + } + + set currentModal(value) { + try { + this.currentModalElement = value; + } catch (error) { + this._handleError(error, 'currentModal.set', { value }); + } + } + + // 기존 메서드 호환성 유지 (VideoModalBase의 메서드 사용) + adjustVideoListHeight() { + try { + if (this.currentModalElement) { + super.adjustVideoListHeight(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'adjustVideoListHeight'); + } + } + + setupCommentResizer() { + try { + if (this.currentModalElement) { + super.setupCommentResizer(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'setupCommentResizer'); + } + } + + setupCommentBox() { + try { + if (this.currentModalElement) { + super.setupCommentBox(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'setupCommentBox'); + } + } + + showCommentSection() { + try { + if (this.currentModalElement) { + super.showCommentSection(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'showCommentSection'); + } + } + + adjustCommentOnlyLayout() { + try { + if (this.currentModalElement) { + super.adjustCommentOnlyLayout(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'adjustCommentOnlyLayout'); + } + } + + setupEssentialLayout() { + try { + if (this.currentModalElement) { + super.setupEssentialLayout(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'setupEssentialLayout'); + } + } + + setupLearningLayout() { + try { + if (this.currentModalElement) { + super.setupLearningLayout(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'setupLearningLayout'); + } + } + + initializeHeightAdjustment() { + try { + if (this.currentModalElement) { + super.initializeHeightAdjustment(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'initializeHeightAdjustment'); + } + } + + setupResizeObserver() { + try { + if (this.currentModalElement) { + super.setupResizeObserver(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'setupResizeObserver'); + } + } + + setupMutationObserver() { + try { + if (this.currentModalElement) { + super.setupMutationObserver(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'setupMutationObserver'); + } + } + + async waitForImagesAndAdjust() { + try { + if (this.currentModalElement) { + await super.waitForImagesAndAdjust(this.currentModalElement); + } + } catch (error) { + this._handleError(error, 'waitForImagesAndAdjust'); + } + } + + destroyModal() { + try { + // 이벤트 리스너 제거 + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id, type }) => { + if (type === 'delegate') { + this.eventManager.undelegate(element, id); + } else { + this.eventManager.off(element, id); + } + }); + this.listenerIds = []; + } + + // 부모 클래스의 destroy 호출 + if (super.destroy && typeof super.destroy === 'function') { + super.destroy(); + } else if (this.destroy && typeof this.destroy === 'function') { + this.destroy(); + } + } catch (error) { + this._handleError(error, 'destroyModal'); + } + } +} diff --git a/src/js/mypage.js b/src/js/mypage.js new file mode 100644 index 0000000..b41f05e --- /dev/null +++ b/src/js/mypage.js @@ -0,0 +1,45 @@ +/** + * 마이페이지 전용 스크립트 + * ===================== + * 이 파일은 mypage.html에서만 사용됩니다. + * 초보자도 이해하기 쉽도록 주석을 많이 달았습니다. + */ + +(function () { + "use strict"; + + // ------------------------------ + // 1. 컨텐츠 제안 영역 + // ------------------------------ + // 사용자가 텍스트를 입력하면 "제안 보내기" 버튼이 활성화됩니다. + var suggestInput = document.querySelector(".suggest-section .suggest-input"); + var suggestBtn = document.querySelector(".suggest-section .btn-primary"); + + if (suggestInput && suggestBtn) { + suggestInput.addEventListener("input", function () { + // trim(): 앞뒤 공백 제거. 빈 칸만 입력한 경우도 비활성화 + var hasText = suggestInput.value.trim().length > 0; + suggestBtn.disabled = !hasText; + }); + } + + // ------------------------------ + // 2. 제안현황 아코디언 + // ------------------------------ + // "제안현황" 버튼을 클릭하면 목록이 펼쳐지거나 접힙니다. + var accordion = document.querySelector(".suggest-status.accordion"); + + if (accordion) { + var accordionTrigger = accordion.querySelector(".accordion-trigger"); + var accordionContent = accordion.querySelector(".accordion-content"); + + accordionTrigger.addEventListener("click", function () { + // is-open 클래스가 있으면 열림, 없으면 닫힘 + var isOpen = accordion.classList.toggle("is-open"); + + // 접근성: 스크린 리더가 열림/닫힘 상태를 인식할 수 있게 함 + accordionTrigger.setAttribute("aria-expanded", isOpen); + accordionContent.hidden = !isOpen; + }); + } +})(); diff --git a/src/js/puzzle-onboarding.js b/src/js/puzzle-onboarding.js new file mode 100644 index 0000000..b773bb9 --- /dev/null +++ b/src/js/puzzle-onboarding.js @@ -0,0 +1,4021 @@ +// ============================================================================ +// 퍼즐 온보딩 시스템 (챕터 기반) +// ============================================================================ + +// ============================================================================ +// 설정 및 상수 +// ============================================================================ +const DEFAULT_CONFIG = { + SVG: { + NAMESPACE: "http://www.w3.org/2000/svg", + XLINK_NAMESPACE: "http://www.w3.org/1999/xlink", + VIEWBOX: "0 0 1870 801", + DIMENSIONS: { width: 1870, height: 801 }, + }, + + ANIMATION: { + FADE_DURATION: 300, + HOVER_TRANSITION: "0.3s ease", + CELEBRATION_DELAY: 600, + COMPLETION_ANIMATION_DURATION: 2500, + }, + + FILTER_IDS: { + INNER_SHADOW: "inner-shadow-effect", + HOVER_SHADOW: "hover-shadow-effect", + PIECE_EMBOSSING: "piece-embossing-effect", + }, + + GRADIENT_IDS: { + BOARD_1: "board_fill_1", + BOARD_2: "board_fill_2", + BOARD_3: "board_fill_3", + BOARD_4: "board_fill_4", + }, + + IMAGE_PATHS: { + BASE: "./assets/images/onboarding/bg_piece.jpg", + COMPLETED: { + ACTIVE: "./assets/images/onboarding/bg_piece_completed.png", + COMPLETED: "./assets/images/onboarding/bg_piece_finish.png", + ALL_COMPLETED: "./assets/images/onboarding/bg_piece_all_completed.png", + }, + FINISH: { + ACTIVE: "./assets/images/onboarding/bg_piece_all_completed.png", + COMPLETED: "./assets/images/onboarding/bg_piece_finish.png", + ALL_COMPLETED: "./assets/images/onboarding/bg_piece_all_completed.png", + }, + }, + + COMPLETION_MODE: "FINISH", + + PLAY_BUTTON: { + RADIUS: 28, + ICON_SIZE: { width: 22, height: 26 }, + COLOR: "#E8643D", + OPACITY: 0.9, + }, + + FILE_BUTTON: { + RADIUS: 35, + ICON_SIZE: { width: 32, height: 28 }, + COLOR: "#4A90E2", + OPACITY: 0.9, + }, + + GROUP_COLORS: { + 1: "#306743", + 2: "#8F360B", + 3: "#1D375D", + 4: "#7B6029", + }, + + GAUGE: { + HEIGHT: 7, + RADIUS: 2, + VERTICAL_OFFSET: -10, + BG_COLOR: "#A0A0A0", + BG_OPACITY: 0.4, + FILL_COLOR: "#D74800", + }, +}; + +const CONFIG = { + ...DEFAULT_CONFIG, + ...(window.puzzleConfig || {}), +}; + +const CELEBRATION_RIBBON_POSITION = { + pieceId: 2, + offsetX: 0, + offsetY: -200, +}; + +const PUZZLE_PIECES = [ + { + id: 1, + title: "왜 다윈인인가 (최재천 교수)", + // 바로세운_퍼즐판.svg line 14 + path: "M256.658 780V651.625H22V523H709V780H256.658Z", + }, + { + id: 2, + title: "새로운 시대 준비된 우리", + // 바로세운_퍼즐판.svg line 16 + path: "M709 11H1162L1161.99 268H709V11Z", + }, + { + id: 3, + title: "기술개발센터소개", + // 바로세운_퍼즐판.svg line 17 + path: "M1162 11H1615L1614.99 268H1162V11Z", + }, + { + id: 4, + title: "건설산업의 디지털 전환을", + // 바로세운_퍼즐판.svg line 19 + path: "M1162 268H1615L1614.99 523H1162V268Z", + }, + { + id: 5, + title: "상용 S/W 소개", + // 바로세운_퍼즐판.svg line 18 + path: "M709 268H1162L1161.99 523H709V268Z", + }, + { + id: 6, + title: "축적의 시간", + // 바로세운_퍼즐판.svg line 21 + path: "M709 523H1162L1161.99 780H709V523Z", + }, + { + id: 7, + title: "회사생활 (경력)", + // 바로세운_퍼즐판.svg line 22 + path: "M1162 523H1615L1614.99 780H1162V523Z", + }, + { + id: 8, + title: "회사생활 (신규입사자편)", + // 바로세운_퍼즐판.svg line 20 + path: "M1615 268H1853L1852.99 617H1615V268Z", + }, + { + id: 9, + title: "한맥가족 소개 및 경영이념", + // 바로세운_퍼즐판.svg line 23 + path: "M256 11V267H330V523H709V11H256Z", + }, + { + id: 10, + title: "삼안 소개", + // 바로세운_퍼즐판.svg line 15 + path: "M330 267H22L22.4984 523H330V267Z", + }, +]; + +// PUZZLE_PIECES 정의 바로 아래에 추가 +const BOUNDARY_LINES = [ + // 수직 경계선들 (세로) + { x1: 709, y1: 11, x2: 709, y2: 780, label: "vertical-709" }, + { x1: 1162, y1: 11, x2: 1162, y2: 780, label: "vertical-1162" }, + { x1: 1615, y1: 268, x2: 1615, y2: 617, label: "vertical-1615" }, + { x1: 330, y1: 267, x2: 330, y2: 523, label: "vertical-330" }, + { x1: 256, y1: 11, x2: 256, y2: 267, label: "vertical-256-top" }, + + // 수평 경계선들 (가로) + { x1: 709, y1: 268, x2: 1615, y2: 268, label: "horizontal-268" }, + { x1: 22, y1: 523, x2: 1615, y2: 523, label: "horizontal-523" }, + { x1: 256, y1: 267, x2: 330, y2: 267, label: "horizontal-267" }, +]; + +const BOARD_PATHS = [ + { + d: "M1866 258C1866 263.523 1861.52 268 1856 268L709 268L709 9.99991C709 4.47706 713.477 -0.000100757 719 -0.000100274L1856 -8.74227e-07C1861.52 -3.91404e-07 1866 4.47715 1866 10L1866 258Z", + fill: `url(#${CONFIG.GRADIENT_IDS.BOARD_1})`, + }, + { + d: "M4 10C4 4.47715 8.47715 0 14 0H699C704.523 0 709 4.47715 709 10V525H14C8.47717 525 4 520.523 4 515V10Z", + fill: `url(#${CONFIG.GRADIENT_IDS.BOARD_2})`, + }, + { + d: "M1856 268C1856.17 268 1856.34 268.004 1856.51 268.013C1861.8 268.281 1866 272.65 1866 278V783C1866 788.523 1861.52 793 1856 793H1171.39C1165.87 793 1161.39 788.523 1161.39 783V523H709V268H1856Z", + fill: `url(#${CONFIG.GRADIENT_IDS.BOARD_3})`, + }, + { + d: "M4 533C4 527.477 8.47715 523 14 523H1162V783C1162 788.523 1157.52 793 1152 793H14C8.47715 793 4 788.523 4 783V533Z", + fill: `url(#${CONFIG.GRADIENT_IDS.BOARD_4})`, + }, +]; + +const GRADIENTS = [ + { + id: CONFIG.GRADIENT_IDS.BOARD_1, + x1: "69.54%", + y1: "3.97%", + x2: "30.46%", + y2: "96.03%", + stops: [ + { offset: "14.97%", color: "#8E2F00" }, + { offset: "89.98%", color: "#662A0D" }, + ], + }, + { + id: CONFIG.GRADIENT_IDS.BOARD_2, + x1: "69.54%", + y1: "3.97%", + x2: "30.46%", + y2: "96.03%", + stops: [ + { offset: "14.97%", color: "#2A5338" }, + { offset: "89.98%", color: "#306843" }, + ], + }, + { + id: CONFIG.GRADIENT_IDS.BOARD_3, + x1: "69.54%", + y1: "3.97%", + x2: "30.46%", + y2: "96.03%", + stops: [ + { offset: "14.97%", color: "#5B4822" }, + { offset: "89.98%", color: "#795711" }, + ], + }, + { + id: CONFIG.GRADIENT_IDS.BOARD_4, + x1: "69.54%", + y1: "3.97%", + x2: "30.46%", + y2: "96.03%", + stops: [ + { offset: "14.97%", color: "#1D375D" }, + { offset: "89.98%", color: "#385888" }, + ], + }, +]; + +const BUTTON_POSITIONS = [ + { id: 1, x: 475, y: 686 }, + { id: 2, x: 935, y: 175 }, + { id: 3, x: 1388, y: 175 }, + { id: 4, x: 1388, y: 420 }, + { id: 5, x: 935, y: 420 }, + { id: 6, x: 935, y: 686 }, + { id: 7, x: 1388, y: 686 }, + { id: 8, x: 1734, y: 492 }, + { id: 9, x: 482, y: 317 }, + { id: 10, x: 176, y: 420 }, +]; + +// 초기 타이틀은 PUZZLE_PIECES에서 가져오고, 챕터 데이터로 업데이트됨 +const TITLE_POSITIONS = [ + { + id: 1, + x: 475, + y: 630, + lines: ["왜 다윈인인가 (최재천 교수)"], + }, + { id: 2, x: 935, y: 120, lines: ["새로운 시대 준비된 우리"] }, + { id: 3, x: 1388, y: 120, lines: ["기술개발센터소개"] }, + { id: 4, x: 1388, y: 365, lines: ["건설산업의 디지털 전환을"] }, + { id: 5, x: 935, y: 365, lines: ["상용 S/W 소개"] }, + { id: 6, x: 935, y: 630, lines: ["축적의 시간"] }, + { id: 7, x: 1388, y: 630, lines: ["회사생활 (경력)"] }, + { id: 8, x: 1734, y: 402, lines: ["회사생활", "(신규입사자편)"] }, + { id: 9, x: 482, y: 227, lines: ["한맥가족 소개", "및 경영이념"] }, + { id: 10, x: 176, y: 365, lines: ["삼안 소개"] }, +]; + +const GAUGE_CONFIG = { + POSITIONS: { + 1: 780, + 2: 268, + 3: 268, + 4: 523, + 5: 523, + 6: 780, + 7: 780, + 8: 617, + 9: 523, + 10: 523, + }, + X_RANGES: { + 1: { left: 21, right: 709, align: "right" }, + 2: { left: 709.158, right: 1162 }, + 3: { left: 1162.16, right: 1615 }, + 4: { left: 1162.16, right: 1615 }, + 5: { left: 709.158, right: 1162 }, + 6: { left: 709.158, right: 1162 }, + 7: { left: 1162.16, right: 1615 }, + 8: { left: 1615.08, right: 1853 }, + 9: { left: 330.61, right: 709.008, align: "right" }, + 10: { left: 21.5, right: 330.5 }, + }, + MAX_LENGTHS: { + 1: 453, + 2: 452.842, + 3: 452.84, + 4: 452.84, + 5: 452.842, + 6: 452.842, + 7: 452.84, + 8: 237.92, + 9: 378.398, + 10: 309, + }, +}; + +// ============================================================================ +// 챕터 관리 클래스 +// ============================================================================ +class ChapterManager { + constructor(chapterData, dependencies = {}) { + this.chapters = chapterData || []; + + // 의존성 주입 (폴백 포함) + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + + console.log('[ChapterManager] 초기화:', this.chapters); + + try { + this._validateAndInitialize(); + } catch (error) { + this._handleError(error, 'ChapterManager.constructor'); + } + } + + _validateAndInitialize() { + try { + this.chapters.forEach((chapter, index) => { + if (!chapter.lessons || !Array.isArray(chapter.lessons)) { + chapter.lessons = []; + } + + // completed 속성 초기화 + if (chapter.completed === undefined) { + chapter.completed = false; + } + + chapter.lessons.forEach((lesson) => { + if (lesson.completed === undefined) { + lesson.completed = false; + } + }); + + console.log(`[ChapterManager] 챕터 ${index}: ${chapter.name}, pieceId: ${chapter.pieceId}, lessons: ${chapter.lessons.length}`); + }); + } catch (error) { + this._handleError(error, 'ChapterManager._validateAndInitialize'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context, + component: 'ChapterManager', + ...additionalInfo + }, false); + } else { + console.error(`[ChapterManager] ${context}:`, error, additionalInfo); + } + } + + /** + * 챕터 가져오기 + */ + getChapter(chapterIndex) { + return this.chapters[chapterIndex]; + } + + /** + * pieceId로 챕터 찾기 + */ + getChapterByPieceId(pieceId) { + const chapterIndex = this.chapters.findIndex( + (chapter) => chapter.pieceId === pieceId + ); + if (chapterIndex === -1) return null; + + return { + chapterIndex, + chapter: this.chapters[chapterIndex], + }; + } + + /** + * 챕터의 진행률 계산 (completed 기반) + */ + getChapterProgress(chapterIndex) { + const chapter = this.chapters[chapterIndex]; + if (!chapter || !chapter.lessons || chapter.lessons.length === 0) { + return 0; + } + + const completedCount = chapter.lessons.filter( + (lesson) => lesson.completed + ).length; + const totalCount = chapter.lessons.length; + + return Math.round((completedCount / totalCount) * 100); + } + + /** + * 전체 진행률 계산 + */ + getTotalProgress() { + let totalLessons = 0; + let completedLessons = 0; + + this.chapters.forEach((chapter) => { + if (chapter.lessons && chapter.lessons.length > 0) { + totalLessons += chapter.lessons.length; + completedLessons += chapter.lessons.filter( + (lesson) => lesson.completed + ).length; + } + }); + + if (totalLessons === 0) return 0; + return Math.round((completedLessons / totalLessons) * 100); + } + + /** + * 학습 완료 처리 + */ + completeLesson(chapterIndex, lessonIndex) { + try { + const chapter = this.chapters[chapterIndex]; + if (!chapter || !chapter.lessons || !chapter.lessons[lessonIndex]) { + const error = new Error(`Invalid chapter or lesson index: [${chapterIndex}-${lessonIndex}]`); + this._handleError(error, 'ChapterManager.completeLesson', { chapterIndex, lessonIndex }); + return; + } + + const lesson = chapter.lessons[lessonIndex]; + + // 이미 완료된 학습이면 처리하지 않음 + if (lesson.completed) { + console.log( + `[ChapterManager] 이미 완료된 학습: [${chapterIndex}-${lessonIndex}] ${lesson.label}` + ); + return; + } + + lesson.completed = true; + console.log( + `[ChapterManager] 학습 완료: [${chapterIndex}-${lessonIndex}] ${lesson.label}` + ); + + // 챕터의 모든 학습이 완료되었는지 확인 + const allCompleted = chapter.lessons.every((l) => l.completed); + if (allCompleted && !chapter.completed) { + chapter.completed = true; + console.log( + `[ChapterManager] 챕터 완료: [${chapterIndex}] ${chapter.name}` + ); + } + } catch (error) { + this._handleError(error, 'ChapterManager.completeLesson', { chapterIndex, lessonIndex }); + } + } + + /** + * 챕터가 완료되었는지 확인 + */ + isChapterCompleted(chapterIndex) { + const chapter = this.chapters[chapterIndex]; + if (!chapter) return false; + return chapter.completed === true; + } + + /** + * 모든 챕터가 완료되었는지 확인 + */ + isAllCompleted() { + return this.chapters.every((chapter) => chapter.completed === true); + } + + /** + * 챕터 데이터 가져오기 + */ + getChapterData(chapterIndex) { + return this.chapters[chapterIndex]; + } + + /** + * 전체 챕터 수 + */ + getTotalChapters() { + return this.chapters.length; + } +} + +// ============================================================================ +// SVG 유틸리티 클래스 +// ============================================================================ +class SVGHelper { + static createElement(tagName, attributes = {}) { + const element = document.createElementNS(CONFIG.SVG.NAMESPACE, tagName); + Object.entries(attributes).forEach(([key, value]) => { + if (key === "href" || key === "xlink:href") { + element.setAttributeNS(CONFIG.SVG.XLINK_NAMESPACE, "xlink:href", value); + element.setAttribute("href", value); + } else { + element.setAttribute(key, value); + } + }); + return element; + } + + static createGradient(gradientData) { + const attributes = { + id: gradientData.id, + }; + + // 그라데이션 방향 설정 + if (gradientData.x1) attributes.x1 = gradientData.x1; + if (gradientData.y1) attributes.y1 = gradientData.y1; + if (gradientData.x2) attributes.x2 = gradientData.x2; + if (gradientData.y2) attributes.y2 = gradientData.y2; + + const gradient = this.createElement("linearGradient", attributes); + + gradientData.stops.forEach((stop) => { + const stopElement = this.createElement("stop", { + offset: stop.offset, + "stop-color": stop.color, + }); + gradient.appendChild(stopElement); + }); + + return gradient; + } + + static createPattern(patternId, imageUrl, isThumbnail = false) { + const patternAttrs = { id: patternId }; + const imageAttrs = { + href: imageUrl, + "xlink:href": imageUrl, + }; + + if (isThumbnail) { + Object.assign(patternAttrs, { + patternUnits: "objectBoundingBox", + patternContentUnits: "objectBoundingBox", + width: "1", + height: "1", + }); + + Object.assign(imageAttrs, { + x: "0", + y: "0", + width: "1", + height: "1", + preserveAspectRatio: "xMidYMid slice", + }); + } else { + Object.assign(patternAttrs, { + patternUnits: "userSpaceOnUse", + patternContentUnits: "userSpaceOnUse", + x: "0", + y: "0", + width: String(CONFIG.SVG.DIMENSIONS.width), + height: String(CONFIG.SVG.DIMENSIONS.height), + }); + + Object.assign(imageAttrs, { + x: "0", + y: "0", + width: String(CONFIG.SVG.DIMENSIONS.width), + height: String(CONFIG.SVG.DIMENSIONS.height), + preserveAspectRatio: "none", + }); + } + + const pattern = this.createElement("pattern", patternAttrs); + const image = this.createElement("image", imageAttrs); + pattern.appendChild(image); + + return pattern; + } +} + +// ============================================================================ +// 필터 생성 클래스 +// ============================================================================ +class FilterFactory { + static createInnerShadowFilter() { + const filter = SVGHelper.createElement("filter", { + id: CONFIG.FILTER_IDS.INNER_SHADOW, + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + filterUnits: "userSpaceOnUse", + "color-interpolation-filters": "sRGB", + }); + + filter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-opacity": "0", + result: "BackgroundImageFix", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in: "SourceGraphic", + in2: "BackgroundImageFix", + result: "shape", + }) + ); + + // box-shadow: -14px -11px 4px 0 rgba(0, 0, 0, 0.25) inset (어두운 이너 쉐도우 - 좌상단) + this._addInnerShadow( + filter, + -14, + -11, + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0", + "shape", + "effect1_innerShadow", + 4 // blur: 4px + ); + + // box-shadow: 11px 6px 4px 0 rgba(255, 255, 255, 0.25) inset (밝은 이너 쉐도우 - 우하단) + this._addInnerShadow( + filter, + 11, + 6, + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + "0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.25 0", + "effect1_innerShadow", + "effect2_innerShadow", + 4 // blur: 4px + ); + + return filter; + } + + static _addInnerShadow( + filter, + dx, + dy, + alphaMatrix, + colorMatrix, + blendIn2, + resultName, + blur = 4 + ) { + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: alphaMatrix, + result: "hardAlpha", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feOffset", { dx: String(dx), dy: String(dy) }) + ); + // blur 값에 맞게 stdDeviation 설정 (blur / 2) + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { stdDeviation: String(blur / 2) }) + ); + + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha", + operator: "arithmetic", + k2: "-1", + k3: "1", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: colorMatrix, + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in2: blendIn2, + result: resultName, + }) + ); + } + + static createHoverShadowFilter() { + const filter = SVGHelper.createElement("filter", { + id: CONFIG.FILTER_IDS.HOVER_SHADOW, + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + }); + + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + in: "SourceAlpha", + stdDeviation: "4", + result: "blurHover", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feOffset", { + in: "blurHover", + dx: "0", + dy: "-10", + result: "offsetBlurHover", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-color": "rgba(0, 0, 0, 0.5)", + result: "floodColorHover", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feComposite", { + in: "floodColorHover", + in2: "offsetBlurHover", + operator: "in", + result: "shadowHover", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feComposite", { + in: "shadowHover", + in2: "SourceAlpha", + operator: "in", + result: "innerShadowHover", + }) + ); + + const merge = SVGHelper.createElement("feMerge"); + merge.appendChild( + SVGHelper.createElement("feMergeNode", { in: "SourceGraphic" }) + ); + merge.appendChild( + SVGHelper.createElement("feMergeNode", { in: "innerShadowHover" }) + ); + filter.appendChild(merge); + + return filter; + } + + static createGaugeFillFilter() { + const filter = SVGHelper.createElement("filter", { + id: "gauge-fill-inner-shadow", + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + filterUnits: "userSpaceOnUse", + "color-interpolation-filters": "sRGB", + }); + + // 배경 이미지 고정 + filter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-opacity": "0", + result: "BackgroundImageFix", + }) + ); + + // 원본 모양 유지 + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in: "SourceGraphic", + in2: "BackgroundImageFix", + result: "shape", + }) + ); + + + // Alpha 채널 추출 + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "hardAlpha", + }) + ); + + filter.appendChild(SVGHelper.createElement("feOffset", { dy: "-4" })); + + // 블러 (6px) + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + stdDeviation: "3", + }) + ); + + // 내부 그림자 처리 + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha", + operator: "arithmetic", + k2: "-1", + k3: "1", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0", + result: "innerShadow", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "dropShadowAlpha", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feOffset", { + dx: "0", + dy: "4", + in: "dropShadowAlpha", + result: "dropShadowOffset", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + stdDeviation: "2", + in: "dropShadowOffset", + result: "dropShadowBlur", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0", + in: "dropShadowBlur", + result: "dropShadow", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in: "innerShadow", + in2: "dropShadow", + result: "combinedShadow", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in: "shape", + in2: "combinedShadow", + result: "final", + }) + ); + + return filter; + } + + static createPlayButtonFilters() { + const filters = []; + + const outerFilter = SVGHelper.createElement("filter", { + id: "play-button-outer-filter", + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + filterUnits: "userSpaceOnUse", + "color-interpolation-filters": "sRGB", + }); + + outerFilter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + in: "SourceAlpha", + stdDeviation: "2.7", + result: "blur1", + }) + ); + + outerFilter.appendChild( + SVGHelper.createElement("feOffset", { + in: "blur1", + result: "offset1", + }) + ); + + outerFilter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-color": "rgba(0, 0, 0, 0.25)", + result: "color1", + }) + ); + + outerFilter.appendChild( + SVGHelper.createElement("feComposite", { + in: "color1", + in2: "offset1", + operator: "in", + result: "shadow1", + }) + ); + + const merge1 = SVGHelper.createElement("feMerge"); + merge1.appendChild( + SVGHelper.createElement("feMergeNode", { in: "shadow1" }) + ); + merge1.appendChild( + SVGHelper.createElement("feMergeNode", { in: "SourceGraphic" }) + ); + outerFilter.appendChild(merge1); + + filters.push(outerFilter); + + const iconFilter = SVGHelper.createElement("filter", { + id: "play-button-icon-filter", + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + filterUnits: "userSpaceOnUse", + "color-interpolation-filters": "sRGB", + }); + + iconFilter.appendChild( + SVGHelper.createElement("feOffset", { + dx: "2", + dy: "2", + result: "offset2", + }) + ); + + iconFilter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + in: "offset2", + stdDeviation: "0.5", + result: "blur2", + }) + ); + + iconFilter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-color": "rgba(0, 0, 0, 0.25)", + result: "color2", + }) + ); + + iconFilter.appendChild( + SVGHelper.createElement("feComposite", { + in: "color2", + in2: "blur2", + operator: "in", + result: "shadow2", + }) + ); + + const merge2 = SVGHelper.createElement("feMerge"); + merge2.appendChild( + SVGHelper.createElement("feMergeNode", { in: "shadow2" }) + ); + merge2.appendChild( + SVGHelper.createElement("feMergeNode", { in: "SourceGraphic" }) + ); + iconFilter.appendChild(merge2); + + filters.push(iconFilter); + + return filters; + } + + static createTextShadowFilter() { + const filter = SVGHelper.createElement("filter", { + id: "text-shadow-filter", + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + }); + + filter.appendChild( + SVGHelper.createElement("feDropShadow", { + dx: "0", + dy: "4", + stdDeviation: "3", + "flood-color": "rgba(0, 0, 0, 0.3)", + }) + ); + + return filter; + } + + static createBoardFilter() { + const filter = SVGHelper.createElement("filter", { + id: "board-3d-effect", + x: "-10%", + y: "-10%", + width: "120%", + height: "120%", + filterUnits: "userSpaceOnUse", + "color-interpolation-filters": "sRGB", + }); + + // Drop shadow (외부 그림자) + filter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-opacity": "0", + result: "BackgroundImageFix", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "hardAlpha", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feOffset", { + dy: "4", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + stdDeviation: "2", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha", + operator: "out", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in2: "BackgroundImageFix", + result: "effect1_dropShadow", + }) + ); + + // Shape (원본 모양 유지) + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in: "SourceGraphic", + in2: "effect1_dropShadow", + result: "shape", + }) + ); + + // Inner shadow (내부 그림자 - 상단) + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "hardAlpha2", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feOffset", { + dy: "-4", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha2", + operator: "arithmetic", + k2: "-1", + k3: "1", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in2: "shape", + result: "effect2_innerShadow", + }) + ); + + return filter; + } + + + /** + * 퍼즐 조각 엠보싱 효과 필터 생성 + * box-shadow: 13px 12px 6px 0 rgba(254, 227, 179, 0.80) inset, + * -11px -11px 5px 0 rgba(0, 0, 0, 0.40) inset + * filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.80)) + */ + static createPieceEmbossingFilter() { + const filter = SVGHelper.createElement("filter", { + id: "piece-embossing-effect", + x: "-50%", + y: "-50%", + width: "200%", + height: "200%", + filterUnits: "userSpaceOnUse", + "color-interpolation-filters": "sRGB", + }); + + // 배경 이미지 고정 + filter.appendChild( + SVGHelper.createElement("feFlood", { + "flood-opacity": "0", + result: "BackgroundImageFix", + }) + ); + + // 원본 모양 유지 + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in: "SourceGraphic", + in2: "BackgroundImageFix", + result: "shape", + }) + ); + + // ======================================== + // Inner Shadow 1: 우하단 밝은 그림자 + // 13px 12px 6px rgba(254, 227, 179, 0.80) + // ======================================== + + // Alpha 채널 추출 + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "hardAlpha1", + }) + ); + + // 오프셋 (13px, 12px) + filter.appendChild( + SVGHelper.createElement("feOffset", { + dx: "13", + dy: "12", + }) + ); + + // 블러 (6px) + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + stdDeviation: "2", // 6px / 2 + }) + ); + + // 내부 그림자 처리 + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha1", + operator: "arithmetic", + k2: "-1", + k3: "1", + }) + ); + + // 색상 적용: rgba(254, 227, 179, 0.80) + // R: 254/255 = 0.996, G: 227/255 = 0.890, B: 179/255 = 0.702, A: 0.80 + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0.996 0 0 0 0 0.890 0 0 0 0 0.702 0 0 0 0.80 0", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "color-dodge", + in2: "shape", + result: "effect1_innerShadow", + }) + ); + + // ======================================== + // Inner Shadow 2: 좌상단 어두운 그림자 + // -11px -11px 5px rgba(0, 0, 0, 0.40) + // ======================================== + + // Alpha 채널 추출 + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "hardAlpha2", + }) + ); + + // 오프셋 (-11px, -11px) + filter.appendChild( + SVGHelper.createElement("feOffset", { + dx: "-11", + dy: "-11", + }) + ); + + // 블러 (5px) + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + stdDeviation: "2.5", // 5px / 2 + }) + ); + + // 내부 그림자 처리 + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha2", + operator: "arithmetic", + k2: "-1", + k3: "1", + }) + ); + + // 색상 적용: rgba(0, 0, 0, 0.40) + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.60 0", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in2: "effect1_innerShadow", + result: "effect2_innerShadow", + }) + ); + + // ======================================== + // Drop Shadow: 외부 그림자 + // 1px 1px 2px rgba(0, 0, 0, 0.80) + // ======================================== + + // 외부 그림자용 Alpha + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + in: "SourceAlpha", + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", + result: "hardAlpha3", + }) + ); + + // 오프셋 (1px, 1px) + filter.appendChild( + SVGHelper.createElement("feOffset", { + dx: "1", + dy: "1", + }) + ); + + // 블러 (2px) + filter.appendChild( + SVGHelper.createElement("feGaussianBlur", { + stdDeviation: "1", // 2px / 2 + }) + ); + + // 외부 그림자 처리 + filter.appendChild( + SVGHelper.createElement("feComposite", { + in2: "hardAlpha3", + operator: "out", + }) + ); + + // 색상 적용: rgba(0, 0, 0, 0.80) + filter.appendChild( + SVGHelper.createElement("feColorMatrix", { + type: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.80 0", + }) + ); + + filter.appendChild( + SVGHelper.createElement("feBlend", { + mode: "normal", + in2: "BackgroundImageFix", + result: "effect3_dropShadow", + }) + ); + + // 최종 합성 + const merge = SVGHelper.createElement("feMerge"); + merge.appendChild( + SVGHelper.createElement("feMergeNode", { + in: "effect3_dropShadow", + }) + ); + merge.appendChild( + SVGHelper.createElement("feMergeNode", { + in: "effect2_innerShadow", + }) + ); + filter.appendChild(merge); + + return filter; + } +} + +// ============================================================================ +// 콘텐츠 관리 클래스 +// ============================================================================ +class ContentManager { + constructor(chapterManager) { + this.chapterManager = chapterManager; + } + + /** + * pieceId로 챕터 정보 가져오기 + */ + getChapterByPieceId(pieceId) { + return this.chapterManager.getChapterByPieceId(pieceId); + } + + /** + * 썸네일 URL 가져오기 + */ + getThumbnailUrl(pieceId) { + const chapterInfo = this.getChapterByPieceId(pieceId); + if (!chapterInfo || !chapterInfo.chapter.lessons || + chapterInfo.chapter.lessons.length === 0) { + return CONFIG.IMAGE_PATHS.BASE; + } + + const firstLesson = chapterInfo.chapter.lessons[0]; + if (!firstLesson.url) { + return CONFIG.IMAGE_PATHS.BASE; + } + + return `https://img.youtube.com/vi/${firstLesson.url}/maxresdefault.jpg`; + } + + /** + * 그룹 색상 가져오기 + */ + getGroupColor(pieceId) { + const chapterInfo = this.getChapterByPieceId(pieceId); + if (!chapterInfo) return CONFIG.GROUP_COLORS[1]; + + const group = chapterInfo.chapter.group || 1; + return CONFIG.GROUP_COLORS[group] || CONFIG.GROUP_COLORS[1]; + } + + /** + * 타입 가져오기 + */ + getType(pieceId) { + const chapterInfo = this.getChapterByPieceId(pieceId); + if (!chapterInfo) return "youtube"; + + return chapterInfo.chapter.type || "youtube"; + } +} + +// ============================================================================ +// 퍼즐 조각 클래스 +// ============================================================================ + +class PuzzlePiece { + constructor(pieceData, contentManager, chapterManager, dependencies = {}) { + this.data = pieceData; + this.contentManager = contentManager; + this.chapterManager = chapterManager; + this.group = null; + this.isCompleted = false; + + // 의존성 주입 (폴백 포함) + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + } + + createElement() { + this.group = SVGHelper.createElement("g", { + class: "puzzle-piece-group", + "data-piece": this.data.id, + "data-title": this.data.title, + }); + + const currentUrl = window.location.href.split("#")[0]; + + // Base 이미지 - stroke: 1, 엠보싱: 없음 + this._createImageLayer( + "piece-base-image", + `bg-image-${this.data.id}`, + currentUrl + ); + + // Hover 이미지 - stroke: 2, 엠보싱: 적용 + this._createImageLayer( + "piece-hover-image", + `bg-image-hover-${this.data.id}`, + currentUrl, + "0" + ); + + // Completed 이미지 - stroke: 2, 엠보싱: 적용 + this._createImageLayer( + "piece-completed-image", + `bg-image-completed-${this.data.id}`, + currentUrl, + "1.0", + true + ); + + // All Completed 이미지 - stroke: 0, 엠보싱: 없음 (하나의 이미지처럼) + this._createImageLayer( + "piece-all-completed-image", + `bg-image-all-completed-${this.data.id}`, + currentUrl, + "1.0", + true + ); + + // Finish 이미지 - stroke: 2, 엠보싱: 적용 + this._createImageLayer( + "piece-finish-image", + `bg-image-finish-${this.data.id}`, + currentUrl, + "1.0", + true + ); + + this._createOverlay("piece-overlay-base"); + this._createOverlay("piece-overlay-hover"); + + this._attachEventListeners(); + + return this.group; + } + + /** + * 이미지 레이어 생성 (최종 버전) + * @private + */ + _createImageLayer( + className, + patternId, + baseUrl, + opacity = "1.0", + hidden = false + ) { + const strokeWidth = + className === "piece-base-image" ? "2" : + className === "piece-all-completed-image" ? "0" : + className === "piece-completed-image" ? "2" : + className === "piece-finish-image" ? "2" : "2"; + + // ✅ 모든 레이어에서 stroke 색상을 #333 (검은색)으로 통일 + const strokeColor = "#000"; + + const flatLayers = ["piece-base-image", "piece-all-completed-image", "piece-hover-image"]; + const shouldApplyEmbossing = !flatLayers.includes(className); + + const attributes = { + d: this.data.path, + class: className, + fill: `url(${baseUrl}#${patternId})`, + "fill-opacity": opacity, + stroke: strokeColor, // ✅ 항상 검은색 + "stroke-width": strokeWidth, + "stroke-linejoin": "round", + "stroke-linecap": "round", + }; + + if (shouldApplyEmbossing) { + // PIECE_EMBOSSING만 적용 (completed-effect 제거) + attributes.filter = `url(#${CONFIG.FILTER_IDS.PIECE_EMBOSSING})`; + } + + if (className === "piece-completed-image") { + attributes["clip-path"] = `url(#clip-piece-${this.data.id})`; + } + + const path = SVGHelper.createElement("path", attributes); + + if (hidden) path.style.display = "none"; + if (className === "piece-hover-image") { + path.style.transition = CONFIG.ANIMATION.HOVER_TRANSITION; + } + + this.group.appendChild(path); + } + _createOverlay(className) { + const overlay = SVGHelper.createElement("path", { + d: this.data.path, + class: className, + }); + this.group.appendChild(overlay); + } + + _attachEventListeners() { + try { + if (!this.group) { + console.warn('[PuzzlePiece] group 요소가 없어 이벤트 리스너를 등록할 수 없습니다.'); + return; + } + + const mouseEnterHandler = () => { + try { + this._handleHover(true); + } catch (error) { + this._handleError(error, 'PuzzlePiece._handleHover.enter'); + } + }; + + const mouseLeaveHandler = () => { + try { + this._handleHover(false); + } catch (error) { + this._handleError(error, 'PuzzlePiece._handleHover.leave'); + } + }; + + const clickHandler = () => { + try { + this._handleClick(); + } catch (error) { + this._handleError(error, 'PuzzlePiece._handleClick'); + } + }; + + if (this.eventManager) { + const enterId = this.eventManager.on(this.group, "mouseenter", mouseEnterHandler); + const leaveId = this.eventManager.on(this.group, "mouseleave", mouseLeaveHandler); + const clickId = this.eventManager.on(this.group, "click", clickHandler); + + this.listenerIds.push( + { element: this.group, id: enterId, type: 'mouseenter' }, + { element: this.group, id: leaveId, type: 'mouseleave' }, + { element: this.group, id: clickId, type: 'click' } + ); + } else { + this.group.addEventListener("mouseenter", mouseEnterHandler); + this.group.addEventListener("mouseleave", mouseLeaveHandler); + this.group.addEventListener("click", clickHandler); + } + } catch (error) { + this._handleError(error, 'PuzzlePiece._attachEventListeners'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context, + component: 'PuzzlePiece', + pieceId: this.data?.id, + ...additionalInfo + }, false); + } else { + console.error(`[PuzzlePiece] ${context}:`, error, additionalInfo); + } + } + + /** + * 리소스 정리 (이벤트 리스너 제거) + */ + destroy() { + try { + if (this.eventManager && this.listenerIds.length > 0) { + this.listenerIds.forEach(({ element, id }) => { + this.eventManager.off(element, id); + }); + this.listenerIds = []; + } + this.group = null; + } catch (error) { + this._handleError(error, 'PuzzlePiece.destroy'); + } + } + + _handleHover(isHovering) { + this._toggleText(isHovering); + + const hoverImg = this.group.querySelector(".piece-hover-image"); + const overlayBase = this.group.querySelector(".piece-overlay-base"); + + if (hoverImg) { + hoverImg.setAttribute("fill-opacity", isHovering ? "1.0" : "0"); + // 호버 시 INNER_SHADOW 필터 적용 + if (isHovering) { + hoverImg.setAttribute("filter", `url(#${CONFIG.FILTER_IDS.INNER_SHADOW})`); + } else { + hoverImg.setAttribute("filter", ""); + } + } + + if (overlayBase) { + overlayBase.style.display = isHovering ? "none" : "block"; + } + + if (this.isCompleted) { + const completedImg = this.group.querySelector(".piece-completed-image"); + const allCompletedImg = this.group.querySelector( + ".piece-all-completed-image" + ); + const finishImg = this.group.querySelector(".piece-finish-image"); + + [completedImg, allCompletedImg, finishImg].forEach((img) => { + if (img && img.style.display !== "none") { + img.style.opacity = isHovering ? "0" : "1"; + } + }); + } + } + + _toggleText(hide) { + const svg = PuzzleManager.instance?.svg; + if (!svg) return; + + const titlePos = TITLE_POSITIONS.find((t) => t.id === this.data.id); + if (!titlePos) return; + + const texts = svg.querySelectorAll("text"); + for (let text of texts) { + const x = parseFloat(text.getAttribute("x")); + const y = parseFloat(text.getAttribute("y")); + + if (Math.abs(x - titlePos.x) < 1 && Math.abs(y - titlePos.y) < 1) { + text.style.opacity = hide ? "0" : "1"; + text.style.transition = "opacity 0.3s ease"; + break; + } + } + } + + _handleClick() { + const chapterInfo = this.contentManager.getChapterByPieceId(this.data.id); + if (!chapterInfo) { + console.error(`챕터를 찾을 수 없습니다: pieceId=${this.data.id}`); + return; + } + + const chapter = chapterInfo.chapter; + + if (chapter.type === "file") { + console.log(`[PuzzlePiece] file 타입: ${chapter.name}`); + this._handleFileType(chapter, chapterInfo.chapterIndex); + return; + } + + PuzzleManager.instance.openChapterModal( + chapterInfo.chapterIndex, + chapterInfo.chapter + ); + } + + _handleFileType(chapter, chapterIndex) { + if (!chapter.lessons || chapter.lessons.length === 0) { + console.warn('[PuzzlePiece] file 타입이지만 lessons가 없습니다'); + return; + } + + const firstLesson = chapter.lessons[0]; + const fileUrl = firstLesson.url; + + if (!fileUrl) { + console.warn('[PuzzlePiece] file URL이 없습니다'); + alert('파일 URL이 설정되지 않았습니다.'); + return; + } + + if (fileUrl.startsWith('http://') || fileUrl.startsWith('https://')) { + console.log('[PuzzlePiece] 새창에서 파일 열기:', fileUrl); + window.open(fileUrl, '_blank'); + } else { + console.log('[PuzzlePiece] 파일 다운로드:', fileUrl); + const link = document.createElement('a'); + link.href = fileUrl; + link.download = firstLesson.label || 'download'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + + if (!firstLesson.completed) { + console.log(`[PuzzlePiece] file 타입 학습 완료 처리: [${chapterIndex}-0] ${firstLesson.label}`); + PuzzleManager.instance.chapterManager.completeLesson(chapterIndex, 0); + PuzzleManager.instance.updatePieceGauge(chapter.pieceId); + PuzzleManager.instance._updateTotalProgress(); + } + } + + markComplete() { + if (this.isCompleted) return; + + this.isCompleted = true; + this.group.classList.add("completed"); + + const baseImage = this.group.querySelector(".piece-base-image"); + const completedImage = this.group.querySelector(".piece-completed-image"); + + // ✅ base 이미지를 완전히 숨기고 completed 이미지만 표시 + if (baseImage) { + baseImage.style.display = "none"; + } + + if (completedImage) { + completedImage.style.display = "block"; + // completed-effect 필터 완전히 제거하고 PIECE_EMBOSSING만 적용 + completedImage.removeAttribute("filter"); + completedImage.setAttribute( + "filter", + `url(#${CONFIG.FILTER_IDS.PIECE_EMBOSSING})` + ); + } + } + + // ✅ 새로운 메서드: Finish Image 표시 (최종 표시) +showFinish() { + const baseImg = this.group.querySelector(".piece-base-image"); + const completedImg = this.group.querySelector(".piece-completed-image"); + const allCompletedImg = this.group.querySelector(".piece-all-completed-image"); + const finishImg = this.group.querySelector(".piece-finish-image"); + + // 모든 이미지 숨기기 + if (baseImg) baseImg.style.display = "none"; + if (completedImg) completedImg.style.display = "none"; + if (allCompletedImg) allCompletedImg.style.display = "none"; + + // ✅ FINISH 이미지 표시 (bg_piece_finish.png - COMPLETED 이미지) + if (finishImg) { + finishImg.style.display = "block"; + } +} + // ❌ showAllCompleted는 이제 사용 안 함 (showFinish로 대체) + showAllCompleted() { + // 더 이상 사용하지 않음 - showFinish로 대체 + this.showFinish(); + } +} + +// ============================================================================ +// 게이지 관리 클래스 +// ============================================================================ +class GaugeManager { + static createGauge(pieceId, svg) { + const xRange = GAUGE_CONFIG.X_RANGES[pieceId]; + if (!xRange) return; + + const gaugeGroup = SVGHelper.createElement("g", { + class: `piece-gauge piece-gauge-${pieceId}`, + "data-piece": pieceId, + }); + + const gaugeY = + (GAUGE_CONFIG.POSITIONS[pieceId] || 300) + CONFIG.GAUGE.VERTICAL_OFFSET; + + const fullWidth = xRange.right - xRange.left; + + // 초기 생성 시에는 EMBOSSING_INSET을 적용하지 않음 (전체 영역 사용) + // 완료 시에는 updateGauge에서 EMBOSSING_INSET을 적용 + const EMBOSSING_INSET = 0; // 초기에는 0, 완료 시 updateGauge에서 조정 + + // 게이지 너비 결정 (MAX_LENGTHS가 있으면 사용, 없으면 fullWidth 사용) + let bgWidth = fullWidth; + if (GAUGE_CONFIG.MAX_LENGTHS && GAUGE_CONFIG.MAX_LENGTHS[pieceId]) { + bgWidth = GAUGE_CONFIG.MAX_LENGTHS[pieceId]; + } + + // 1번 피스는 하단 영역 안쪽 중앙에 배치 + let centerX; + if (pieceId === 1) { + // 하단 영역: 256부터 right까지의 영역의 중앙 + const bottomAreaLeft = 256; + const bottomAreaRight = xRange.right; + centerX = (bottomAreaLeft + bottomAreaRight) / 2; + } else { + // 다른 피스는 전체 xRange의 중앙 + centerX = (xRange.left + xRange.right) / 2; + } + + // 중앙 정렬: centerX에서 bgWidth의 절반을 빼서 시작점 계산 + let gaugeX = centerX - (bgWidth / 2); + + // strokeRadius 고려하여 최소 여백 확보 + const strokeRadius = CONFIG.GAUGE.HEIGHT / 2; + const minMargin = strokeRadius; + + // 왼쪽 경계 확인 및 조정 + if (gaugeX < xRange.left + minMargin) { + gaugeX = xRange.left + minMargin; + // 오른쪽 경계도 확인하여 너비 조정 + const maxEndX = xRange.right - minMargin; + bgWidth = Math.min(bgWidth, maxEndX - gaugeX); + } + + // 오른쪽 경계 확인 및 조정 + const calculatedEndX = gaugeX + bgWidth; + const maxEndX = xRange.right - minMargin; + if (calculatedEndX > maxEndX) { + bgWidth = maxEndX - gaugeX; + // 너비가 줄어들었으므로 다시 중앙 정렬 + gaugeX = centerX - (bgWidth / 2); + // 다시 왼쪽 경계 확인 + if (gaugeX < xRange.left + minMargin) { + gaugeX = xRange.left + minMargin; + bgWidth = maxEndX - gaugeX; + } + } + + let fillWidth = bgWidth; + + const bgLine = SVGHelper.createElement("line", { + x1: gaugeX, + y1: gaugeY + CONFIG.GAUGE.HEIGHT / 2, + x2: gaugeX + bgWidth, + y2: gaugeY + CONFIG.GAUGE.HEIGHT / 2, + stroke: CONFIG.GAUGE.BG_COLOR, + "stroke-width": CONFIG.GAUGE.HEIGHT, + "stroke-linecap": "round", + opacity: 0, // 초기에는 학습 내용이 없으므로 opacity 0 + class: "gauge-bg-line", + "data-original-x1": gaugeX, // 원본 좌표 저장 + "data-original-x2": gaugeX + bgWidth, // 원본 좌표 저장 + "data-original-opacity": CONFIG.GAUGE.BG_OPACITY, // 원본 opacity 저장 + }); + bgLine.style.mixBlendMode = "multiply"; + gaugeGroup.appendChild(bgLine); + + const fillLine = SVGHelper.createElement("rect", { + x: gaugeX, + y: gaugeY, + width: 0, + height: CONFIG.GAUGE.HEIGHT, + rx: CONFIG.GAUGE.RADIUS, + fill: CONFIG.GAUGE.FILL_COLOR, + class: "gauge-fill-line", + filter: "url(#gauge-fill-inner-shadow)", + "data-gauge-length": Math.round(fillWidth), // 반올림하여 정수로 저장 + "data-original-x1": gaugeX, // 원본 시작점 저장 + }); + + fillLine.style.transition = "width 0.6s ease-out"; + + gaugeGroup.appendChild(fillLine); + svg.appendChild(gaugeGroup); + } + + static updateGauge(pieceId, progress, isCompleted = false) { + progress = Math.max(0, Math.min(100, progress)); + + const fillLine = document.querySelector( + `.piece-gauge-${pieceId} .gauge-fill-line` + ); + const bgLine = document.querySelector( + `.piece-gauge-${pieceId} .gauge-bg-line` + ); + + if (!fillLine) return; + + // 원본 길이 가져오기 + let originalLength = parseFloat(fillLine.getAttribute("data-original-length")); + if (!originalLength || isNaN(originalLength)) { + originalLength = parseFloat(fillLine.getAttribute("data-gauge-length")); + fillLine.setAttribute("data-original-length", originalLength); + } + + // 완료 시에만 EMBOSSING_INSET 적용 + const EMBOSSING_INSET = isCompleted ? 8 : 0; + const adjustedLength = originalLength - (EMBOSSING_INSET * 2); + + // 원본 시작점 가져오기 + let originalX1 = parseFloat(fillLine.getAttribute("data-original-x1")); + if (!originalX1 || isNaN(originalX1)) { + originalX1 = parseFloat(fillLine.getAttribute("x1")); + fillLine.setAttribute("data-original-x1", originalX1); + } + + // 중앙 정렬 유지: 원본 중앙에서 조정된 길이의 절반씩 빼고 더함 + const originalX2 = originalX1 + originalLength; + const originalCenterX = (originalX1 + originalX2) / 2; + const newX = originalCenterX - (adjustedLength / 2); + + // 게이지 길이 및 위치 업데이트 + fillLine.setAttribute("data-gauge-length", Math.round(adjustedLength)); // 반올림하여 정수로 저장 + fillLine.setAttribute("x", newX); + + // 배경 라인도 조정 (중앙 정렬 유지) + if (bgLine) { + let originalX1 = parseFloat(bgLine.getAttribute("data-original-x1")); + let originalX2 = parseFloat(bgLine.getAttribute("data-original-x2")); + + // 원본 좌표가 없으면 현재 좌표를 원본으로 저장 + if (!originalX1 || isNaN(originalX1)) { + originalX1 = parseFloat(bgLine.getAttribute("x1")); + originalX2 = parseFloat(bgLine.getAttribute("x2")); + bgLine.setAttribute("data-original-x1", originalX1); + bgLine.setAttribute("data-original-x2", originalX2); + } + + // 원본 opacity 가져오기 + let originalOpacity = parseFloat(bgLine.getAttribute("data-original-opacity")); + if (!originalOpacity || isNaN(originalOpacity)) { + originalOpacity = CONFIG.GAUGE.BG_OPACITY; + bgLine.setAttribute("data-original-opacity", originalOpacity); + } + + // progress에 따라 opacity 설정: 학습 내용이 없으면(progress === 0) opacity 0, 있으면 원본 opacity + if (progress > 0) { + bgLine.setAttribute("opacity", originalOpacity); + } else { + bgLine.setAttribute("opacity", 0); + } + + if (isCompleted) { + // 중앙 정렬 유지: 원본 중앙에서 조정된 너비의 절반씩 빼고 더함 + const originalCenterX = (originalX1 + originalX2) / 2; + const adjustedBgWidth = (originalX2 - originalX1) - (EMBOSSING_INSET * 2); + const newX1 = originalCenterX - (adjustedBgWidth / 2); + const newX2 = originalCenterX + (adjustedBgWidth / 2); + + bgLine.setAttribute("x1", newX1); + bgLine.setAttribute("x2", newX2); + } else { + // 완료되지 않았으면 원본 좌표로 복원 + bgLine.setAttribute("x1", originalX1); + bgLine.setAttribute("x2", originalX2); + } + } + + const newWidth = adjustedLength * (progress / 100); + + requestAnimationFrame(() => { + fillLine.style.width = `${newWidth}px`; + }); + } +} + +// ============================================================================ +// UI 요소 생성 클래스 +// ============================================================================ +class UIElementFactory { + static createPlayButton(pieceId, svg, contentManager) { + const position = BUTTON_POSITIONS.find((p) => p.id === pieceId); + if (!position) return; + + const isFileType = contentManager.getType(pieceId) === "file"; + const groupColor = contentManager.getGroupColor(pieceId); + + const buttonGroup = SVGHelper.createElement("g", { + class: `piece-play-button piece-play-${pieceId}`, + "data-piece": pieceId, + }); + buttonGroup.style.cursor = "pointer"; + + if (isFileType) { + this._createFileIconWithBackground( + buttonGroup, + position.x, + position.y, + groupColor + ); + } else { + this._createPlayIcon(buttonGroup, position.x, position.y, groupColor); + } + + svg.appendChild(buttonGroup); + } + + static _createPlayIcon(group, x, y, groupColor) { + const outerCircleGroup = SVGHelper.createElement("g", { + filter: "url(#play-button-outer-filter)", + }); + + const mainCircle = SVGHelper.createElement("ellipse", { + cx: x, + cy: y, + rx: CONFIG.PLAY_BUTTON.RADIUS, + ry: CONFIG.PLAY_BUTTON.RADIUS - 1, + fill: groupColor, + opacity: 0.95, + }); + outerCircleGroup.appendChild(mainCircle); + + const strokeCircle = SVGHelper.createElement("ellipse", { + cx: x, + cy: y, + rx: CONFIG.PLAY_BUTTON.RADIUS, + ry: CONFIG.PLAY_BUTTON.RADIUS, + fill: "none", + stroke: "white", + "stroke-opacity": "0.9", + "stroke-width": "4", + }); + outerCircleGroup.appendChild(strokeCircle); + + group.appendChild(outerCircleGroup); + + const playIconGroup = SVGHelper.createElement("g", { + filter: "url(#play-button-icon-filter)", + }); + + const iconSize = CONFIG.PLAY_BUTTON.ICON_SIZE; + const trianglePath = + `M${x - iconSize.width / 3},${y - iconSize.height / 2.5} ` + + `L${x - iconSize.width / 3},${y + iconSize.height / 2.5} ` + + `L${x + iconSize.width / 2},${y} Z`; + + const playTriangle = SVGHelper.createElement("path", { + d: trianglePath, + fill: "white", + }); + playIconGroup.appendChild(playTriangle); + + group.appendChild(playIconGroup); + } + + static _createFileIconWithBackground(group, centerX, centerY, groupColor) { + const outerCircleGroup = SVGHelper.createElement("g", { + filter: "url(#play-button-outer-filter)", + }); + + const mainCircle = SVGHelper.createElement("ellipse", { + cx: centerX, + cy: centerY, + rx: CONFIG.PLAY_BUTTON.RADIUS, + ry: CONFIG.PLAY_BUTTON.RADIUS - 1, + fill: groupColor, + opacity: 0.95, + }); + outerCircleGroup.appendChild(mainCircle); + + const strokeCircle = SVGHelper.createElement("ellipse", { + cx: centerX, + cy: centerY, + rx: CONFIG.PLAY_BUTTON.RADIUS, + ry: CONFIG.PLAY_BUTTON.RADIUS, + fill: "none", + stroke: "white", + "stroke-opacity": "0.9", + "stroke-width": "4", + }); + outerCircleGroup.appendChild(strokeCircle); + + group.appendChild(outerCircleGroup); + + this._createFileIcon(group, centerX, centerY, groupColor); + } + + static _createFileIcon(group, centerX, centerY, groupColor = "#8F360B") { + const iconWidth = 32; + const iconHeight = 28; + const x = centerX - iconWidth / 2; + const y = centerY - iconHeight / 2; + + const iconGroup = SVGHelper.createElement("g", { + transform: `translate(${x}, ${y})`, + }); + + const pdfBox = SVGHelper.createElement("rect", { + x: "11.4277", + y: "13.7109", + width: "20.5714", + height: "10.2845", + rx: "1.14279", + fill: "white", + }); + iconGroup.appendChild(pdfBox); + + const docPath = SVGHelper.createElement("path", { + d: "M10.2855 21.7114H6.85687L6.85687 3.42563H13.7141L13.7141 8.56851C13.7154 9.02275 13.8965 9.458 14.2177 9.7792C14.5389 10.1004 14.9741 10.2815 15.4283 10.2828H20.5712L20.5712 12.5685H22.2855L22.2855 8.56851C22.2886 8.45586 22.2671 8.3439 22.2227 8.24032C22.1784 8.13674 22.112 8.04401 22.0284 7.96851L16.0284 1.96848C15.9532 1.88436 15.8606 1.81774 15.7569 1.77331C15.6532 1.72888 15.5411 1.70773 15.4283 1.71133H6.85687C6.40263 1.71269 5.96738 1.89374 5.64618 2.21494C5.32498 2.53613 5.14393 2.97138 5.14258 3.42563L5.14258 21.7114C5.14393 22.1657 5.32498 22.6009 5.64618 22.9221C5.96738 23.2433 6.40263 23.4244 6.85687 23.4257H10.2855L10.2855 21.7114ZM15.4283 3.76849L20.2284 8.56851H15.4283L15.4283 3.76849Z", + fill: "white", + }); + iconGroup.appendChild(docPath); + + const pdfText = SVGHelper.createElement("path", { + d: "M14.2689 22.5625L14.2689 16.1878H16.7839C17.2674 16.1878 17.6793 16.2802 18.0196 16.4648C18.3599 16.6475 18.6193 16.9017 18.7978 17.2274C18.9783 17.5512 19.0686 17.9247 19.0686 18.348C19.0686 18.7713 18.9773 19.1448 18.7947 19.4685C18.6121 19.7923 18.3475 20.0444 18.0009 20.2249C17.6565 20.4054 17.2394 20.4957 16.7497 20.4957H15.1467L15.1467 19.4156H16.5318C16.7912 19.4156 17.0049 19.371 17.173 19.2818C17.3431 19.1905 17.4697 19.0649 17.5527 18.9052C17.6378 18.7433 17.6803 18.5576 17.6803 18.348C17.6803 18.1363 17.6378 17.9517 17.5527 17.7939C17.4697 17.6342 17.3431 17.5107 17.173 17.4235C17.0028 17.3343 16.787 17.2897 16.5256 17.2897H15.6167L15.6167 22.5625H14.2689ZM21.9408 22.5625H19.681L19.681 16.1878H21.9595C22.6007 16.1878 23.1527 16.3154 23.6154 16.5707C24.0782 16.8238 24.434 17.188 24.6831 17.6632C24.9341 18.1384 25.0597 18.707 25.0597 19.3689C25.0597 20.033 24.9341 20.6036 24.6831 21.0809C24.434 21.5582 24.0761 21.9244 23.6092 22.1796C23.1444 22.4349 22.5882 22.5625 21.9408 22.5625ZM21.0288 21.4077H21.8848C22.2832 21.4077 22.6183 21.3372 22.8902 21.1961C23.1641 21.0529 23.3695 20.8319 23.5065 20.5331C23.6455 20.2322 23.715 19.8441 23.715 19.3689C23.715 18.8979 23.6455 18.513 23.5065 18.2141C23.3695 17.9153 23.1651 17.6954 22.8933 17.5543C22.6215 17.4132 22.2863 17.3426 21.8879 17.3426H21.0288L21.0288 21.4077ZM25.7951 22.5625L25.7951 16.1878H30.0158L30.0158 17.299H27.1429L27.1429 18.818H29.7357L29.7357 19.9292H27.1429L27.1429 22.5625H25.7951Z", + fill: groupColor, + }); + iconGroup.appendChild(pdfText); + + group.appendChild(iconGroup); + } + + static createTitles(svg) { + TITLE_POSITIONS.forEach((pos) => { + const text = SVGHelper.createElement("text", { + x: pos.x, + y: pos.y, + "text-anchor": "middle", + "dominant-baseline": "middle", + fill: "black", + stroke: "white", + "stroke-width": "8", + "stroke-linejoin": "round", + "stroke-linecap": "round", + "paint-order": "stroke fill", + "font-size": "30", + "font-weight": "bold", + "pointer-events": "none", + filter: "url(#text-shadow-filter)", + }); + + const lineHeight = 36; + const totalHeight = (pos.lines.length - 1) * lineHeight; + const startY = pos.y - totalHeight / 2; + + pos.lines.forEach((line, index) => { + const tspan = SVGHelper.createElement("tspan", { + x: pos.x, + dy: index === 0 ? 0 : lineHeight, + }); + tspan.textContent = line; + text.appendChild(tspan); + }); + + svg.appendChild(text); + }); + } + + /** + * 퍼즐 조각 경계선 생성 + */ +static createBoundaryLines(svg) { + const boundaryGroup = SVGHelper.createElement("g", { + class: "puzzle-boundaries", + id: "puzzleBoundaries", + style: "pointer-events: none;", // 클릭 이벤트 차단 + }); + + BOUNDARY_LINES.forEach((boundary) => { + const line = SVGHelper.createElement("line", { + x1: boundary.x1, + y1: boundary.y1, + x2: boundary.x2, + y2: boundary.y2, + stroke: "#000000", // 검은색 + "stroke-width": "2", + "stroke-linecap": "butt", + "stroke-opacity": "0.8", // 약간 투명하게 + class: "boundary-line", + "data-boundary": boundary.label, + }); + + boundaryGroup.appendChild(line); + }); + + svg.appendChild(boundaryGroup); +} +} +// ============================================================================ +// 모달 관리 클래스 (VideoModalBase 활용) - 퍼즐 온보딩 전용 +// ============================================================================ +class PuzzleModalManager { + // VideoModalBase 인스턴스 저장 + static videoModalBase = null; + + /** + * VideoModalBase 초기화 + * @private + */ + static _initVideoModalBase() { + if (!this.videoModalBase) { + this.videoModalBase = new VideoModalBase({ + videos: [], + modalPath: "./_modal/video-onboarding.html", + modalPathTemplate: "./_modal/video-{type}.html", + enableHeightAdjustment: false, // onboarding은 자체 높이 조정 사용 + enableCommentResizer: false, + enableCommentBox: true, // 댓글 입력 시 작성 버튼 활성화 + }); + } + return this.videoModalBase; + } + + /** + * 챕터 모달 열기 (VideoModalBase 활용) + * @param {number} chapterIndex - 챕터 인덱스 + * @param {Object} chapter - 챕터 데이터 + */ + static async openChapterModal(chapterIndex, chapter) { + console.log( + `[PuzzleModalManager] 챕터 모달 열기: [${chapterIndex}] ${chapter.name}` + ); + + try { + // VideoModalBase 초기화 + const videoModalBase = this._initVideoModalBase(); + + // 기존 모달 제거 + this._removeExistingModal(); + + // VideoModalBase를 사용하여 모달 HTML 로드 + const modalHTML = await videoModalBase.loadModalHTML("onboarding"); + + // VideoModalBase를 사용하여 모달 요소 생성 + const modal = videoModalBase.createModalFromHTML(modalHTML, "onboarding"); + + // DOM에 추가 + document.body.appendChild(modal); + + // ✅ 모달을 먼저 숨긴 상태로 표시 (높이 조정이 보이지 않도록) + modal.style.display = "block"; + modal.style.visibility = "hidden"; + modal.style.opacity = "0"; + + // 모달 초기화 + setTimeout(() => { + this._setupModal(modal, chapter, chapterIndex); + }, 50); + } catch (error) { + console.error("모달 로드 오류:", error); + this._openFallbackModal(chapterIndex, chapter); + } + } + + /** + * 기존 모달 제거 (VideoModalBase 활용) + * @private + */ + static _removeExistingModal() { + const existingModal = document.querySelector(".modal.video"); + if (existingModal) { + // VideoBase를 사용하여 비디오 중지 + const iframe = existingModal.querySelector("#videoFrame"); + if (iframe) { + VideoBase.stop(iframe); + } + + // Observer들 정리 + if (existingModal._resizeObserver) { + existingModal._resizeObserver.disconnect(); + } + if (existingModal._mutationObserver) { + existingModal._mutationObserver.disconnect(); + } + if (existingModal._heightAdjustTimer) { + clearTimeout(existingModal._heightAdjustTimer); + } + if (existingModal._windowResizeHandler) { + window.removeEventListener("resize", existingModal._windowResizeHandler); + } + existingModal.remove(); + } + + // VideoModalBase 인스턴스도 정리 + if (this.videoModalBase) { + this.videoModalBase.destroy(); + } + } + + /** + * 모달 설정 + * @private + */ + static _setupModal(modal, chapter, chapterIndex) { + // 모달 상태 객체 생성 + const modalState = { + currentLessonIndex: 0, + retryCount: 0, + isVisible: false, + isAdjustingHeight: false, + isInitialLoadComplete: false, + }; + + // 첫 번째 학습 로드 + this._loadLesson(modal, chapter, modalState.currentLessonIndex); + + // 진행률 업데이트 + this._updateProgress(modal, chapter); + + // 닫기 이벤트 먼저 설정 + this._setupCloseEvents(modal, chapter, chapterIndex, modalState); + + // 댓글 입력 시 작성/취소 버튼 활성화 + if (this.videoModalBase && this.videoModalBase.setupCommentBox) { + this.videoModalBase.setupCommentBox(modal); + } + + // 학습 목차 생성 + this._createLearningList(modal, chapter, chapterIndex, modalState); + + // ✅ 높이 조정 후 모달 표시 + this._initializeHeightAdjustment(modal, modalState, () => { + // 높이 조정 완료 후 모달을 보이게 함 + modal.style.visibility = "visible"; + modal.style.opacity = "1"; + modal.style.transition = "opacity 0.3s ease"; + modalState.isVisible = true; + modalState.isInitialLoadComplete = true; + + // 현재 학습으로 스크롤 + setTimeout(() => { + this._scrollToCurrentLesson(modal); + }, 100); + }); + } + + // ============================================================================ + // 높이 조정 시스템 (VideoModal 방식 적용) + // ============================================================================ + + /** + * 높이 조정 초기화 + * @private + * @param {HTMLElement} modal - 모달 요소 + * @param {Object} modalState - 모달 상태 + * @param {Function} onComplete - 높이 조정 완료 후 실행할 콜백 + */ + static _initializeHeightAdjustment(modal, modalState, onComplete) { + // ✅ 최적화: 높이 조정을 3단계로 축소 (리플로우 최소화) + let pendingAdjust = null; + + const scheduleAdjust = () => { + if (pendingAdjust) { + cancelAnimationFrame(pendingAdjust); + } + pendingAdjust = requestAnimationFrame(() => { + this._adjustModalContentHeight(modal, modalState); + this._adjustVideoListHeight(modal, modalState); + pendingAdjust = null; + }); + }; + + // 1단계: 즉시 시도 (첫 렌더링) + scheduleAdjust(); + + // 2단계: requestAnimationFrame (2프레임 대기) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + scheduleAdjust(); + }); + }); + + // 3단계: 지연 시도 (이미지 로딩 대기 후) + this._waitForImagesAndAdjust(modal, modalState).then(() => { + scheduleAdjust(); + if (onComplete) { + onComplete(); + } + }); + + // Observer 설정 (초기 로딩 후에만 작동) + this._setupResizeObserver(modal, modalState); + this._setupMutationObserver(modal, modalState); + } + + /** + * 모달 컨텐츠 전체 높이 조정 + * 화면 높이의 80% 이상일 때만 조절하고, 아닐 경우 80%로 유지 + * @private + */ + static _adjustModalContentHeight(modal, modalState) { + const modalContent = modal?.querySelector(".modal-content"); + if (!modalContent) { + console.warn("[ModalManager] modal-content 요소를 찾을 수 없습니다"); + return; + } + + // 화면 높이의 80% 계산 + const viewportHeight = window.innerHeight; + const maxHeight = viewportHeight * 0.8; + + // 현재 모달 컨텐츠의 실제 높이 (스타일이 적용되기 전의 자연스러운 높이) + const originalHeight = modalContent.style.height; + modalContent.style.height = "auto"; + const actualHeight = modalContent.scrollHeight; + modalContent.style.height = originalHeight; + + // 실제 높이가 80% 이상이면 조절하지 않고 그대로 유지, 그렇지 않으면 80%로 설정 + if (actualHeight >= maxHeight) { + // 80% 이상이면 높이를 조절하지 않음 + modalContent.style.height = "auto"; + modalContent.style.overflowY = "auto"; + } else { + // 80% 미만이면 80%로 설정 + modalContent.style.height = maxHeight + "px"; + modalContent.style.overflowY = "auto"; + } + + console.log("[ModalManager] 모달 컨텐츠 높이 조정:", { + viewportHeight, + maxHeight, + actualHeight, + finalHeight: modalContent.style.height, + }); + } + + /** + * video-list 높이 조정 (개선 버전) + * @private + * @returns {boolean} 성공 여부 + */ + static _adjustVideoListHeight(modal, modalState) { + const videoSide = modal.querySelector(".video-side"); + const videoHeader = modal.querySelector(".video-header"); + const videoList = modal.querySelector(".video-list"); + const learningList = modal.querySelector(".learning-list"); + const commentWrap = modal.querySelector(".comment-wrap"); + + if (!videoSide || !videoHeader || !videoList || !learningList) { + console.warn("[ModalManager] 필요한 요소를 찾을 수 없습니다"); + modalState.isAdjustingHeight = false; + return false; + } + + // ✅ 높이 조정 중 플래그 설정 + modalState.isAdjustingHeight = true; + + // ✅ 스크롤 위치 저장 + const savedScrollTop = learningList.scrollTop; + + // 전체 높이 + const totalHeight = videoSide.clientHeight; + + // 높이가 0이거나 비정상적으로 작으면 DOM이 아직 렌더링되지 않은 것 + if (totalHeight < 100) { + console.warn( + `[ModalManager] videoSide 높이가 비정상적으로 작습니다: ${totalHeight}px. 재측정 예약...` + ); + + // 최대 3번까지만 재시도 + if (modalState.retryCount < 3) { + modalState.retryCount++; + modalState.isAdjustingHeight = false; + setTimeout(() => this._adjustVideoListHeight(modal, modalState), 100); + } else { + console.error("[ModalManager] 높이 측정 재시도 횟수 초과"); + modalState.retryCount = 0; + modalState.isAdjustingHeight = false; + } + return false; + } + + // 재시도 카운터 초기화 + modalState.retryCount = 0; + + // 헤더 높이 + const headerHeight = videoHeader.offsetHeight; + + // comment-wrap 높이 + const commentWrapHeight = commentWrap ? commentWrap.offsetHeight : 0; + + // comment-list-wrap 높이 (comment-wrap 내부에 있을 수 있음) + const commentListWrap = modal.querySelector(".comment-list-wrap"); + const commentListWrapHeight = commentListWrap ? commentListWrap.offsetHeight : 0; + + // video-list의 제목 높이 + const videoListTitle = videoList.querySelector("h5.tit"); + const titleHeight = videoListTitle ? videoListTitle.offsetHeight : 0; + + // video-list의 padding 값 계산 + const videoListStyle = window.getComputedStyle(videoList); + const paddingTop = parseInt(videoListStyle.paddingTop) || 0; + const paddingBottom = parseInt(videoListStyle.paddingBottom) || 0; + + // comment-info 존재 여부 확인 + const commentInfo = modal.querySelector(".comment-info"); + const commentInfoOffset = commentInfo ? 40 : 0; + + // learning-list에 사용 가능한 최대 높이 + // comment-list-wrap 높이도 고려 (comment-wrap과 별도로 계산) + const availableHeight = totalHeight - headerHeight - commentWrapHeight - + (commentListWrapHeight > 0 ? commentListWrapHeight : 0) - + titleHeight - paddingTop - paddingBottom - + commentInfoOffset - 20; + + // 사용 가능한 높이가 음수이거나 너무 작으면 경고 + if (availableHeight < 50) { + console.warn( + `[ModalManager] 사용 가능한 높이가 너무 작습니다: ${availableHeight}px` + ); + modalState.isAdjustingHeight = false; + return false; + } + + // ✅ learning-list의 실제 컨텐츠 높이 측정 (스타일 제거 후) + const originalHeight = learningList.style.height; + const originalOverflow = learningList.style.overflowY; + + learningList.style.height = 'auto'; + learningList.style.overflowY = 'visible'; + + const listContentHeight = learningList.scrollHeight; + + learningList.style.height = originalHeight; + learningList.style.overflowY = originalOverflow; + + // ✅ video-list의 실제 컨텐츠 높이 측정 (comment-list-wrap 높이 고려) + const videoListOriginalHeight = videoList.style.height; + const videoListOriginalOverflow = videoList.style.overflowY; + + videoList.style.height = 'auto'; + videoList.style.overflowY = 'visible'; + + const videoListContentHeight = videoList.scrollHeight; + + videoList.style.height = videoListOriginalHeight; + videoList.style.overflowY = videoListOriginalOverflow; + + // video-list에 사용 가능한 높이 계산 + // video-list는 컨테이너이므로, 내부 요소(제목, padding)는 빼지 않음 + // comment-wrap만 빼면 됨 (comment-list-wrap은 이미 commentWrapHeight에 포함됨) + const videoListAvailableHeight = totalHeight - headerHeight - commentWrapHeight - 10; + + // 컨텐츠가 적으면 컨텐츠 높이만큼, 많으면 사용 가능한 높이만큼 + const listHeight = Math.min(listContentHeight, availableHeight); + const videoListHeight = Math.min(videoListContentHeight, videoListAvailableHeight); + + // 최소 높이 보장 + const finalHeight = Math.max(listHeight, 100); + const videoListFinalHeight = Math.max(videoListHeight, 100); + + // ✅ 현재 설정된 높이 확인 + const currentHeight = learningList.style.height + ? parseInt(learningList.style.height) + : learningList.offsetHeight; + + const videoListCurrentHeight = videoList.style.height + ? parseInt(videoList.style.height) + : videoList.offsetHeight; + + // 스크롤 필요 여부 확인 + const needsScroll = listContentHeight > availableHeight; + const videoListNeedsScroll = videoListContentHeight > videoListAvailableHeight; + + console.log("[ModalManager] 높이 측정 성공:", { + totalHeight, + headerHeight, + commentWrapHeight, + commentListWrapHeight, + titleHeight, + paddingTop, + paddingBottom, + availableHeight, + listContentHeight, + listHeight, + finalHeight, + currentHeight, + needsScroll, + videoListContentHeight, + videoListAvailableHeight, + videoListHeight, + videoListFinalHeight, + videoListCurrentHeight, + videoListNeedsScroll, + savedScrollTop, + }); + + // ✅ 높이가 실제로 변경되는 경우에만 스타일 업데이트 + const heightChanged = Math.abs(currentHeight - finalHeight) > 1; + const videoListHeightChanged = Math.abs(videoListCurrentHeight - videoListFinalHeight) > 1; + + // learning-list의 height와 overflow-y는 CSS로 관리 (인라인 스타일 제거) + + // ✅ video-list 높이 및 스크롤 설정 (overflow-y: hidden 제거, CSS 기본값 사용) + if (videoListHeightChanged) { + videoList.style.height = videoListFinalHeight + "px"; + } + // 스크롤이 필요할 때만 auto 설정, 필요 없을 때는 CSS 기본값 사용 + if (videoListNeedsScroll) { + videoList.style.overflowY = "auto"; + } else { + videoList.style.overflowY = ""; // CSS 기본값 사용 + } + + // ✅ 컨텐츠 높이를 CSS 변수로 설정 (::before 요소에서 사용) + learningList.style.setProperty('--scroll-height', `${listContentHeight}px`); + + // ✅ 스크롤 위치 복원 (높이 변경 여부와 관계없이) + requestAnimationFrame(() => { + learningList.scrollTop = savedScrollTop; + // 높이 조정 완료 후 플래그 해제 + modalState.isAdjustingHeight = false; + }); + + return true; + } + + /** + * ResizeObserver 설정 + * @private + */ + static _setupResizeObserver(modal, modalState) { + const videoSide = modal.querySelector(".video-side"); + const commentWrap = modal.querySelector(".comment-wrap"); + const modalContent = modal.querySelector(".modal-content"); + + if (!videoSide) return; + + if (!modal._resizeObserver) { + // ✅ throttle 적용 (리플로우 최소화) + const throttledAdjust = Utils.throttle(() => { + if (!modalState.isInitialLoadComplete) { + return; + } + + let pendingAdjust = null; + if (pendingAdjust) { + cancelAnimationFrame(pendingAdjust); + } + pendingAdjust = requestAnimationFrame(() => { + this._adjustModalContentHeight(modal, modalState); + this._adjustVideoListHeight(modal, modalState); + pendingAdjust = null; + }); + }, 100); // 100ms throttle + + modal._resizeObserver = new ResizeObserver((entries) => { + // ✅ 초기 로딩 중에는 무시 + if (!modalState.isInitialLoadComplete) { + return; + } + throttledAdjust(); + }); + + modal._resizeObserver.observe(videoSide); + if (commentWrap) { + modal._resizeObserver.observe(commentWrap); + } + if (modalContent) { + modal._resizeObserver.observe(modalContent); + } + } + + // Window resize 이벤트 (throttle 적용) + if (!modal._windowResizeHandler) { + const throttledResize = Utils.throttle(() => { + if (!modalState.isInitialLoadComplete) { + return; + } + + let pendingAdjust = null; + if (pendingAdjust) { + cancelAnimationFrame(pendingAdjust); + } + pendingAdjust = requestAnimationFrame(() => { + this._adjustModalContentHeight(modal, modalState); + this._adjustVideoListHeight(modal, modalState); + pendingAdjust = null; + }); + }, 100); // 100ms throttle + + modal._windowResizeHandler = throttledResize; + window.addEventListener("resize", modal._windowResizeHandler); + } + } + + /** + * MutationObserver 설정 + * @private + */ + static _setupMutationObserver(modal, modalState) { + const learningList = modal.querySelector(".learning-list"); + + if (!learningList) return; + + if (!modal._mutationObserver) { + modal._mutationObserver = new MutationObserver((mutations) => { + // ✅ 초기 로딩 중에는 무시 + if (!modalState.isInitialLoadComplete) { + console.log("[ModalManager] 초기 로딩 중 - MutationObserver 무시"); + return; + } + + // ✅ 높이 조정 중이면 무시 + if (modalState.isAdjustingHeight) { + return; + } + + const hasRelevantChange = mutations.some((mutation) => { + if ( + mutation.type === "attributes" && + mutation.attributeName === "style" && + mutation.target === learningList + ) { + return false; + } + return mutation.type === "childList" || + (mutation.type === "attributes" && mutation.target !== learningList); + }); + + if (!hasRelevantChange) { + return; + } + + // ✅ throttle 적용 (리플로우 최소화) + if (!modal._mutationThrottle) { + modal._mutationThrottle = Utils.throttle(() => { + let pendingAdjust = null; + if (pendingAdjust) { + cancelAnimationFrame(pendingAdjust); + } + pendingAdjust = requestAnimationFrame(() => { + this._adjustModalContentHeight(modal, modalState); + this._adjustVideoListHeight(modal, modalState); + pendingAdjust = null; + }); + }, 100); // 100ms throttle + } + modal._mutationThrottle(); + }); + + modal._mutationObserver.observe(learningList, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["style", "class"], + }); + } + } + + /** + * 이미지 로딩 대기 후 높이 조정 + * @private + */ + static async _waitForImagesAndAdjust(modal, modalState) { + const learningList = modal.querySelector(".learning-list"); + if (!learningList) return; + + const images = learningList.querySelectorAll("img"); + + if (images.length === 0) { + console.log("[ModalManager] 학습 목록에 이미지 없음"); + return; + } + + console.log(`[ModalManager] 이미지 ${images.length}개 로딩 대기 중...`); + + const imagePromises = Array.from(images).map((img) => { + if (img.complete) return Promise.resolve(); + + return new Promise((resolve) => { + img.onload = () => { + console.log("[ModalManager] 이미지 로드 완료:", img.src); + resolve(); + }; + img.onerror = () => { + console.warn("[ModalManager] 이미지 로드 실패:", img.src); + resolve(); + }; + + // 10초 타임아웃 + setTimeout(resolve, 10000); + }); + }); + + await Promise.all(imagePromises); + + console.log("[ModalManager] 모든 이미지 로딩 완료: 높이 재조정"); + this._adjustModalContentHeight(modal, modalState); + this._adjustVideoListHeight(modal, modalState); + + // 이미지 로딩 후 다시 스크롤 + setTimeout(() => { + this._scrollToCurrentLesson(modal); + }, 100); + } + + // ============================================================================ + // 스크롤 관리 (개선 버전) + // ============================================================================ + + /** + * 현재 학습 중인 항목으로 스크롤 + * @private + */ + static _scrollToCurrentLesson(modal) { + const learningList = modal.querySelector(".learning-list"); + if (!learningList) { + console.warn("[ModalManager] 학습 목록을 찾을 수 없습니다"); + return; + } + + // 현재 학습 중인 항목 찾기 + const currentItem = learningList.querySelector('li[data-current="true"]'); + if (!currentItem) { + console.warn("[ModalManager] 현재 학습 항목을 찾을 수 없습니다"); + return; + } + + // 스크롤 위치 계산 + const listRect = learningList.getBoundingClientRect(); + const itemRect = currentItem.getBoundingClientRect(); + + // 목록 중앙에 현재 항목이 오도록 스크롤 + const scrollOffset = + itemRect.top - listRect.top - listRect.height / 2 + itemRect.height / 2; + + // 부드러운 스크롤 + learningList.scrollTo({ + top: learningList.scrollTop + scrollOffset, + behavior: "smooth", + }); + + console.log(`[ModalManager] 현재 학습으로 스크롤 이동:`, { + currentItem: currentItem.querySelector(".title")?.textContent, + scrollOffset, + }); + + // ✅ 시각적 강조 효과 + this._highlightCurrentLesson(currentItem); + } + + /** + * 현재 학습 항목 시각적 강조 + * @private + */ + static _highlightCurrentLesson(currentItem) { + // 간단한 강조 효과 + currentItem.style.transition = "all 0.3s ease"; + + setTimeout(() => { + currentItem.style.transition = ""; + }, 1000); + } + + // ============================================================================ + // 기존 메서드들 (학습 로드, 진행률, 목차 등) + // ============================================================================ + + /** + * 학습 로드 (VideoModalBase 활용) + * @private + */ + static _loadLesson(modal, chapter, lessonIndex) { + const lesson = chapter.lessons[lessonIndex]; + const videoModalBase = this._initVideoModalBase(); + + // VideoModalBase의 setupVideo 메서드 활용 + if (chapter.type === "youtube" && lesson.url) { + const videoData = { url: lesson.url, id: lesson.url }; + videoModalBase.setupVideo(modal, videoData); + } else if (chapter.type === "file") { + // 파일 타입은 직접 설정 + const iframe = modal.querySelector("#videoFrame"); + if (iframe) { + iframe.src = lesson.url || ""; + } + } + + // 현재 강의 제목 업데이트 + const subTxt = modal.querySelector(".video-header .sub-txt"); + if (subTxt) { + subTxt.textContent = lesson.label; + } + } + + /** + * 진행률 업데이트 + * @private + */ + static _updateProgress(modal, chapter) { + const completedCount = chapter.lessons.filter(l => l.completed).length; + const totalCount = chapter.lessons.length; + const progressPercent = Math.round((completedCount / totalCount) * 100); + + // 게이지바 + const gaugeFill = modal.querySelector("#gaugeFill"); + if (gaugeFill) { + gaugeFill.style.width = progressPercent + "%"; + } + + // 진도율 텍스트 + const currentValue = modal.querySelector("#currentValue em"); + if (currentValue) { + currentValue.textContent = progressPercent; + } + + // 차시 정보 + const labelElement = modal.querySelector(".gauge-labels .label:not(.current)"); + if (labelElement) { + labelElement.innerHTML = `${completedCount} /${totalCount} 강`; + } + } + + /** + * 학습 목차 생성 + * @private + */ + static _createLearningList(modal, chapter, chapterIndex, modalState) { + try { + const domUtils = typeof DOMUtils !== 'undefined' ? DOMUtils : null; + const eventManager = typeof eventManager !== 'undefined' ? eventManager : null; + const errorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; + + const list = domUtils?.$(".learning-list", modal) || modal.querySelector(".learning-list"); + if (!list) { + console.warn('[ModalManager] 학습 목록 요소를 찾을 수 없습니다.'); + return; + } + + domUtils?.empty(list) || (list.innerHTML = ""); + + chapter.lessons.forEach((lesson, index) => { + try { + const li = domUtils?.createElement('li') || document.createElement("li"); + + // 상태 클래스 + if (index === modalState.currentLessonIndex) { + domUtils?.addClasses(li, 'active') || li.classList.add("active"); + li.setAttribute("data-current", "true"); + } else if (lesson.completed) { + domUtils?.addClasses(li, 'complet') || li.classList.add("complet"); + } + + // 이미지 번호 (1-6 순환) + const imageNum = (index % 6) + 1; + + // 상태 텍스트 + let stateText = "미진행"; + if (index === modalState.currentLessonIndex) { + stateText = "학습중"; + } else if (lesson.completed) { + stateText = "학습완료"; + } + + li.innerHTML = ` + + ${index + 1}차시 +
+
+ ${lesson.label} +
+
+
${lesson.label}
+ ${stateText} +
+
+
+ `; + + // 클릭 이벤트 (EventManager 사용) + const link = domUtils?.$(".list", li) || li.querySelector(".list"); + if (link) { + const clickHandler = (e) => { + try { + e.preventDefault(); + + const clickedIndex = parseInt(e.currentTarget.dataset.lessonIndex); + if (isNaN(clickedIndex)) { + console.error('[ModalManager] 유효하지 않은 lesson index:', e.currentTarget.dataset.lessonIndex); + return; + } + + console.log(`[ModalManager] 학습 클릭: ${clickedIndex}, 이전: ${modalState.currentLessonIndex}`); + + // 이전 학습 완료 처리 + if (modalState.currentLessonIndex !== clickedIndex) { + const prevLesson = chapter.lessons[modalState.currentLessonIndex]; + if (prevLesson && !prevLesson.completed) { + console.log(`[ModalManager] 학습 완료 처리: [${chapterIndex}-${modalState.currentLessonIndex}] ${prevLesson.label}`); + PuzzleManager.instance.chapterManager.completeLesson( + chapterIndex, + modalState.currentLessonIndex + ); + PuzzleManager.instance.updatePieceGauge(chapter.pieceId); + } + } + + // 새 학습 로드 + modalState.currentLessonIndex = clickedIndex; + console.log(`[ModalManager] currentLessonIndex 업데이트: ${modalState.currentLessonIndex}`); + + this._loadLesson(modal, chapter, modalState.currentLessonIndex); + this._updateProgress(modal, chapter); + this._createLearningList(modal, chapter, chapterIndex, modalState); + + // ✅ 목록 재생성 후 높이 재조정 및 스크롤 + setTimeout(() => { + this._adjustModalContentHeight(modal, modalState); + this._adjustVideoListHeight(modal, modalState); + setTimeout(() => { + this._scrollToCurrentLesson(modal); + }, 100); + }, 50); + } catch (error) { + if (errorHandler) { + errorHandler.handle(error, { + context: 'PuzzleModalManager._createLearningList.clickHandler', + chapterIndex, + lessonIndex: index + }, false); + } else { + console.error('[ModalManager] 클릭 핸들러 에러:', error); + } + } + }; + + if (eventManager) { + const listenerId = eventManager.on(link, "click", clickHandler); + // 리스너 ID를 modal에 저장하여 나중에 정리할 수 있도록 + if (!modal._learningListListenerIds) { + modal._learningListListenerIds = []; + } + modal._learningListListenerIds.push({ element: link, id: listenerId }); + } else { + link.addEventListener("click", clickHandler); + } + } + + list.appendChild(li); + } catch (error) { + if (errorHandler) { + errorHandler.handle(error, { + context: 'PuzzleModalManager._createLearningList.lesson', + chapterIndex, + lessonIndex: index + }, false); + } else { + console.error(`[ModalManager] 학습 항목 생성 에러 [${index}]:`, error); + } + } + }); + } catch (error) { + const errorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; + if (errorHandler) { + errorHandler.handle(error, { + context: 'PuzzleModalManager._createLearningList', + chapterIndex + }, false); + } else { + console.error('[ModalManager] 학습 목록 생성 에러:', error); + } + } + } + + /** + * 닫기 이벤트 설정 (ModalUtils 활용) + * @private + */ + static _setupCloseEvents(modal, chapter, chapterIndex, modalState) { + const closeModal = () => { + console.log(`[ModalManager] closeModal 호출 - currentLessonIndex: ${modalState.currentLessonIndex}`); + + // 현재 학습 완료 처리 + const currentLesson = chapter.lessons[modalState.currentLessonIndex]; + console.log(`[ModalManager] 현재 학습 정보:`, { + chapterIndex, + lessonIndex: modalState.currentLessonIndex, + lesson: currentLesson, + completed: currentLesson?.completed + }); + + if (currentLesson && !currentLesson.completed) { + console.log(`[ModalManager] 모달 닫기 - 학습 완료 처리: [${chapterIndex}-${modalState.currentLessonIndex}] ${currentLesson.label}`); + PuzzleManager.instance.chapterManager.completeLesson( + chapterIndex, + modalState.currentLessonIndex + ); + PuzzleManager.instance.updatePieceGauge(chapter.pieceId); + PuzzleManager.instance._updateTotalProgress(); + } else if (currentLesson && currentLesson.completed) { + console.log(`[ModalManager] 모달 닫기 - 이미 완료된 학습: [${chapterIndex}-${modalState.currentLessonIndex}] ${currentLesson.label}`); + } else { + console.error(`[ModalManager] 학습 정보를 찾을 수 없음: [${chapterIndex}-${modalState.currentLessonIndex}]`); + } + + // ModalUtils를 사용하여 정리 및 제거 + ModalUtils.remove(modal, { + duration: 300, + onComplete: () => { + console.log(`[ModalManager] 모달 제거 완료`); + }, + }); + }; + + // ModalUtils를 사용하여 공통 닫기 이벤트 설정 + ModalUtils.setupCloseEvents(modal, { + onClose: closeModal, + onCleanup: () => { + // Observer 정리는 ModalUtils.remove에서 자동 처리됨 + }, + }); + } + + /** + * 폴백 모달 (모달 파일 로드 실패 시) + * @private + */ + static _openFallbackModal(chapterIndex, chapter) { + console.log("[ModalManager] 폴백 모달 사용"); + + const modalHTML = ` + + `; + + document.body.insertAdjacentHTML("beforeend", modalHTML); + const modal = document.querySelector(".modal.video"); + + const modalState = { + currentLessonIndex: 0, + retryCount: 0, + isVisible: false, + isAdjustingHeight: false, + isInitialLoadComplete: false, + }; + + this._setupModal(modal, chapter, chapterIndex); + } +} + +// ============================================================================ +// 메인 퍼즐 관리 클래스 +// ============================================================================ +class PuzzleManager { + static instance = null; + + constructor(boardElementId, chapterData = null, dependencies = {}) { + if (PuzzleManager.instance) { + return PuzzleManager.instance; + } + + console.log('[PuzzleManager] 초기화 시작'); + console.log('[PuzzleManager] 챕터 데이터:', chapterData); + + // 의존성 주입 (폴백 포함) + this.domUtils = dependencies.domUtils || (typeof DOMUtils !== 'undefined' ? DOMUtils : null); + this.eventManager = dependencies.eventManager || (typeof eventManager !== 'undefined' ? eventManager : null); + this.errorHandler = dependencies.errorHandler || (typeof ErrorHandler !== 'undefined' ? ErrorHandler : null); + this.utils = dependencies.utils || (typeof Utils !== 'undefined' ? Utils : null); + this.animationUtils = dependencies.animationUtils || (typeof AnimationUtils !== 'undefined' ? AnimationUtils : null); + + // 이벤트 리스너 ID 저장 (정리용) + this.listenerIds = []; + + try { + this.boardElement = this.domUtils?.$(`#${boardElementId}`) || document.getElementById(boardElementId); + if (!this.boardElement) { + const error = new Error(`보드 엘리먼트를 찾을 수 없습니다: ${boardElementId}`); + this._handleError(error, 'PuzzleManager.constructor'); + return; + } + + this.svg = null; + this.pieces = []; + this.chapterManager = new ChapterManager(chapterData, { + errorHandler: this.errorHandler, + utils: this.utils + }); + this.contentManager = new ContentManager(this.chapterManager); + this.videoModal = null; // VideoModal 인스턴스 + + // VideoModal 초기화 시도 + this._initializeVideoModal(); + + PuzzleManager.instance = this; + console.log('[PuzzleManager] 초기화 완료'); + } catch (error) { + this._handleError(error, 'PuzzleManager.constructor'); + } + } + + /** + * 에러 처리 헬퍼 + * @private + */ + _handleError(error, context, additionalInfo = {}) { + if (this.errorHandler) { + this.errorHandler.handle(error, { + context, + component: 'PuzzleManager', + ...additionalInfo + }, false); + } else { + console.error(`[PuzzleManager] ${context}:`, error, additionalInfo); + } + } + + /** + * VideoModal 초기화 + * @private + */ + _initializeVideoModal() { + if (window.VideoModal) { + console.log('[PuzzleManager] VideoModal 감지 - 통합 모드'); + + // VideoModal에 필요한 config와 markerManager 어댑터 생성 + const puzzleConfig = { + modalPath: "./_modal/video-onboarding.html", + }; + + const puzzleMarkerManager = { + completeLesson: (chapterIndex, lessonIndex) => { + this.chapterManager.completeLesson(chapterIndex, lessonIndex); + }, + }; + + try { + this.videoModal = { + openPuzzleChapter: async (chapter, chapterIndex, videoData) => { + console.log(`[PuzzleManager] VideoModal로 챕터 열기: ${chapter.name}`); + + // 실제 VideoModal 인스턴스가 필요한 경우 + // 여기서는 간단한 모달을 사용 + await PuzzleModalManager._openSimpleModal(chapterIndex, chapter); + } + }; + + console.log('[PuzzleManager] VideoModal 어댑터 생성 완료'); + } catch (error) { + console.error('[PuzzleManager] VideoModal 초기화 실패:', error); + this.videoModal = null; + } + } else { + console.log('[PuzzleManager] VideoModal 없음 - 기본 모달 사용'); + } + } + + initialize() { + this.svg = this._createSVG(); + this._setupDefs(); + this._createBoardBackground(); + this._createPuzzlePieces(); + + // ✅ 경계선 추가 (타이틀과 버튼 전에 추가하여 아래에 배치) + UIElementFactory.createBoundaryLines(this.svg); + + UIElementFactory.createTitles(this.svg); + this._createPlayButtonsAndGauges(); + + this.boardElement.appendChild(this.svg); + + // 챕터 데이터로 타이틀 업데이트 + this._updateTitlesFromChapters(); + + this._initializeCompletedPieces(); + } + + /** + * 챕터 데이터로 퍼즐 조각 타이틀 업데이트 + * @private + */ + _updateTitlesFromChapters() { + this.chapterManager.chapters.forEach((chapter) => { + if (!chapter.pieceId || !chapter.name) return; + + const pieceId = chapter.pieceId; + const titlePos = TITLE_POSITIONS.find((t) => t.id === pieceId); + if (!titlePos) return; + + // 타이틀 업데이트 + const texts = this.svg.querySelectorAll("text"); + for (let text of texts) { + const x = parseFloat(text.getAttribute("x")); + const y = parseFloat(text.getAttribute("y")); + + if (Math.abs(x - titlePos.x) < 1 && Math.abs(y - titlePos.y) < 1) { + const tspans = text.querySelectorAll("tspan"); + + // 한 줄 또는 두 줄로 나누기 + const words = chapter.name.split(" "); + if (tspans.length >= 2 && words.length >= 3) { + // 두 줄로 나누기 + const midPoint = Math.ceil(words.length / 2); + tspans[0].textContent = words.slice(0, midPoint).join(" "); + tspans[1].textContent = words.slice(midPoint).join(" "); + } else if (tspans.length >= 1) { + // 한 줄로 + tspans[0].textContent = chapter.name; + if (tspans[1]) tspans[1].textContent = ""; + } + break; + } + } + }); + } + + _createSVG() { + return SVGHelper.createElement("svg", { + viewBox: CONFIG.SVG.VIEWBOX, + fill: "none", + }); + } + + _setupDefs() { + const defs = SVGHelper.createElement("defs"); + + // 그라데이션 + GRADIENTS.forEach((grad) => { + defs.appendChild(SVGHelper.createGradient(grad)); + }); + + + // 필터 + defs.appendChild(FilterFactory.createInnerShadowFilter()); + defs.appendChild(FilterFactory.createHoverShadowFilter()); + defs.appendChild(FilterFactory.createGaugeFillFilter()); + defs.appendChild(FilterFactory.createBoardFilter()); // 보드 + // 3D 효과 필터 + defs.appendChild(FilterFactory.createPieceEmbossingFilter()); + + FilterFactory.createPlayButtonFilters().forEach((filter) => { + defs.appendChild(filter); + }); + + defs.appendChild(FilterFactory.createTextShadowFilter()); + + PUZZLE_PIECES.forEach((piece) => { + defs.appendChild( + SVGHelper.createPattern( + `bg-image-${piece.id}`, + CONFIG.IMAGE_PATHS.BASE, + false + ) + ); + + defs.appendChild( + SVGHelper.createPattern( + `bg-image-hover-${piece.id}`, + this.contentManager.getThumbnailUrl(piece.id), + true + ) + ); + + defs.appendChild( + SVGHelper.createPattern( + `bg-image-completed-${piece.id}`, + CONFIG.IMAGE_PATHS[CONFIG.COMPLETION_MODE].ACTIVE, + false + ) + ); + + defs.appendChild( + SVGHelper.createPattern( + `bg-image-all-completed-${piece.id}`, + CONFIG.IMAGE_PATHS[CONFIG.COMPLETION_MODE].ALL_COMPLETED, + false + ) + ); + + defs.appendChild( + SVGHelper.createPattern( + `bg-image-finish-${piece.id}`, + CONFIG.IMAGE_PATHS[CONFIG.COMPLETION_MODE].COMPLETED, + false + ) + ); + + // clipPath 생성: completed-image가 base-image 영역 밖으로 나가지 않도록 + const clipPath = SVGHelper.createElement("clipPath", { + id: `clip-piece-${piece.id}`, + }); + const clipPathElement = SVGHelper.createElement("path", { + d: piece.path, + }); + clipPath.appendChild(clipPathElement); + defs.appendChild(clipPath); + + // mask 제거: completed-image도 base-image와 동일한 stroke를 표시하도록 함 + }); + + this.svg.appendChild(defs); + } + + _createBoardBackground() { + BOARD_PATHS.forEach((boardData) => { + const path = SVGHelper.createElement("path", { + d: boardData.d, + fill: boardData.fill, + filter: "url(#board-3d-effect)", // 3D 효과 필터 적용 + }); + this.svg.appendChild(path); + }); + } + + _createPuzzlePieces() { + try { + // 의존성 전달 + const dependencies = { + eventManager: this.eventManager, + errorHandler: this.errorHandler, + domUtils: this.domUtils + }; + + PUZZLE_PIECES.forEach((pieceData) => { + try { + const piece = new PuzzlePiece( + pieceData, + this.contentManager, + this.chapterManager, + dependencies + ); + const element = piece.createElement(); + if (element && this.svg) { + this.pieces.push(piece); + this.svg.appendChild(element); + } else { + console.warn(`[PuzzleManager] 퍼즐 조각 ${pieceData.id} 생성 실패`); + } + } catch (error) { + this._handleError(error, 'PuzzleManager._createPuzzlePieces.piece', { pieceId: pieceData.id }); + } + }); + } catch (error) { + this._handleError(error, 'PuzzleManager._createPuzzlePieces'); + } + } + + _createPlayButtonsAndGauges() { + PUZZLE_PIECES.forEach((piece) => { + UIElementFactory.createPlayButton( + piece.id, + this.svg, + this.contentManager + ); + GaugeManager.createGauge(piece.id, this.svg); + }); + } + + /** + * 챕터 모달 열기 + */ + openChapterModal(chapterIndex, chapter) { + PuzzleModalManager.openChapterModal(chapterIndex, chapter); + } + + /** + * 퍼즐 조각의 게이지 업데이트 (completed 기반) + */ + updatePieceGauge(pieceId) { + const chapterInfo = this.contentManager.getChapterByPieceId(pieceId); + if (!chapterInfo) return; + + const progress = this.chapterManager.getChapterProgress( + chapterInfo.chapterIndex + ); + + // 완료 상태 확인 + const isCompleted = this.chapterManager.isChapterCompleted( + chapterInfo.chapterIndex + ); + + console.log( + `[PuzzleManager] 게이지 업데이트: piece=${pieceId}, progress=${progress}%, completed=${isCompleted}` + ); + + GaugeManager.updateGauge(pieceId, progress, isCompleted); + + // 챕터가 완료되면 퍼즐 조각 완료 표시 + if (isCompleted) { + const piece = this.pieces.find((p) => p.data.id === pieceId); + if (piece && !piece.isCompleted) { + console.log(`[PuzzleManager] 퍼즐 조각 완료: piece=${pieceId}`); + piece.markComplete(); + this._checkAllCompleted(); + } + } + } + + /** + * 전체 진행률 업데이트 + */ + _updateTotalProgress() { + const percentage = this.chapterManager.getTotalProgress(); + + const progressFill = document.getElementById("progressFill"); + const progressPercent = document.getElementById("progressPercent"); + + if (progressFill) progressFill.style.width = percentage + "%"; + if (progressPercent) progressPercent.textContent = Math.round(percentage); + + console.log(`[PuzzleManager] 전체 진행률: ${percentage}%`); + } + + /** + * 모든 퍼즐 완료 확인 + */ + _checkAllCompleted() { + if (this.chapterManager.isAllCompleted()) { + console.log("[PuzzleManager] 모든 챕터 완료!"); + this._handleAllComplete(); + } + } + + _handleAllComplete() { + this._showCompletionAnimation(); + + setTimeout(() => { + this._showRibbonAnimation(); + }, 500); + + setTimeout(() => { + this.boardElement.classList.add("all-completed"); + + // ✅ 경계선 숨기기 + const boundaries = this.svg.querySelector("#puzzleBoundaries"); + if (boundaries) { + boundaries.style.opacity = "0"; + boundaries.style.transition = "opacity 0.5s ease"; + } + }, 3100); + + // 애니메이션 시작 시 문구 (이름님 수고하셨습니다 / 온보딩 필수 콘텐츠 시청을 완료하셨습니다) + this._showCelebrationStart(); + } + + async _showRibbonAnimation() { + const piece2 = this.pieces.find((p) => p.data.id === 2); + if (!piece2) return; + + const piece2Group = piece2.group; + const piece2Bbox = piece2Group.getBBox(); + const boardRect = this.boardElement.getBoundingClientRect(); + const svgRect = this.svg.getBoundingClientRect(); + + const piece2CenterX = piece2Bbox.x + piece2Bbox.width / 2.1; + const piece2BottomY = piece2Bbox.y + piece2Bbox.height / 2.2; + + const ribbonOriginalWidth = 377; + const ribbonOriginalHeight = 336; + const ribbonAspectRatio = ribbonOriginalWidth / ribbonOriginalHeight; + const ribbonWidthInSVG = piece2Bbox.width * 0.9; + const ribbonHeightInSVG = ribbonWidthInSVG / ribbonAspectRatio; + + try { + const response = await fetch("./assets/images/onboarding/img_ribbon.svg"); + const svgText = await response.text(); + + const ribbonContainer = document.createElement("div"); + ribbonContainer.className = "ribbon-animation-container"; + ribbonContainer.style.cssText = ` + position: fixed; + top: ${svgRect.top}px; + left: ${svgRect.left}px; + width: ${svgRect.width}px; + height: ${svgRect.height}px; + pointer-events: none; + z-index: 10000; + overflow: visible; + `; + + const svg = document.createElementNS(CONFIG.SVG.NAMESPACE, "svg"); + svg.setAttribute("viewBox", CONFIG.SVG.VIEWBOX); + svg.setAttribute("width", "100%"); + svg.setAttribute("height", "100%"); + svg.style.cssText = `overflow: visible;`; + + const ribbonGroup = document.createElementNS(CONFIG.SVG.NAMESPACE, "g"); + const ribbonScale = ribbonWidthInSVG / ribbonOriginalWidth; + + ribbonGroup.setAttribute( + "transform", + `translate(${piece2CenterX - ribbonWidthInSVG / 2}, ${piece2BottomY - ribbonHeightInSVG / 2}) scale(${ribbonScale})` + ); + + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(svgText, "image/svg+xml"); + const ribbonSvg = svgDoc.documentElement; + + Array.from(ribbonSvg.children).forEach((child) => { + const importedNode = document.importNode(child, true); + ribbonGroup.appendChild(importedNode); + }); + + svg.appendChild(ribbonGroup); + ribbonContainer.appendChild(svg); + document.body.appendChild(ribbonContainer); + + const circles = ribbonGroup.querySelectorAll("circle"); + circles.forEach((circle) => { + circle.style.opacity = "0"; + circle.style.transition = "opacity 0.8s ease"; + circle.style.mixBlendMode = "overlay"; + setTimeout(() => { + circle.style.opacity = "1"; + }, 100); + }); + + const pathGroups = ribbonGroup.querySelectorAll("g[filter]"); + + pathGroups.forEach((pathGroup, index) => { + const startY = -300 - Math.random() * 200; + const delay = index * 100 + Math.random() * 100; + const duration = 800 + Math.random() * 400; + + pathGroup.style.opacity = "0"; + pathGroup.style.transform = `translateY(${startY}px)`; + pathGroup.style.transition = `all ${duration}ms cubic-bezier(0.34, 1.56, 0.64, 1)`; + pathGroup.style.transitionDelay = `${delay}ms`; + + setTimeout(() => { + pathGroup.style.opacity = "1"; + pathGroup.style.transform = `translateY(0)`; + }, 50); + }); + + setTimeout(() => { + ribbonContainer.style.transition = "opacity 0.5s ease"; + ribbonContainer.style.opacity = "0"; + }, 2500); + + setTimeout(() => { + ribbonContainer.remove(); + }, 3000); + } catch (error) { + console.error("리본 SVG 로드 실패:", error); + } + } + + _showCompletionAnimation() { + // 애니메이션 시작 시 h3 문구 먼저 변경 (한 프레임 뒤 오버레이 표시해 변경이 보이도록) + this._showCelebrationStart(); + requestAnimationFrame(() => { + this._runCompletionAnimationOverlay(); + }); + } + + _runCompletionAnimationOverlay() { + const boardRect = this.boardElement.getBoundingClientRect(); + + const animationContainer = document.createElement("div"); + animationContainer.className = "completion-animation-container"; + animationContainer.style.cssText = ` + position: fixed; + top: ${boardRect.top}px; + left: ${boardRect.left}px; + width: ${boardRect.width}px; + height: ${boardRect.height}px; + pointer-events: none; + z-index: 9999; + opacity: 0; + animation: completionFadeIn 0.5s ease forwards, completionPulse 2.5s ease-in-out 0.5s infinite; + `; + + // CSS 애니메이션 스타일 추가 + if (!document.getElementById('completion-animation-styles')) { + const style = document.createElement('style'); + style.id = 'completion-animation-styles'; + style.textContent = ` + @keyframes completionFadeIn { + 0% { + opacity: 0; + transform: scale(0.95); + } + 100% { + opacity: 1; + transform: scale(1); + } + } + @keyframes completionPulse { + 0%, 100% { + transform: scale(1); + filter: drop-shadow(0 0 20px rgba(255, 235, 59, 0.6)); + } + 50% { + transform: scale(1.02); + filter: drop-shadow(0 0 35px rgba(255, 235, 59, 0.9)) drop-shadow(0 0 50px rgba(255, 193, 7, 0.5)); + } + } + `; + document.head.appendChild(style); + } + + const svg = SVGHelper.createElement("svg", { + viewBox: CONFIG.SVG.VIEWBOX, + style: ` + width: 100%; + height: 100%; + `, + }); + + const defs = SVGHelper.createElement("defs"); + const completedPattern = SVGHelper.createPattern( + "completion-animation-pattern", + CONFIG.IMAGE_PATHS[CONFIG.COMPLETION_MODE].ALL_COMPLETED, + false + ); + defs.appendChild(completedPattern); + svg.appendChild(defs); + + const boardGroup = SVGHelper.createElement("g"); + boardGroup.style.cssText = ` + animation: completionGlow 2.5s ease-in-out infinite; + `; + + // 글로우 애니메이션 스타일 추가 + if (!document.getElementById('completion-glow-styles')) { + const glowStyle = document.createElement('style'); + glowStyle.id = 'completion-glow-styles'; + glowStyle.textContent = ` + @keyframes completionGlow { + 0%, 100% { + filter: drop-shadow(0 0 15px rgba(255, 235, 59, 0.4)); + } + 50% { + filter: drop-shadow(0 0 30px rgba(255, 235, 59, 0.8)) drop-shadow(0 0 45px rgba(255, 193, 7, 0.4)); + } + } + `; + document.head.appendChild(glowStyle); + } + + PUZZLE_PIECES.forEach((piece) => { + const path = SVGHelper.createElement("path", { + d: piece.path, + fill: "url(#completion-animation-pattern)", + stroke: "#333", + "stroke-width": "0", // ✅ stroke: 0 (하나의 이미지처럼) + // ✅ 엠보싱 없음 (필터 적용 안 함) + }); + boardGroup.appendChild(path); + }); + + svg.appendChild(boardGroup); + animationContainer.appendChild(svg); + document.body.appendChild(animationContainer); + + // 페이드 아웃 애니메이션 + setTimeout(() => { + animationContainer.style.animation = 'completionFadeOut 0.5s ease forwards'; + + // 페이드 아웃 애니메이션 스타일 추가 + if (!document.getElementById('completion-fadeout-styles')) { + const fadeOutStyle = document.createElement('style'); + fadeOutStyle.id = 'completion-fadeout-styles'; + fadeOutStyle.textContent = ` + @keyframes completionFadeOut { + 0% { + opacity: 1; + transform: scale(1); + } + 100% { + opacity: 0; + transform: scale(0.98); + } + } + `; + document.head.appendChild(fadeOutStyle); + } + }, 3000); + + setTimeout(() => { + animationContainer.remove(); + + // ✅ 애니메이션 후 Finish Image 표시 + console.log('[PuzzleManager] 애니메이션 종료 - showFinish 호출 시작'); + this.pieces.forEach((piece) => { + piece.showFinish(); + }); + console.log('[PuzzleManager] 애니메이션 종료 - showFinish 호출 완료'); + + // ✅ 애니메이션 종료 시 문구 변경 + this._showCelebrationEnd(); + }, 3500); + } + + /** 애니메이션 시작 시 표시 (h3: 온보딩 필수 콘텐츠 시청을 완료하셨습니다) */ + _showCelebrationStart() { + const pageTitle = document.querySelector(".page-title"); + if (!pageTitle) return; + + const p = pageTitle.querySelector("p"); + const emText = pageTitle.querySelector("h3 em")?.textContent; + + pageTitle.querySelector("h3").innerHTML = ` + ${emText}님, 수고하셨습니다. + `; + if (p) { + p.innerHTML = ` + 온보딩 필수 콘텐츠 시청을 완료하셨습니다. + `; + } + } + + /** 애니메이션 종료 시 표시 (p: 필요할 땐 언제든 다시 볼 수 있어요) */ + _showCelebrationEnd() { + const pageTitle = document.querySelector(".page-title"); + if (!pageTitle) return; + const p = pageTitle.querySelector("p"); + pageTitle.querySelector("h3").innerHTML = ` + 온보딩 필수 콘텐츠 시청을 완료하셨습니다. + `; + if (p) { + p.classList.add("fw-b"); + p.innerHTML = ` + 필요할 땐 언제든 다시 볼 수 있어요. + `; + } + } + + _initializeCompletedPieces() { + console.log('[PuzzleManager] 완료 상태 초기화 시작'); + + // 각 챕터의 완료 상태 확인 + this.chapterManager.chapters.forEach((chapter, chapterIndex) => { + if (!chapter.pieceId) { + console.warn(`[PuzzleManager] 챕터 ${chapterIndex}에 pieceId가 없습니다`); + return; + } + + // 게이지 초기화 + const progress = this.chapterManager.getChapterProgress(chapterIndex); + const isCompleted = this.chapterManager.isChapterCompleted(chapterIndex); + console.log(`[PuzzleManager] 챕터 ${chapterIndex} (piece ${chapter.pieceId}): ${progress}% 진행, completed=${isCompleted}`); + GaugeManager.updateGauge(chapter.pieceId, progress, isCompleted); + + // 완료된 챕터의 퍼즐 조각 완료 표시 + if (this.chapterManager.isChapterCompleted(chapterIndex)) { + const piece = this.pieces.find((p) => p.data.id === chapter.pieceId); + if (piece) { + console.log(`[PuzzleManager] 퍼즐 조각 ${chapter.pieceId} 완료 표시`); + piece.markComplete(); + } + } + }); + + // 모든 챕터가 완료되었으면 완료 상태 표시 + if (this.chapterManager.isAllCompleted()) { + console.log('[PuzzleManager] 모든 챕터 완료!'); + this.boardElement.classList.add("all-completed"); + this.pieces.forEach((piece) => { + piece.showFinish(); // ✅ showAllCompleted → showFinish 변경 + }); + } + + // 전체 진행률 업데이트 + this._updateTotalProgress(); + console.log('[PuzzleManager] 완료 상태 초기화 종료'); + } +} + +// ============================================================================ +// 전역 함수 +// ============================================================================ +function updatePieceGaugeByCompletion(pieceId) { + if (PuzzleManager.instance) { + PuzzleManager.instance.updatePieceGauge(pieceId); + } +} + +// ============================================================================ +// 초기화 +// ============================================================================ +function initializeOverlay() { + try { + const domUtils = typeof DOMUtils !== 'undefined' ? DOMUtils : null; + const eventManager = typeof eventManager !== 'undefined' ? eventManager : null; + const errorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; + + const overlay = domUtils?.$("#overlay") || document.getElementById("overlay"); + const celebration = domUtils?.$("#celebration") || document.getElementById("celebration"); + + if (overlay && celebration) { + const clickHandler = function () { + try { + this.classList.remove("show"); + celebration.classList.remove("show"); + } catch (error) { + if (errorHandler) { + errorHandler.handle(error, { context: 'initializeOverlay.clickHandler' }, false); + } else { + console.error('[Overlay] 클릭 핸들러 에러:', error); + } + } + }; + + if (eventManager) { + eventManager.on(overlay, "click", clickHandler); + } else { + overlay.addEventListener("click", clickHandler); + } + } + } catch (error) { + const errorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; + if (errorHandler) { + errorHandler.handle(error, { context: 'initializeOverlay' }, false); + } else { + console.error('[Overlay] 초기화 에러:', error); + } + } +} + +// 초기화 함수 (에러 처리 포함) +function initializePuzzleOnboarding() { + try { + const chapterData = window.puzzleChapterData || null; + + // 의존성 주입 + const dependencies = { + domUtils: typeof DOMUtils !== 'undefined' ? DOMUtils : null, + eventManager: typeof eventManager !== 'undefined' ? eventManager : null, + errorHandler: typeof ErrorHandler !== 'undefined' ? ErrorHandler : null, + utils: typeof Utils !== 'undefined' ? Utils : null, + animationUtils: typeof AnimationUtils !== 'undefined' ? AnimationUtils : null, + }; + + const puzzleManager = new PuzzleManager("puzzleBoard", chapterData, dependencies); + + if (puzzleManager && puzzleManager.initialize) { + puzzleManager.initialize(); + } else { + console.error('[PuzzleOnboarding] PuzzleManager 초기화 실패'); + } + + initializeOverlay(); + } catch (error) { + const errorHandler = typeof ErrorHandler !== 'undefined' ? ErrorHandler : null; + if (errorHandler) { + errorHandler.handle(error, { + context: 'initializePuzzleOnboarding' + }, true); // 사용자에게 표시 + } else { + console.error('[PuzzleOnboarding] 초기화 에러:', error); + alert('퍼즐 온보딩 초기화 중 오류가 발생했습니다.'); + } + } +} + +// DOMContentLoaded 이벤트 처리 +if (document.readyState === 'loading') { + document.addEventListener("DOMContentLoaded", initializePuzzleOnboarding); +} else { + // 이미 로드된 경우 즉시 실행 + initializePuzzleOnboarding(); +} + +// ============================================================================ +// Export +// ============================================================================ +if (typeof module !== "undefined" && module.exports) { + module.exports = { + PuzzleManager, + ChapterManager, + ContentManager, + GaugeManager, + updatePieceGaugeByCompletion, + }; +} \ No newline at end of file diff --git a/src/skin/_include/_head.php b/src/skin/_include/_head.php new file mode 100644 index 0000000..d01cae6 --- /dev/null +++ b/src/skin/_include/_head.php @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +배움터 diff --git a/src/skin/_include/_header.php b/src/skin/_include/_header.php new file mode 100644 index 0000000..99e76bf --- /dev/null +++ b/src/skin/_include/_header.php @@ -0,0 +1,65 @@ + + + + diff --git a/src/skin/_include/_nav.php b/src/skin/_include/_nav.php new file mode 100644 index 0000000..ce3e3e7 --- /dev/null +++ b/src/skin/_include/_nav.php @@ -0,0 +1,91 @@ + diff --git a/src/skin/_modal/keyword.php b/src/skin/_modal/keyword.php new file mode 100644 index 0000000..fad7003 --- /dev/null +++ b/src/skin/_modal/keyword.php @@ -0,0 +1,106 @@ + + + diff --git a/src/skin/_modal/learning.php b/src/skin/_modal/learning.php new file mode 100644 index 0000000..8702c14 --- /dev/null +++ b/src/skin/_modal/learning.php @@ -0,0 +1,50 @@ + + diff --git a/src/skin/_modal/video-comment.php b/src/skin/_modal/video-comment.php new file mode 100644 index 0000000..827ca70 --- /dev/null +++ b/src/skin/_modal/video-comment.php @@ -0,0 +1,347 @@ + + diff --git a/src/skin/_modal/video-learning.php b/src/skin/_modal/video-learning.php new file mode 100644 index 0000000..7f5eed9 --- /dev/null +++ b/src/skin/_modal/video-learning.php @@ -0,0 +1,171 @@ + + diff --git a/src/skin/_modal/video-onboarding.php b/src/skin/_modal/video-onboarding.php new file mode 100644 index 0000000..7d65722 --- /dev/null +++ b/src/skin/_modal/video-onboarding.php @@ -0,0 +1,155 @@ + + diff --git a/src/skin/_modal/video.php b/src/skin/_modal/video.php new file mode 100644 index 0000000..48026d8 --- /dev/null +++ b/src/skin/_modal/video.php @@ -0,0 +1,219 @@ + + diff --git a/src/skin/biztrend.php b/src/skin/biztrend.php new file mode 100644 index 0000000..93c91e9 --- /dev/null +++ b/src/skin/biztrend.php @@ -0,0 +1,471 @@ + + + + + + + + + +
+ + + + + +
+ + + + + + + +
+
+ + +
+
+
+ +
+

+ KBS 제1라디오 '성공예감&별책부록'을 통해
+ 경제 정보를 쉽게 접하고, 생활 속 경제 흐름을 알아보세요. +

+
+ + +
+ + + + + +
+
+ + + + + +
+
+
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
+
+
+ + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+ +
+
+ +
+
+
+
+
+
+ +
+
+ + + + diff --git a/src/skin/guide.php b/src/skin/guide.php new file mode 100644 index 0000000..b53282b --- /dev/null +++ b/src/skin/guide.php @@ -0,0 +1,119 @@ +
+ + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/src/skin/index.php b/src/skin/index.php index 6b8fe79..8d8eb5e 100644 --- a/src/skin/index.php +++ b/src/skin/index.php @@ -1,11 +1,413 @@ - - - - - 한맥가족사 배움터 - - -

🎉 메인 스킨 화면에 성공적으로 들어왔습니다!

-

여기가 진짜 사용자가 보게 될 첫 화면(skin)입니다.

- - \ No newline at end of file + + + + + + + +
+ + +
+
+
    +
  • +
  • +
  • +
  • +
+
+
+ +
+
+
+ 홍길동 선임연구원님 +
+ +
+ + + +
+
+ + +
+
+

취향저격 영상 추천드려요.

+
+ +
+ +
+ + + + + +
+ + + + +
+
+ + +
+ + + + + + + + diff --git a/src/skin/index_guide.php b/src/skin/index_guide.php new file mode 100644 index 0000000..255a6e7 --- /dev/null +++ b/src/skin/index_guide.php @@ -0,0 +1,439 @@ + + + + + + + +
+ + +
+
+
    +
  • +
  • +
  • +
  • +
+
+
+ +
+
+
+ 홍길동 선임연구원님 +
+ +
+ + + +
+
+ + +
+
+

취향저격 영상 추천드려요.

+
+ +
+ +
+ + + + + +
+ + + + + + +
+
+ + + +
+ + + + + + + + diff --git a/src/skin/index_over.php b/src/skin/index_over.php new file mode 100644 index 0000000..a6c4bb9 --- /dev/null +++ b/src/skin/index_over.php @@ -0,0 +1,413 @@ + + + + + + + +
+ + +
+
+
    +
  • +
  • +
  • +
  • +
+
+
+ +
+
+
+ 홍길동 선임연구원님 +
+ +
+ + + +
+
+ + +
+
+

취향저격 영상 추천드려요.

+
+ +
+ +
+ + + + + +
+ + + + +
+
+ + +
+ + + + + + + + diff --git a/src/skin/insight.php b/src/skin/insight.php new file mode 100644 index 0000000..c69008a --- /dev/null +++ b/src/skin/insight.php @@ -0,0 +1,286 @@ + + + + + + + + + +
+ + +
+ + + + +
+
+
+
+
+
+ Editor's pick! of the Month +
+
+
+

+ 회사에서 추천하는 영상에 대해
+ 왜 봐야하는지 문구를 넣습니다.
+

+

보조 설명이 필요한 경우, 생략 가능

+
+ +
+
+
+
+
+
+
+ Top Viewed Pick! of the Month +
+
+
+

+ 공식 선정!
+ 한맥인들이 이번 달 가장 많이 본 콘텐츠

+ 1월 14일 공개! +

+
+ +
+
+ +
+
+
+ D-4 +
+
+
+ HOT Issue of the Month +
+
+
+

+ TPU-GPU 경쟁이 가속되면서
+ 디지털트윈과 실시간 모니터링,
+ BIM 협업 기술도 빠르게 진화하고 있습니다. +

+

지금, 우리의 준비 상태를 점검해볼 때입니다.

+
+ +
+
+ +
+
+
+ +
+
+
+ Top Learner's Choice of the Month +
+
+
+
+
+
+ +
+ +
+

+ 홍길동 선임 총괄기획실 +

+
+ + 우수학습자 +
+
+
+

+ 우수학습자가 추천하는 영상,
+ 왜 이 컨텐츠를추천하는지 적습니다.
+

+

+ 예) 지금, 우리의 준비 상태를 점검해볼
때입니다. 보조글/ 생략가능 +

+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ + + diff --git a/src/skin/intro.php b/src/skin/intro.php new file mode 100644 index 0000000..23d8d6d --- /dev/null +++ b/src/skin/intro.php @@ -0,0 +1,180 @@ + + + + + + + +
+ +
+ +
+ +
+ +
+

+ | +
안녕하세요! +

+

+
+ + + +
+ + +
+
+
+
01
+
+ 내 학습차곡차곡
+ 모아보세요 +
+
+ "콘텐츠를 저장"해두고
+ 언제든 다시
+ 참고할 수 있어요. +
+
+
+
02
+
+ 중요한 공지사항
+ 놓치지 않아요 +
+
+ 상단 "알림기능"을 통해
+ 새 소식을 빠르게
+ 확인할 수 있어요 +
+
+
+
03
+
+ 좋은 학습 콘텐츠를
+ 공유할 수 있어요 +
+
+ 팀원들과 함께
+ 학습할 수 있도록
+ "콘텐츠를 제안"해보세요 +
+
+
+
04
+
+ 나의 학습 현황을
+ 확인할 수 있어요 +
+
+ 마이페이지에서
+ 학습현황을
+ "한 눈에 확인"해보세요 +
+
+
+
+
+ + + + + +
+ Scroll + +
+
+
+ + + + + + + + + + + diff --git a/src/skin/learning.php b/src/skin/learning.php new file mode 100644 index 0000000..800b90d --- /dev/null +++ b/src/skin/learning.php @@ -0,0 +1,50 @@ + + + + + + +
+ + +
+
+
+

홍길동 선임연구원님

+

+ D-66(3월 20일) 까지 필수 콘텐츠 시청을 + 완료해주세요. +

+
+ +
+
+ +
+ start +
+ + + +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + diff --git a/src/skin/learning_2.php b/src/skin/learning_2.php new file mode 100644 index 0000000..5a21ac9 --- /dev/null +++ b/src/skin/learning_2.php @@ -0,0 +1,85 @@ + + + + + + +
    + + +
    +
    +
    +

    홍길동 선임연구원님

    +

    + D-66(3월 20일) 까지 필수 콘텐츠 시청을 + 완료해주세요. +

    +
    + +
    +
    + +
    + start +
    + + + +
    + + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skin/learning_svg.php b/src/skin/learning_svg.php new file mode 100644 index 0000000..b54b424 --- /dev/null +++ b/src/skin/learning_svg.php @@ -0,0 +1,523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/skin/modal.php b/src/skin/modal.php new file mode 100644 index 0000000..ab50c5f --- /dev/null +++ b/src/skin/modal.php @@ -0,0 +1,165 @@ + + + + + + + +
      + + + + + + diff --git a/src/skin/mypage.php b/src/skin/mypage.php new file mode 100644 index 0000000..083625d --- /dev/null +++ b/src/skin/mypage.php @@ -0,0 +1,691 @@ + + + + + + + + +
      + +
      +
      + + + + +
      +
      +

      + + 나의 학습 활동 (2026년 기준) +

      +
      +
      +
      +

      + 총 학습시간 340분 +

      +
      +
      +
      +
      +
      + 전체평균 +
      + + +
        +
      • +
        + 마이클래스 + 74분 (33%) +
        +
        +
        +
        +
        +
        + 0 + · + 6개 +
        +
        +
      • +
      • +
        + 온보딩 + 110분 (80%) +
        +
        +
        +
        +

        ① 필수 시청 : 26.01.14

        +
        +
        + 0 + · + 10개 +
        +
        + +
      • +
      • +
        + 법정교육 + 30분 (20%) +
        +
        +
        +
        +
        +
        + 0 + · + 6개 +
        +
        +
      • +
      • +
        + 리더십 + 60 +
        +
        +
        + 소통 + 협업 + 웰니스 +
        +
        + 0 + · + 60분 +
        +
        +
      • +
      • +
        + 인사이트 + 43 +
        +
        +
        + 인물 + 마인드셋 + 기술과 미래 +
        +
        + 0 + · + 43분 +
        +
        + +
      • +
      • +
        + 비즈트렌드 + 23 +
        +
        +
        + 금리 + 증시 +
        +
        + 0 + · + 23분 +
        +
        + +
      • +
      +
      + +
      + + +
      +

      + + 시청중인 콘텐츠 15 +

      + +
      + + +
      +

      + + 저장한 콘텐츠 3 +

      + +
      + + +
      +

      + + 한줄 소감 6 +

      +
        +
      • +
        + 인사이트 > 기술과 미래 +

        무슨 말을 해야 할지 모르겠어요, 스몰토크 잘하는 법 무슨 말을 해야 할지 모르겠어요, 스몰토크 잘하는 법

        +
        +
        +
        + 홍길동 프로필 +
        + 홍길동 사소한 말이 관계를 살린다는 걸 느꼈습니다. +
        +
      • +
      • +
        + 리더십 > 관계리더십 +

        회계를 조금이라도 이해하면 인생이 달라지는 이..

        +
        +
        +
        + 홍길동 프로필 +
        + 홍길동 숫자가 말하는 힘을 알게 되니, 결정도 훨씬 똑똑해졌어요. +
        +
      • +
      • +
        + 리더십 > 관계리더십 +

        프로그래머는 코딩하는 사람이 아닙니다.

        +
        +
        +
        + 홍길동 프로필 +
        + 홍길동 문제를 정의하고 구조화하는 사고가 진짜 실력이더라고요. +
        +
      • +
      • +
        + 인사이트 > 경제와 사회 +

        숨겨진 가능성을 무한히 발견하는 법

        +
        +
        +
        + 홍길동 프로필 +
        + 홍길동 시야를 넓히는 법보다, 시야를 바꾸는 법을 배운 느낌이에요. +
        +
      • +
      • +
        + 인사이트 > 경제와 사회 +

        변화의 시대에 변화하지 않는게 리스크더라구..

        +
        +
        +
        + 홍길동 프로필 +
        + 홍길동 실무에 바로 적용할 수 있는 인사이트를 얻었어요. +
        +
      • +
      • +
        + 인사이트 > 경제와 사회 +

        프로와 아마추어를 가르는 차이점!

        +
        +
        +
        + 홍길동 프로필 +
        + 홍길동 같은 일을 해도, 접근 방식이 다르면 결과가 완전히 달라지네요. +
        +
      • +
      +
      +
      +
      +
      +
      + +
      + + + + diff --git a/src/skin/onboarding.php b/src/skin/onboarding.php new file mode 100644 index 0000000..6f621a1 --- /dev/null +++ b/src/skin/onboarding.php @@ -0,0 +1,258 @@ + + + + + + + +
      + + +
      +
      +
      +

      + 홍길동 선임연구원님, 입사를 환영합니다. +

      +

      + D-14(1월 14일)까지 필수 콘텐츠 시청을 + 완료해주세요. +

      +
      +
      +
      + + +

      + +

      +

      가족사 소개

      + +

      + +

      +

      한맥가족의 DX

      + +

      + +

      +

      가치공유

      + +

      + +

      +

      회사생활

      +
      +
      +
      +
      + +
      + + + + + + + \ No newline at end of file diff --git a/src/skin/onboarding_completed.php b/src/skin/onboarding_completed.php new file mode 100644 index 0000000..f3302e2 --- /dev/null +++ b/src/skin/onboarding_completed.php @@ -0,0 +1,264 @@ + + + + + + + +
      + + +
      +
      +
      +

      + 홍길동 선임연구원님, 입사를 환영합니다. +

      +

      + D-14(1월 14일)까지 필수 콘텐츠 시청을 + 완료해주세요. +

      +
      +
      +
      + +

      + +

      +

      가족사 소개

      + +

      + +

      +

      한맥가족의 DX

      + +

      + +

      +

      가치공유

      + +

      + +

      +

      회사생활

      +
      +
      +
      +
      + +
      + + + + + diff --git a/src/skin/onboarding_finish.php b/src/skin/onboarding_finish.php new file mode 100644 index 0000000..b7fb708 --- /dev/null +++ b/src/skin/onboarding_finish.php @@ -0,0 +1,266 @@ + + + + + + + +
      + + +
      +
      +
      +

      + 온보딩 필수 콘텐츠 시청을 완료하셨습니다. +

      +

      + 필요할 땐 언제든 다시 볼 수 있어요. +

      +
      +
      +
      + + +

      + +

      +

      가족사 소개

      + +

      + +

      +

      한맥가족의 DX

      + +

      + +

      +

      가치공유

      + +

      + +

      +

      회사생활

      +
      +
      +
      +
      + +
      + + + + + + diff --git a/src/skin/search_result.php b/src/skin/search_result.php new file mode 100644 index 0000000..b6cbd6d --- /dev/null +++ b/src/skin/search_result.php @@ -0,0 +1,322 @@ + + + + + + + +
      + + +
      +
      + + + + +
      + + + + +
      + + +
      + +
      +
      +

      리더십 21

      +
      +
      + +
      +
      +
      + +
      + + +
      +
      +

      인사이트 4

      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      + +
      + + + + \ No newline at end of file