PageSourceSearch

https://shop.sqcf.org/js/header_mega_dropdown.js?1783056169

js sqcf.org collected 2026-09-24 10:12:45 UTC 12,641 bytes, 375 lines download raw bytes

1var HEADER_MEGA_DROPDOWN = function(){
2	var uniq_id = '';
3	var resize_time = {};
4	var dropdown_state = 'closed'; // 'closed', 'opening', 'open', 'closing'
5	var $body;
6	var code;
7	var widget_offset_top = 0;
8	var widget_height = 0;
9	var widget_bottom = 0;
10	var section_offset_top = 0;
11	var section_height = 0;
12	var section_bottom = 0;
13	var org_row_size = 5;
14	var calculate_row_size = 5;
15	var row_size = 5;
16	var align = 'center';
17	var menu_width = 0;
18	var gap = 0;
19	var event_type = 'mouseover';
20	var menu_sync = false;
21	var $section;
22	var $widget;
23	var $drop_down;
24	var option;
25	var $has_child = false;		// 하위 메뉴 소유 여부
26  var is_header_config = false; // 디자인모드 상단설정 여부 true, false
27	var close_pending = false;	// 여는 도중 예약됐던 close 를 열림 완료 후 재평가하기 위한 플래그
28	var last_leave_e = null;	// 재평가 시 close 에 넘길 마지막 mouseleave 이벤트
29	var isHovering = function(){
30		return $widget.is(':hover') || $widget.find(':hover').length > 0
31			|| $drop_down.is(':hover') || $drop_down.find(':hover').length > 0;
32	};
33	var getFullHeight = function(){
34		var _h = 0;
35		var _style_h = $drop_down[0].style.height;
36		var _style_d = $drop_down.css('display');
37		var _style_v = $drop_down.css('visibility');
38		$drop_down.css({'height':'auto', 'display':'block', 'visibility':'hidden'});
39		_h = $drop_down.height();
40		$drop_down.css({'visibility':_style_v, 'display':_style_d});
41		if(_style_h) $drop_down.css('height', _style_h);
42		else $drop_down.css('height', '');
43		return _h;
44	};
45
46	var init = function($w, $d, o){
47		uniq_id = makeUniq();
48    is_header_config = o.is_header_config;
49		$widget = $w;
50		$drop_down = $d;
51		option = o;
52		if(typeof $widget == 'undefined')
53			return;
54		if(typeof $drop_down == 'undefined')
55			return;
56
57		if($widget.length == 0)
58			return;
59
60		if($drop_down.length == 0)
61			return;
62
63		code = option.code;
64		event_type = typeof option.event_type == 'undefined'? 'mouseover' : option.event_type;
65
66		//ios 에서 mouseover 이벤트를 더블탭하여 함. 다른 링크들에 영향을 줌
67		if(IS_MOBILE){
68			event_type = 'click';
69		}
70		org_row_size = option.row_size;
71		menu_width = option.menu_width;
72		menu_sync = option.menu_sync;
73		align = option.align;
74		calculateRowSize();
75
76		$has_child = $widget.find('.sub_menu').length > 0 ? true : false;		// 하위 메뉴 요소가 1개 이상 존재할 경우 true
77		if($('#dropdown_' + code).attr('data-widget-type') === 'inline_menu_btn'){
78			$has_child = true;
79		};
80
81		$section = $widget.closest('div[data-type=section-wrap]');
82		$section.find('._mega_dropdown_container_'+code).remove();
83		$section.append($drop_down);
84
85		widget_offset_top = $widget.offset().top;
86		widget_height = $widget.outerHeight();
87		widget_bottom = widget_offset_top+widget_height;
88		section_offset_top = $section.offset().top;
89		section_height = $section.outerHeight();
90		section_bottom = section_offset_top+section_height;
91		$body = $('body');
92
93		gap = (widget_offset_top - section_offset_top)+widget_height;
94		$drop_down.css({'top' : gap + 'px'});
95		setDropDownTop();
96
97		if(menu_sync){
98			//setMenuSync();
99		}else{
100			$drop_down.find('._mega_dropdown_sync').css('visibility','visible');
101			$(window).off('resize.'+uniq_id).on('resize.'+uniq_id,function(){
102				if(resize_time) {
103					clearTimeout(resize_time);
104				}
105				resize_time = setTimeout(function() {
106					$(this).trigger('resizeEnd.'+uniq_id);
107				}, 500);
108			});
109			$(window).off('resizeEnd.'+uniq_id).on('resizeEnd.'+uniq_id,function(){
110				// resize 시 한줄 메뉴 개수 재계산
111				calculateRowSize();
112			});
113		}
114		$widget.find('._inline_menu_container').on('more_menu_complete',function(){
115			setMenuSync();
116		});
117
118		        if (IS_MOBILE) {
119		            $widget.off(event_type+".mega_dropdown_"+code).on(event_type+".mega_dropdown_"+code,function(e){
120		                if(dropdown_state === 'open' && event_type=='click'){
121		                    close(e);
122		                }else{
123		                    open(e);
124		                }
125		            });
126		
127		            $(document).off(event_type+".mega_dropdown_"+code).on(event_type+".mega_dropdown_"+code,function(e){
128		                if(($(e.target).closest("#"+code).length == 0 || (event_type=='click' && dropdown_state !== 'opening')) && $(e.target).closest("#dropdown_"+code).length == 0){
129		                    close(e);
130		                }
131		            });
132		        } else {			var close_timer = null;
133			$widget.add($drop_down).off('mouseenter.mega_dropdown_' + code).on('mouseenter.mega_dropdown_' + code, function(e) {
134				if (close_timer) {
135					clearTimeout(close_timer);
136					close_timer = null;
137				}
138				close_pending = false;
139				open(e);
140			});
141
142			$widget.add($drop_down).off('mouseleave.mega_dropdown_' + code).on('mouseleave.mega_dropdown_' + code, function(e) {
143				last_leave_e = e;
144				close_timer = setTimeout(function() {
145					// 여는 중이면 커서가 아직 펼쳐지는 패널 밖(아래쪽)에 있을 수 있어(sweep) mouseleave 가 오탐일 수 있다.
146					// 이때 close 하면 slideUp 이 시작되고 곧 패널이 커서에 닿아 open 이 stop(true,true) 로 가로채 상단이 떨린다.
147					// 그래서 close 판정을 '열림 완료' 시점으로 미룬다. 그 시점엔 패널이 full 크기라 :hover 판정이 정확하다. (IMOPS-12866)
148					if(dropdown_state === 'opening'){
149						close_pending = true;
150						return;
151					}
152					if(isHovering()){
153						return;
154					}
155					close(e);
156				}, 50);
157			});
158		}
159
160		// 페이지 로드 직후 마우스가 이미 메뉴 위에 있을 때도 서브메뉴를 열어준다
161		if (!IS_MOBILE) {
162			setTimeout(function() {
163				// 마우스를 한 번만 감지해 영역 안에 있으면 열기
164				$(document).one('mousemove.mega_dropdown_init_' + code, function(e) {
165					var rect = $widget[0].getBoundingClientRect();
166					if (e.clientX >= rect.left && e.clientX <= rect.right &&
167						e.clientY >= rect.top && e.clientY <= rect.bottom) {
168						open(e);
169					}
170				});
171
172				// 이미 호버 상태라면 즉시 열기
173				if ($widget.is(':hover') || $widget.find(':hover').length > 0) {
174					var fakeEvent = { currentTarget: $widget[0] };
175					open(fakeEvent);
176				}
177			}, 100);
178		}
179	};
180
181	var setMenuSync = function(){
182		if(menu_sync && $(window).width() > 768){
183			setDropDownTop();
184			var $mega_dropdown_sync = $drop_down.find('._mega_dropdown_sync');
185			$mega_dropdown_sync.css('visibility','hidden');
186			$mega_dropdown_sync.find('._title_anchor').css('visibility', 'hidden');
187			$mega_dropdown_sync.find('._title_anchor').css('height', '1px');
188			var $menu_items = $widget.find('._main_menu li');
189			var $more_menu = $widget.find('._main_menu li._more_menu');
190			var $drop_down_menu_items = $mega_dropdown_sync.find('._item');
191			if($more_menu.length >0){
192				var _last_code = '';
193				$menu_items.each(function(){
194					if(isBlank($(this).attr('data-code')))
195						return false;
196					_last_code = $(this).attr('data-code');
197				});
198				var more_list = [];
199				var show_list = [];
200				var hit_more = false;
201				if(_last_code == '')
202					hit_more = true;
203				$drop_down_menu_items.each(function(){
204					if(hit_more){
205						more_list.push($(this));
206					}else{
207						show_list.push($(this));
208					}
209					var _code = $(this).attr('data-code');
210					if(_code == _last_code){
211						hit_more = true;
212					}
213				});
214				if(more_list.length > 0){
215					// 공간상 노출되지 않은 메뉴들을 드롭다운 마지막 메뉴 아래로 노출시킴
216					var $more_ul = $('<ul class="sub_menu mega_more_list"/>');
217					$.each(more_list, function(e, $_obj){
218						$_obj.find('ul.mega_more_list').remove();
219						$_obj.find('span').show();
220						var $clone_title = $_obj.find('._title_anchor').clone().removeClass('_title_anchor');
221						$clone_title.show();
222						$more_ul.append($('<li class="mega_more"/>').append($clone_title));
223						$_obj.find('._title_anchor').hide();
224						$_obj.hide();
225					});
226					var $origin_ul = more_list[0].find('ul');
227					$origin_ul.hide();
228					more_list[0].append($more_ul);
229					more_list[0].show();
230					$more_ul.show();
231				}
232				if(show_list.length > 0){
233					$.each(show_list, function(e, $_obj){
234						$_obj.find('ul.mega_more_list').remove();
235						$_obj.find('ul').show();
236						$_obj.show();
237					});
238				}
239			}else{
240				// 브라우저 크기가 넓어져 more_menu가 사라졌을 때 숨겼던 메뉴들 다시 노출시킴
241				$drop_down_menu_items.each(function(){
242					var $show_item = $(this);
243					$show_item.find('ul.mega_more_list').remove();
244					$show_item.find('ul').show();
245					$show_item.show();
246				});
247			}
248
249			var left = $widget.offset().left;
250			if($body.hasClass('admin'))
251				left = left-$('#edit').offset().left;
252			$mega_dropdown_sync.css({
253				'padding' : 0,
254				'position' : 'relative',
255				'left' : left,
256				'visibility' : 'visible'
257			});
258		}
259	};
260
261	var open = function(e){
262		if($body.hasClass('admin') && is_header_config)
263			return;
264		if($('#dropdown_' + code).find('._item').length == 0)
265			return;
266		if(!$has_child && $(e.currentTarget).find('._main_menu ._more_menu').length == 0)		// 하위 메뉴가 존재하지 않거나 메뉴 더보기 버튼이 존재하지 않을 경우
267			return;
268
269		// 이미 열려있거나 여는 중이면 재진입 no-op. 여는 도중 mouseenter 가 stop(true,true) 로 full 높이까지 순간 점프하는 것을 막는다. (IMOPS-12866)
270		if(dropdown_state === 'opening' || dropdown_state === 'open')
271			return;
272
273		$drop_down.stop(true, true);
274		var current_h = $drop_down.height();
275		if($drop_down.is(':hidden')) current_h = 0;
276		var full_h = getFullHeight();
277		var duration = 400;
278		if(full_h > 0) duration = Math.round(400 * (Math.abs(full_h - current_h) / full_h));
279
280		$drop_down.slideDown({
281			'duration' : duration,
282			'easing' : 'swing',
283			'start' : function(animation){
284				dropdown_state = 'opening';
285			},
286			'progress' : function(animation, progress, remainingMs){
287
288			},
289			'done' : function(animation,jumpedToEnd){
290				dropdown_state = 'open';
291				// 열리는 도중 예약됐던 close 를 지금 재평가한다. 패널이 full 크기라 :hover 판정이 정확하다.
292				if(close_pending){
293					close_pending = false;
294					if(!isHovering())
295						close(last_leave_e);
296				}
297			}
298		});
299	};
300
301	var close = function(e){
302		if($body.hasClass('admin') && is_header_config)
303			return;
304		if($('#dropdown_' + code).find('._item').length == 0)
305			return;
306		if(!$has_child && $(e.currentTarget).find('._main_menu ._more_menu').length == 0)		// 하위 메뉴가 존재하지 않거나 메뉴 더보기 버튼이 존재하지 않을 경우
307			return;
308
309		// 이미 닫혀있거나 닫히는 중이면 재진입 no-op (open 과 대칭). (IMOPS-12866)
310		if(dropdown_state === 'closing' || dropdown_state === 'closed')
311			return;
312
313		// 애니메이션 중단 시 높이가 남아 클릭 영역을 가리는 문제 예방
314		$drop_down.stop(true, true);
315		var current_h = $drop_down.height();
316		if($drop_down.is(':hidden')) current_h = 0;
317		var full_h = getFullHeight();
318		var duration = 400;
319		if(full_h > 0) duration = Math.round(400 * (current_h / full_h));
320
321		$drop_down.slideUp({
322			'duration' : duration,
323			'easing' : 'swing',
324			'start' : function(animation){
325				dropdown_state = 'closing';
326			},
327			'progress' : function(animation, progress, remainingMs){
328
329			},
330			'done' : function(animation,jumpedToEnd){
331				dropdown_state = 'closed';
332			}
333		});
334	};
335
336	var setDropDownTop = function(){
337		setTimeout(function(){
338			widget_offset_top = $widget.offset().top;
339			widget_height = $widget.outerHeight();
340			widget_bottom = widget_offset_top+widget_height;
341			section_offset_top = $section.offset().top;
342			section_height = $section.outerHeight();
343			section_bottom = section_offset_top+section_height;
344			gap = (widget_offset_top - section_offset_top)+widget_height;
345			$drop_down.css({'top' : gap + 'px'});
346		},1000);
347	};
348
349	var calculateRowSize = function(){
350		if(menu_width){
351			calculate_row_size = align == 'center' ? Math.floor($(window).width() / menu_width) : Math.floor(($(window).width() - (menu_width / 2)) / menu_width);
352			if(calculate_row_size < 1) calculate_row_size = 1;
353			row_size = calculate_row_size > org_row_size ? org_row_size : calculate_row_size;
354		}else{
355			row_size = option.row_size;
356		}
357		if(row_size <1 || row_size>20) row_size = 5;
358		if(!menu_sync){
359			$('#dropdown_' + code).find('._item').each(function(e){
360				if(e % row_size == 0){
361					$(this).addClass('clear-both');
362				}else{
363					$(this).removeClass('clear-both');
364				}
365			});
366		}
367	};
368
369	return {
370		'init' : function($widget, $drop_down, option){
371			init($widget, $drop_down, option);
372		}
373	}
374
375};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.