Grammar Joy Class

강의 미리보기

미리보기 이미지가 곧 추가될 예정입니다

강의 들어가기
d='egb_inline_script_egb_csr' rel='preload' as='script' fetchpriority='high' nonce='5bhuWS0/a3YQYKJ67FwscQ=='> //전역 변수 설정 let egbOriginDomain = window.location.origin; //현재 도메인 let egbUserAgent = navigator.userAgent; // 사용자 에이전트 정보 (브라우저 및 운영체제 정보) let egbLanguage = navigator.language || navigator.userLanguage; //브라우저 설정 언어 let egbIsMobile = /Mobi/i.test(egbUserAgent); // 모바일 기기 여부를 변수에 저장 모바일인 경우 true //브라우져 창 사이즈 let windowWidth = document.documentElement.clientWidth; let windowHeight = document.documentElement.clientHeight; //패턴에 대한 정의 let xyRegex = /\b(?:xx-|yy-)[\w\d-_]+\b/g; let tulrRegex = /\b(?:top-|bottom-|left-|right-)[\w\d-_]+\b/g; let positionRegex = /position1|position2|position3|position4/g; let position_t_Regex = /\b(?:top-)[\w\d-_]+\b/g; let position_u_Regex = /\b(?:bottom-)[\w\d-_]+\b/g; let position_l_Regex = /\b(?:left-)[\w\d-_]+\b/g; let position_r_Regex = /\b(?:right-)[\w\d-_]+\b/g; let egbRegexPattern = /(font_px_|r_font_|font_vw_|font_rem_|line_height_|linem_height_|letter_spacing_|letterm_spacing_|word_spacing_|wordm_spacing_|transform_|transform_[a-z]+_|transform_3d_|transform_3dm_|padding_px-[a-z]+_|padding_vw-[a-z]+_|margin_px-[a-z]+_|margin_vw-[a-z]+_|gap_px_|min_width_|max_width_|min_width_px_|min_width_vw_|min_width_vh_|min_width_rem_|min_width_per_|max_width_px_|max_width_vw_|max_width_vh_|max_width_rem_|max_width_per_|width_px_|width_per_|width_vw_|min_height_|height_px_|height_per_|height_vw_|height_vh_|max_height_|min_height_px_|min_height_vw_|min_height_vh_|min_height_rem_|min_height_per_|max_height_px_|max_height_vw_|max_height_vh_|max_height_rem_|max_height_per_|r_width_|r_height_|position1-[a-z]+_|position2-[a-z]+_|position3-[a-z]+_|border_bre-[a-z]+_|border_be-[a-z]+_|bd-a-color-[a-zA-Z0-9~]|bd-x-color-[a-zA-Z0-9~]|bd-y-color-[a-zA-Z0-9~]|bd-t-color-[a-zA-Z0-9~]|bd-u-color-[a-zA-Z0-9~]|bd-l-color-[a-zA-Z0-9~]|bd-r-color-[a-zA-Z0-9~]|color-[a-zA-Z0-9~]|bg-color-[a-zA-Z0-9~]|z-index_|zm-index_)(\d+)/g; let srefRegex = /(sref_)([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+))?)?)?/; let clefRegex = /(clef_)([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+))?)?)?/; let hoefRegex = /(hoef_)([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+)(?:_([a-zA-Z0-9-]+))?)?)?/; let widthRegex = /width_px_\d+|width_per_\d+|r_width_\d+|width_vw_\d+|window-width-\d+|width_box|window_width|width_auto|width_none|width_off/g; let maxWidthRegex = /max_width_\d+/g; let minWidthRegex = /min_width_\d+/g; let gapRegex = /gap_\d+/g; let heightRegex = /height_px_\d+|height_per_\d+|r_height_\d+|height_vw_\d+|height_vh_\d+|window-height-\d+|height_box|window_height|height_auto|height_none|height_off/g; let maxHeightRegex = /max_height_\d+/g; let minHeightRegex = /min_height_\d+/g; let fontRegex = /font_px_\d+|r_font_\d+|font_vw_\d+|font_rem_\d+/g; let padding_a_Regex = /padding_px-a_\d+|padding_vw-a_\d+|padding_px-x_\d+|padding_vw-x_\d+|padding_px-y_\d+|padding_vw-y_\d+|padding_px-l_\d+|padding_vw-l_\d+|padding_px-r_\d+|padding_vw-r_\d+|padding_px-t_\d+|padding_vw-t_\d+|padding_px-u_\d+|padding_vw-u_\d+|padding_px-b_\d+|padding_vw-b_\d+/g; let padding_x_Regex = /padding_px-x_\d+|padding_vw-x_\d+|padding_px-l_\d+|padding_vw-l_\d+|padding_px-r_\d+|padding_vw-r_\d+/g; let padding_y_Regex = /padding_px-y_\d+|padding_vw-y_\d+|padding_px-t_\d+|padding_vw-t_\d+|padding_px-u_\d+|padding_vw-u_\d+|padding_px-b_\d+|padding_vw-b_\d+/g; let padding_l_Regex = /padding_px-l_\d+|padding_vw-l_\d+/g; let padding_r_Regex = /padding_px-r_\d+|padding_vw-r_\d+/g; let padding_t_Regex = /padding_px-t_\d+|padding_vw-t_\d+/g; let padding_u_Regex = /padding_px-u_\d+|padding_vw-u_\d+|padding_px-b_\d+|padding_vw-b_\d+/g; let margin_a_Regex = /margin_px-a_\d+|margin_a_auto|margin_px-a_auto|margin_vw-a_\d+|margin_px-x_\d+|margin_x_auto|margin_px-x_auto|margin_vw-x_\d+|margin_px-y_\d+|margin_y_auto|margin_px-y_auto|margin_vw-y_\d+|margin_px-l_\d+|margin_l_auto|margin_px-l_auto|margin_vw-l_\d+|margin_px-r_\d+|margin_r_auto|margin_px-r_auto|margin_vw-r_\d+|margin_px-t_\d+|margin_t_auto|margin_px-t_auto|margin_vw-t_\d+|margin_px-u_\d+|margin_u_auto|margin_px-u_auto|margin_vw-u_\d+|margin_px-b_\d+|margin_b_auto|margin_px-b_auto|margin_vw-b_\d+/g; let margin_x_Regex = /margin_px-x_\d+|margin_x_auto|margin_px-x_auto|margin_vw-x_\d+|margin_px-l_\d+|margin_l_auto|margin_px-l_auto|margin_vw-l_\d+|margin_px-r_\d+|margin_r_auto|margin_px-r_auto|margin_vw-r_\d+/g; let margin_y_Regex = /margin_px-y_\d+|margin_y_auto|margin_px-y_auto|margin_vw-y_\d+|margin_px-t_\d+|margin_t_auto|margin_px-t_auto|margin_vw-t_\d+|margin_px-u_\d+|margin_u_auto|margin_px-u_auto|margin_vw-u_\d+|margin_px-b_\d+|margin_b_auto|margin_px-b_auto|margin_vw-b_\d+/g; let margin_l_Regex = /margin_px-l_\d+|margin_l_auto|margin_px-l_auto|margin_vw-l_\d+/g; let margin_r_Regex = /margin_px-r_\d+|margin_r_auto|margin_px-r_auto|margin_vw-r_\d+/g; let margin_t_Regex = /margin_px-t_\d+|margin_t_auto|margin_px-t_auto|margin_vw-t_\d+/g; let margin_u_Regex = /margin_px-u_\d+|margin_u_auto|margin_px-u_auto|margin_vw-u_\d+|margin_px-b_\d+|margin_b_auto|margin_px-b_auto|margin_vw-b_\d+/g; let displayRegex = /display_block|display_none|display_flex|display_grid|display_contents|display_flow|display_flow_root|display_inline|display_inline_block|display_inline_grid|display_inline_flex|display_inline_table|display_list_item|display_table|display_table_caption|display_table_cell|display_table_column|display_table_column_group|display_table_header_group|display_table_footer_group|display_table_row|display_table_row_group|grid_[\w\d-_]+|flex_[\w\d-_]+/g; let transitionRegex = /transition|transition_[a-z_]+_\d+_[a-z_]+(_\d+)?/g; let colorRegex = /color-[a-zA-Z0-9_]+/g; let bgColorRegex = /bg-color-[a-zA-Z0-9_]+/g; let eventsRegex = /^(click|mouseover|mouseout|mousedown|mouseup|dblclick|mousemove|keydown|keyup|keypress|change|focus|blur|input|submit|load|resize|scroll|touchstart|touchend|drag|drop|contextmenu|wheel|select|cut|copy|paste|dragenter|dragleave|dragover|animationstart|animationend|animationiteration|transitionend|pointerdown|pointerup|pointermove|pointerover|pointerout|pointercancel|visibilitychange|focusin|focusout)$/; ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 스타일 중복 체크를 위한 캐시 const generatedStylesCache = new Set(); ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 검사시 무시할 노드 이름 목록 정의 const IGNORE_NODE_NAMES = [ 'SCRIPT', 'LINK', 'META', 'STYLE', 'SVG', 'PATH', 'CIRCLE', 'ELLIPSE', 'RECT', 'LINE', 'POLYLINE', 'POLYGON', 'G', 'MARKER', 'DEFS', 'CLIPPATH', 'MASK', 'USE', 'SYMBOL', 'TEXT', 'TSPAN' ]; // 훅 저장소 초기화 (EGB.form 모듈과 동기화) let dataHooks = new Map(); // 디바운스 타이머 저장용 Map let debounceTimers = new Map(); //함수목록 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 타겟 아이디 요소의 클래스를 토글 하는 함수 function egbToggle(targetId, ...classNames) { // 요소 선택 const targetElement = document.getElementById(targetId); // 요소가 존재하는지 확인 if (targetElement) { // 각 클래스에 대해 토글 수행 classNames.forEach(className => { targetElement.classList.toggle(className); }); } else { //console.log(`아이디가 '${targetId}'인 요소를 찾을 수 없습니다.`); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// function egbDragBox() { } //전체화면에 대한 function function egbFullScreen() { if (!document.fullscreenElement) { // 전체 화면이 아닐 때, 전체 화면으로 전환하고 egbFullScreenTrue() 실행 document.documentElement.requestFullscreen().then(() => { if (typeof egbFullScreenTrue === "function") { egbFullScreenTrue(); // 전체 화면으로 전환된 후 실행 해당 함수는 프로젝트마다 별도 생성 } }).catch((error) => { console.error("Failed to enter fullscreen:", error); }); } else { // 이미 전체 화면일 때, 전체 화면 해제하고 egbFullScreenFalse() 실행 document.exitFullscreen().then(() => { if (typeof egbFullScreenFalse === "function") { egbFullScreenFalse(); // 전체 화면 해제된 후 실행 해당 함수는 프로젝트마다 별도 생성 } }).catch((error) => { console.error("Failed to exit fullscreen:", error); }); } } function egbClassHover(id, className) { const element = document.getElementById(id); // ID로 요소 선택 if (!element) { console.warn(`ID가 '${id}'인 요소를 찾을 수 없습니다.`); return; } element.addEventListener('mouseover', function() { element.classList.add(className); // 호버 시 클래스 추가 }); element.addEventListener('mouseout', function() { element.classList.remove(className); // 호버 해제 시 클래스 제거 }); } // 타겟 아이디 요소의 클래스를 토글 하는 함수 function egbToggle(targetId, ...classNames) { // 요소 선택 const targetElement = document.getElementById(targetId); // 요소가 존재하는지 확인 if (targetElement) { // 각 클래스에 대해 토글 수행 classNames.forEach(className => { targetElement.classList.toggle(className); }); } else { //console.log(`아이디가 '${targetId}'인 요소를 찾을 수 없습니다.`); } } function egbClick(targetId, delay = 0) { setTimeout(() => { const targetElement = document.getElementById(targetId); if (targetElement) { targetElement.click(); //console.log(`${targetId}가 클릭되었습니다.`); } else { console.error(`ID가 '${targetId}'인 요소를 찾을 수 없습니다.`); } }, delay); } // 클래스 추가 함수 (여러 개 지원) function egbClassAdd(targetId, ...classNames) { const element = document.getElementById(targetId); if (element) { classNames.forEach(className => { if (!element.classList.contains(className)) { element.classList.add(className); } }); } } // 클래스 삭제 함수 (여러 개 지원) function egbClassDel(targetId, ...classNames) { const element = document.getElementById(targetId); if (element) { classNames.forEach(className => { if (element.classList.contains(className)) { element.classList.remove(className); } }); } } function egbUrlToggle(targetId, includeQueryParams, compareUrl, ...classes) { // 현재 URL 경로와 도메인 가져오기 let currentPath = window.location.pathname; if (includeQueryParams) { currentPath += window.location.search; // 쿼리 문자열 포함 } const currentDomain = window.location.origin; // ID로 요소 가져오기 const element = document.getElementById(targetId); // 요소가 존재하는 경우에만 클래스 추가 또는 제거 if (element) { if (currentPath.includes(compareUrl) || currentDomain === compareUrl) { // compareUrl이 경로나 도메인과 정확히 일치하면 클래스 추가 classes.forEach(className => { element.classList.add(className); }); } else { // 포함되지 않으면 클래스 제거 classes.forEach(className => { element.classList.remove(className); }); } } else { //console.log(`ID가 '${targetId}'인 요소를 찾을 수 없습니다.`); } } function egbUrlClassAdd(targetId, includeQueryParams, compareUrl, ...classes) { // 현재 URL 경로와 도메인 가져오기 let currentPath = window.location.pathname; if (includeQueryParams) { currentPath += window.location.search; // 쿼리 문자열 포함 } const currentDomain = window.location.origin; // ID로 요소 가져오기 const element = document.getElementById(targetId); // 요소가 존재하는 경우에만 클래스 추가 if (element) { if (currentPath.includes(compareUrl) || currentDomain === compareUrl) { classes.forEach(className => { element.classList.add(className); }); } } else { //console.log(`ID가 '${targetId}'인 요소를 찾을 수 없습니다.`); } } function egbUrlClassDel(targetId, includeQueryParams, compareUrl, ...classes) { // 현재 URL 경로와 도메인 가져오기 let currentPath = window.location.pathname; if (includeQueryParams) { currentPath += window.location.search; // 쿼리 문자열 포함 } const currentDomain = window.location.origin; // ID로 요소 가져오기 const element = document.getElementById(targetId); // 요소가 존재하는 경우에만 클래스 제거 if (element) { if (currentPath.includes(compareUrl) || currentDomain === compareUrl) { classes.forEach(className => { element.classList.remove(className); }); } } else { //console.log(`ID가 '${targetId}'인 요소를 찾을 수 없습니다.`); } } function egbScrollToggle(targetId, offset, ...classNames) { // 1. 타겟 요소 가져오기 const targetElement = document.getElementById(targetId); if (!targetElement) { //console.log(`Element with id '${targetId}' not found`); return; } // 2. 스크롤 이벤트 핸들러 function handleScroll() { const elementTop = targetElement.getBoundingClientRect().top; const viewportHeight = window.innerHeight; // 퍼센트값으로 offset을 계산 (예: 50%라면 뷰포트의 50% 위치) const offsetInPx = viewportHeight * (offset / 100); // 뷰포트에 offset 만큼 들어오면 class 추가 if (elementTop < viewportHeight - offsetInPx) { targetElement.classList.add(...classNames); } else { targetElement.classList.remove(...classNames); } } // 3. 스크롤 이벤트 리스너 등록 window.addEventListener('scroll', handleScroll); // 4. 초기화 - 페이지 로드 후 바로 실행하여 스크롤 상태 반영 window.addEventListener('load', handleScroll); } function egbScrollClassAdd(targetId, offset, ...classNames) { // 1. 타겟 요소 가져오기 const targetElement = document.getElementById(targetId); if (!targetElement) { //console.log(`Element with id '${targetId}' not found`); return; } // 2. 스크롤 이벤트 핸들러 function handleScrollClassAdd() { const elementTop = targetElement.getBoundingClientRect().top; const viewportHeight = window.innerHeight; // 퍼센트값으로 offset을 계산 (예: 50%라면 뷰포트의 50% 위치) const offsetInPx = viewportHeight * (offset / 100); // 뷰포트에 offset 만큼 들어오면 class 추가 if (elementTop < viewportHeight - offsetInPx) { targetElement.classList.add(...classNames); } } // 3. 스크롤 이벤트 리스너 등록 window.addEventListener('scroll', handleScrollClassAdd); // 4. 초기화 - 페이지 로드 후 바로 실행하여 스크롤 상태 반영 window.addEventListener('load', handleScrollClassAdd); } function egbScrollClassDel(targetId, offset, ...classNames) { // 1. 타겟 요소 가져오기 const targetElement = document.getElementById(targetId); if (!targetElement) { //console.log(`Element with id '${targetId}' not found`); return; } // 2. 스크롤 이벤트 핸들러 function handleScrollClassDel() { const elementTop = targetElement.getBoundingClientRect().top; const viewportHeight = window.innerHeight; // 퍼센트값으로 offset을 계산 (예: 50%라면 뷰포트의 50% 위치) const offsetInPx = viewportHeight * (offset / 100); // 뷰포트에 offset 만큼 들어오면 class 추가 if (elementTop < viewportHeight - offsetInPx) { targetElement.classList.remove(...classNames); } } // 3. 스크롤 이벤트 리스너 등록 window.addEventListener('scroll', handleScrollClassDel); // 4. 초기화 - 페이지 로드 후 바로 실행하여 스크롤 상태 반영 window.addEventListener('load', handleScrollClassDel); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // egb 대상 노드를 html로 설정 const egbTargetNode = document.documentElement; // egb 감시할 옵션을 설정 const egbConfig = { childList: true, subtree: true, attributes: true, attributeFilter: ['class'], attributeOldValue: false }; // 스크립트 로드 상태 추적 (전역으로 관리) if (!window.egbLoadedScripts) { window.egbLoadedScripts = new Set(); } // 인라인 스크립트 로드 함수 (EGB.function.loadInlineScript와 동일) function egbLoadInlineScriptInternal(egbUrl, Id, egbCallback) { // 스크립트가 이미 존재하는지 확인 (CSR 동적 로드된 것) const existingScript = document.getElementById(Id); if (existingScript) { if (egbCallback) egbCallback(); return; } // SSR에서 주입된 인라인 스크립트도 확인 (egb_inline_script_{filename} 형식) const scriptPath = egbUrl.split('/').pop().split('?')[0]; const ssrInlineScriptId = `egb_inline_script_${scriptPath}`; const ssrInlineScript = document.getElementById(ssrInlineScriptId); if (ssrInlineScript) { if (egbCallback) egbCallback(); return; } // inline 파라미터를 추가하여 요청 const url = egbUrl + (egbUrl.includes('?') ? '&' : '?') + 'inline=1'; fetch(url) .then(response => response.text()) .then(scriptHtml => { //