diff --git a/muk_web_theme/__manifest__.py b/muk_web_theme/__manifest__.py index a532cdd..a96a603 100644 --- a/muk_web_theme/__manifest__.py +++ b/muk_web_theme/__manifest__.py @@ -20,7 +20,7 @@ { "name": "MuK Backend Theme", "summary": "Odoo 12.0 community backend theme", - "version": "12.0.1.1.2", + "version": "12.0.1.1.3", "category": "Themes/Backend", "license": "AGPL-3", "author": "MuK IT", diff --git a/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.css b/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.css deleted file mode 100644 index 0fce147..0000000 --- a/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.css +++ /dev/null @@ -1,61 +0,0 @@ -/** - * jquery-bootstrap-scrolling-tabs - * @version v2.4.0 - * @link https://github.com/mikejacobson/jquery-bootstrap-scrolling-tabs - * @author Mike Jacobson - * @license MIT License, http://www.opensource.org/licenses/MIT - */ -.scrtabs-tab-container * { - box-sizing: border-box; } - -.scrtabs-tab-container { - height: 42px; } - .scrtabs-tab-container .tab-content { - clear: left; } - -.scrtabs-tab-container.scrtabs-bootstrap4 .scrtabs-tabs-movable-container > .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; } - -.scrtabs-tabs-fixed-container { - float: left; - height: 42px; - overflow: hidden; - width: 100%; } - -.scrtabs-tabs-movable-container { - position: relative; } - .scrtabs-tabs-movable-container .tab-content { - display: none; } - -.scrtabs-tab-container.scrtabs-rtl .scrtabs-tabs-movable-container > ul.nav-tabs { - padding-right: 0; } - -.scrtabs-tab-scroll-arrow { - border: 1px solid #dddddd; - color: #428bca; - display: none; - float: left; - font-size: 12px; - padding: 0.55rem 0.5rem; - width: 20px; } - .scrtabs-tab-scroll-arrow:hover { - background-color: #eeeeee; } - -.scrtabs-tab-scroll-arrow, -.scrtabs-tab-scroll-arrow .scrtabs-click-target { - cursor: pointer; } - -.scrtabs-tab-scroll-arrow.scrtabs-with-click-target { - cursor: default; } - -.scrtabs-tab-scroll-arrow.scrtabs-disable, -.scrtabs-tab-scroll-arrow.scrtabs-disable .scrtabs-click-target { - color: #ddd; - cursor: default; } - -.scrtabs-tab-scroll-arrow.scrtabs-disable:hover { - background-color: initial; } - -.scrtabs-tabs-fixed-container ul.nav-tabs > li { - white-space: nowrap; } \ No newline at end of file diff --git a/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.js b/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.js deleted file mode 100644 index 4b33cb1..0000000 --- a/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.js +++ /dev/null @@ -1,1963 +0,0 @@ -/** - * jquery-bootstrap-scrolling-tabs - * @version v2.4.0 - * @link https://github.com/mikejacobson/jquery-bootstrap-scrolling-tabs - * @author Mike Jacobson - * @license MIT License, http://www.opensource.org/licenses/MIT - */ -/** - * jQuery plugin version of Angular directive angular-bootstrap-scrolling-tabs: - * https://github.com/mikejacobson/angular-bootstrap-scrolling-tabs - * - * Usage: - * - * Use case #1: HTML-defined tabs - * ------------------------------ - * Demo: http://plnkr.co/edit/thyD0grCxIjyU4PoTt4x?p=preview - * - * Sample HTML: - * - * - * - * - * - *
- *
Tab 1 content...
- *
Tab 2 content...
- *
Tab 3 content...
- *
Tab 4 content...
- *
- * - * - * JavaScript: - * - * $('.nav-tabs').scrollingTabs(); - * - * - * Use Case #2: Data-driven tabs - * ----------------------------- - * Demo: http://plnkr.co/edit/MWBjLnTvJeetjU3NEimg?p=preview - * - * Sample HTML: - * - * - *
- * - * - * JavaScript: - * - * $('#tabs-inside-here').scrollingTabs({ - * tabs: tabs, // required - * propPaneId: 'paneId', // optional - * propTitle: 'title', // optional - * propActive: 'active', // optional - * propDisabled: 'disabled', // optional - * propContent: 'content', // optional - * ignoreTabPanes: false, // optional - * scrollToTabEdge: false, // optional - * disableScrollArrowsOnFullyScrolled: false, // optional - * reverseScroll: false // optional - * }); - * - * Settings/Options: - * - * tabs: tabs data array - * prop*: name of your tab object's property name that - * corresponds to that required tab property if - * your property name is different than the - * standard name (paneId, title, etc.) - * tabsLiContent: - * optional string array used to define custom HTML - * for each tab's
  • element. Each entry is an HTML - * string defining the tab
  • element for the - * corresponding tab in the tabs array. - * The default for a tab is: - * '
  • ' - * So, for example, if you had 3 tabs and you needed - * a custom 'tooltip' attribute on each one, your - * tabsLiContent array might look like this: - * [ - * '', - * '', - * '' - * ] - * This plunk demonstrates its usage (in conjunction - * with tabsPostProcessors): - * http://plnkr.co/edit/ugJLMk7lmDCuZQziQ0k0 - * tabsPostProcessors: - * optional array of functions, each one associated - * with an entry in the tabs array. When a tab element - * has been created, its associated post-processor - * function will be called with two arguments: the - * newly created $li and $a jQuery elements for that tab. - * This allows you to, for example, attach a custom - * event listener to each anchor tag. - * This plunk demonstrates its usage (in conjunction - * with tabsLiContent): - * http://plnkr.co/edit/ugJLMk7lmDCuZQziQ0k0 - * ignoreTabPanes: relevant for data-driven tabs only--set to true if - * you want the plugin to only touch the tabs - * and to not generate the tab pane elements - * that go in .tab-content. By default, the plugin - * will generate the tab panes based on the content - * property in your tab data, if a content property - * is present. - * scrollToTabEdge: set to true if you want to force full-width tabs - * to display at the left scroll arrow. i.e., if the - * scrolling stops with only half a tab showing, - * it will snap the tab to its edge so the full tab - * shows. - * disableScrollArrowsOnFullyScrolled: - * set to true if you want the left scroll arrow to - * disable when the tabs are scrolled fully left, - * and the right scroll arrow to disable when the tabs - * are scrolled fully right. - * reverseScroll: - * set to true if you want the left scroll arrow to - * slide the tabs left instead of right, and the right - * scroll arrow to slide the tabs right. - * enableSwiping: - * set to true if you want to enable horizontal swiping - * for touch screens. - * widthMultiplier: - * set to a value less than 1 if you want the tabs - * container to be less than the full width of its - * parent element. For example, set it to 0.5 if you - * want the tabs container to be half the width of - * its parent. - * tabClickHandler: - * a callback function to execute any time a tab is clicked. - * The function is simply passed as the event handler - * to jQuery's .on(), so the function will receive - * the jQuery event as an argument, and the 'this' - * inside the function will be the clicked tab's anchor - * element. - * cssClassLeftArrow, cssClassRightArrow: - * custom values for the class attributes for the - * left and right scroll arrows. The defaults are - * 'glyphicon glyphicon-chevron-left' and - * 'glyphicon glyphicon-chevron-right'. - * Using different icons might require you to add - * custom styling to the arrows to position the icons - * correctly; the arrows can be targeted with these - * selectors: - * .scrtabs-tab-scroll-arrow - * .scrtabs-tab-scroll-arrow-left - * .scrtabs-tab-scroll-arrow-right - * leftArrowContent, rightArrowContent: - * custom HTML string for the left and right scroll - * arrows. This will override any custom cssClassLeftArrow - * and cssClassRightArrow settings. - * For example, if you wanted to use svg icons, you - * could set them like so: - * - * leftArrowContent: [ - * '
    ', - * ' ', - * ' ', - * ' ', - * '
    ' - * ].join(''), - * rightArrowContent: [ - * '
    ', - * ' ', - * ' ', - * ' ', - * '
    ' - * ].join('') - * - * You would then need to add some CSS to make them - * work correctly if you don't give them the - * default scrtabs-tab-scroll-arrow classes. - * This plunk shows it working with svg icons: - * http://plnkr.co/edit/2MdZCAnLyeU40shxaol3?p=preview - * - * When using this option, you can also mark a child - * element within the arrow content as the click target - * if you don't want the entire content to be - * clickable. You do that my adding the CSS class - * 'scrtabs-click-target' to the element that should - * be clickable, like so: - * - * leftArrowContent: [ - * '
    ', - * ' ', - * '
    ' - * ].join(''), - * rightArrowContent: [ - * '
    ', - * ' ', - * '
    ' - * ].join('') - * - * enableRtlSupport: - * set to true if you want your site to support - * right-to-left languages. If true, the plugin will - * check the page's tag for attribute dir="rtl" - * and will adjust its behavior accordingly. - * bootstrapVersion: - * set to 4 if you're using Boostrap 4. Default is 3. - * Bootstrap 4 handles some things differently than 3 - * (e.g., the 'active' class gets applied to the tab's - * 'li > a' element rather than the 'li' itself). - * - * - * On tabs data change: - * - * $('#tabs-inside-here').scrollingTabs('refresh'); - * - * On tabs data change, if you want the active tab to be set based on - * the updated tabs data (i.e., you want to override the current - * active tab setting selected by the user), for example, if you - * added a new tab and you want it to be the active tab: - * - * $('#tabs-inside-here').scrollingTabs('refresh', { - * forceActiveTab: true - * }); - * - * Any options that can be passed into the plugin can be set on the - * plugin's 'defaults' object instead so you don't have to pass them in: - * - * $.fn.scrollingTabs.defaults.tabs = tabs; - * $.fn.scrollingTabs.defaults.forceActiveTab = true; - * $.fn.scrollingTabs.defaults.scrollToTabEdge = true; - * $.fn.scrollingTabs.defaults.disableScrollArrowsOnFullyScrolled = true; - * $.fn.scrollingTabs.defaults.reverseScroll = true; - * $.fn.scrollingTabs.defaults.widthMultiplier = 0.5; - * $.fn.scrollingTabs.defaults.tabClickHandler = function () { }; - * - * - * Methods - * ----------------------------- - * - refresh - * On window resize, the tabs should refresh themselves, but to force a refresh: - * - * $('.nav-tabs').scrollingTabs('refresh'); - * - * - scrollToActiveTab - * On window resize, the active tab will automatically be scrolled to - * if it ends up offscreen, but you can also programmatically force a - * scroll to the active tab any time (if, for example, you're - * programmatically setting the active tab) by calling the - * 'scrollToActiveTab' method: - * - * $('.nav-tabs').scrollingTabs('scrollToActiveTab'); - * - * - * Events - * ----------------------------- - * The plugin triggers event 'ready.scrtabs' when the tabs have - * been wrapped in the scroller and are ready for viewing: - * - * $('.nav-tabs') - * .scrollingTabs() - * .on('ready.scrtabs', function() { - * // tabs ready, do my other stuff... - * }); - * - * $('#tabs-inside-here') - * .scrollingTabs({ tabs: tabs }) - * .on('ready.scrtabs', function() { - * // tabs ready, do my other stuff... - * }); - * - * - * Destroying - * ----------------------------- - * To destroy: - * - * $('.nav-tabs').scrollingTabs('destroy'); - * - * $('#tabs-inside-here').scrollingTabs('destroy'); - * - * If you were wrapping markup, the markup will be restored; if your tabs - * were data-driven, the tabs will be destroyed along with the plugin. - * - */ - -;(function ($, window) { - 'use strict'; - /* jshint unused:false */ - - /* exported CONSTANTS */ - var CONSTANTS = { - CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL: 50, // timeout interval for repeatedly moving the tabs container - // by one increment while the mouse is held down--decrease to - // make mousedown continous scrolling faster - SCROLL_OFFSET_FRACTION: 6, // each click moves the container this fraction of the fixed container--decrease - // to make the tabs scroll farther per click - - DATA_KEY_DDMENU_MODIFIED: 'scrtabsddmenumodified', - DATA_KEY_IS_MOUSEDOWN: 'scrtabsismousedown', - - CSS_CLASSES: { - BOOTSTRAP4: 'scrtabs-bootstrap4', - RTL: 'scrtabs-rtl', - SCROLL_ARROW_CLICK_TARGET: 'scrtabs-click-target', - SCROLL_ARROW_DISABLE: 'scrtabs-disable', - SCROLL_ARROW_WITH_CLICK_TARGET: 'scrtabs-with-click-target' - }, - - SLIDE_DIRECTION: { - LEFT: 1, - RIGHT: 2 - }, - - EVENTS: { - CLICK: 'click.scrtabs', - DROPDOWN_MENU_HIDE: 'hide.bs.dropdown.scrtabs', - DROPDOWN_MENU_SHOW: 'show.bs.dropdown.scrtabs', - FORCE_REFRESH: 'forcerefresh.scrtabs', - MOUSEDOWN: 'mousedown.scrtabs', - MOUSEUP: 'mouseup.scrtabs', - TABS_READY: 'ready.scrtabs', - TOUCH_END: 'touchend.scrtabs', - TOUCH_MOVE: 'touchmove.scrtabs', - TOUCH_START: 'touchstart.scrtabs', - WINDOW_RESIZE: 'resize.scrtabs' - } - }; - - // smartresize from Paul Irish (debounced window resize) - (function (sr) { - var debounce = function (func, threshold, execAsap) { - var timeout; - - return function debounced() { - var obj = this, args = arguments; - function delayed() { - if (!execAsap) { - func.apply(obj, args); - } - timeout = null; - } - - if (timeout) { - clearTimeout(timeout); - } else if (execAsap) { - func.apply(obj, args); - } - - timeout = setTimeout(delayed, threshold || 100); - }; - }; - $.fn[sr] = function (fn, customEventName) { - var eventName = customEventName || CONSTANTS.EVENTS.WINDOW_RESIZE; - return fn ? this.bind(eventName, debounce(fn)) : this.trigger(sr); - }; - - })('smartresizeScrtabs'); - - /* *********************************************************************************** - * ElementsHandler - Class that each instance of ScrollingTabsControl will instantiate - * **********************************************************************************/ - function ElementsHandler(scrollingTabsControl) { - var ehd = this; - - ehd.stc = scrollingTabsControl; - } - - // ElementsHandler prototype methods - (function (p) { - p.initElements = function (options) { - var ehd = this; - - ehd.setElementReferences(options); - ehd.setEventListeners(options); - }; - - p.listenForTouchEvents = function () { - var ehd = this, - stc = ehd.stc, - smv = stc.scrollMovement, - ev = CONSTANTS.EVENTS; - - var touching = false; - var touchStartX; - var startingContainerLeftPos; - var newLeftPos; - - stc.$movableContainer - .on(ev.TOUCH_START, function (e) { - touching = true; - startingContainerLeftPos = stc.movableContainerLeftPos; - touchStartX = e.originalEvent.changedTouches[0].pageX; - }) - .on(ev.TOUCH_END, function () { - touching = false; - }) - .on(ev.TOUCH_MOVE, function (e) { - if (!touching) { - return; - } - - var touchPageX = e.originalEvent.changedTouches[0].pageX; - var diff = touchPageX - touchStartX; - if (stc.rtl) { - diff = -diff; - } - var minPos; - - newLeftPos = startingContainerLeftPos + diff; - if (newLeftPos > 0) { - newLeftPos = 0; - } else { - minPos = smv.getMinPos(); - if (newLeftPos < minPos) { - newLeftPos = minPos; - } - } - stc.movableContainerLeftPos = newLeftPos; - - var leftOrRight = stc.rtl ? 'right' : 'left'; - stc.$movableContainer.css(leftOrRight, smv.getMovableContainerCssLeftVal()); - smv.refreshScrollArrowsDisabledState(); - }); - }; - - p.refreshAllElementSizes = function () { - var ehd = this, - stc = ehd.stc, - smv = stc.scrollMovement, - scrollArrowsWereVisible = stc.scrollArrowsVisible, - actionsTaken = { - didScrollToActiveTab: false - }, - isPerformingSlideAnim = false, - minPos; - - ehd.setElementWidths(); - ehd.setScrollArrowVisibility(); - - // this could have been a window resize or the removal of a - // dynamic tab, so make sure the movable container is positioned - // correctly because, if it is far to the left and we increased the - // window width, it's possible that the tabs will be too far left, - // beyond the min pos. - if (stc.scrollArrowsVisible) { - // make sure container not too far left - minPos = smv.getMinPos(); - - isPerformingSlideAnim = smv.scrollToActiveTab({ - isOnWindowResize: true - }); - - if (!isPerformingSlideAnim) { - smv.refreshScrollArrowsDisabledState(); - - if (stc.rtl) { - if (stc.movableContainerRightPos < minPos) { - smv.incrementMovableContainerLeft(minPos); - } - } else { - if (stc.movableContainerLeftPos < minPos) { - smv.incrementMovableContainerRight(minPos); - } - } - } - - actionsTaken.didScrollToActiveTab = true; - - } else if (scrollArrowsWereVisible) { - // scroll arrows went away after resize, so position movable container at 0 - stc.movableContainerLeftPos = 0; - smv.slideMovableContainerToLeftPos(); - } - - return actionsTaken; - }; - - p.setElementReferences = function (settings) { - var ehd = this, - stc = ehd.stc, - $tabsContainer = stc.$tabsContainer, - $leftArrow, - $rightArrow, - $leftArrowClickTarget, - $rightArrowClickTarget; - - stc.isNavPills = false; - - if (stc.rtl) { - $tabsContainer.addClass(CONSTANTS.CSS_CLASSES.RTL); - } - - if (stc.usingBootstrap4) { - $tabsContainer.addClass(CONSTANTS.CSS_CLASSES.BOOTSTRAP4); - } - - stc.$fixedContainer = $tabsContainer.find('.scrtabs-tabs-fixed-container'); - $leftArrow = stc.$fixedContainer.prev(); - $rightArrow = stc.$fixedContainer.next(); - - // if we have custom arrow content, we might have a click target defined - if (settings.leftArrowContent) { - $leftArrowClickTarget = $leftArrow.find('.' + CONSTANTS.CSS_CLASSES.SCROLL_ARROW_CLICK_TARGET); - } - - if (settings.rightArrowContent) { - $rightArrowClickTarget = $rightArrow.find('.' + CONSTANTS.CSS_CLASSES.SCROLL_ARROW_CLICK_TARGET); - } - - if ($leftArrowClickTarget && $leftArrowClickTarget.length) { - $leftArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_WITH_CLICK_TARGET); - } else { - $leftArrowClickTarget = $leftArrow; - } - - if ($rightArrowClickTarget && $rightArrowClickTarget.length) { - $rightArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_WITH_CLICK_TARGET); - } else { - $rightArrowClickTarget = $rightArrow; - } - - stc.$movableContainer = $tabsContainer.find('.scrtabs-tabs-movable-container'); - stc.$tabsUl = $tabsContainer.find('.nav-tabs'); - - // check for pills - if (!stc.$tabsUl.length) { - stc.$tabsUl = $tabsContainer.find('.nav-pills'); - - if (stc.$tabsUl.length) { - stc.isNavPills = true; - } - } - - stc.$tabsLiCollection = stc.$tabsUl.find('> li'); - - stc.$slideLeftArrow = stc.reverseScroll ? $leftArrow : $rightArrow; - stc.$slideLeftArrowClickTarget = stc.reverseScroll ? $leftArrowClickTarget : $rightArrowClickTarget; - stc.$slideRightArrow = stc.reverseScroll ? $rightArrow : $leftArrow; - stc.$slideRightArrowClickTarget = stc.reverseScroll ? $rightArrowClickTarget : $leftArrowClickTarget; - stc.$scrollArrows = stc.$slideLeftArrow.add(stc.$slideRightArrow); - - stc.$win = $(window); - }; - - p.setElementWidths = function () { - var ehd = this, - stc = ehd.stc; - - stc.winWidth = stc.$win.width(); - stc.scrollArrowsCombinedWidth = stc.$slideLeftArrow.outerWidth() + stc.$slideRightArrow.outerWidth(); - - ehd.setFixedContainerWidth(); - ehd.setMovableContainerWidth(); - }; - - p.setEventListeners = function (settings) { - var ehd = this, - stc = ehd.stc, - evh = stc.eventHandlers, - ev = CONSTANTS.EVENTS, - resizeEventName = ev.WINDOW_RESIZE + stc.instanceId; - - if (settings.enableSwiping) { - ehd.listenForTouchEvents(); - } - - stc.$slideLeftArrowClickTarget - .off('.scrtabs') - .on(ev.MOUSEDOWN, function (e) { evh.handleMousedownOnSlideMovContainerLeftArrow.call(evh, e); }) - .on(ev.MOUSEUP, function (e) { evh.handleMouseupOnSlideMovContainerLeftArrow.call(evh, e); }) - .on(ev.CLICK, function (e) { evh.handleClickOnSlideMovContainerLeftArrow.call(evh, e); }); - - stc.$slideRightArrowClickTarget - .off('.scrtabs') - .on(ev.MOUSEDOWN, function (e) { evh.handleMousedownOnSlideMovContainerRightArrow.call(evh, e); }) - .on(ev.MOUSEUP, function (e) { evh.handleMouseupOnSlideMovContainerRightArrow.call(evh, e); }) - .on(ev.CLICK, function (e) { evh.handleClickOnSlideMovContainerRightArrow.call(evh, e); }); - - if (stc.tabClickHandler) { - stc.$tabsLiCollection - .find('a[data-toggle="tab"]') - .off(ev.CLICK) - .on(ev.CLICK, stc.tabClickHandler); - } - - stc.$win - .off(resizeEventName) - .smartresizeScrtabs(function (e) { evh.handleWindowResize.call(evh, e); }, resizeEventName); - - $('body').on(CONSTANTS.EVENTS.FORCE_REFRESH, stc.elementsHandler.refreshAllElementSizes.bind(stc.elementsHandler)); - }; - - p.setFixedContainerWidth = function () { - var ehd = this, - stc = ehd.stc, - tabsContainerRect = stc.$tabsContainer.get(0).getBoundingClientRect(); - /** - * @author poletaew - * It solves problem with rounding by jQuery.outerWidth - * If we have real width 100.5 px, jQuery.outerWidth returns us 101 px and we get layout's fail - */ - stc.fixedContainerWidth = tabsContainerRect.width || (tabsContainerRect.right - tabsContainerRect.left); - stc.fixedContainerWidth = stc.fixedContainerWidth * stc.widthMultiplier; - - stc.$fixedContainer.width(stc.fixedContainerWidth); - }; - - p.setFixedContainerWidthForHiddenScrollArrows = function () { - var ehd = this, - stc = ehd.stc; - - stc.$fixedContainer.width(stc.fixedContainerWidth); - }; - - p.setFixedContainerWidthForVisibleScrollArrows = function () { - var ehd = this, - stc = ehd.stc; - - stc.$fixedContainer.width(stc.fixedContainerWidth - stc.scrollArrowsCombinedWidth); - }; - - p.setMovableContainerWidth = function () { - var ehd = this, - stc = ehd.stc, - $tabLi = stc.$tabsUl.find('> li'); - - stc.movableContainerWidth = 0; - - if ($tabLi.length) { - - $tabLi.each(function () { - var $li = $(this), - totalMargin = 0; - - if (stc.isNavPills) { // pills have a margin-left, tabs have no margin - totalMargin = parseInt($li.css('margin-left'), 10) + parseInt($li.css('margin-right'), 10); - } - - stc.movableContainerWidth += ($li.outerWidth() + totalMargin); - }); - - stc.movableContainerWidth += 1; - - // if the tabs don't span the width of the page, force the - // movable container width to full page width so the bottom - // border spans the page width instead of just spanning the - // width of the tabs - if (stc.movableContainerWidth < stc.fixedContainerWidth) { - stc.movableContainerWidth = stc.fixedContainerWidth; - } - } - - stc.$movableContainer.width(stc.movableContainerWidth); - }; - - p.setScrollArrowVisibility = function () { - var ehd = this, - stc = ehd.stc, - shouldBeVisible = stc.movableContainerWidth > stc.fixedContainerWidth; - - if (shouldBeVisible && !stc.scrollArrowsVisible) { - stc.$scrollArrows.show(); - stc.scrollArrowsVisible = true; - } else if (!shouldBeVisible && stc.scrollArrowsVisible) { - stc.$scrollArrows.hide(); - stc.scrollArrowsVisible = false; - } - - if (stc.scrollArrowsVisible) { - ehd.setFixedContainerWidthForVisibleScrollArrows(); - } else { - ehd.setFixedContainerWidthForHiddenScrollArrows(); - } - }; - - }(ElementsHandler.prototype)); - - /* *********************************************************************************** - * EventHandlers - Class that each instance of ScrollingTabsControl will instantiate - * **********************************************************************************/ - function EventHandlers(scrollingTabsControl) { - var evh = this; - - evh.stc = scrollingTabsControl; - } - - // prototype methods - (function (p){ - p.handleClickOnSlideMovContainerLeftArrow = function () { - var evh = this, - stc = evh.stc; - - stc.scrollMovement.incrementMovableContainerLeft(); - }; - - p.handleClickOnSlideMovContainerRightArrow = function () { - var evh = this, - stc = evh.stc; - - stc.scrollMovement.incrementMovableContainerRight(); - }; - - p.handleMousedownOnSlideMovContainerLeftArrow = function () { - var evh = this, - stc = evh.stc; - - stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true); - stc.scrollMovement.continueSlideMovableContainerLeft(); - }; - - p.handleMousedownOnSlideMovContainerRightArrow = function () { - var evh = this, - stc = evh.stc; - - stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true); - stc.scrollMovement.continueSlideMovableContainerRight(); - }; - - p.handleMouseupOnSlideMovContainerLeftArrow = function () { - var evh = this, - stc = evh.stc; - - stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false); - }; - - p.handleMouseupOnSlideMovContainerRightArrow = function () { - var evh = this, - stc = evh.stc; - - stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false); - }; - - p.handleWindowResize = function () { - var evh = this, - stc = evh.stc, - newWinWidth = stc.$win.width(); - - if (newWinWidth === stc.winWidth) { - return false; - } - - stc.winWidth = newWinWidth; - stc.elementsHandler.refreshAllElementSizes(); - }; - - }(EventHandlers.prototype)); - - /* *********************************************************************************** - * ScrollMovement - Class that each instance of ScrollingTabsControl will instantiate - * **********************************************************************************/ - function ScrollMovement(scrollingTabsControl) { - var smv = this; - - smv.stc = scrollingTabsControl; - } - - // prototype methods - (function (p) { - - p.continueSlideMovableContainerLeft = function () { - var smv = this, - stc = smv.stc; - - setTimeout(function() { - if (stc.movableContainerLeftPos <= smv.getMinPos() || - !stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN)) { - return; - } - - if (!smv.incrementMovableContainerLeft()) { // haven't reached max left - smv.continueSlideMovableContainerLeft(); - } - }, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL); - }; - - p.continueSlideMovableContainerRight = function () { - var smv = this, - stc = smv.stc; - - setTimeout(function() { - if (stc.movableContainerLeftPos >= 0 || - !stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN)) { - return; - } - - if (!smv.incrementMovableContainerRight()) { // haven't reached max right - smv.continueSlideMovableContainerRight(); - } - }, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL); - }; - - p.decrementMovableContainerLeftPos = function (minPos) { - var smv = this, - stc = smv.stc; - - stc.movableContainerLeftPos -= (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION); - if (stc.movableContainerLeftPos < minPos) { - stc.movableContainerLeftPos = minPos; - } else if (stc.scrollToTabEdge) { - smv.setMovableContainerLeftPosToTabEdge(CONSTANTS.SLIDE_DIRECTION.LEFT); - - if (stc.movableContainerLeftPos < minPos) { - stc.movableContainerLeftPos = minPos; - } - } - }; - - p.disableSlideLeftArrow = function () { - var smv = this, - stc = smv.stc; - - if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { - return; - } - - stc.$slideLeftArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); - }; - - p.disableSlideRightArrow = function () { - var smv = this, - stc = smv.stc; - - if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { - return; - } - - stc.$slideRightArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); - }; - - p.enableSlideLeftArrow = function () { - var smv = this, - stc = smv.stc; - - if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { - return; - } - - stc.$slideLeftArrow.removeClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); - }; - - p.enableSlideRightArrow = function () { - var smv = this, - stc = smv.stc; - - if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { - return; - } - - stc.$slideRightArrow.removeClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); - }; - - p.getMinPos = function () { - var smv = this, - stc = smv.stc; - - return stc.scrollArrowsVisible ? (stc.fixedContainerWidth - stc.movableContainerWidth - stc.scrollArrowsCombinedWidth) : 0; - }; - - p.getMovableContainerCssLeftVal = function () { - var smv = this, - stc = smv.stc; - - return (stc.movableContainerLeftPos === 0) ? '0' : stc.movableContainerLeftPos + 'px'; - }; - - p.incrementMovableContainerLeft = function () { - var smv = this, - stc = smv.stc, - minPos = smv.getMinPos(); - - smv.decrementMovableContainerLeftPos(minPos); - smv.slideMovableContainerToLeftPos(); - smv.enableSlideRightArrow(); - - // return true if we're fully left, false otherwise - return (stc.movableContainerLeftPos === minPos); - }; - - p.incrementMovableContainerRight = function (minPos) { - var smv = this, - stc = smv.stc; - - // if minPos passed in, the movable container was beyond the minPos - if (minPos) { - stc.movableContainerLeftPos = minPos; - } else { - stc.movableContainerLeftPos += (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION); - - if (stc.movableContainerLeftPos > 0) { - stc.movableContainerLeftPos = 0; - } else if (stc.scrollToTabEdge) { - smv.setMovableContainerLeftPosToTabEdge(CONSTANTS.SLIDE_DIRECTION.RIGHT); - } - } - - smv.slideMovableContainerToLeftPos(); - smv.enableSlideLeftArrow(); - - // return true if we're fully right, false otherwise - // left pos of 0 is the movable container's max position (farthest right) - return (stc.movableContainerLeftPos === 0); - }; - - p.refreshScrollArrowsDisabledState = function() { - var smv = this, - stc = smv.stc; - - if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { - return; - } - - if (stc.movableContainerLeftPos >= 0) { // movable container fully right - smv.disableSlideRightArrow(); - smv.enableSlideLeftArrow(); - return; - } - - if (stc.movableContainerLeftPos <= smv.getMinPos()) { // fully left - smv.disableSlideLeftArrow(); - smv.enableSlideRightArrow(); - return; - } - - smv.enableSlideLeftArrow(); - smv.enableSlideRightArrow(); - }; - - p.scrollToActiveTab = function () { - var smv = this, - stc = smv.stc, - $activeTab, - $activeTabAnchor, - activeTabLeftPos, - activeTabRightPos, - rightArrowLeftPos, - activeTabWidth, - leftPosOffset, - offsetToMiddle, - leftScrollArrowWidth, - rightScrollArrowWidth; - - if (!stc.scrollArrowsVisible) { - return; - } - - if (stc.usingBootstrap4) { - $activeTabAnchor = stc.$tabsUl.find('li > .nav-link.active'); - if ($activeTabAnchor.length) { - $activeTab = $activeTabAnchor.parent(); - } - } else { - $activeTab = stc.$tabsUl.find('li.active'); - } - - if (!$activeTab || !$activeTab.length) { - return; - } - - rightScrollArrowWidth = stc.$slideRightArrow.outerWidth(); - activeTabWidth = $activeTab.outerWidth(); - - /** - * @author poletaew - * We need relative offset (depends on $fixedContainer), don't absolute - */ - activeTabLeftPos = $activeTab.offset().left - stc.$fixedContainer.offset().left; - activeTabRightPos = activeTabLeftPos + activeTabWidth; - - rightArrowLeftPos = stc.fixedContainerWidth - rightScrollArrowWidth; - - if (stc.rtl) { - leftScrollArrowWidth = stc.$slideLeftArrow.outerWidth(); - - if (activeTabLeftPos < 0) { // active tab off left side - stc.movableContainerLeftPos += activeTabLeftPos; - smv.slideMovableContainerToLeftPos(); - return true; - } else { // active tab off right side - if (activeTabRightPos > rightArrowLeftPos) { - stc.movableContainerLeftPos += (activeTabRightPos - rightArrowLeftPos) + (2 * rightScrollArrowWidth); - smv.slideMovableContainerToLeftPos(); - return true; - } - } - } else { - if (activeTabRightPos > rightArrowLeftPos) { // active tab off right side - leftPosOffset = activeTabRightPos - rightArrowLeftPos + rightScrollArrowWidth; - offsetToMiddle = stc.fixedContainerWidth / 2; - leftPosOffset += offsetToMiddle - (activeTabWidth / 2); - stc.movableContainerLeftPos -= leftPosOffset; - smv.slideMovableContainerToLeftPos(); - return true; - } else { - leftScrollArrowWidth = stc.$slideLeftArrow.outerWidth(); - if (activeTabLeftPos < 0) { // active tab off left side - offsetToMiddle = stc.fixedContainerWidth / 2; - stc.movableContainerLeftPos += (-activeTabLeftPos) + offsetToMiddle - (activeTabWidth / 2); - smv.slideMovableContainerToLeftPos(); - return true; - } - } - } - - return false; - }; - - p.setMovableContainerLeftPosToTabEdge = function (slideDirection) { - var smv = this, - stc = smv.stc, - offscreenWidth = -stc.movableContainerLeftPos, - totalTabWidth = 0; - - // make sure LeftPos is set so that a tab edge will be against the - // left scroll arrow so we won't have a partial, cut-off tab - stc.$tabsLiCollection.each(function () { - var tabWidth = $(this).width(); - - totalTabWidth += tabWidth; - - if (totalTabWidth > offscreenWidth) { - stc.movableContainerLeftPos = (slideDirection === CONSTANTS.SLIDE_DIRECTION.RIGHT) ? -(totalTabWidth - tabWidth) : -totalTabWidth; - return false; // exit .each() loop - } - - }); - }; - - p.slideMovableContainerToLeftPos = function () { - var smv = this, - stc = smv.stc, - minPos = smv.getMinPos(), - leftOrRightVal; - - if (stc.movableContainerLeftPos > 0) { - stc.movableContainerLeftPos = 0; - } else if (stc.movableContainerLeftPos < minPos) { - stc.movableContainerLeftPos = minPos; - } - - stc.movableContainerLeftPos = stc.movableContainerLeftPos / 1; - leftOrRightVal = smv.getMovableContainerCssLeftVal(); - - smv.performingSlideAnim = true; - - var targetPos = stc.rtl ? { right: leftOrRightVal } : { left: leftOrRightVal }; - - stc.$movableContainer.stop().animate(targetPos, 'slow', function __slideAnimComplete() { - var newMinPos = smv.getMinPos(); - - smv.performingSlideAnim = false; - - // if we slid past the min pos--which can happen if you resize the window - // quickly--move back into position - if (stc.movableContainerLeftPos < newMinPos) { - smv.decrementMovableContainerLeftPos(newMinPos); - - targetPos = stc.rtl ? { right: smv.getMovableContainerCssLeftVal() } : { left: smv.getMovableContainerCssLeftVal() }; - - stc.$movableContainer.stop().animate(targetPos, 'fast', function() { - smv.refreshScrollArrowsDisabledState(); - }); - } else { - smv.refreshScrollArrowsDisabledState(); - } - }); - }; - - }(ScrollMovement.prototype)); - - /* ********************************************************************** - * ScrollingTabsControl - Class that each directive will instantiate - * **********************************************************************/ - function ScrollingTabsControl($tabsContainer) { - var stc = this; - - stc.$tabsContainer = $tabsContainer; - stc.instanceId = $.fn.scrollingTabs.nextInstanceId++; - - stc.movableContainerLeftPos = 0; - stc.scrollArrowsVisible = false; - stc.scrollToTabEdge = false; - stc.disableScrollArrowsOnFullyScrolled = false; - stc.reverseScroll = false; - stc.widthMultiplier = 1; - - stc.scrollMovement = new ScrollMovement(stc); - stc.eventHandlers = new EventHandlers(stc); - stc.elementsHandler = new ElementsHandler(stc); - } - - // prototype methods - (function (p) { - p.initTabs = function (options, $scroller, readyCallback, attachTabContentToDomCallback) { - var stc = this, - elementsHandler = stc.elementsHandler, - num; - - if (options.enableRtlSupport && $('html').attr('dir') === 'rtl') { - stc.rtl = true; - } - - if (options.scrollToTabEdge) { - stc.scrollToTabEdge = true; - } - - if (options.disableScrollArrowsOnFullyScrolled) { - stc.disableScrollArrowsOnFullyScrolled = true; - } - - if (options.reverseScroll) { - stc.reverseScroll = true; - } - - if (options.widthMultiplier !== 1) { - num = Number(options.widthMultiplier); // handle string value - - if (!isNaN(num)) { - stc.widthMultiplier = num; - } - } - - if (options.bootstrapVersion.toString().charAt(0) === '4') { - stc.usingBootstrap4 = true; - } - - setTimeout(initTabsAfterTimeout, 100); - - function initTabsAfterTimeout() { - var actionsTaken; - - // if we're just wrapping non-data-driven tabs, the user might - // have the .nav-tabs hidden to prevent the clunky flash of - // multi-line tabs on page refresh, so we need to make sure - // they're visible before trying to wrap them - $scroller.find('.nav-tabs').show(); - - elementsHandler.initElements(options); - actionsTaken = elementsHandler.refreshAllElementSizes(); - - $scroller.css('visibility', 'visible'); - - if (attachTabContentToDomCallback) { - attachTabContentToDomCallback(); - } - - if (readyCallback) { - readyCallback(); - } - } - }; - - p.scrollToActiveTab = function(options) { - var stc = this, - smv = stc.scrollMovement; - - smv.scrollToActiveTab(options); - }; - }(ScrollingTabsControl.prototype)); - - - /* exported buildNavTabsAndTabContentForTargetElementInstance */ - var tabElements = (function () { - - return { - getElTabPaneForLi: getElTabPaneForLi, - getNewElNavTabs: getNewElNavTabs, - getNewElScrollerElementWrappingNavTabsInstance: getNewElScrollerElementWrappingNavTabsInstance, - getNewElTabAnchor: getNewElTabAnchor, - getNewElTabContent: getNewElTabContent, - getNewElTabLi: getNewElTabLi, - getNewElTabPane: getNewElTabPane - }; - - /////////////////// - - // ---- retrieve existing elements from the DOM ---------- - function getElTabPaneForLi($li) { - return $($li.find('a').attr('href')); - } - - - // ---- create new elements ---------- - function getNewElNavTabs() { - return $(''); - } - - function getNewElScrollerElementWrappingNavTabsInstance($navTabsInstance, settings) { - var $tabsContainer = $('
    '), - leftArrowContent = settings.leftArrowContent || '
    ', - $leftArrow = $(leftArrowContent), - rightArrowContent = settings.rightArrowContent || '
    ', - $rightArrow = $(rightArrowContent), - $fixedContainer = $('
    '), - $movableContainer = $('
    '); - - if (settings.disableScrollArrowsOnFullyScrolled) { - $leftArrow.add($rightArrow).addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); - } - - return $tabsContainer - .append($leftArrow, - $fixedContainer.append($movableContainer.append($navTabsInstance)), - $rightArrow); - } - - function getNewElTabAnchor(tab, propNames) { - return $('') - .attr('href', '#' + tab[propNames.paneId]) - .html(tab[propNames.title]); - } - - function getNewElTabContent() { - return $('
    '); - } - - function getNewElTabLi(tab, propNames, options) { - var liContent = options.tabLiContent || '
  • ', - $li = $(liContent), - $a = getNewElTabAnchor(tab, propNames).appendTo($li); - - if (tab[propNames.disabled]) { - $li.addClass('disabled'); - $a.attr('data-toggle', ''); - } else if (options.forceActiveTab && tab[propNames.active]) { - $li.addClass('active'); - } - - if (options.tabPostProcessor) { - options.tabPostProcessor($li, $a); - } - - return $li; - } - - function getNewElTabPane(tab, propNames, options) { - var $pane = $('
    ') - .attr('id', tab[propNames.paneId]) - .html(tab[propNames.content]); - - if (options.forceActiveTab && tab[propNames.active]) { - $pane.addClass('active'); - } - - return $pane; - } - - - }()); // tabElements - - var tabUtils = (function () { - - return { - didTabOrderChange: didTabOrderChange, - getIndexOfClosestEnabledTab: getIndexOfClosestEnabledTab, - getTabIndexByPaneId: getTabIndexByPaneId, - storeDataOnLiEl: storeDataOnLiEl - }; - - /////////////////// - - function didTabOrderChange($currTabLis, updatedTabs, propNames) { - var isTabOrderChanged = false; - - $currTabLis.each(function (currDomIdx) { - var newIdx = getTabIndexByPaneId(updatedTabs, propNames.paneId, $(this).data('tab')[propNames.paneId]); - - if ((newIdx > -1) && (newIdx !== currDomIdx)) { // tab moved - isTabOrderChanged = true; - return false; // exit .each() loop - } - }); - - return isTabOrderChanged; - } - - function getIndexOfClosestEnabledTab($currTabLis, startIndex) { - var lastIndex = $currTabLis.length - 1, - closestIdx = -1, - incrementFromStartIndex = 0, - testIdx = 0; - - // expand out from the current tab looking for an enabled tab; - // we prefer the tab after us over the tab before - while ((closestIdx === -1) && (testIdx >= 0)) { - - if ( (((testIdx = startIndex + (++incrementFromStartIndex)) <= lastIndex) && - !$currTabLis.eq(testIdx).hasClass('disabled')) || - (((testIdx = startIndex - incrementFromStartIndex) >= 0) && - !$currTabLis.eq(testIdx).hasClass('disabled')) ) { - - closestIdx = testIdx; - - } - } - - return closestIdx; - } - - function getTabIndexByPaneId(tabs, paneIdPropName, paneId) { - var idx = -1; - - tabs.some(function (tab, i) { - if (tab[paneIdPropName] === paneId) { - idx = i; - return true; // exit loop - } - }); - - return idx; - } - - function storeDataOnLiEl($li, tabs, index) { - $li.data({ - tab: $.extend({}, tabs[index]), // store a clone so we can check for changes - index: index - }); - } - - }()); // tabUtils - - function buildNavTabsAndTabContentForTargetElementInstance($targetElInstance, settings, readyCallback) { - var tabs = settings.tabs, - propNames = { - paneId: settings.propPaneId, - title: settings.propTitle, - active: settings.propActive, - disabled: settings.propDisabled, - content: settings.propContent - }, - ignoreTabPanes = settings.ignoreTabPanes, - hasTabContent = tabs.length && tabs[0][propNames.content] !== undefined, - $navTabs = tabElements.getNewElNavTabs(), - $tabContent = tabElements.getNewElTabContent(), - $scroller, - attachTabContentToDomCallback = ignoreTabPanes ? null : function() { - $scroller.after($tabContent); - }; - - if (!tabs.length) { - return; - } - - tabs.forEach(function(tab, index) { - var options = { - forceActiveTab: true, - tabLiContent: settings.tabsLiContent && settings.tabsLiContent[index], - tabPostProcessor: settings.tabsPostProcessors && settings.tabsPostProcessors[index] - }; - - tabElements - .getNewElTabLi(tab, propNames, options) - .appendTo($navTabs); - - // build the tab panes if we weren't told to ignore them and there's - // tab content data available - if (!ignoreTabPanes && hasTabContent) { - tabElements - .getNewElTabPane(tab, propNames, options) - .appendTo($tabContent); - } - }); - - $scroller = wrapNavTabsInstanceInScroller($navTabs, - settings, - readyCallback, - attachTabContentToDomCallback); - - $scroller.appendTo($targetElInstance); - - $targetElInstance.data({ - scrtabs: { - tabs: tabs, - propNames: propNames, - ignoreTabPanes: ignoreTabPanes, - hasTabContent: hasTabContent, - tabsLiContent: settings.tabsLiContent, - tabsPostProcessors: settings.tabsPostProcessors, - scroller: $scroller - } - }); - - // once the nav-tabs are wrapped in the scroller, attach each tab's - // data to it for reference later; we need to wait till they're - // wrapped in the scroller because we wrap a *clone* of the nav-tabs - // we built above, not the original nav-tabs - $scroller.find('.nav-tabs > li').each(function (index) { - tabUtils.storeDataOnLiEl($(this), tabs, index); - }); - - return $targetElInstance; - } - - - function wrapNavTabsInstanceInScroller($navTabsInstance, settings, readyCallback, attachTabContentToDomCallback) { - var $scroller = tabElements.getNewElScrollerElementWrappingNavTabsInstance($navTabsInstance.clone(true), settings), // use clone because we replaceWith later - scrollingTabsControl = new ScrollingTabsControl($scroller), - navTabsInstanceData = $navTabsInstance.data('scrtabs'); - - if (!navTabsInstanceData) { - $navTabsInstance.data('scrtabs', { - scroller: $scroller - }); - } else { - navTabsInstanceData.scroller = $scroller; - } - - $navTabsInstance.replaceWith($scroller.css('visibility', 'hidden')); - - if (settings.tabClickHandler && (typeof settings.tabClickHandler === 'function')) { - $scroller.hasTabClickHandler = true; - scrollingTabsControl.tabClickHandler = settings.tabClickHandler; - } - - $scroller.initTabs = function () { - scrollingTabsControl.initTabs(settings, - $scroller, - readyCallback, - attachTabContentToDomCallback); - }; - - $scroller.scrollToActiveTab = function() { - scrollingTabsControl.scrollToActiveTab(settings); - }; - - $scroller.initTabs(); - - listenForDropdownMenuTabs($scroller, scrollingTabsControl); - - return $scroller; - } - - /* exported listenForDropdownMenuTabs, - refreshTargetElementInstance, - scrollToActiveTab */ - function checkForTabAdded(refreshData) { - var updatedTabsArray = refreshData.updatedTabsArray, - updatedTabsLiContent = refreshData.updatedTabsLiContent || [], - updatedTabsPostProcessors = refreshData.updatedTabsPostProcessors || [], - propNames = refreshData.propNames, - ignoreTabPanes = refreshData.ignoreTabPanes, - options = refreshData.options, - $currTabLis = refreshData.$currTabLis, - $navTabs = refreshData.$navTabs, - $currTabContentPanesContainer = ignoreTabPanes ? null : refreshData.$currTabContentPanesContainer, - $currTabContentPanes = ignoreTabPanes ? null : refreshData.$currTabContentPanes, - isInitTabsRequired = false; - - // make sure each tab in the updated tabs array has a corresponding DOM element - updatedTabsArray.forEach(function (tab, idx) { - var $li = $currTabLis.find('a[href="#' + tab[propNames.paneId] + '"]'), - isTabIdxPastCurrTabs = (idx >= $currTabLis.length), - $pane; - - if (!$li.length) { // new tab - isInitTabsRequired = true; - - // add the tab, add its pane (if necessary), and refresh the scroller - options.tabLiContent = updatedTabsLiContent[idx]; - options.tabPostProcessor = updatedTabsPostProcessors[idx]; - $li = tabElements.getNewElTabLi(tab, propNames, options); - tabUtils.storeDataOnLiEl($li, updatedTabsArray, idx); - - if (isTabIdxPastCurrTabs) { // append to end of current tabs - $li.appendTo($navTabs); - } else { // insert in middle of current tabs - $li.insertBefore($currTabLis.eq(idx)); - } - - if (!ignoreTabPanes && tab[propNames.content] !== undefined) { - $pane = tabElements.getNewElTabPane(tab, propNames, options); - if (isTabIdxPastCurrTabs) { // append to end of current tabs - $pane.appendTo($currTabContentPanesContainer); - } else { // insert in middle of current tabs - $pane.insertBefore($currTabContentPanes.eq(idx)); - } - } - - } - - }); - - return isInitTabsRequired; - } - - function checkForTabPropertiesUpdated(refreshData) { - var tabLiData = refreshData.tabLi, - ignoreTabPanes = refreshData.ignoreTabPanes, - $li = tabLiData.$li, - $contentPane = tabLiData.$contentPane, - origTabData = tabLiData.origTabData, - newTabData = tabLiData.newTabData, - propNames = refreshData.propNames, - isInitTabsRequired = false; - - // update tab title if necessary - if (origTabData[propNames.title] !== newTabData[propNames.title]) { - $li.find('a[role="tab"]') - .html(origTabData[propNames.title] = newTabData[propNames.title]); - - isInitTabsRequired = true; - } - - // update tab disabled state if necessary - if (origTabData[propNames.disabled] !== newTabData[propNames.disabled]) { - if (newTabData[propNames.disabled]) { // enabled -> disabled - $li.addClass('disabled'); - $li.find('a[role="tab"]').attr('data-toggle', ''); - } else { // disabled -> enabled - $li.removeClass('disabled'); - $li.find('a[role="tab"]').attr('data-toggle', 'tab'); - } - - origTabData[propNames.disabled] = newTabData[propNames.disabled]; - isInitTabsRequired = true; - } - - // update tab active state if necessary - if (refreshData.options.forceActiveTab) { - // set the active tab based on the tabs array regardless of the current - // DOM state, which could have been changed by the user clicking a tab - // without those changes being reflected back to the tab data - $li[newTabData[propNames.active] ? 'addClass' : 'removeClass']('active'); - - $contentPane[newTabData[propNames.active] ? 'addClass' : 'removeClass']('active'); - - origTabData[propNames.active] = newTabData[propNames.active]; - - isInitTabsRequired = true; - } - - // update tab content pane if necessary - if (!ignoreTabPanes && origTabData[propNames.content] !== newTabData[propNames.content]) { - $contentPane.html(origTabData[propNames.content] = newTabData[propNames.content]); - isInitTabsRequired = true; - } - - return isInitTabsRequired; - } - - function checkForTabRemoved(refreshData) { - var tabLiData = refreshData.tabLi, - ignoreTabPanes = refreshData.ignoreTabPanes, - $li = tabLiData.$li, - idxToMakeActive; - - if (tabLiData.newIdx !== -1) { // tab was not removed--it has a valid index - return false; - } - - // if this was the active tab, make the closest enabled tab active - if ($li.hasClass('active')) { - - idxToMakeActive = tabUtils.getIndexOfClosestEnabledTab(refreshData.$currTabLis, tabLiData.currDomIdx); - if (idxToMakeActive > -1) { - refreshData.$currTabLis - .eq(idxToMakeActive) - .addClass('active'); - - if (!ignoreTabPanes) { - refreshData.$currTabContentPanes - .eq(idxToMakeActive) - .addClass('active'); - } - } - } - - $li.remove(); - - if (!ignoreTabPanes) { - tabLiData.$contentPane.remove(); - } - - return true; - } - - function checkForTabsOrderChanged(refreshData) { - var $currTabLis = refreshData.$currTabLis, - updatedTabsArray = refreshData.updatedTabsArray, - propNames = refreshData.propNames, - ignoreTabPanes = refreshData.ignoreTabPanes, - newTabsCollection = [], - newTabPanesCollection = ignoreTabPanes ? null : []; - - if (!tabUtils.didTabOrderChange($currTabLis, updatedTabsArray, propNames)) { - return false; - } - - // the tab order changed... - updatedTabsArray.forEach(function (t) { - var paneId = t[propNames.paneId]; - - newTabsCollection.push( - $currTabLis - .find('a[role="tab"][href="#' + paneId + '"]') - .parent('li') - ); - - if (!ignoreTabPanes) { - newTabPanesCollection.push($('#' + paneId)); - } - }); - - refreshData.$navTabs.append(newTabsCollection); - - if (!ignoreTabPanes) { - refreshData.$currTabContentPanesContainer.append(newTabPanesCollection); - } - - return true; - } - - function checkForTabsRemovedOrUpdated(refreshData) { - var $currTabLis = refreshData.$currTabLis, - updatedTabsArray = refreshData.updatedTabsArray, - propNames = refreshData.propNames, - isInitTabsRequired = false; - - - $currTabLis.each(function (currDomIdx) { - var $li = $(this), - origTabData = $li.data('tab'), - newIdx = tabUtils.getTabIndexByPaneId(updatedTabsArray, propNames.paneId, origTabData[propNames.paneId]), - newTabData = (newIdx > -1) ? updatedTabsArray[newIdx] : null; - - refreshData.tabLi = { - $li: $li, - currDomIdx: currDomIdx, - newIdx: newIdx, - $contentPane: tabElements.getElTabPaneForLi($li), - origTabData: origTabData, - newTabData: newTabData - }; - - if (checkForTabRemoved(refreshData)) { - isInitTabsRequired = true; - return; // continue to next $li in .each() since we removed this tab - } - - if (checkForTabPropertiesUpdated(refreshData)) { - isInitTabsRequired = true; - } - }); - - return isInitTabsRequired; - } - - function listenForDropdownMenuTabs($scroller, stc) { - var $ddMenu; - - // for dropdown menus to show, we need to move them out of the - // scroller and append them to the body - $scroller - .on(CONSTANTS.EVENTS.DROPDOWN_MENU_SHOW, handleDropdownShow) - .on(CONSTANTS.EVENTS.DROPDOWN_MENU_HIDE, handleDropdownHide); - - function handleDropdownHide(e) { - // move the dropdown menu back into its tab - $(e.target).append($ddMenu.off(CONSTANTS.EVENTS.CLICK)); - } - - function handleDropdownShow(e) { - var $ddParentTabLi = $(e.target), - ddLiOffset = $ddParentTabLi.offset(), - $currActiveTab = $scroller.find('li[role="presentation"].active'), - ddMenuRightX, - tabsContainerMaxX, - ddMenuTargetLeft; - - $ddMenu = $ddParentTabLi - .find('.dropdown-menu') - .attr('data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED, true); - - // if the dropdown's parent tab li isn't already active, - // we need to deactivate any active menu item in the dropdown - if ($currActiveTab[0] !== $ddParentTabLi[0]) { - $ddMenu.find('li.active').removeClass('active'); - } - - // we need to do our own click handling because the built-in - // bootstrap handlers won't work since we moved the dropdown - // menu outside the tabs container - $ddMenu.on(CONSTANTS.EVENTS.CLICK, 'a[role="tab"]', handleClickOnDropdownMenuItem); - - $('body').append($ddMenu); - - // make sure the menu doesn't go off the right side of the page - ddMenuRightX = $ddMenu.width() + ddLiOffset.left; - tabsContainerMaxX = $scroller.width() - (stc.$slideRightArrow.outerWidth() + 1); - ddMenuTargetLeft = ddLiOffset.left; - - if (ddMenuRightX > tabsContainerMaxX) { - ddMenuTargetLeft -= (ddMenuRightX - tabsContainerMaxX); - } - - $ddMenu.css({ - 'display': 'block', - 'top': ddLiOffset.top + $ddParentTabLi.outerHeight() - 2, - 'left': ddMenuTargetLeft - }); - - function handleClickOnDropdownMenuItem() { - /* jshint validthis: true */ - var $selectedMenuItemAnc = $(this), - $selectedMenuItemLi = $selectedMenuItemAnc.parent('li'), - $selectedMenuItemDropdownMenu = $selectedMenuItemLi.parent('.dropdown-menu'), - targetPaneId = $selectedMenuItemAnc.attr('href'); - - if ($selectedMenuItemLi.hasClass('active')) { - return; - } - - // once we select a menu item from the dropdown, deactivate - // the current tab (unless it's our parent tab), deactivate - // any active dropdown menu item, make our parent tab active - // (if it's not already), and activate the selected menu item - $scroller - .find('li.active') - .not($ddParentTabLi) - .add($selectedMenuItemDropdownMenu.find('li.active')) - .removeClass('active'); - - $ddParentTabLi - .add($selectedMenuItemLi) - .addClass('active'); - - // manually deactivate current active pane and activate our pane - $('.tab-content .tab-pane.active').removeClass('active'); - $(targetPaneId).addClass('active'); - } - - } - } - - function refreshDataDrivenTabs($container, options) { - var instanceData = $container.data().scrtabs, - scroller = instanceData.scroller, - $navTabs = $container.find('.scrtabs-tab-container .nav-tabs'), - $currTabContentPanesContainer = $container.find('.tab-content'), - isInitTabsRequired = false, - refreshData = { - options: options, - updatedTabsArray: instanceData.tabs, - updatedTabsLiContent: instanceData.tabsLiContent, - updatedTabsPostProcessors: instanceData.tabsPostProcessors, - propNames: instanceData.propNames, - ignoreTabPanes: instanceData.ignoreTabPanes, - $navTabs: $navTabs, - $currTabLis: $navTabs.find('> li'), - $currTabContentPanesContainer: $currTabContentPanesContainer, - $currTabContentPanes: $currTabContentPanesContainer.find('.tab-pane') - }; - - // to preserve the tab positions if we're just adding or removing - // a tab, don't completely rebuild the tab structure, but check - // for differences between the new tabs array and the old - if (checkForTabAdded(refreshData)) { - isInitTabsRequired = true; - } - - if (checkForTabsOrderChanged(refreshData)) { - isInitTabsRequired = true; - } - - if (checkForTabsRemovedOrUpdated(refreshData)) { - isInitTabsRequired = true; - } - - if (isInitTabsRequired) { - scroller.initTabs(); - } - - return isInitTabsRequired; - } - - function refreshTargetElementInstance($container, options) { - if (!$container.data('scrtabs')) { // target element doesn't have plugin on it - return; - } - - // force a refresh if the tabs are static html or they're data-driven - // but the data didn't change so we didn't call initTabs() - if ($container.data('scrtabs').isWrapperOnly || !refreshDataDrivenTabs($container, options)) { - $('body').trigger(CONSTANTS.EVENTS.FORCE_REFRESH); - } - } - - function scrollToActiveTab() { - /* jshint validthis: true */ - var $targetElInstance = $(this), - scrtabsData = $targetElInstance.data('scrtabs'); - - if (!scrtabsData) { - return; - } - - scrtabsData.scroller.scrollToActiveTab(); - } - - var methods = { - destroy: function() { - var $targetEls = this; - - return $targetEls.each(destroyPlugin); - }, - - init: function(options) { - var $targetEls = this, - targetElsLastIndex = $targetEls.length - 1, - settings = $.extend({}, $.fn.scrollingTabs.defaults, options || {}); - - // ---- tabs NOT data-driven ------------------------- - if (!settings.tabs) { - - // just wrap the selected .nav-tabs element(s) in the scroller - return $targetEls.each(function(index) { - var dataObj = { - isWrapperOnly: true - }, - $targetEl = $(this).data({ scrtabs: dataObj }), - readyCallback = (index < targetElsLastIndex) ? null : function() { - $targetEls.trigger(CONSTANTS.EVENTS.TABS_READY); - }; - - wrapNavTabsInstanceInScroller($targetEl, settings, readyCallback); - }); - - } - - // ---- tabs data-driven ------------------------- - return $targetEls.each(function (index) { - var $targetEl = $(this), - readyCallback = (index < targetElsLastIndex) ? null : function() { - $targetEls.trigger(CONSTANTS.EVENTS.TABS_READY); - }; - - buildNavTabsAndTabContentForTargetElementInstance($targetEl, settings, readyCallback); - }); - }, - - refresh: function(options) { - var $targetEls = this, - settings = $.extend({}, $.fn.scrollingTabs.defaults, options || {}); - - return $targetEls.each(function () { - refreshTargetElementInstance($(this), settings); - }); - }, - - scrollToActiveTab: function() { - return this.each(scrollToActiveTab); - } - }; - - function destroyPlugin() { - /* jshint validthis: true */ - var $targetElInstance = $(this), - scrtabsData = $targetElInstance.data('scrtabs'), - $tabsContainer; - - if (!scrtabsData) { - return; - } - - if (scrtabsData.enableSwipingElement === 'self') { - $targetElInstance.removeClass(CONSTANTS.CSS_CLASSES.ALLOW_SCROLLBAR); - } else if (scrtabsData.enableSwipingElement === 'parent') { - $targetElInstance.closest('.scrtabs-tab-container').parent().removeClass(CONSTANTS.CSS_CLASSES.ALLOW_SCROLLBAR); - } - - scrtabsData.scroller - .off(CONSTANTS.EVENTS.DROPDOWN_MENU_SHOW) - .off(CONSTANTS.EVENTS.DROPDOWN_MENU_HIDE); - - // if there were any dropdown menus opened, remove the css we added to - // them so they would display correctly - scrtabsData.scroller - .find('[data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED + ']') - .css({ - display: '', - left: '', - top: '' - }) - .off(CONSTANTS.EVENTS.CLICK) - .removeAttr('data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED); - - if (scrtabsData.scroller.hasTabClickHandler) { - $targetElInstance - .find('a[data-toggle="tab"]') - .off('.scrtabs'); - } - - if (scrtabsData.isWrapperOnly) { // we just wrapped nav-tabs markup, so restore it - // $targetElInstance is the ul.nav-tabs - $tabsContainer = $targetElInstance.parents('.scrtabs-tab-container'); - - if ($tabsContainer.length) { - $tabsContainer.replaceWith($targetElInstance); - } - - } else { // we generated the tabs from data so destroy everything we created - if (scrtabsData.scroller && scrtabsData.scroller.initTabs) { - scrtabsData.scroller.initTabs = null; - } - - // $targetElInstance is the container for the ul.nav-tabs we generated - $targetElInstance - .find('.scrtabs-tab-container') - .add('.tab-content') - .remove(); - } - - $targetElInstance.removeData('scrtabs'); - - while(--$.fn.scrollingTabs.nextInstanceId >= 0) { - $(window).off(CONSTANTS.EVENTS.WINDOW_RESIZE + $.fn.scrollingTabs.nextInstanceId); - } - - $('body').off(CONSTANTS.EVENTS.FORCE_REFRESH); - } - - - $.fn.scrollingTabs = function(methodOrOptions) { - - if (methods[methodOrOptions]) { - return methods[methodOrOptions].apply(this, Array.prototype.slice.call(arguments, 1)); - } else if (!methodOrOptions || (typeof methodOrOptions === 'object')) { - return methods.init.apply(this, arguments); - } else { - $.error('Method ' + methodOrOptions + ' does not exist on $.scrollingTabs.'); - } - }; - - $.fn.scrollingTabs.nextInstanceId = 0; - - $.fn.scrollingTabs.defaults = { - tabs: null, - propPaneId: 'paneId', - propTitle: 'title', - propActive: 'active', - propDisabled: 'disabled', - propContent: 'content', - ignoreTabPanes: false, - scrollToTabEdge: false, - disableScrollArrowsOnFullyScrolled: false, - forceActiveTab: false, - reverseScroll: false, - widthMultiplier: 1, - tabClickHandler: null, - cssClassLeftArrow: 'fa fa-chevron-left', - cssClassRightArrow: 'fa fa-chevron-right', - leftArrowContent: '', - rightArrowContent: '', - tabsLiContent: null, - tabsPostProcessors: null, - enableSwiping: false, - enableRtlSupport: false, - bootstrapVersion: 4 - }; - - - -}(jQuery, window)); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/views/form_renderer.js b/muk_web_theme/static/src/js/views/form_renderer.js index fb9cd14..2f4ab3f 100644 --- a/muk_web_theme/static/src/js/views/form_renderer.js +++ b/muk_web_theme/static/src/js/views/form_renderer.js @@ -40,10 +40,6 @@ FormRenderer.include({ } return $buttons; }, - _updateView: function () { - this._super.apply(this, arguments); - this.$('.nav-tabs').scrollingTabs(); - }, }); }); \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/form_view.scss b/muk_web_theme/static/src/scss/form_view.scss index edf2e3a..879d9b6 100644 --- a/muk_web_theme/static/src/scss/form_view.scss +++ b/muk_web_theme/static/src/scss/form_view.scss @@ -1,123 +1,139 @@ -/********************************************************************************** -* -* Copyright (C) 2017 MuK IT GmbH -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see . -* -**********************************************************************************/ - -.o_form_view { - .o_form_sheet_bg { - background: $o-webclient-background-color; - border-bottom: 1px solid gray('300'); - > .o_form_sheet { - background-color: $o-view-background-color; - border: 1px solid gray('400'); - box-shadow: 0 5px 20px -15px black; - max-width: calc(100% - #{$o-horizontal-padding*2}); - } - .oe_button_box { - .btn.oe_stat_button > .o_button_icon { - color: $o-brand-odoo; - } - } - .o_notebook { - .table-responsive { - overflow-x: inherit; - } - } - } - @include media-breakpoint-down(sm) { - .o_form_sheet, .oe_chatter { - min-width: 100% !important; - max-width: 100% !important; - } - .o_group_col_6 { - width: 100%; - } - .o_statusbar_buttons_dropdown { - border: { - bottom: 0; - radius: 0; - top: 0; - } - height: 100%; - } - .o_statusbar_buttons > .btn { - border-radius: 0; - border: 0; - width: 100%; - margin-bottom: 0.2rem; - &:last-child { - margin-bottom: 0; - } - } - .o_statusbar_status { - .o_arrow_button:first-child::before { - content: none; - display: none; - } - } - .app_settings_block { - .row { - margin: 0; - } - } - } - @include media-breakpoint-down(xs) { - .oe_button_box { - float: none; - display: block; - text-align: right; - margin-top: -17px; - margin-bottom: $o-sheet-vpadding; - margin-left: -$o-horizontal-padding; - width: calc(100% + #{$o-horizontal-padding*2}); - > .btn.oe_stat_button { - flex: 0 0 auto !important; - width: percentage(1/3) !important; - } - > .btn.o_button_more, > .o_dropdown_more { - flex: 0 0 auto !important; - width: calc(#{percentage(1/3)} + 3px) !important; - } - &, & + .oe_avatar { - + .oe_title { - width: 100%; - } - } - } - } -} - -.mk_chatter_position_sided { - @include media-breakpoint-up(xl) { - .o_form_view:not(.o_form_nosheet) { - display: flex; - flex-flow: row nowrap; - height: 100%; - .o_form_sheet_bg { - flex: 1 1 65%; - overflow: auto; - } - .o_chatter { - border-left: 1px solid gray('400'); - flex: 1 1 35%; - max-width: 40%; - min-width: 30%; - overflow: auto; - } - } - } +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_form_view { + .o_form_sheet_bg { + background: $o-webclient-background-color; + border-bottom: 1px solid gray('300'); + > .o_form_sheet { + background-color: $o-view-background-color; + border: 1px solid gray('400'); + box-shadow: 0 5px 20px -15px black; + max-width: calc(100% - #{$o-horizontal-padding*2}); + } + .oe_button_box { + .btn.oe_stat_button > .o_button_icon { + color: $o-brand-odoo; + } + .btn.oe_stat_button > .o_stat_info { + padding-right: 3px; + } + } + .o_notebook { + .nav-tabs { + overflow-x : auto; + overflow-y: hidden; + flex-flow: nowrap; + .nav-item { + white-space: nowrap; + } + } + .table-responsive { + overflow-x: inherit; + } + } + } + @include media-breakpoint-down(sm) { + .o_form_sheet, .oe_chatter { + min-width: 100% !important; + max-width: 100% !important; + } + .o_group_col_6 { + width: 100%; + } + .o_statusbar_buttons_dropdown { + border: { + bottom: 0; + radius: 0; + top: 0; + } + height: 100%; + } + .o_statusbar_buttons > .btn { + border-radius: 0; + border: 0; + width: 100%; + margin-bottom: 0.2rem; + &:last-child { + margin-bottom: 0; + } + } + .o_statusbar_status { + .o_arrow_button:first-child::before { + content: none; + display: none; + } + } + .app_settings_block { + .row { + margin: 0; + } + } + .o_notebook .nav-tabs { + &::-webkit-scrollbar { + display: none; + } + } + } + @include media-breakpoint-down(xs) { + .oe_button_box { + float: none; + display: block; + text-align: right; + margin-top: -17px; + margin-bottom: $o-sheet-vpadding; + margin-left: -$o-horizontal-padding; + width: calc(100% + #{$o-horizontal-padding*2}); + > .btn.oe_stat_button { + flex: 0 0 auto !important; + width: percentage(1/3) !important; + } + > .btn.o_button_more, > .o_dropdown_more { + flex: 0 0 auto !important; + width: calc(#{percentage(1/3)} + 3px) !important; + } + &, & + .oe_avatar { + + .oe_title { + width: 100%; + } + } + } + } +} + +.mk_chatter_position_sided { + @include media-breakpoint-up(xl) { + .o_form_view:not(.o_form_nosheet) { + display: flex; + flex-flow: row nowrap; + height: 100%; + .o_form_sheet_bg { + flex: 1 1 65%; + overflow: auto; + } + .o_chatter { + border-left: 1px solid gray('400'); + flex: 1 1 35%; + max-width: 40%; + min-width: 30%; + overflow: auto; + } + } + } } \ No newline at end of file diff --git a/muk_web_theme/template/assets.xml b/muk_web_theme/template/assets.xml index 3f3f396..0246866 100644 --- a/muk_web_theme/template/assets.xml +++ b/muk_web_theme/template/assets.xml @@ -49,11 +49,9 @@