/* * Tab control * * Documentation: ../docs/tab.md * * Require: * - bootstrap/transition * - bootstrap/tab * - storm/toolbar */ +function ($) { "use strict"; var Tab = function (element, options) { var $el = this.$el = $(element); this.options = options || {} this.$tabsContainer = $('.nav-tabs:first', $el) this.$pagesContainer = $('.tab-content:first', $el) this.tabId = 'tabs' + $el.parents().length + Math.round(Math.random()*1000); if (this.options.closable !== undefined && this.options.closable !== false) $el.attr('data-closable', '') this.init() } Tab.prototype.init = function() { var self = this; this.options.slidable = this.options.slidable !== undefined && this.options.slidable !== false $('> li', this.$tabsContainer).each(function(index){ self.initTab(this) }) this.$el.on('close.oc.tab', function(ev, data){ ev.preventDefault() var force = (data !== undefined && data.force !== undefined) ? data.force : false; self.closeTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div'), force) }) this.$el.on('mousedown', "li[data-tab-id]", function (ev) { if (ev.key === '2') { $(ev.target).trigger('close.oc.tab'); } }) this.$el.on('toggleCollapse.oc.tab', function(ev, data){ ev.preventDefault() $(ev.target).closest('div.tab-content > div').toggleClass('collapsed') }) this.$el.on('modified.oc.tab', function(ev){ ev.preventDefault() self.modifyTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div')) }) this.$el.on('unmodified.oc.tab', function(ev){ ev.preventDefault() self.unmodifyTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div')) }) this.$tabsContainer.on('shown.bs.tab', 'li', function(){ // self.$tabsContainer.dragScroll('fixScrollClasses') $(window).trigger('oc.updateUi') var tabUrl = $('> a', this).data('tabUrl') if (tabUrl) { window.history.replaceState({}, 'Tab link reference', tabUrl) } }) if (this.options.slidable) { this.$pagesContainer.touchwipe({ wipeRight: function(){self.prev();}, wipeLeft: function(){self.next();}, preventDefaultEvents: false, min_move_x: 60 }); } this.$tabsContainer.toolbar({ scrollClassContainer: this.$el }) this.updateClasses() } Tab.prototype.initTab = function(li) { var $tabs = $('>li', this.$tabsContainer), tabIndex = $tabs.index(li), time = new Date().getTime(), targetId = this.tabId + '-tab-' + tabIndex + time, $anchor = $('a', li) $anchor .data('target', '#'+targetId) .attr('data-target', '#'+targetId) .attr('data-toggle', 'tab') if (!$anchor.attr('title')) $anchor.attr('title', $anchor.text()) var html = $anchor.html() $anchor.html('') $anchor .append($('') .append($('').html(html))) var pane = $('> .tab-pane', this.$pagesContainer).eq(tabIndex).attr('id', targetId) if (!$('span.tab-close', li).length) { $(li).append($('×').click(function(){ $(this).trigger('close.oc.tab') return false })) } pane.data('tab', li) this.$el.trigger('initTab.oc.tab', [{'pane': pane, 'tab': li}]) } Tab.prototype.addTab = function(title, content, identifier, tabClass) { var processedTitle = this.generateTitleText(title, -1), $link = $('').attr('href', 'javascript:;').text(processedTitle), $li = $('
  • '), $pane = $('
    ').html(content).addClass('tab-pane'); $link.attr('title', title) $li.append($link) this.$tabsContainer.append($li) this.$pagesContainer.append($pane) if (tabClass !== undefined) $link.addClass(tabClass) if (identifier !== undefined) $li.attr('data-tab-id', identifier) if (this.options.paneClasses !== undefined) $pane.addClass(this.options.paneClasses) this.initTab($li) $link.tab('show') $(window).trigger('resize') this.$tabsContainer.dragScroll('goToElement', $li) var defaultFocus = $('[default-focus]', $pane) if (defaultFocus.is(":visible")) defaultFocus.focus() this.updateClasses() } Tab.prototype.updateTab = function(tab, title, content) { var tabIndex = this.findTabIndex(tab) if (tabIndex == -1) return var processedTitle = this.generateTitleText(title, -1), $tab = $('> li', this.$tabsContainer).eq(tabIndex), $pane = $('> div', this.$pagesContainer).eq(tabIndex), $link = $('a', $tab) $link.text(processedTitle).attr('title', title) $pane.html(content) this.initTab($tab) this.updateClasses() } Tab.prototype.generateTitleText = function(title, tabIndex) { var newTitle = title if (this.options.titleAsFileNames) newTitle = title.replace(/^.*[\\\/]/, '') if (this.options.maxTitleSymbols && newTitle.length > this.options.maxTitleSymbols) newTitle = '...'+newTitle.substring(newTitle.length - this.options.maxTitleSymbols) return newTitle } Tab.prototype.closeTab = function(tab, force) { var tabIndex = this.findTabIndex(tab) if (tabIndex == -1) return var $tab = $('> li', this.$tabsContainer).eq(tabIndex), $pane = $('> div', this.$pagesContainer).eq(tabIndex), isActive = $tab.hasClass('active'), isModified = $tab.attr('data-modified') !== undefined; if (isModified && this.options.closeConfirmation !== undefined && force !== true) { if (!confirm(this.options.closeConfirmation)) return } var e = $.Event('beforeClose.oc.tab', { relatedTarget: $pane }) this.$el.trigger(e) if (e.isDefaultPrevented()) return $.oc.foundation.controlUtils.disposeControls($pane.get(0)) $pane.remove() $tab.remove() if (isActive) $('> li > a', this.$tabsContainer).eq(tabIndex-1).tab('show') if ($('> li > a', this.$tabsContainer).length == 0) this.$el.trigger('afterAllClosed.oc.tab') this.$el.trigger('closed.oc.tab', [$tab, $pane]) $(window).trigger('resize') this.updateClasses() } Tab.prototype.updateClasses = function() { if (this.$tabsContainer.children().length > 0) this.$el.addClass('has-tabs') else this.$el.removeClass('has-tabs') } Tab.prototype.modifyTab = function(tab) { var tabIndex = this.findTabIndex(tab) if (tabIndex == -1) return $('> li', this.$tabsContainer).eq(tabIndex).attr('data-modified', '') $('> div', this.$pagesContainer).eq(tabIndex).attr('data-modified', '') } Tab.prototype.unmodifyTab = function(tab) { var tabIndex = this.findTabIndex(tab) if (tabIndex == -1) return $('> li', this.$tabsContainer).eq(tabIndex).removeAttr('data-modified') $('> div', this.$pagesContainer).eq(tabIndex).removeAttr('data-modified') } Tab.prototype.findTabIndex = function(tab) { var tabToFind = tab if (tab === undefined) tabToFind = $('li.active', this.$tabsContainer) var tabParent = this.$pagesContainer if ($(tabToFind).parent().hasClass('nav-tabs')) tabParent = this.$tabsContainer return tabParent.children().index($(tabToFind)) } Tab.prototype.findTabFromPane = function(pane) { var id = '#' + $(pane).attr('id'), tab = $('[data-target="' + id + '"]', this.$tabsContainer) return tab } Tab.prototype.findPaneFromTab = function(tab) { var id = $(tab).find('> a').data('target'), pane = this.$pagesContainer.find(id) return pane } Tab.prototype.goTo = function(identifier) { var $tab = $('[data-tab-id="'+identifier+'" ]', this.$tabsContainer) if ($tab.length == 0) return false var tabIndex = this.findTabIndex($tab) if (tabIndex == -1) return false this.goToIndex(tabIndex) this.$tabsContainer.dragScroll('goToElement', $tab) return true } Tab.prototype.goToPane = function(pane) { var $pane = $(pane), $tab = this.findTabFromPane($pane) if ($pane.length == 0) return $pane.removeClass('collapsed') var tabIndex = this.findTabIndex($pane) if (tabIndex == -1) return false this.goToIndex(tabIndex) if ($tab.length > 0) this.$tabsContainer.dragScroll('goToElement', $tab) return true } Tab.prototype.goToElement = function(element) { return this.goToPane(element.closest('.tab-pane')) } Tab.prototype.findByIdentifier = function(identifier) { return $('[data-tab-id="'+identifier+'" ]', this.$tabsContainer); } Tab.prototype.updateIdentifier = function(tab, identifier) { var index = this.findTabIndex(tab) if (index == -1) return $('> li', this.$tabsContainer).eq(index).attr('data-tab-id', identifier) } Tab.prototype.updateTitle = function(tab, title) { var index = this.findTabIndex(tab) if (index == -1) return var processedTitle = this.generateTitleText(title, index), $link = $('> li > a span.title', this.$tabsContainer).eq(index) $link.attr('title', title) $link.text(processedTitle) } Tab.prototype.goToIndex = function(index) { $('> li > a', this.$tabsContainer).eq(index).tab('show') } Tab.prototype.prev = function() { var tabIndex = this.findTabIndex() if (tabIndex <= 0) return this.goToIndex(tabIndex-1) } Tab.prototype.next = function() { var tabIndex = this.findTabIndex() if (tabIndex == -1) return this.goToIndex(tabIndex+1) } Tab.DEFAULTS = { } // TAB PLUGIN DEFINITION // ============================ var old = $.fn.ocTab $.fn.ocTab = function (option) { var args = arguments; return this.each(function () { var $this = $(this) var data = $this.data('oc.tab') var options = $.extend({}, Tab.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('oc.tab', (data = new Tab(this, options))) if (typeof option == 'string') { var methodArgs = []; for (var i=1; i