//tabs 选项卡 namespace web.layout.behavior.tabs; var targetTab; targetTab = function( ele,tabsContainer) { if( !ele || (ele == tabsContainer) ) return; if( ele.getAttribute("panel") ) return ele; return targetTab( ele.parent(), tabsContainer ); } var selectTab = function( ltOwner,eleTab) { if( ! eleTab.isVisible() ){ var ltStrip = ltOwner.querySelector(":root>.strip"); var ltLastFixed = ltOwner.querySelector(":root>.strip>[panel][closeable]" ); if( ltLastFixed ) ltLastFixed = ltLastFixed.previous(); if(!ltLastFixed) ltLastFixed = ltOwner.querySelector(":root>.strip>[panel]:last-child" ); ltStrip.insert( eleTab,ltLastFixed.index() + 1 ); ltStrip.update(true); adjustStrip(ltOwner); } // already selected, nothing to do.. if( eleTab.state.current ) return true; // but we've handled it. //find currently selected element (tab and panel) and remove "selected" from them var elePanelPrev = ltOwner.querySelector(":root>[name]:expanded"); var eleTabPrev = ltOwner.querySelector(":root>.strip>[panel][selected]"); // find new tab and panel var panelName = eleTab.getAttribute("panel"); var elePanel = ltOwner.querySelector(':root>[name="%s"]', panelName); if( !elePanel || !eleTab ) { // panel="somename" without matching name="somename" return true; } if( elePanelPrev ) { // remove selected attribute - just in case somone is using attribute selectors elePanelPrev.selected = null; // eleTabPrev.setAttribute("selected",null) elePanelPrev.state.collapsed = true; // elePanelPrev.modifyState( 0x400/*_HL_STATE_COLLAPSED*/ ); } if( eleTabPrev ) { eleTab.prevPanelName = eleTabPrev.getAttribute("panel"); eleTabPrev.selected = null; eleTabPrev.state.current = false; } elePanel.selected = ""; elePanel.state.expanded = true; eleTab.selected = "" eleTab.state.current = true; if(elePanelPrev) elePanelPrev.postEvent("onElementCollapsed",,elePanelPrev); elePanel.postEvent("onElementExpanded",,elePanel); /* NOTE #1: these event will bubble from panel elements up to the root so panel itself, tabs ctl, its parent, etc. will receive these notifications. Handle them if you need to change UI dependent from current tab. NOTE #2: while handling this event in: onElementExpanded = function (ltTarget,ltOwner,reason,behaviorParams) onElementCollapsed = function (ltTarget,ltOwner,reason,behaviorParams) ltTarget is the panel element being collapsed/expanded */ return true; } var findTab = function( ltEle, eleTab,direction ) { var eleTabNew; select( direction ) { case -2{ eleTabNew = eleTab.firstSibling(); while( eleTabNew ) { if( !eleTabNew.state.disabled && ( eleTabNew.isVisible() || eleTabNew["closeable"] ) ) break; eleTabNew = eleTabNew.nextSibling(); } } case -1{ eleTabNew = eleTab.previousSibling(); while( eleTabNew ) { if( !eleTabNew.state.disabled && ( eleTabNew.isVisible() || eleTabNew["closeable"] ) ) break; eleTabNew = eleTabNew.previousSibling(); } } case 1{ eleTabNew = eleTab.nextSibling(); while( eleTabNew ) { if( !eleTabNew.state.disabled && ( eleTabNew.isVisible() || eleTabNew["closeable"] ) ) break; eleTabNew = eleTabNew.nextSibling(); } } case 2{ eleTabNew = eleTab.lastSibling(); while( eleTabNew ) { if( !eleTabNew.state.disabled && ( eleTabNew.isVisible() || eleTabNew["closeable"] ) ) break; eleTabNew = eleTabNew.previousSibling(); } } } if( !eleTabNew || !eleTabNew.getAttribute("panel") ) //is not a tab element return false; return selectTab( ltEle, eleTabNew ); } /*}}*/ /*事件回调函数名都是以on开头{{*/ onAttach = function( ltOwner ){ //Elements that have overflow:auto | scroll are also intrinsically focusable ltOwner.style.overflow = "auto"; var eleStrip = ltOwner.querySelector(":root>.strip"); if(!eleStrip) return; eleStrip.style.overflow = "hidden"; //:root below matches the element we use to start lookup from. var eleTab = eleStrip.querySelector(":root>[panel][selected]"); // initialy selected var panelName = eleTab.getAttribute("panel"); // find panel we need to show by default var elePanel = ltOwner.querySelector(':root>[name="%s"]', panelName); if( !elePanel) { //what a ...!, panel="somename" without matching name="somename" return; } var eleTabStrip = eleTab.parent(); eleTabStrip.state.current = true; // :current - make tab strip as current element inside focusable. eleTab.state.current = true; // :current - current tab is, well, current. elePanel.state.expanded = true; // :expanded - current panel is expanded.. } onActiveChild = function (ltTarget,ltOwner,reason,behaviorParams) { var newtab = targetTab(ltTarget,ltOwner); if(!newtab) { return true; // ltTarget is not a tab here. } selectTab(ltOwner,newtab); return true; } onMouseDown = function (ltTarget,ltOwner, x,y,mouseParams) { var eleTab = targetTab( ltTarget, ltOwner ); if(!eleTab) return false; return selectTab( ltOwner, eleTab ) } onMouseDblClick = function (ltTarget,ltOwner, x,y,mouseParams) { var eleTab = targetTab( ltTarget, ltOwner ); if(!eleTab) return false; return selectTab( ltOwner, eleTab ) } var isInFocus = function(ltEle) { return ltEle.test(":focus",true) ; } var catchKeys = {[0x9/*_VK_TAB*/]=1;[0x25/*_VK_LEFT*/]=1;[0x27/*_VK_RIGHT*/]=1;[0x24/*_VK_HOME*/]=1;[0x23/*_VK_END*/]=1} onKeyDown = function (ltTarget,ltOwner, keyCode, altState, ltKeyParams) { if(!catchKeys[keyCode]) return; var eleTab = ltOwner.querySelector(":root>.strip>[panel][selected]"); select( keyCode ) { case 0x9/*_VK_TAB*/ { if( altState & 1/*_HL_CONTROL_KEY_PRESSED*/ ) return findTab( ltOwner, eleTab, (ltKeyParams.alt_state & 2/*_HL_SHIFT_KEY_PRESSED*/)? -1:1 ); } case 0x25/*_VK_LEFT*/ { return isInFocus(eleTab)? findTab( ltOwner, eleTab, -1 ) ; } case 0x27/*_VK_RIGHT*/{ return isInFocus(eleTab)? findTab( ltOwner, eleTab, 1 ) } case 0x24/*_VK_HOME*/{ return isInFocus(eleTab)? findTab( ltOwner, eleTab, -2 ) } case 0x23/*_VK_END*/{ return isInFocus(eleTab)? findTab( ltOwner, eleTab, 2 ) }; } return false; } onButtonClick = function (ltTarget,ltOwner,reason,behaviorParams) { if( ltTarget.class == "close-panel" && ltTarget.queryParent(".strip>[panel]") ){ close(ltOwner,ltTarget); return true; } } sinking = { onHyperlinkClick = function (ltTarget,ltOwner,reason,behaviorParams) { var href = ltTarget.href; if( ! ( href && ltTarget.target && ltTarget.title ) ) return; if( ..string.startsWith(href,"tabs://") ){ open( ltOwner,ltTarget.title ,..string.right(href,-8),ltTarget.target) return true;//取消默认行为 } } } onVisualStateChanged = function (ltTarget,ltOwner,shown,behaviorParams) { if(shown) ltTarget.update(true); //resize custom control } onSize = function (ltOwner) { adjustStrip(ltOwner); } /* CSS定义 .tabs > .strip 的宽度为 width:calc( 100% - 下拉按钮宽度 ); 然后将下拉按钮的CSS类名指定为 tabs-menu,使用 position: absolute; 绝对定位到选项卡右上角 */ onMenuItemClick = function(ltTarget,ltOwner,reason,behaviorParams) { var ltPopupOwner = ..web.layout.element( behaviorParams.he ); if( !ltPopupOwner.test(".tabs-menu" ) ) return; self.select(ltOwner,ltTarget["panel-name"]); return true; }; onPopupRequest = function (ltTarget,ltOwner,reason,behaviorParams) { var ltPopupOwner = ..web.layout.element( behaviorParams.he ); if( !ltPopupOwner.test(".tabs-menu" ) ) return; ltTarget.innerHTML = ""; var ltStrip = ltOwner.querySelector(':root>.strip'); for(i,eleChild in ltStrip.eachChild() ){ if(!eleChild.isVisible() && eleChild.closeable) ltTarget.printf("