PageSourceSearch

https://www.777english.co.kr/js/header_more_menu.js?1751004987

js 777english.co.kr collected 2026-09-24 12:13:55 UTC 12,667 bytes, 406 lines download raw bytes

1var HEADER_MORE_MENU = function(){
2	var uniq_id = '';
3	var $menu_wrap;
4	var $menu_grid;
5	var $menu;
6	var $menu_obj_clone, $menu_obj_clone_temp;
7	var more_width;
8	var width_adj = 1;
9	var prev_more_width = 60;
10	var $more_menu;
11	var $col_group;
12	var use_mega_dropdown = false;
13	var resize_time = {};
14	var more_menu_html = '<li class="dropdown _more_menu"><a data-toggle="dropdown" class="fixed_transform dropdown-toggle disabled dropdown-more _header_dropdown" data-toggle="dropdown" aria-expanded="false"><i class="icon-options vertical-middle" aria-hidden="true"></i></a><ul class="dropdown-menu more_list _more_list"></ul></li>';
15	var widget_code = ''; // optional
16	var $inline_menu_container;
17
18	var getAdjWidth = function(){
19		return width_adj;
20	};
21
22	var resetAdjWidth = function(){
23		width_adj = 1;
24	};
25
26	var init = function($_obj,_use_mega_dropdown,callback){
27		uniq_id = makeUniq();
28		$menu_wrap = $_obj;
29		use_mega_dropdown = _use_mega_dropdown;
30    
31		$menu = $menu_wrap.find('._main_menu');
32		$menu_wrap.css({'visibility' : 'hidden'});
33
34		$menu_obj_clone = $menu.clone(true);
35		$menu_obj_clone.removeClass('_main_menu');
36		$menu_obj_clone.addClass('_main_clone_menu main_clone_menu');
37		$menu_wrap.find('._main_clone_menu').remove();
38		$menu_wrap.append($('<div class="_main_clone_menu_wrap"/>').css({
39			position : 'absolute',
40			top : -9999,
41			left : -9999
42		}).append($menu_obj_clone));
43
44		$menu_obj_clone_temp = $menu.clone(true);
45		$menu_obj_clone_temp.empty();
46		$menu_obj_clone_temp.removeClass('_main_menu');
47		$menu_obj_clone_temp.addClass('main_clone_menu');
48		$more_menu = $(more_menu_html);
49
50		$inline_menu_container = $('#inline_header_normal *[data-type=col-group]:has(*[data-widget-type=inline_menu])');
51
52		setTimeout(function() {
53			$menu_obj_clone_temp.append($more_menu);
54			$menu_wrap.append($('<div class="_main_clone_menu_wrap"/>').css({
55				position : 'absolute',
56				top : -9999,
57				left : -9999
58			}).append($menu_obj_clone_temp));
59			more_width = Math.ceil($more_menu.outerWidth())+width_adj;
60
61			calculateMenuWidth(callback);
62    }, 500);
63
64
65		$(window).off('resize.'+uniq_id).on('resize.'+uniq_id,function(){
66			if(resize_time) {
67				clearTimeout(resize_time);
68			}
69			resize_time = setTimeout(function() {
70				$(this).trigger('resizeEnd.'+uniq_id);
71			}, 500);
72		});
73
74		$(window).off('resizeEnd.'+uniq_id).on('resizeEnd.'+uniq_id,function(){
75			calculateMenuWidth(callback);
76		});
77		$('body').on('section_change.'+uniq_id,function(e,event_section_code){
78			var $section = $menu.closest('div[data-type=section]');
79			var section_code = $section.attr('section-code');
80			if(section_code != event_section_code)
81				return false;
82			calculateMenuWidth(callback);
83		});
84	};
85
86	var setWidgetCode = function(widgetCode) {
87		widget_code = widgetCode;
88	}
89
90	var allowDropdownHover = function(){
91		if($(window).width() > 991){
92			$menu_wrap.find('._header_dropdown').dropdownHover();
93		}
94	};
95
96	var calculateMenuWidth = function(callback){
97    const main_clone_menu_wrap = $menu_wrap[0].querySelector('._main_clone_menu_wrap');
98    main_clone_menu_wrap.style.display = 'block';
99		resetAdjWidth();
100		$col_group = $menu_wrap.closest('div[data-type=col-group]');
101		$menu_grid = $menu_wrap.closest('div[data-type=grid]');
102		$col_group.css('visibility', 'hidden');
103		var menu_cnt = 0;
104		$menu.children("li").filter(function(){
105			return $(this).css('display') != 'none';
106		}).each(function(index){
107			$(this).data('width', Math.ceil($(this).outerWidth(true)));
108			menu_cnt++;
109		});
110		prev_more_width = more_width;
111		more_width = Math.ceil($more_menu.outerWidth())+getAdjWidth();
112		if(more_width <= 50)
113			more_width = prev_more_width;
114
115
116		var col_group_width = Math.floor($col_group.width());
117		var menu_width = 0;
118
119		var $menu_grid_siblings = $menu_grid.siblings().not('div[data-type=line-guide]');
120		$menu_grid_siblings.each(function(){
121			var _s_width = Math.ceil($(this).outerWidth(true));
122			menu_width += _s_width;
123		});
124
125
126		var is_split = false;
127
128		var calc_list = [];
129		$menu_obj_clone.children("li").filter(function(){
130			return $(this).css('display') != 'none';
131		}).each(function(index){
132			var _width_adj = getAdjWidth();
133			var _width_data = {'obj':$(this), 'width' : Math.ceil($(this).outerWidth())+_width_adj};
134			calc_list.unshift(_width_data);
135			menu_width += Math.ceil($(this).outerWidth(true));
136			menu_width += _width_adj;
137			if(menu_width >= col_group_width){
138				is_split = true;
139				return false;
140			}
141		});
142
143		if(is_split){
144			$.each(calc_list, function(e, _calc_data){
145				menu_width = menu_width - _calc_data.width;
146				var more_contain_width = menu_width + more_width+40;
147				if(more_contain_width >= col_group_width){
148					if(calc_list.length == 1){
149						splitMoreMenu(_calc_data.obj, callback);
150						return false;
151					}
152					return true;
153				}else{
154					splitMoreMenu(_calc_data.obj, callback);
155					return false;
156				}
157			});
158		}else{
159			splitMoreMenu(false, callback);
160		}
161    main_clone_menu_wrap.style.display = 'none';
162
163		window.setTimeout(() => {
164			// Browser resize 시 상단 메뉴를 다시 그리기 때문에 이벤트를 다시 등록해야함
165			attachWheelEventOnDropdownElements();
166
167			const isExistInlineMenuContainer = Boolean($inline_menu_container.length);
168
169			if (isExistInlineMenuContainer)  {
170				handleInlineMenuLastDropdown();
171				handleClickMenuLink();
172			}
173		}, 0);
174	};
175
176	var splitMoreMenu = function($split_item, callback){
177		if(!$split_item){
178			$menu.empty();
179			$menu_obj_clone.children("li").each(function(index){
180				$menu.append($(this).clone(true));
181			});
182		}else{
183			var $menu_items = $split_item.prevAll();
184			$menu.empty();
185			$menu_items.each(function(){
186				$menu.prepend($(this).clone(true));
187			});
188			var $more_items = $split_item.nextAll();
189			$more_menu = $(more_menu_html);
190			$menu.append($more_menu);
191
192			if(!use_mega_dropdown){
193				$more_menu.find('._more_list').append($split_item.clone(true).removeClass('dropdown').addClass('dropdown-submenu'));
194				$more_items.each(function(){
195					var $_clone_item = $(this).clone(true);
196					$_clone_item.removeClass('dropdown').addClass('dropdown-submenu');
197					$more_menu.find('._more_list').append($_clone_item);
198				});
199			}
200
201		}
202
203		if(!use_mega_dropdown){
204			$menu.find("li.dropdown").each(function(index){
205				$(this).find("li.dropdown-submenu").each(function(index){
206					if(!$(this).hasClass('pulldown-hide')){
207						if($(this).find(".dropdown-menu > li").length > 0) $(this).addClass("sub-active");
208					}else{
209						$(this).find('ul').removeClass('dropdown-menu');
210						$(this).find('ul li').hide();
211					}
212				});
213			});
214			$more_menu.find("li").each(function(index){
215				$(this).find("li.dropdown-submenu").each(function(index){
216					if(!$(this).hasClass('pulldown-hide')){
217						if($(this).find(".dropdown-menu > li").length > 0) $(this).addClass("sub-active");
218					}else{
219						$(this).find('ul').removeClass('dropdown-menu');
220						$(this).find('ul li').hide();
221					}
222				});
223			});
224		}
225		$menu_wrap.css('visibility', 'visible');
226		$col_group.css('visibility', 'visible');
227
228		allowDropdownHover();
229
230		if(typeof callback == 'function'){
231			callback();
232		}
233
234		$menu_wrap.trigger('more_menu_complete');
235
236
237	};
238
239	/**
240	 * 상단 메뉴가 viewport 높이보다 큰 경우 마우스로 상단 메뉴를 스크롤링 하기 위함
241	 * @description 디자인모드가 아니며, 서브메뉴의 타입이 mega dropdown이 아니며, 상단메뉴가 고정인 경우 이벤트 추가
242	 */
243	const attachWheelEventOnDropdownElements = () => {
244		// PC 헤더(#inline_header_normal)만 적용되도록 처리
245		const $root = $("._new_org_header #inline_header_normal ._fixed_header_section");
246
247		const isDesignMode = window.location.pathname.includes('admin/design');
248
249		const shouldAttachWheelEvent = !isDesignMode && !use_mega_dropdown && Boolean($root.length);
250
251		if (!shouldAttachWheelEvent) {
252			return;
253		}
254
255		const $targetDropdowns = $root.find('._inline_menu_container ._main_menu > .dropdown > .dropdown-menu');
256		const $targetHeaderDropdowns = $targetDropdowns.parent().find('> ._header_dropdown');
257
258		initDropdowns($targetDropdowns);
259
260		$targetDropdowns.on('wheel.dropdown', handleWheelDropdown);
261
262		// bootstrap-hover-dropdown lib에서 dropdownHover함수를 호출한 요소에 이벤트를 실행시킴
263		$targetHeaderDropdowns.on('hide.bs.dropdown', handleHideDropdown);
264	}
265
266	const initDropdowns = ($dropdowns) => {
267		resetDropdowns($dropdowns);
268	}
269
270	const resetDropdowns = ($dropdowns) => {
271		$dropdowns.data('currentY', 0);
272		$dropdowns.data('initHoverGuard', false);
273
274		$dropdowns.css('transform', 'translateY(0)');
275
276		$dropdowns.removeClass('init-hover-guard');
277	}
278
279	const handleWheelDropdown = function(e) {
280		const $element = $(this);
281
282		const innerHeight = window.innerHeight;
283		const elementScrollHeight = $element.prop('scrollHeight');
284
285		const isOverflow = innerHeight < elementScrollHeight;
286
287		// 브라우저 높이가 dropdown의 높이보다 작을경우 처리하지 않음
288		if (!isOverflow)  {
289			return;
290		}
291
292		const event = e.originalEvent;
293		const initHoverGuard = $element.data('initHoverGuard');
294
295		const acc = { min: 20, max: 30 };
296		const deltaY = event.deltaY;
297		const accValue = Math.floor(Math.min(acc.max, Math.max(acc.min, Math.abs(deltaY * 0.1))));
298		const currentY = $element.data('currentY') || 0;
299
300		const threshold = Math.floor(innerHeight / 3);
301		const position = deltaY > 0 ? -1 : 1;
302
303		const sumAccValue = Math.min(0, currentY + accValue * position);
304		const maxAccValue = currentY;
305		const nextY = elementScrollHeight - Math.abs(sumAccValue) < threshold ? maxAccValue : sumAccValue;
306
307		$element.css('transform', `translateY(${nextY}px)`);
308		$element.data('currentY', nextY);
309
310		if (!initHoverGuard) {
311			$element.addClass('init-hover-guard');
312			$element.data('initHoverGuard', true);
313		}
314
315		// 바깥 스크롤 방지
316		event.preventDefault();
317	}
318
319	const handleHideDropdown = function() {
320		const $dropdown = $(this).parent().find('> .dropdown-menu');
321
322		resetDropdowns($dropdown);
323	}
324
325	const handleInlineMenuLastDropdown = function() {
326		// 기존에 추가된 클래스로 인해 pos 계산이 부정확할 수 있으므로 제거
327		$inline_menu_container.removeClass('overflow-last-dropdown');
328
329		const $lastDropdown = $inline_menu_container.find(`#${widget_code} ._main_menu li.dropdown:last-of-type > ul.dropdown-menu`);
330
331		const hasLastDropdown = Boolean($lastDropdown.length);
332
333		if (!hasLastDropdown) {
334			return;
335		}
336
337		const viewportWidth = document.documentElement.clientWidth || document.body.clientWidth || window.innerWidth;
338		const lastDropdownRect = $lastDropdown.get(0).getBoundingClientRect();
339
340		const remainRightSpace = viewportWidth - lastDropdownRect.left;
341
342		const isOverflowDropdown = lastDropdownRect.width > remainRightSpace;
343
344		if (isOverflowDropdown) {
345			$inline_menu_container.addClass('overflow-last-dropdown');
346		}
347	}
348
349	const handleClickMenuLink = function() {
350		/**
351		 * mega dropdown 설정인 경우
352		 * - 아이패드, Chrome, Safari에서 메뉴 클릭 시 다르게 동작하는 부분이 있어 아래와 같이 처리
353		 * - mega dropdown이 보이는 경우에만 링크 클릭이 가능하도록 처리
354 		 */
355		if (use_mega_dropdown) {
356			const $widget = $(`#${widget_code}`);
357			const $dropdown = $(`#dropdown_${widget_code}`);
358
359			const hasItemMegaDropdown = !!$dropdown.find('._item').length;
360			const hasSubMenuMegaDropdown = !!$dropdown.find('.sub_menu').length || $dropdown.attr('data-widget-type') === 'inline_menu_btn';
361			const hasMoreMenu = !!$widget.find('._main_menu ._more_menu').length;
362
363			/**
364			 * 하위 메뉴가 없고 더보기 버튼이 존재하지 않는 경우
365			 * @ref header_mega_dropdown.js, open 함수
366			 */
367			if (!hasItemMegaDropdown || (!hasSubMenuMegaDropdown && !hasMoreMenu)) {
368				return;
369			}
370
371			$menu.find('a._header_dropdown').off('click.megaDropdown').on('click.megaDropdown', function(e) {
372				const $megaDropdownContainer = $(`._mega_dropdown_container_${widget_code}`);
373
374				const hasMegaDropdownContainer = !!$megaDropdownContainer.length;
375
376				if (!hasMegaDropdownContainer) {
377					return;
378				}
379
380				/** mega dropdown이 펼쳐질 때 height가 동적으로 style 속성에 지정됨 */
381				const isProgressing = $megaDropdownContainer.get(0).style.height !== '';
382
383				const isComplete = !isProgressing && $megaDropdownContainer.css('display') === 'block';
384
385				if (!isComplete) {
386					e.preventDefault();
387				}
388			});
389		}
390	}
391
392
393	return {
394		'init' : function($obj,callback){
395			init($obj,callback);
396		},
397		'setWidgetCode': function(widgetCode) {
398			setWidgetCode(widgetCode);
399		},
400		'calculateMenuWidth' : function(callback){
401			calculateMenuWidth(callback);
402		}
403	}
404
405};
406

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.