/**메뉴의 생성과 선택 시 동작을 지원한다. */ class MenuSupport { /**새 MenuSupport를 생성한다. * @param selector {string} 메뉴를 담을 컨테이너에 대한 css selector */ constructor(conf) { conf = conf || {}; this._selector = ifEmpty(conf.selector, "#menus"); this._containerSelector = ifEmpty(conf.containerSelector, "#layout-menu"); this._container = document.querySelector(this._containerSelector); let onclick = conf.onclick || (menu => 'onclick="openMenu(\'{url}\')" '.replace(/{url}/gi, wctx.url(menu.url))); this._onclick = (menu => menu && menu.url ? onclick(menu) : ""); this._horizontal = this._container && this._container.classList.contains('menu-horizontal'); this._orientation = this._horizontal ? "horizontal" : "vertical"; this._template = this._orientation + "-menu-template"; this._menuItem = ''; this._menuSub = ''; this._menus = []; this._init(); } /**메뉴를 생성할 정보를 설정한다. * @param menus {array} 메뉴 정보 배열
* 각 메뉴 정보의 레이아웃은 다음과 같다.
* {"id":메뉴 아이디, "name":"메뉴 이름", "url":"실행 url", "parentID":"상위 메뉴 아이디", "description":"메뉴 설명", "imageConf":"이미지 설정", "displayWindow":"표시 창", "children":[하위 메뉴 배열]} * @returns MenuSupport */ setMenuInfo(menus) { let setParent = menu => { let children = menu.children || []; if (children.length < 1) return; children.forEach(child => { child.parent = menu; setParent(child); }); }; menus.forEach(menu => setParent(menu)); this._menus = menus; let menuItemTag = menu => { let tag = this._menuItem .replace(/{menuID}/gi, menu.id) .replace(/{menuName}/gi, menu.name) .replace(/{onclick}/gi, this._onclick(menu)) .replace(/{title}/gi, (menu.description || "")) .replace(/{imageConf}/gi, (menu.imageConf || "bx bx-layout")); let parent = menu.children && menu.children.length > 0; tag = tag.replace(/{toggle}/gi, !parent ? "" : " menu-toggle"); if (!parent) return tag.replace(/{menuSub}/gi, ""); let children = menu.children.map(child => menuItemTag(child)).join("\n\t") return tag.replace(/{menuSub}/gi, this._menuSub.replace(/{children}/gi, children)); } let tags = (menus || []).map(menu => menuItemTag(menu)); document.querySelector(this._selector).innerHTML = tags.join(""); return this._init(); } _toggleMenuAwares(collapse) { document.querySelectorAll(".menu-aware").forEach(menuAware => menuAware.style.width = collapse ? "calc(100% - 5.25rem)" : ""); } _init() { let menu = new Menu(this._container, { orientation: this._orientation, closeChildren: this._horizontal, showDropdownOnHover: localStorage.getItem('templateCustomizer-' + this._template + '--ShowDropdownOnHover') ? localStorage.getItem('templateCustomizer-' + this._template + '--ShowDropdownOnHover') === 'true' : window.templateCustomizer !== undefined ? window.templateCustomizer.settings.defaultShowDropdownOnHover : true }); window.Helpers.scrollToActive(false); //animate = false window.Helpers.mainMenu = menu; //Sets toggler document.querySelectorAll('.layout-menu-toggle').forEach(item => { item.addEventListener('click', event => { if (event.fired) { return delete event.fired; } event.fired = true; event.preventDefault(); window.Helpers.toggleCollapsed(); if (config.enableMenuLocalStorage && !window.Helpers.isSmallScreen()) { try { localStorage.setItem( 'templateCustomizer-' + this._template + '--LayoutCollapsed', String(window.Helpers.isCollapsed()) ); } catch (e) {} } this._toggleMenuAwares(Helpers.isCollapsed()); }); }); // Display menu toggle (layout-menu-toggle) on hover with delay let delay = (elem, callback) => { let timeout = null; elem.onmouseenter = () => { // Set timeout to be a timer which will invoke callback after 300ms (not for small screen) timeout = !Helpers.isSmallScreen() ? setTimeout(callback, 300) : setTimeout(callback, 0); }; elem.onmouseleave = () => { // Clear any timers set to timeout document.querySelector('.layout-menu-toggle').classList.remove('d-block'); clearTimeout(timeout); }; }; if (this._container) { delay(this._container, () => { // not for small screen if (!Helpers.isSmallScreen()) { document.querySelector('.layout-menu-toggle').classList.add('d-block'); } }); } // Detect swipe gesture on the target element and call swipe In window.Helpers.swipeIn('.drag-target', function (e) { window.Helpers.setCollapsed(false); }); // Detect swipe gesture on the target element and call swipe Out window.Helpers.swipeOut(this._containerSelector, function (e) { if (window.Helpers.isSmallScreen()) window.Helpers.setCollapsed(true); }); // Display in main menu when menu scrolls let menuInnerContainer = document.getElementsByClassName('menu-inner'), menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0]; if (menuInnerContainer.length > 0 && menuInnerShadow) { menuInnerContainer[0].addEventListener('ps-scroll-y', function () { if (this.querySelector('.ps__thumb-y').offsetTop) { menuInnerShadow.style.display = 'block'; } else { menuInnerShadow.style.display = 'none'; } }); } return this; } open(url) { top.document.location.href = url; } /**지정한 url의 메뉴를 활성화 한다. * @param url {string} 메뉴 url * @returns MenuSupport */ setActive(url) { let menu = this.getMenu(url); if (!menu) return this; document .querySelectorAll(this._selector + " li") .forEach(li => li.classList.remove("active", "open")); let a = document.querySelector(this._selector + " li[data-key=\"" + menu.id + "\"]"); if (!a) return this; let activate = (e, open) => { e.classList.add("active"); let p = e.parentNode; let tag = (p != this._container ? p : null) ? p.tagName : ""; if (!tag) return; if ("li" == tag.toLowerCase()) { p.classList.add("active"); if (open) p.classList.add("open"); } activate(p, true); }; activate(a); return this; } /**지정하는 url의 메뉴 정보를 반환한다. * @param url {string} 메뉴 url * @returns 지정하는 url의 메뉴 정보 */ getMenu(url) { let find = menus => { for (let i = 0; i < menus.length; ++i) { let menu = menus[i]; if (url == menu.url) return menu; let found = find(menu.children || []); if (found) return found; } return null; }; return find(this._menus); } breadcrumb(url, separator = " / ") { let menu = this.getMenu(url); if (!menu) return ""; let getName = menu => { let name = menu.name; let parent = !menu.parent ? "" : getName(menu.parent); return parent ? parent + separator + name : name; } return getName(menu); } } class TabControl extends Dataset { constructor(conf) { if (!conf) conf = {}; conf.keymapper = info => { if (!info.index) { info.index = "ndx-" + new Date().getTime(); } return info.index; }; conf.dataGetter = obj => null; super(conf); this.sticky = conf.sticky || {}; this.maxCount = conf.maxCount || 8; this.getMenu = conf.getMenu || ((url) => {throw "getMenu(url) missing"}); } getTab(by) { if (by.startsWith("ndx-")) return this.getData(by, "item"); if (by != this.sticky.url) { let found = this._items.filter(item => item.data.url == by), empty = !found || found.length < 1; if (empty) return null; found = found[0]; return !found.unreachable ? found : null; } else { let found = this._items.filter(item => item.data.url == this.sticky.url); if (!found || found.length < 1) { this.addData([this.sticky]); } } } open(url) { if (!url || url.startsWith("javascript:void") || url == this.sticky.url) return; let tab = this.getTab(url); if (tab) return this.setCurrent(tab.data.index); ajax.get({ url: wctx.url(url), success: resp => { let menu = this.getMenu(url); if (!menu) throw "Menu not found for " + url; if (this.length == this.maxCount) { let index = isEmpty(this.sticky) ? 0 : 1, first = this._items[index]; this.close(first.data.url) } menu.content = resp; this.addData([menu]); this.open(url); } }); } close(url) { let tab = this.getTab(url); this.erase(tab.data.index); } }