/** * Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ RED.tabs = (function() { var defaultTabIcon = "fa fa-lemon-o"; function createTabs(options) { var tabs = {}; var pinnedTabsCount = 0; var currentTabWidth; var currentActiveTabWidth = 0; var collapsibleMenu; var ul = options.element || $("#"+options.id); var wrapper = ul.wrap( "
" ).parent(); var scrollContainer = ul.wrap( "
" ).parent(); wrapper.addClass("red-ui-tabs"); if (options.vertical) { wrapper.addClass("red-ui-tabs-vertical"); } if (options.addButton) { wrapper.addClass("red-ui-tabs-add"); var addButton = $('
').appendTo(wrapper); addButton.find('a').on("click", function(evt) { evt.preventDefault(); if (typeof options.addButton === 'function') { options.addButton(); } else if (typeof options.addButton === 'string') { RED.actions.invoke(options.addButton); } }) if (typeof options.addButton === 'string') { var l = options.addButton; if (options.addButtonCaption) { l = options.addButtonCaption } RED.popover.tooltip(addButton,l,options.addButton); } ul.on("dblclick", function(evt) { var existingTabs = ul.children(); var clickX = evt.clientX; var targetIndex = 0; existingTabs.each(function(index) { var pos = $(this).offset(); if (pos.left > clickX) { return false; } targetIndex = index+1; }) if (typeof options.addButton === 'function') { options.addButton({index:targetIndex}); } else if (typeof options.addButton === 'string') { RED.actions.invoke(options.addButton,{index:targetIndex}); } }); } if (options.searchButton) { wrapper.addClass("red-ui-tabs-search"); var searchButton = $('').appendTo(wrapper); searchButton.find('a').on("click", function(evt) { evt.preventDefault(); if (typeof options.searchButton === 'function') { options.searchButton() } else if (typeof options.searchButton === 'string') { RED.actions.invoke(options.searchButton); } }) if (typeof options.searchButton === 'string') { var l = options.searchButton; if (options.searchButtonCaption) { l = options.searchButtonCaption } RED.popover.tooltip(searchButton,l,options.searchButton); } } var scrollLeft; var scrollRight; if (options.scrollable) { wrapper.addClass("red-ui-tabs-scrollable"); scrollContainer.addClass("red-ui-tabs-scroll-container"); scrollContainer.on("scroll",updateScroll); scrollLeft = $('
').appendTo(wrapper).find("a"); scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();}); scrollRight = $('
').appendTo(wrapper).find("a"); scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();}); } if (options.collapsible) { // var dropDown = $('
',{class:"red-ui-tabs-select"}).appendTo(wrapper); // ul.hide(); wrapper.addClass("red-ui-tabs-collapsible"); var collapsedButtonsRow = $('').appendTo(wrapper); if (options.menu !== false) { var selectButton = $('').appendTo(collapsedButtonsRow); selectButton.addClass("red-ui-tab-link-button-menu") selectButton.on("click", function(evt) { evt.stopPropagation(); evt.preventDefault(); if (!collapsibleMenu) { var pinnedOptions = []; var options = []; ul.children().each(function(i,el) { var id = $(el).data('tabId'); var opt = { id:"red-ui-tabs-menu-option-"+id, icon: tabs[id].iconClass || defaultTabIcon, label: tabs[id].name, onselect: function() { activateTab(id); } }; if (tabs[id].pinned) { pinnedOptions.push(opt); } else { options.push(opt); } }); options = pinnedOptions.concat(options); collapsibleMenu = RED.menu.init({id:"debug-message-option-menu",options: options}); collapsibleMenu.css({ position: "absolute" }) collapsibleMenu.appendTo("body"); } var elementPos = selectButton.offset(); collapsibleMenu.css({ top: (elementPos.top+selectButton.height()-2)+"px", left: (elementPos.left - collapsibleMenu.width() + selectButton.width())+"px" }) if (collapsibleMenu.is(":visible")) { $(document).off("click.tabmenu"); } else { $(document).on("click.tabmenu", function(evt) { $(document).off("click.tabmenu"); collapsibleMenu.hide(); }); } collapsibleMenu.toggle(); }) } } function scrollEventHandler(evt,dir) { evt.preventDefault(); if ($(this).hasClass('disabled')) { return; } var currentScrollLeft = scrollContainer.scrollLeft(); scrollContainer.animate( { scrollLeft: dir }, 100); var interval = setInterval(function() { var newScrollLeft = scrollContainer.scrollLeft() if (newScrollLeft === currentScrollLeft) { clearInterval(interval); return; } currentScrollLeft = newScrollLeft; scrollContainer.animate( { scrollLeft: dir }, 100); },100); $(this).one('mouseup',function() { clearInterval(interval); }) } ul.children().first().addClass("active"); ul.children().addClass("red-ui-tab"); function getSelection() { var selection = ul.find("li.red-ui-tab.selected"); var selectedTabs = []; selection.each(function() { selectedTabs.push(tabs[$(this).find('a').attr('href').slice(1)]) }) return selectedTabs; } function selectionChanged() { options.onselect(getSelection()); } function onTabClick(evt) { evt.preventDefault(); var currentTab = ul.find("li.red-ui-tab.active"); var thisTab = $(this).parent(); var fireSelectionChanged = false; if (options.onselect) { if (evt.metaKey) { if (thisTab.hasClass("selected")) { thisTab.removeClass("selected"); if (thisTab[0] !== currentTab[0]) { // Deselect background tab // - don't switch to it selectionChanged(); return; } else { // Deselect current tab // - if nothing remains selected, do nothing // - otherwise switch to first selected tab var selection = ul.find("li.red-ui-tab.selected"); if (selection.length === 0) { selectionChanged(); return; } thisTab = selection.first(); } } else { if (!currentTab.hasClass("selected")) { var currentTabObj = tabs[currentTab.find('a').attr('href').slice(1)]; // Auto select current tab currentTab.addClass("selected"); } thisTab.addClass("selected"); } fireSelectionChanged = true; } else if (evt.shiftKey) { if (currentTab[0] !== thisTab[0]) { var firstTab,lastTab; if (currentTab.index() < thisTab.index()) { firstTab = currentTab; lastTab = thisTab; } else { firstTab = thisTab; lastTab = currentTab; } ul.find("li.red-ui-tab").removeClass("selected"); firstTab.addClass("selected"); lastTab.addClass("selected"); firstTab.nextUntil(lastTab).addClass("selected"); } fireSelectionChanged = true; } else { var selection = ul.find("li.red-ui-tab.selected"); if (selection.length > 0) { selection.removeClass("selected"); fireSelectionChanged = true; } } } var thisTabA = thisTab.find("a"); if (options.onclick) { options.onclick(tabs[thisTabA.attr('href').slice(1)]); } activateTab(thisTabA); if (fireSelectionChanged) { selectionChanged(); } return false; } function updateScroll() { if (ul.children().length !== 0) { var sl = scrollContainer.scrollLeft(); var scWidth = scrollContainer.width(); var ulWidth = ul.width(); if (sl === 0) { scrollLeft.hide(); } else { scrollLeft.show(); } if (sl === ulWidth-scWidth) { scrollRight.hide(); } else { scrollRight.show(); } } } function onTabDblClick(evt) { evt.preventDefault(); evt.stopPropagation(); if (evt.metaKey || evt.shiftKey) { return; } if (options.ondblclick) { options.ondblclick(tabs[$(this).attr('href').slice(1)]); } return false; } function activateTab(link) { if (typeof link === "string") { link = ul.find("a[href='#"+link+"']"); } if (link.length === 0) { return; } if (!link.parent().hasClass("active")) { ul.children().removeClass("active"); ul.children().css({"transition": "width 100ms"}); link.parent().addClass("active"); var parentId = link.parent().attr('id'); wrapper.find(".red-ui-tab-link-button").removeClass("active selected"); $("#"+parentId+"-link-button").addClass("active selected"); if (options.scrollable) { var pos = link.parent().position().left; if (pos-21 < 0) { scrollContainer.animate( { scrollLeft: '+='+(pos-50) }, 300); } else if (pos + 120 > scrollContainer.width()) { scrollContainer.animate( { scrollLeft: '+='+(pos + 140-scrollContainer.width()) }, 300); } } if (options.onchange) { options.onchange(tabs[link.attr('href').slice(1)]); } updateTabWidths(); setTimeout(function() { ul.children().css({"transition": ""}); },100); } } function activatePreviousTab() { var previous = ul.find("li.active").prev(); if (previous.length > 0) { activateTab(previous.find("a")); } } function activateNextTab() { var next = ul.find("li.active").next(); if (next.length > 0) { activateTab(next.find("a")); } } function updateTabWidths() { if (options.vertical) { return; } var tabs = ul.find("li.red-ui-tab"); var width = wrapper.width(); var tabCount = tabs.length; var tabWidth; if (options.collapsible) { tabWidth = width - collapsedButtonsRow.width()-10; if (tabWidth < 198) { var delta = 198 - tabWidth; var b = collapsedButtonsRow.find("a:last").prev(); while (b.is(":not(:visible)")) { b = b.prev(); } if (!b.hasClass("red-ui-tab-link-button-pinned")) { b.hide(); } tabWidth = width - collapsedButtonsRow.width()-10; } else { var space = width - 198 - collapsedButtonsRow.width(); if (space > 40) { collapsedButtonsRow.find("a:not(:visible):first").show(); tabWidth = width - collapsedButtonsRow.width()-10; } } tabs.css({width:tabWidth}); } else { var tabWidth = (width-12-(tabCount*6))/tabCount; currentTabWidth = (100*tabWidth/width)+"%"; currentActiveTabWidth = currentTabWidth+"%"; if (options.scrollable) { tabWidth = Math.max(tabWidth,140); currentTabWidth = tabWidth+"px"; currentActiveTabWidth = 0; var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount); ul.width(listWidth); updateScroll(); } else if (options.hasOwnProperty("minimumActiveTabWidth")) { if (tabWidth < options.minimumActiveTabWidth) { tabCount -= 1; tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount; currentTabWidth = (100*tabWidth/width)+"%"; currentActiveTabWidth = options.minimumActiveTabWidth+"px"; } else { currentActiveTabWidth = 0; } } // if (options.collapsible) { // console.log(currentTabWidth); // } tabs.css({width:currentTabWidth}); if (tabWidth < 50) { // ul.find(".red-ui-tab-close").hide(); ul.find(".red-ui-tab-icon").hide(); ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"}) } else { // ul.find(".red-ui-tab-close").show(); ul.find(".red-ui-tab-icon").show(); ul.find(".red-ui-tab-label").css({paddingLeft:""}) } if (currentActiveTabWidth !== 0) { ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth}); // ul.find("li.red-ui-tab.active .red-ui-tab-close").show(); ul.find("li.red-ui-tab.active .red-ui-tab-icon").show(); ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""}) } } } ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick); setTimeout(function() { updateTabWidths(); },0); function removeTab(id) { if (options.onselect) { var selection = ul.find("li.red-ui-tab.selected"); if (selection.length > 0) { selection.removeClass("selected"); selectionChanged(); } } var li = ul.find("a[href='#"+id+"']").parent(); if (li.hasClass("active")) { var tab = li.prev(); if (tab.length === 0) { tab = li.next(); } activateTab(tab.find("a")); } li.remove(); if (tabs[id].pinned) { pinnedTabsCount--; } if (options.onremove) { options.onremove(tabs[id]); } delete tabs[id]; updateTabWidths(); collapsibleMenu = null; } return { addTab: function(tab,targetIndex) { if (options.onselect) { var selection = ul.find("li.red-ui-tab.selected"); if (selection.length > 0) { selection.removeClass("selected"); selectionChanged(); } } tabs[tab.id] = tab; var li = $("
  • ",{class:"red-ui-tab"}); if (ul.children().length === 0) { targetIndex = undefined; } if (targetIndex === 0) { li.prependTo(ul); } else if (targetIndex > 0) { li.insertAfter(ul.find("li:nth-child("+(targetIndex)+")")); } else { li.appendTo(ul); } li.attr('id',"red-ui-tab-"+(tab.id.replace(".","-"))); li.data("tabId",tab.id); if (options.maximumTabWidth) { li.css("maxWidth",options.maximumTabWidth+"px"); } var link = $("",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li); if (tab.icon) { $('').appendTo(link); } else if (tab.iconClass) { $('',{class:"red-ui-tab-icon "+tab.iconClass}).appendTo(link); } var span = $('',{class:"bidiAware"}).text(tab.label).appendTo(link); span.attr('dir', RED.text.bidi.resolveBaseTextDir(tab.label)); if (options.collapsible) { li.addClass("red-ui-tab-pinned"); var pinnedLink = $(''); if (tab.pinned) { if (pinnedTabsCount === 0) { pinnedLink.prependTo(collapsedButtonsRow) } else { pinnedLink.insertAfter(collapsedButtonsRow.find("a.red-ui-tab-link-button-pinned:last")); } } else { if (options.menu !== false) { pinnedLink.insertBefore(collapsedButtonsRow.find("a:last")); } else { pinnedLink.appendTo(collapsedButtonsRow); } } pinnedLink.attr('id',li.attr('id')+"-link-button"); if (tab.iconClass) { $('',{class:tab.iconClass}).appendTo(pinnedLink); } else { $('',{class:defaultTabIcon}).appendTo(pinnedLink); } pinnedLink.on("click", function(evt) { evt.preventDefault(); activateTab(tab.id); }); if (tab.pinned) { pinnedLink.addClass("red-ui-tab-link-button-pinned"); pinnedTabsCount++; } RED.popover.tooltip($(pinnedLink), tab.name, tab.action); } link.on("click",onTabClick); link.on("dblclick",onTabDblClick); if (tab.closeable) { li.addClass("red-ui-tabs-closeable") var closeLink = $("",{href:"#",class:"red-ui-tab-close"}).appendTo(li); closeLink.append(''); closeLink.on("click",function(event) { event.preventDefault(); removeTab(tab.id); }); } var badges = $('').appendTo(li); if (options.onselect) { $('').appendTo(badges); $('').appendTo(badges); } if (options.onadd) { options.onadd(tab); } link.attr("title",tab.label); if (ul.find("li.red-ui-tab").length == 1) { activateTab(link); } if (options.onreorder) { var originalTabOrder; var tabDragIndex; var tabElements = []; var startDragIndex; li.draggable({ axis:"x", distance: 20, start: function(event,ui) { originalTabOrder = []; tabElements = []; ul.children().each(function(i) { tabElements[i] = { el:$(this), text: $(this).text(), left: $(this).position().left, width: $(this).width() }; if ($(this).is(li)) { tabDragIndex = i; startDragIndex = i; } originalTabOrder.push($(this).data("tabId")); }); ul.children().each(function(i) { if (i!==tabDragIndex) { $(this).css({ position: 'absolute', left: tabElements[i].left+"px", width: tabElements[i].width+2, transition: "left 0.3s" }); } }) if (!li.hasClass('active')) { li.css({'zIndex':1}); } }, drag: function(event,ui) { ui.position.left += tabElements[tabDragIndex].left+scrollContainer.scrollLeft(); var tabCenter = ui.position.left + tabElements[tabDragIndex].width/2 - scrollContainer.scrollLeft(); for (var i=0;i tabElements[i].left && tabCenter < tabElements[i].left+tabElements[i].width) { if (i < tabDragIndex) { tabElements[i].left += tabElements[tabDragIndex].width+8; tabElements[tabDragIndex].el.detach().insertBefore(tabElements[i].el); } else { tabElements[i].left -= tabElements[tabDragIndex].width+8; tabElements[tabDragIndex].el.detach().insertAfter(tabElements[i].el); } tabElements[i].el.css({left:tabElements[i].left+"px"}); tabElements.splice(i, 0, tabElements.splice(tabDragIndex, 1)[0]); tabDragIndex = i; break; } } }, stop: function(event,ui) { ul.children().css({position:"relative",left:"",transition:""}); if (!li.hasClass('active')) { li.css({zIndex:""}); } updateTabWidths(); if (startDragIndex !== tabDragIndex) { options.onreorder(originalTabOrder, $.makeArray(ul.children().map(function() { return $(this).data('tabId');}))); } activateTab(tabElements[tabDragIndex].el.data('tabId')); } }) } setTimeout(function() { updateTabWidths(); },10); collapsibleMenu = null; }, removeTab: removeTab, activateTab: activateTab, nextTab: activateNextTab, previousTab: activatePreviousTab, resize: updateTabWidths, count: function() { return ul.find("li.red-ui-tab").length; }, contains: function(id) { return ul.find("a[href='#"+id+"']").length > 0; }, renameTab: function(id,label) { tabs[id].label = label; var tab = ul.find("a[href='#"+id+"']"); tab.attr("title",label); tab.find("span.bidiAware").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label)); updateTabWidths(); }, selection: getSelection, order: function(order) { var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');})); if (existingTabOrder.length !== order.length) { return } var i; var match = true; for (i=0;i