PageSourceSearch

https://www.piramalaranya.com/wp-content/themes/aranya/assets/js/ranibaug-tab.js?ver=6.3.1

js piramalaranya.com collected 2026-09-26 04:41:38 UTC 70,252 bytes, 1,974 lines download raw bytes

1/**
2 * jquery-bootstrap-scrolling-tabs
3 * @version v2.4.0
4 * @link https://github.com/mikejacobson/jquery-bootstrap-scrolling-tabs
5 * @author Mike Jacobson <[email protected]>
6 * @license MIT License, http://www.opensource.org/licenses/MIT
7 */
8/**
9 * jQuery plugin version of Angular directive angular-bootstrap-scrolling-tabs:
10 * https://github.com/mikejacobson/angular-bootstrap-scrolling-tabs
11 *
12 * Usage:
13 *
14 *    Use case #1: HTML-defined tabs
15 *    ------------------------------
16 *    Demo: http://plnkr.co/edit/thyD0grCxIjyU4PoTt4x?p=preview
17 *
18 *      Sample HTML:
19 *
20 *           <!-- Nav tabs -->
21 *           <ul class="nav nav-tabs" role="tablist">
22 *             <li role="presentation" class="active"><a href="#tab1" role="tab" data-toggle="tab">Tab Number 1</a></li>
23 *             <li role="presentation"><a href="#tab2" role="tab" data-toggle="tab">Tab Number 2</a></li>
24 *             <li role="presentation"><a href="#tab3" role="tab" data-toggle="tab">Tab Number 3</a></li>
25 *             <li role="presentation"><a href="#tab4" role="tab" data-toggle="tab">Tab Number 4</a></li>
26 *           </ul>
27 *
28 *           <!-- Tab panes -->
29 *           <div class="tab-content">
30 *             <div role="tabpanel" class="tab-pane active" id="tab1">Tab 1 content...</div>
31 *             <div role="tabpanel" class="tab-pane" id="tab2">Tab 2 content...</div>
32 *             <div role="tabpanel" class="tab-pane" id="tab3">Tab 3 content...</div>
33 *             <div role="tabpanel" class="tab-pane" id="tab4">Tab 4 content...</div>
34 *           </div>
35 *
36 *
37 *      JavaScript:
38 *
39 *            $('.nav-tabs').scrollingTabs();
40 *
41 *
42 *    Use Case #2: Data-driven tabs
43 *    -----------------------------
44 *    Demo: http://plnkr.co/edit/MWBjLnTvJeetjU3NEimg?p=preview
45 *
46 *      Sample HTML:
47 *
48 *          <!-- build .nav-tabs and .tab-content in here -->
49 *          <div id="tabs-inside-here"></div>
50 *
51 *
52 *      JavaScript:
53 *
54 *             $('#tabs-inside-here').scrollingTabs({
55 *               tabs: tabs, // required
56 *               propPaneId: 'paneId', // optional
57 *               propTitle: 'title', // optional
58 *               propActive: 'active', // optional
59 *               propDisabled: 'disabled', // optional
60 *               propContent: 'content', // optional
61 *               ignoreTabPanes: false, // optional
62 *               scrollToTabEdge: false, // optional
63 *               disableScrollArrowsOnFullyScrolled: false, // optional
64 *               reverseScroll: false // optional
65 *             });
66 *
67 *      Settings/Options:
68 *
69 *        tabs:             tabs data array
70 *        prop*:            name of your tab object's property name that
71 *                          corresponds to that required tab property if
72 *                          your property name is different than the
73 *                          standard name (paneId, title, etc.)
74 *        tabsLiContent:
75 *                          optional string array used to define custom HTML
76 *                          for each tab's <li> element. Each entry is an HTML
77 *                          string defining the tab <li> element for the
78 *                          corresponding tab in the tabs array.
79 *                          The default for a tab is:
80 *                          '<li role="presentation" class=""></li>'
81 *                          So, for example, if you had 3 tabs and you needed
82 *                          a custom 'tooltip' attribute on each one, your
83 *                          tabsLiContent array might look like this:
84 *                            [
85 *                              '<li role="presentation" tooltip="Custom TT 1" class="custom-li"></li>',
86 *                              '<li role="presentation" tooltip="Custom TT 2" class="custom-li"></li>',
87 *                              '<li role="presentation" tooltip="Custom TT 3" class="custom-li"></li>'
88 *                            ]
89 *                          This plunk demonstrates its usage (in conjunction
90 *                          with tabsPostProcessors):
91 *                          http://plnkr.co/edit/ugJLMk7lmDCuZQziQ0k0
92 *        tabsPostProcessors:
93 *                          optional array of functions, each one associated
94 *                          with an entry in the tabs array. When a tab element
95 *                          has been created, its associated post-processor
96 *                          function will be called with two arguments: the
97 *                          newly created $li and $a jQuery elements for that tab.
98 *                          This allows you to, for example, attach a custom
99 *                          event listener to each anchor tag.
100 *                          This plunk demonstrates its usage (in conjunction
101 *                          with tabsLiContent):
102 *                          http://plnkr.co/edit/ugJLMk7lmDCuZQziQ0k0
103 *        ignoreTabPanes:   relevant for data-driven tabs only--set to true if
104 *                          you want the plugin to only touch the tabs
105 *                          and to not generate the tab pane elements
106 *                          that go in .tab-content. By default, the plugin
107 *                          will generate the tab panes based on the content
108 *                          property in your tab data, if a content property
109 *                          is present.
110 *        scrollToTabEdge:  set to true if you want to force full-width tabs
111 *                          to display at the left scroll arrow. i.e., if the
112 *                          scrolling stops with only half a tab showing,
113 *                          it will snap the tab to its edge so the full tab
114 *                          shows.
115 *        disableScrollArrowsOnFullyScrolled:
116 *                          set to true if you want the left scroll arrow to
117 *                          disable when the tabs are scrolled fully left,
118 *                          and the right scroll arrow to disable when the tabs
119 *                          are scrolled fully right.
120 *        reverseScroll:
121 *                          set to true if you want the left scroll arrow to
122 *                          slide the tabs left instead of right, and the right
123 *                          scroll arrow to slide the tabs right.
124 *        enableSwiping:
125 *                          set to true if you want to enable horizontal swiping
126 *                          for touch screens.
127 *        widthMultiplier:
128 *                          set to a value less than 1 if you want the tabs
129 *                          container to be less than the full width of its
130 *                          parent element. For example, set it to 0.5 if you
131 *                          want the tabs container to be half the width of
132 *                          its parent.
133 *        tabClickHandler:
134 *                          a callback function to execute any time a tab is clicked.
135 *                          The function is simply passed as the event handler
136 *                          to jQuery's .on(), so the function will receive
137 *                          the jQuery event as an argument, and the 'this'
138 *                          inside the function will be the clicked tab's anchor
139 *                          element.
140 *        cssClassLeftArrow, cssClassRightArrow:
141 *                          custom values for the class attributes for the
142 *                          left and right scroll arrows. The defaults are
143 *                          'glyphicon glyphicon-chevron-left' and
144 *                          'glyphicon glyphicon-chevron-right'.
145 *                          Using different icons might require you to add
146 *                          custom styling to the arrows to position the icons
147 *                          correctly; the arrows can be targeted with these
148 *                          selectors:
149 *                          .scrtabs-tab-scroll-arrow
150 *                          .scrtabs-tab-scroll-arrow-left
151 *                          .scrtabs-tab-scroll-arrow-right
152 *        leftArrowContent, rightArrowContent:
153 *                          custom HTML string for the left and right scroll
154 *                          arrows. This will override any custom cssClassLeftArrow
155 *                          and cssClassRightArrow settings.
156 *                          For example, if you wanted to use svg icons, you
157 *                          could set them like so:
158 *
159 *                           leftArrowContent: [
160 *                               '<div class="custom-arrow">',
161 *                               '  <svg class="icon icon-point-left">',
162 *                               '    <use xlink:href="#icon-point-left"></use>',
163 *                               '  </svg>',
164 *                               '</div>'
165 *                             ].join(''),
166 *                             rightArrowContent: [
167 *                               '<div class="custom-arrow">',
168 *                               '  <svg class="icon icon-point-right">',
169 *                               '    <use xlink:href="#icon-point-right"></use>',
170 *                               '  </svg>',
171 *                               '</div>'
172 *                             ].join('')
173 *
174 *                          You would then need to add some CSS to make them
175 *                          work correctly if you don't give them the
176 *                          default scrtabs-tab-scroll-arrow classes.
177 *                          This plunk shows it working with svg icons:
178 *                          http://plnkr.co/edit/2MdZCAnLyeU40shxaol3?p=preview
179 *
180 *                          When using this option, you can also mark a child
181 *                          element within the arrow content as the click target
182 *                          if you don't want the entire content to be
183 *                          clickable. You do that my adding the CSS class
184 *                          'scrtabs-click-target' to the element that should
185 *                          be clickable, like so:
186 *
187 *                           leftArrowContent: [
188 *                               '<div class="scrtabs-tab-scroll-arrow scrtabs-tab-scroll-arrow-left">',
189 *                               '  <button class="scrtabs-click-target" type="button">',
190 *                               '    <i class="custom-chevron-left"></i>',
191 *                               '  </button>',
192 *                               '</div>'
193 *                             ].join(''),
194 *                             rightArrowContent: [
195 *                               '<div class="scrtabs-tab-scroll-arrow scrtabs-tab-scroll-arrow-right">',
196 *                               '  <button class="scrtabs-click-target" type="button">',
197 *                               '    <i class="custom-chevron-right"></i>',
198 *                               '  </button>',
199 *                               '</div>'
200 *                             ].join('')
201 *
202 *        enableRtlSupport:
203 *                          set to true if you want your site to supp
203ort
204 *                          right-to-left languages. If true, the plugin will
205 *                          check the page's <html> tag for attribute dir="rtl"
206 *                          and will adjust its behavior accordingly.
207 *        bootstrapVersion:
208 *                          set to 4 if you're using Boostrap 4. Default is 3.
209 *                          Bootstrap 4 handles some things differently than 3
210 *                          (e.g., the 'active' class gets applied to the tab's
211 *                          'li > a' element rather than the 'li' itself).
212 *
213 *
214 *      On tabs data change:
215 *
216 *            $('#tabs-inside-here').scrollingTabs('refresh');
217 *
218 *      On tabs data change, if you want the active tab to be set based on
219 *      the updated tabs data (i.e., you want to override the current
220 *      active tab setting selected by the user), for example, if you
221 *      added a new tab and you want it to be the active tab:
222 *
223 *             $('#tabs-inside-here').scrollingTabs('refresh', {
224 *               forceActiveTab: true
225 *             });
226 *
227 *      Any options that can be passed into the plugin can be set on the
228 *      plugin's 'defaults' object instead so you don't have to pass them in:
229 *
230 *             $.fn.scrollingTabs.defaults.tabs = tabs;
231 *             $.fn.scrollingTabs.defaults.forceActiveTab = true;
232 *             $.fn.scrollingTabs.defaults.scrollToTabEdge = true;
233 *             $.fn.scrollingTabs.defaults.disableScrollArrowsOnFullyScrolled = true;
234 *             $.fn.scrollingTabs.defaults.reverseScroll = true;
235 *             $.fn.scrollingTabs.defaults.widthMultiplier = 0.5;
236 *             $.fn.scrollingTabs.defaults.tabClickHandler = function () { };
237 *
238 *
239 *    Methods
240 *    -----------------------------
241 *    - refresh
242 *    On window resize, the tabs should refresh themselves, but to force a refresh:
243 *
244 *      $('.nav-tabs').scrollingTabs('refresh');
245 *
246 *    - scrollToActiveTab
247 *    On window resize, the active tab will automatically be scrolled to
248 *    if it ends up offscreen, but you can also programmatically force a
249 *    scroll to the active tab any time (if, for example, you're
250 *    programmatically setting the active tab) by calling the
251 *    'scrollToActiveTab' method:
252 *
253 *    $('.nav-tabs').scrollingTabs('scrollToActiveTab');
254 *
255 *
256 *    Events
257 *    -----------------------------
258 *    The plugin triggers event 'ready.scrtabs' when the tabs have
259 *    been wrapped in the scroller and are ready for viewing:
260 *
261 *      $('.nav-tabs')
262 *        .scrollingTabs()
263 *        .on('ready.scrtabs', function() {
264 *          // tabs ready, do my other stuff...
265 *        });
266 *
267 *      $('#tabs-inside-here')
268 *        .scrollingTabs({ tabs: tabs })
269 *        .on('ready.scrtabs', function() {
270 *          // tabs ready, do my other stuff...
271 *        });
272 *
273 *
274 *    Destroying
275 *    -----------------------------
276 *    To destroy:
277 *
278 *      $('.nav-tabs').scrollingTabs('destroy');
279 *
280 *      $('#tabs-inside-here').scrollingTabs('destroy');
281 *
282 *    If you were wrapping markup, the markup will be restored; if your tabs
283 *    were data-driven, the tabs will be destroyed along with the plugin.
284 *
285 */
286
287;(function ($, window) {
288  'use strict';
289  /* jshint unused:false */
290
291  /* exported CONSTANTS */
292  var CONSTANTS = {
293    CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL: 50, // timeout interval for repeatedly moving the tabs container
294                                                // by one increment while the mouse is held down--decrease to
295                                                // make mousedown continous scrolling faster
296    SCROLL_OFFSET_FRACTION: 6, // each click moves the container this fraction of the fixed container--decrease
297                               // to make the tabs scroll farther per click
298  
299    DATA_KEY_DDMENU_MODIFIED: 'scrtabsddmenumodified',
300    DATA_KEY_IS_MOUSEDOWN: 'scrtabsismousedown
vendor: 9,664 bytes, lines 300-587
300',
301  
302    CSS_CLASSES: {
303      BOOTSTRAP4: 'scrtabs-bootstrap4',
304      RTL: 'scrtabs-rtl',
305      SCROLL_ARROW_CLICK_TARGET: 'scrtabs-click-target',
306      SCROLL_ARROW_DISABLE: 'scrtabs-disable',
307      SCROLL_ARROW_WITH_CLICK_TARGET: 'scrtabs-with-click-target'
308    },
309  
310    SLIDE_DIRECTION: {
311      LEFT: 1,
312      RIGHT: 2
313    },
314  
315    EVENTS: {
316      CLICK: 'click.scrtabs',
317      DROPDOWN_MENU_HIDE: 'hide.bs.dropdown.scrtabs',
318      DROPDOWN_MENU_SHOW: 'show.bs.dropdown.scrtabs',
319      FORCE_REFRESH: 'forcerefresh.scrtabs',
320      MOUSEDOWN: 'mousedown.scrtabs',
321      MOUSEUP: 'mouseup.scrtabs',
322      TABS_READY: 'ready.scrtabs',
323      TOUCH_END: 'touchend.scrtabs',
324      TOUCH_MOVE: 'touchmove.scrtabs',
325      TOUCH_START: 'touchstart.scrtabs',
326      WINDOW_RESIZE: 'resize.scrtabs'
327    }
328  };
329  
330  // smartresize from Paul Irish (debounced window resize)
331  (function (sr) {
332    var debounce = function (func, threshold, execAsap) {
333      var timeout;
334  
335      return function debounced() {
336        var obj = this, args = arguments;
337        function delayed() {
338          if (!execAsap) {
339            func.apply(obj, args);
340          }
341          timeout = null;
342        }
343  
344        if (timeout) {
345          clearTimeout(timeout);
346        } else if (execAsap) {
347          func.apply(obj, args);
348        }
349  
350        timeout = setTimeout(delayed, threshold || 100);
351      };
352    };
353    $.fn[sr] = function (fn, customEventName) {
354      var eventName = customEventName || CONSTANTS.EVENTS.WINDOW_RESIZE;
355      return fn ? this.bind(eventName, debounce(fn)) : this.trigger(sr);
356    };
357  
358  })('smartresizeScrtabs');
359  
360  /* ***********************************************************************************
361   * ElementsHandler - Class that each instance of ScrollingTabsControl will instantiate
362   * **********************************************************************************/
363  function ElementsHandler(scrollingTabsControl) {
364    var ehd = this;
365  
366    ehd.stc = scrollingTabsControl;
367  }
368  
369  // ElementsHandler prototype methods
370  (function (p) {
371      p.initElements = function (options) {
372        var ehd = this;
373  
374        ehd.setElementReferences(options);
375        ehd.setEventListeners(options);
376      };
377  
378      p.listenForTouchEvents = function () {
379        var ehd = this,
380            stc = ehd.stc,
381            smv = stc.scrollMovement,
382            ev = CONSTANTS.EVENTS;
383  
384        var touching = false;
385        var touchStartX;
386        var startingContainerLeftPos;
387        var newLeftPos;
388  
389        stc.$movableContainer
390          .on(ev.TOUCH_START, function (e) {
391            touching = true;
392            startingContainerLeftPos = stc.movableContainerLeftPos;
393            touchStartX = e.originalEvent.changedTouches[0].pageX;
394          })
395          .on(ev.TOUCH_END, function () {
396            touching = false;
397          })
398          .on(ev.TOUCH_MOVE, function (e) {
399            if (!touching) {
400              return;
401            }
402  
403            var touchPageX = e.originalEvent.changedTouches[0].pageX;
404            var diff = touchPageX - touchStartX;
405            if (stc.rtl) {
406              diff = -diff;
407            }
408            var minPos;
409  
410            newLeftPos = startingContainerLeftPos + diff;
411            if (newLeftPos > 0) {
412              newLeftPos = 0;
413            } else {
414              minPos = smv.getMinPos();
415              if (newLeftPos < minPos) {
416                newLeftPos = minPos;
417              }
418            }
419            stc.movableContainerLeftPos = newLeftPos;
420  
421            var leftOrRight = stc.rtl ? 'right' : 'left';
422            stc.$movableContainer.css(leftOrRight, smv.getMovableContainerCssLeftVal());
423            smv.refreshScrollArrowsDisabledState();
424          });
425      };
426  
427      p.refreshAllElementSizes = function () {
428        var ehd = this,
429            stc = ehd.stc,
430            smv = stc.scrollMovement,
431            scrollArrowsWereVisible = stc.scrollArrowsVisible,
432            actionsTaken = {
433              didScrollToActiveTab: false
434            },
435            isPerformingSlideAnim = false,
436            minPos;
437  
438        ehd.setElementWidths();
439        ehd.setScrollArrowVisibility();
440  
441        // this could have been a window resize or the removal of a
442        // dynamic tab, so make sure the movable container is positioned
443        // correctly because, if it is far to the left and we increased the
444        // window width, it's possible that the tabs will be too far left,
445        // beyond the min pos.
446        if (stc.scrollArrowsVisible) {
447          // make sure container not too far left
448          minPos = smv.getMinPos();
449  
450          isPerformingSlideAnim = smv.scrollToActiveTab({
451            isOnWindowResize: true
452          });
453  
454          if (!isPerformingSlideAnim) {
455            smv.refreshScrollArrowsDisabledState();
456  
457            if (stc.rtl) {
458              if (stc.movableContainerRightPos < minPos) {
459                smv.incrementMovableContainerLeft(minPos);
460              }
461            } else {
462              if (stc.movableContainerLeftPos < minPos) {
463                smv.incrementMovableContainerRight(minPos);
464              }
465            }
466          }
467  
468          actionsTaken.didScrollToActiveTab = true;
469  
470        } else if (scrollArrowsWereVisible) {
471          // scroll arrows went away after resize, so position movable container at 0
472          stc.movableContainerLeftPos = 0;
473          smv.slideMovableContainerToLeftPos();
474        }
475  
476        return actionsTaken;
477      };
478  
479      p.setElementReferences = function (settings) {
480        var ehd = this,
481            stc = ehd.stc,
482            $tabsContainer = stc.$tabsContainer,
483            $leftArrow,
484            $rightArrow,
485            $leftArrowClickTarget,
486            $rightArrowClickTarget;
487  
488        stc.isNavPills = false;
489  
490        if (stc.rtl) {
491          $tabsContainer.addClass(CONSTANTS.CSS_CLASSES.RTL);
492        }
493  
494        if (stc.usingBootstrap4) {
495          $tabsContainer.addClass(CONSTANTS.CSS_CLASSES.BOOTSTRAP4);
496        }
497  
498        stc.$fixedContainer = $tabsContainer.find('.scrtabs-tabs-fixed-container');
499        $leftArrow = stc.$fixedContainer.prev();
500        $rightArrow = stc.$fixedContainer.next();
501  
502        // if we have custom arrow content, we might have a click target defined
503        if (settings.leftArrowContent) {
504          $leftArrowClickTarget = $leftArrow.find('.' + CONSTANTS.CSS_CLASSES.SCROLL_ARROW_CLICK_TARGET);
505        }
506  
507        if (settings.rightArrowContent) {
508          $rightArrowClickTarget = $rightArrow.find('.' + CONSTANTS.CSS_CLASSES.SCROLL_ARROW_CLICK_TARGET);
509        }
510  
511        if ($leftArrowClickTarget && $leftArrowClickTarget.length) {
512          $leftArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_WITH_CLICK_TARGET);
513        } else {
514          $leftArrowClickTarget = $leftArrow;
515        }
516  
517        if ($rightArrowClickTarget && $rightArrowClickTarget.length) {
518          $rightArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_WITH_CLICK_TARGET);
519        } else {
520          $rightArrowClickTarget = $rightArrow;
521        }
522  
523        stc.$movableContainer = $tabsContainer.find('.scrtabs-tabs-movable-container');
524        stc.$tabsUl = $tabsContainer.find('.nav-tabs');
525  
526        // check for pills
527        if (!stc.$tabsUl.length) {
528          stc.$tabsUl = $tabsContainer.find('.nav-pills');
529  
530          if (stc.$tabsUl.length) {
531            stc.isNavPills = true;
532          }
533        }
534  
535        stc.$tabsLiCollection = stc.$tabsUl.find('> li');
536  
537        stc.$slideLeftArrow = stc.reverseScroll ? $leftArrow : $rightArrow;
538        stc.$slideLeftArrowClickTarget = stc.reverseScroll ? $leftArrowClickTarget : $rightArrowClickTarget;
539        stc.$slideRightArrow = stc.reverseScroll ? $rightArrow : $leftArrow;
540        stc.$slideRightArrowClickTarget = stc.reverseScroll ? $rightArrowClickTarget : $leftArrowClickTarget;
541        stc.$scrollArrows = stc.$slideLeftArrow.add(stc.$slideRightArrow);
542  
543        stc.$win = $(window);
544      };
545  
546      p.setElementWidths = function () {
547        var ehd = this,
548            stc = ehd.stc;
549  
550        stc.winWidth = stc.$win.width();
551        stc.scrollArrowsCombinedWidth = stc.$slideLeftArrow.outerWidth() + stc.$slideRightArrow.outerWidth();
552  
553        ehd.setFixedContainerWidth();
554        ehd.setMovableContainerWidth();
555      };
556  
557      p.setEventListeners = function (settings) {
558        var ehd = this,
559            stc = ehd.stc,
560            evh = stc.eventHandlers,
561            ev = CONSTANTS.EVENTS,
562            resizeEventName = ev.WINDOW_RESIZE + stc.instanceId;
563  
564        if (settings.enableSwiping) {
565          ehd.listenForTouchEvents();
566        }
567  
568        stc.$slideLeftArrowClickTarget
569          .off('.scrtabs')
570          .on(ev.MOUSEDOWN, function (e) { evh.handleMousedownOnSlideMovContainerLeftArrow.call(evh, e); })
571          .on(ev.MOUSEUP, function (e) { evh.handleMouseupOnSlideMovContainerLeftArrow.call(evh, e); })
572          .on(ev.CLICK, function (e) { evh.handleClickOnSlideMovContainerLeftArrow.call(evh, e); });
573  
574        stc.$slideRightArrowClickTarget
575          .off('.scrtabs')
576          .on(ev.MOUSEDOWN, function (e) { evh.handleMousedownOnSlideMovContainerRightArrow.call(evh, e); })
577          .on(ev.MOUSEUP, function (e) { evh.handleMouseupOnSlideMovContainerRightArrow.call(evh, e); })
578          .on(ev.CLICK, function (e) { evh.handleClickOnSlideMovContainerRightArrow.call(evh, e); });
579  
580        if (stc.tabClickHandler) {
581          stc.$tabsLiCollection
582            .find('a[data-toggle="tab"]')
583            .off(ev.CLICK)
584            .on(ev.CLICK, stc.tabClickHandler);
585        }
586  
587        
vendor: 13,130 bytes, lines 587-990
587stc.$win
588          .off(resizeEventName)
589          .smartresizeScrtabs(function (e) { evh.handleWindowResize.call(evh, e); }, resizeEventName);
590  
591        $('body').on(CONSTANTS.EVENTS.FORCE_REFRESH, stc.elementsHandler.refreshAllElementSizes.bind(stc.elementsHandler));
592      };
593  
594      p.setFixedContainerWidth = function () {
595        var ehd = this,
596            stc = ehd.stc,
597            tabsContainerRect = stc.$tabsContainer.get(0).getBoundingClientRect();
598        /**
599         * @author poletaew
600         * It solves problem with rounding by jQuery.outerWidth
601         * If we have real width 100.5 px, jQuery.outerWidth returns us 101 px and we get layout's fail
602         */
603        stc.fixedContainerWidth = tabsContainerRect.width || (tabsContainerRect.right - tabsContainerRect.left);
604        stc.fixedContainerWidth = stc.fixedContainerWidth * stc.widthMultiplier;
605  
606        stc.$fixedContainer.width(stc.fixedContainerWidth);
607      };
608  
609      p.setFixedContainerWidthForHiddenScrollArrows = function () {
610        var ehd = this,
611            stc = ehd.stc;
612  
613        stc.$fixedContainer.width(stc.fixedContainerWidth);
614      };
615  
616      p.setFixedContainerWidthForVisibleScrollArrows = function () {
617        var ehd = this,
618            stc = ehd.stc;
619  
620        stc.$fixedContainer.width(stc.fixedContainerWidth - stc.scrollArrowsCombinedWidth);
621      };
622  
623      p.setMovableContainerWidth = function () {
624        var ehd = this,
625            stc = ehd.stc,
626            $tabLi = stc.$tabsUl.find('> li');
627  
628        stc.movableContainerWidth = 0;
629  
630        if ($tabLi.length) {
631  
632          $tabLi.each(function () {
633            var $li = $(this),
634                totalMargin = 0;
635  
636            if (stc.isNavPills) { // pills have a margin-left, tabs have no margin
637              totalMargin = parseInt($li.css('margin-left'), 10) + parseInt($li.css('margin-right'), 10);
638            }
639  
640            stc.movableContainerWidth += ($li.outerWidth() + totalMargin);
641          });
642  
643          stc.movableContainerWidth += 1;
644  
645          // if the tabs don't span the width of the page, force the
646          // movable container width to full page width so the bottom
647          // border spans the page width instead of just spanning the
648          // width of the tabs
649          if (stc.movableContainerWidth < stc.fixedContainerWidth) {
650            stc.movableContainerWidth = stc.fixedContainerWidth;
651          }
652        }
653  
654        stc.$movableContainer.width(stc.movableContainerWidth);
655      };
656  
657      p.setScrollArrowVisibility = function () {
658        var ehd = this,
659            stc = ehd.stc,
660            shouldBeVisible = stc.movableContainerWidth > stc.fixedContainerWidth;
661  
662        if (shouldBeVisible && !stc.scrollArrowsVisible) {
663          stc.$scrollArrows.show();
664          stc.scrollArrowsVisible = true;
665        } else if (!shouldBeVisible && stc.scrollArrowsVisible) {
666          stc.$scrollArrows.hide();
667          stc.scrollArrowsVisible = false;
668        }
669  
670        if (stc.scrollArrowsVisible) {
671          ehd.setFixedContainerWidthForVisibleScrollArrows();
672        } else {
673          ehd.setFixedContainerWidthForHiddenScrollArrows();
674        }
675      };
676  
677  }(ElementsHandler.prototype));
678  
679  /* ***********************************************************************************
680   * EventHandlers - Class that each instance of ScrollingTabsControl will instantiate
681   * **********************************************************************************/
682  function EventHandlers(scrollingTabsControl) {
683    var evh = this;
684  
685    evh.stc = scrollingTabsControl;
686  }
687  
688  // prototype methods
689  (function (p){
690    p.handleClickOnSlideMovContainerLeftArrow = function () {
691      var evh = this,
692          stc = evh.stc;
693  
694      stc.scrollMovement.incrementMovableContainerLeft();
695    };
696  
697    p.handleClickOnSlideMovContainerRightArrow = function () {
698      var evh = this,
699          stc = evh.stc;
700  
701      stc.scrollMovement.incrementMovableContainerRight();
702    };
703  
704    p.handleMousedownOnSlideMovContainerLeftArrow = function () {
705      var evh = this,
706          stc = evh.stc;
707  
708      stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true);
709      stc.scrollMovement.continueSlideMovableContainerLeft();
710    };
711  
712    p.handleMousedownOnSlideMovContainerRightArrow = function () {
713      var evh = this,
714          stc = evh.stc;
715  
716      stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true);
717      stc.scrollMovement.continueSlideMovableContainerRight();
718    };
719  
720    p.handleMouseupOnSlideMovContainerLeftArrow = function () {
721      var evh = this,
722          stc = evh.stc;
723  
724      stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false);
725    };
726  
727    p.handleMouseupOnSlideMovContainerRightArrow = function () {
728      var evh = this,
729          stc = evh.stc;
730  
731      stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false);
732    };
733  
734    p.handleWindowResize = function () {
735      var evh = this,
736          stc = evh.stc,
737          newWinWidth = stc.$win.width();
738  
739      if (newWinWidth === stc.winWidth) {
740        return false;
741      }
742  
743      stc.winWidth = newWinWidth;
744      stc.elementsHandler.refreshAllElementSizes();
745    };
746  
747  }(EventHandlers.prototype));
748  
749  /* ***********************************************************************************
750   * ScrollMovement - Class that each instance of ScrollingTabsControl will instantiate
751   * **********************************************************************************/
752  function ScrollMovement(scrollingTabsControl) {
753    var smv = this;
754  
755    smv.stc = scrollingTabsControl;
756  }
757  
758  // prototype methods
759  (function (p) {
760  
761    p.continueSlideMovableContainerLeft = function () {
762      var smv = this,
763          stc = smv.stc;
764  
765      setTimeout(function() {
766        if (stc.movableContainerLeftPos <= smv.getMinPos()  ||
767            !stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN)) {
768          return;
769        }
770  
771        if (!smv.incrementMovableContainerLeft()) { // haven't reached max left
772          smv.continueSlideMovableContainerLeft();
773        }
774      }, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL);
775    };
776  
777    p.continueSlideMovableContainerRight = function () {
778      var smv = this,
779          stc = smv.stc;
780  
781      setTimeout(function() {
782        if (stc.movableContainerLeftPos >= 0  ||
783            !stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN)) {
784          return;
785        }
786  
787        if (!smv.incrementMovableContainerRight()) { // haven't reached max right
788          smv.continueSlideMovableContainerRight();
789        }
790      }, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL);
791    };
792  
793    p.decrementMovableContainerLeftPos = function (minPos) {
794      var smv = this,
795          stc = smv.stc;
796  
797      stc.movableContainerLeftPos -= (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION);
798      if (stc.movableContainerLeftPos < minPos) {
799        stc.movableContainerLeftPos = minPos;
800      } else if (stc.scrollToTabEdge) {
801        smv.setMovableContainerLeftPosToTabEdge(CONSTANTS.SLIDE_DIRECTION.LEFT);
802  
803        if (stc.movableContainerLeftPos < minPos) {
804          stc.movableContainerLeftPos = minPos;
805        }
806      }
807    };
808  
809    p.disableSlideLeftArrow = function () {
810      var smv = this,
811          stc = smv.stc;
812  
813      if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) {
814        return;
815      }
816  
817      stc.$slideLeftArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE);
818    };
819  
820    p.disableSlideRightArrow = function () {
821      var smv = this,
822          stc = smv.stc;
823  
824      if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) {
825        return;
826      }
827  
828      stc.$slideRightArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE);
829    };
830  
831    p.enableSlideLeftArrow = function () {
832      var smv = this,
833          stc = smv.stc;
834  
835      if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) {
836        return;
837      }
838  
839      stc.$slideLeftArrow.removeClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE);
840    };
841  
842    p.enableSlideRightArrow = function () {
843      var smv = this,
844          stc = smv.stc;
845  
846      if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) {
847        return;
848      }
849  
850      stc.$slideRightArrow.removeClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE);
851    };
852  
853    p.getMinPos = function () {
854      var smv = this,
855          stc = smv.stc;
856  
857      return stc.scrollArrowsVisible ? (stc.fixedContainerWidth - stc.movableContainerWidth - stc.scrollArrowsCombinedWidth) : 0;
858    };
859  
860    p.getMovableContainerCssLeftVal = function () {
861      var smv = this,
862          stc = smv.stc;
863  
864      return (stc.movableContainerLeftPos === 0) ? '0' : stc.movableContainerLeftPos + 'px';
865    };
866  
867    p.incrementMovableContainerLeft = function () {
868      var smv = this,
869          stc = smv.stc,
870          minPos = smv.getMinPos();
871  
872      smv.decrementMovableContainerLeftPos(minPos);
873      smv.slideMovableContainerToLeftPos();
874      smv.enableSlideRightArrow();
875  
876      // return true if we're fully left, false otherwise
877      return (stc.movableContainerLeftPos === minPos);
878    };
879  
880    p.incrementMovableContainerRight = function (minPos) {
881      var smv = this,
882          stc = smv.stc;
883  
884      // if minPos passed in, the movable container was beyond the minPos
885      if (minPos) {
886        stc.movableContainerLeftPos = minPos;
887      } else {
888        stc.movableContainerLeftPos += (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION);
889  
890        if (stc.movableContainerLeftPos > 0) {
891          stc.movableContainerLeftPos = 0;
892        } else if (stc.scrollToTabEdge) {
893          smv.setMovableContainerLeftPosToTabEdge(CONSTANTS.SLIDE_DIRECTION.RIGHT);
894        }
895      }
896  
897      smv.slideMovableContainerToLeftPos();
898      smv.enableSlideLeftArrow();
899  
900      // return true if we're fully right, false otherwise
901      // left pos of 0 is the movable container's max position (farthest right)
902      return (stc.movableContainerLeftPos === 0);
903    };
904  
905    p.refreshScrollArrowsDisabledState = function() {
906      var smv = this,
907          stc = smv.stc;
908  
909      if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) {
910        return;
911      }
912  
913      if (stc.movableContainerLeftPos >= 0) { // movable container fully right
914        smv.disableSlideRightArrow();
915        smv.enableSlideLeftArrow();
916        return;
917      }
918  
919      if (stc.movableContainerLeftPos <= smv.getMinPos()) { // fully left
920        smv.disableSlideLeftArrow();
921        smv.enableSlideRightArrow();
922        return;
923      }
924  
925      smv.enableSlideLeftArrow();
926      smv.enableSlideRightArrow();
927    };
928  
929    p.scrollToActiveTab = function () {
930      var smv = this,
931          stc = smv.stc,
932          $activeTab,
933          $activeTabAnchor,
934          activeTabLeftPos,
935          activeTabRightPos,
936          rightArrowLeftPos,
937          leftScrollArrowWidth,
938          rightScrollArrowWidth;
939  
940      if (!stc.scrollArrowsVisible) {
941        return;
942      }
943  
944      if (stc.usingBootstrap4) {
945        $activeTabAnchor = stc.$tabsUl.find('li > .nav-link.active');
946        if ($activeTabAnchor.length) {
947          $activeTab = $activeTabAnchor.parent();
948        }
949      } else {
950        $activeTab = stc.$tabsUl.find('li.active');
951      }
952  
953      if (!$activeTab || !$activeTab.length) {
954        return;
955      }
956  
957      rightScrollArrowWidth = stc.$slideRightArrow.outerWidth();
958  
959      /**
960       * @author poletaew
961       * We need relative offset (depends on $fixedContainer), don't absolute
962       */
963      activeTabLeftPos = $activeTab.offset().left - stc.$fixedContainer.offset().left;
964      activeTabRightPos = activeTabLeftPos + $activeTab.outerWidth();
965  
966      rightArrowLeftPos = stc.fixedContainerWidth - rightScrollArrowWidth;
967  
968      if (stc.rtl) {
969        leftScrollArrowWidth = stc.$slideLeftArrow.outerWidth();
970  
971        if (activeTabLeftPos < 0) { // active tab off left side
972          stc.movableContainerLeftPos += activeTabLeftPos;
973          smv.slideMovableContainerToLeftPos();
974          return true;
975        } else { // active tab off right side
976          if (activeTabRightPos > rightArrowLeftPos) {
977            stc.movableContainerLeftPos += (activeTabRightPos - rightArrowLeftPos) + (2 * rightScrollArrowWidth);
978            smv.slideMovableContainerToLeftPos();
979            return true;
980          }
981        }
982      } else {
983        if (activeTabRightPos > rightArrowLeftPos) { // active tab off right side
984          stc.movableContainerLeftPos -= (activeTabRightPos - rightArrowLeftPos + rightScrollArrowWidth);
985          smv.slideMovableContainerToLeftPos();
986          return true;
987        } else {
988          leftScrollArrowWidth = stc.$slideLeftArrow.outerWidth();
989          if (activeTabLeftPos < leftScrollArrowWidth) { // active tab off left side
990            stc.movableContainerLeftPos += leftScrollArrowWidth - activeTabLeftPos
vendor: 31,758 bytes, lines 990-1937
990;
991            smv.slideMovableContainerToLeftPos();
992            return true;
993          }
994        }
995      }
996  
997      return false;
998    };
999  
1000    p.setMovableContainerLeftPosToTabEdge = function (slideDirection) {
1001      var smv = this,
1002          stc = smv.stc,
1003          offscreenWidth = -stc.movableContainerLeftPos,
1004          totalTabWidth = 0;
1005  
1006        // make sure LeftPos is set so that a tab edge will be against the
1007        // left scroll arrow so we won't have a partial, cut-off tab
1008        stc.$tabsLiCollection.each(function () {
1009          var tabWidth = $(this).width();
1010  
1011          totalTabWidth += tabWidth;
1012  
1013          if (totalTabWidth > offscreenWidth) {
1014            stc.movableContainerLeftPos = (slideDirection === CONSTANTS.SLIDE_DIRECTION.RIGHT) ? -(totalTabWidth - tabWidth) : -totalTabWidth;
1015            return false; // exit .each() loop
1016          }
1017  
1018        });
1019    };
1020  
1021    p.slideMovableContainerToLeftPos = function () {
1022      var smv = this,
1023          stc = smv.stc,
1024          minPos = smv.getMinPos(),
1025          leftOrRightVal;
1026  
1027      if (stc.movableContainerLeftPos > 0) {
1028        stc.movableContainerLeftPos = 0;
1029      } else if (stc.movableContainerLeftPos < minPos) {
1030        stc.movableContainerLeftPos = minPos;
1031      }
1032  
1033      stc.movableContainerLeftPos = stc.movableContainerLeftPos / 1;
1034      leftOrRightVal = smv.getMovableContainerCssLeftVal();
1035  
1036      smv.performingSlideAnim = true;
1037  
1038      var targetPos = stc.rtl ? { right: leftOrRightVal } : { left: leftOrRightVal };
1039  
1040      stc.$movableContainer.stop().animate(targetPos, 'slow', function __slideAnimComplete() {
1041        var newMinPos = smv.getMinPos();
1042  
1043        smv.performingSlideAnim = false;
1044  
1045        // if we slid past the min pos--which can happen if you resize the window
1046        // quickly--move back into position
1047        if (stc.movableContainerLeftPos < newMinPos) {
1048          smv.decrementMovableContainerLeftPos(newMinPos);
1049  
1050          targetPos = stc.rtl ? { right: smv.getMovableContainerCssLeftVal() } : { left: smv.getMovableContainerCssLeftVal() };
1051  
1052          stc.$movableContainer.stop().animate(targetPos, 'fast', function() {
1053            smv.refreshScrollArrowsDisabledState();
1054          });
1055        } else {
1056          smv.refreshScrollArrowsDisabledState();
1057        }
1058      });
1059    };
1060  
1061  }(ScrollMovement.prototype));
1062  
1063  /* **********************************************************************
1064   * ScrollingTabsControl - Class that each directive will instantiate
1065   * **********************************************************************/
1066  function ScrollingTabsControl($tabsContainer) {
1067    var stc = this;
1068  
1069    stc.$tabsContainer = $tabsContainer;
1070    stc.instanceId = $.fn.scrollingTabs.nextInstanceId++;
1071  
1072    stc.movableContainerLeftPos = 0;
1073    stc.scrollArrowsVisible = false;
1074    stc.scrollToTabEdge = false;
1075    stc.disableScrollArrowsOnFullyScrolled = false;
1076    stc.reverseScroll = false;
1077    stc.widthMultiplier = 1;
1078  
1079    stc.scrollMovement = new ScrollMovement(stc);
1080    stc.eventHandlers = new EventHandlers(stc);
1081    stc.elementsHandler = new ElementsHandler(stc);
1082  }
1083  
1084  // prototype methods
1085  (function (p) {
1086    p.initTabs = function (options, $scroller, readyCallback, attachTabContentToDomCallback) {
1087      var stc = this,
1088          elementsHandler = stc.elementsHandler,
1089          num;
1090  
1091      if (options.enableRtlSupport && $('html').attr('dir') === 'rtl') {
1092        stc.rtl = true;
1093      }
1094  
1095      if (options.scrollToTabEdge) {
1096        stc.scrollToTabEdge = true;
1097      }
1098  
1099      if (options.disableScrollArrowsOnFullyScrolled) {
1100        stc.disableScrollArrowsOnFullyScrolled = true;
1101      }
1102  
1103      if (options.reverseScroll) {
1104        stc.reverseScroll = true;
1105      }
1106  
1107      if (options.widthMultiplier !== 1) {
1108        num = Number(options.widthMultiplier); // handle string value
1109  
1110        if (!isNaN(num)) {
1111          stc.widthMultiplier = num;
1112        }
1113      }
1114  
1115      if (options.bootstrapVersion.toString().charAt(0) === '4') {
1116        stc.usingBootstrap4 = true;
1117      }
1118  
1119      setTimeout(initTabsAfterTimeout, 100);
1120  
1121      function initTabsAfterTimeout() {
1122        var actionsTaken;
1123  
1124        // if we're just wrapping non-data-driven tabs, the user might
1125        // have the .nav-tabs hidden to prevent the clunky flash of
1126        // multi-line tabs on page refresh, so we need to make sure
1127        // they're visible before trying to wrap them
1128        $scroller.find('.nav-tabs').show();
1129  
1130        elementsHandler.initElements(options);
1131        actionsTaken = elementsHandler.refreshAllElementSizes();
1132  
1133        $scroller.css('visibility', 'visible');
1134  
1135        if (attachTabContentToDomCallback) {
1136          attachTabContentToDomCallback();
1137        }
1138  
1139        if (readyCallback) {
1140          readyCallback();
1141        }
1142      }
1143    };
1144  
1145    p.scrollToActiveTab = function(options) {
1146      var stc = this,
1147          smv = stc.scrollMovement;
1148  
1149      smv.scrollToActiveTab(options);
1150    };
1151  }(ScrollingTabsControl.prototype));
1152  
1153
1154  /* exported buildNavTabsAndTabContentForTargetElementInstance */
1155  var tabElements = (function () {
1156  
1157    return {
1158      getElTabPaneForLi: getElTabPaneForLi,
1159      getNewElNavTabs: getNewElNavTabs,
1160      getNewElScrollerElementWrappingNavTabsInstance: getNewElScrollerElementWrappingNavTabsInstance,
1161      getNewElTabAnchor: getNewElTabAnchor,
1162      getNewElTabContent: getNewElTabContent,
1163      getNewElTabLi: getNewElTabLi,
1164      getNewElTabPane: getNewElTabPane
1165    };
1166  
1167    ///////////////////
1168  
1169    // ---- retrieve existing elements from the DOM ----------
1170    function getElTabPaneForLi($li) {
1171      return $($li.find('a').attr('href'));
1172    }
1173  
1174  
1175    // ---- create new elements ----------
1176    function getNewElNavTabs() {
1177      return $('<ul class="nav nav-tabs" role="tablist"></ul>');
1178    }
1179  
1180    function getNewElScrollerElementWrappingNavTabsInstance($navTabsInstance, settings) {
1181      var $tabsContainer = $('<div class="scrtabs-tab-container"></div>'),
1182          leftArrowContent = settings.leftArrowContent || '<div class="scrtabs-tab-scroll-arrow scrtabs-tab-scroll-arrow-left"><span class="' + settings.cssClassLeftArrow + '"></span></div>',
1183          $leftArrow = $(leftArrowContent),
1184          rightArrowContent = settings.rightArrowContent || '<div class="scrtabs-tab-scroll-arrow scrtabs-tab-scroll-arrow-right"><span class="' + settings.cssClassRightArrow + '"></span></div>',
1185          $rightArrow = $(rightArrowContent),
1186          $fixedContainer = $('<div class="scrtabs-tabs-fixed-container"></div>'),
1187          $movableContainer = $('<div class="scrtabs-tabs-movable-container"></div>');
1188  
1189      if (settings.disableScrollArrowsOnFullyScrolled) {
1190        $leftArrow.add($rightArrow).addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE);
1191      }
1192  
1193      return $tabsContainer
1194                .append($leftArrow,
1195                        $fixedContainer.append($movableContainer.append($navTabsInstance)),
1196                        $rightArrow);
1197    }
1198  
1199    function getNewElTabAnchor(tab, propNames) {
1200      return $('<a role="tab" data-toggle="tab"></a>')
1201              .attr('href', '#' + tab[propNames.paneId])
1202              .html(tab[propNames.title]);
1203    }
1204  
1205    function getNewElTabContent() {
1206      return $('<div class="tab-content"></div>');
1207    }
1208  
1209    function getNewElTabLi(tab, propNames, options) {
1210      var liContent = options.tabLiContent || '<li role="presentation" class=""></li>',
1211          $li = $(liContent),
1212          $a = getNewElTabAnchor(tab, propNames).appendTo($li);
1213  
1214      if (tab[propNames.disabled]) {
1215        $li.addClass('disabled');
1216        $a.attr('data-toggle', '');
1217      } else if (options.forceActiveTab && tab[propNames.active]) {
1218        $li.addClass('active');
1219      }
1220  
1221      if (options.tabPostProcessor) {
1222        options.tabPostProcessor($li, $a);
1223      }
1224  
1225      return $li;
1226    }
1227  
1228    function getNewElTabPane(tab, propNames, options) {
1229      var $pane = $('<div role="tabpanel" class="tab-pane"></div>')
1230                  .attr('id', tab[propNames.paneId])
1231                  .html(tab[propNames.content]);
1232  
1233      if (options.forceActiveTab && tab[propNames.active]) {
1234        $pane.addClass('active');
1235      }
1236  
1237      return $pane;
1238    }
1239  
1240  
1241  }()); // tabElements
1242  
1243  var tabUtils = (function () {
1244  
1245    return {
1246      didTabOrderChange: didTabOrderChange,
1247      getIndexOfClosestEnabledTab: getIndexOfClosestEnabledTab,
1248      getTabIndexByPaneId: getTabIndexByPaneId,
1249      storeDataOnLiEl: storeDataOnLiEl
1250    };
1251  
1252    ///////////////////
1253  
1254    function didTabOrderChange($currTabLis, updatedTabs, propNames) {
1255      var isTabOrderChanged = false;
1256  
1257      $currTabLis.each(function (currDomIdx) {
1258        var newIdx = getTabIndexByPaneId(updatedTabs, propNames.paneId, $(this).data('tab')[propNames.paneId]);
1259  
1260        if ((newIdx > -1) && (newIdx !== currDomIdx)) { // tab moved
1261          isTabOrderChanged = true;
1262          return false; // exit .each() loop
1263        }
1264      });
1265  
1266      return isTabOrderChanged;
1267    }
1268  
1269    function getIndexOfClosestEnabledTab($currTabLis, startIndex) {
1270      var lastIndex = $currTabLis.length - 1,
1271          closestIdx = -1,
1272          incrementFromStartIndex = 0,
1273          testIdx = 0;
1274  
1275      // expand out from the current tab looking for an enabled tab;
1276      // we prefer the tab after us over the tab before
1277      while ((closestIdx === -1) && (testIdx >= 0)) {
1278  
1279        if ( (((testIdx = startIndex + (++incrementFromStartIndex)) <= lastIndex) &&
1280              !$currTabLis.eq(testIdx).hasClass('disabled')) ||
1281              (((testIdx = startIndex - incrementFromStartIndex) >= 0) &&
1282               !$currTabLis.eq(testIdx).hasClass('disabled')) ) {
1283  
1284          closestIdx = testIdx;
1285  
1286        }
1287      }
1288  
1289      return closestIdx;
1290    }
1291  
1292    function getTabIndexByPaneId(tabs, paneIdPropName, paneId) {
1293      var idx = -1;
1294  
1295      tabs.some(function (tab, i) {
1296        if (tab[paneIdPropName] === paneId) {
1297          idx = i;
1298          return true; // exit loop
1299        }
1300      });
1301  
1302      return idx;
1303    }
1304  
1305    function storeDataOnLiEl($li, tabs, index) {
1306      $li.data({
1307        tab: $.extend({}, tabs[index]), // store a clone so we can check for changes
1308        index: index
1309      });
1310    }
1311  
1312  }()); // tabUtils
1313  
1314  function buildNavTabsAndTabContentForTargetElementInstance($targetElInstance, settings, readyCallback) {
1315    var tabs = settings.tabs,
1316        propNames = {
1317          paneId: settings.propPaneId,
1318          title: settings.propTitle,
1319          active: settings.propActive,
1320          disabled: settings.propDisabled,
1321          content: settings.propContent
1322        },
1323        ignoreTabPanes = settings.ignoreTabPanes,
1324        hasTabContent = tabs.length && tabs[0][propNames.content] !== undefined,
1325        $navTabs = tabElements.getNewElNavTabs(),
1326        $tabContent = tabElements.getNewElTabContent(),
1327        $scroller,
1328        attachTabContentToDomCallback = ignoreTabPanes ? null : function() {
1329          $scroller.after($tabContent);
1330        };
1331  
1332    if (!tabs.length) {
1333      return;
1334    }
1335  
1336    tabs.forEach(function(tab, index) {
1337      var options = {
1338        forceActiveTab: true,
1339        tabLiContent: settings.tabsLiContent && settings.tabsLiContent[index],
1340        tabPostProcessor: settings.tabsPostProcessors && settings.tabsPostProcessors[index]
1341      };
1342  
1343      tabElements
1344        .getNewElTabLi(tab, propNames, options)
1345        .appendTo($navTabs);
1346  
1347      // build the tab panes if we weren't told to ignore them and there's
1348      // tab content data available
1349      if (!ignoreTabPanes && hasTabContent) {
1350        tabElements
1351          .getNewElTabPane(tab, propNames, options)
1352          .appendTo($tabContent);
1353      }
1354    });
1355  
1356    $scroller = wrapNavTabsInstanceInScroller($navTabs,
1357                                              settings,
1358                                              readyCallback,
1359                                              attachTabContentToDomCallback);
1360  
1361    $scroller.appendTo($targetElInstance);
1362  
1363    $targetElInstance.data({
1364      scrtabs: {
1365        tabs: tabs,
1366        propNames: propNames,
1367        ignoreTabPanes: ignoreTabPanes,
1368        hasTabContent: hasTabContent,
1369        tabsLiContent: settings.tabsLiContent,
1370        tabsPostProcessors: settings.tabsPostProcessors,
1371        scroller: $scroller
1372      }
1373    });
1374  
1375    // once the nav-tabs are wrapped in the scroller, attach each tab's
1376    // data to it for reference later; we need to wait till they're
1377    // wrapped in the scroller because we wrap a *clone* of the nav-tabs
1378    // we built above, not the original nav-tabs
1379    $scroller.find('.nav-tabs > li').each(function (index) {
1380      tabUtils.storeDataOnLiEl($(this), tabs, index);
1381    });
1382  
1383    return $targetElInstance;
1384  }
1385  
1386  
1387  function wrapNavTabsInstanceInScroller($navTabsInstance, settings, readyCallback, attachTabContentToDomCallback) {
1388    var $scroller = tabElements.getNewElScrollerElementWrappingNavTabsInstance($navTabsInstance.clone(true), settings), // use clone because we replaceWith later
1389        scrollingTabsControl = new ScrollingTabsControl($scroller),
1390        navTabsInstanceData = $navTabsInstance.data('scrtabs');
1391  
1392    if (!navTabsInstanceData) {
1393      $navTabsInstance.data('scrtabs', {
1394        scroller: $scroller
1395      });
1396    } else {
1397      navTabsInstanceData.scroller = $scroller;
1398    }
1399  
1400    $navTabsInstance.replaceWith($scroller.css('visibility', 'hidden'));
1401  
1402    if (settings.tabClickHandler && (typeof settings.tabClickHandler === 'function')) {
1403      $scroller.hasTabClickHandler = true;
1404      scrollingTabsControl.tabClickHandler = settings.tabClickHandler;
1405    }
1406  
1407    $scroller.initTabs = function () {
1408      scrollingTabsControl.initTabs(settings,
1409                                    $scroller,
1410                                    readyCallback,
1411                                    attachTabContentToDomCallback);
1412    };
1413  
1414    $scroller.scrollToActiveTab = function() {
1415      scrollingTabsControl.scrollToActiveTab(settings);
1416    };
1417  
1418    $scroller.initTabs();
1419  
1420    listenForDropdownMenuTabs($scroller, scrollingTabsControl);
1421  
1422    return $scroller;
1423  }
1424  
1425  /* exported listenForDropdownMenuTabs,
1426              refreshTargetElementInstance,
1427              scrollToActiveTab */
1428  function checkForTabAdded(refreshData) {
1429    var updatedTabsArray = refreshData.updatedTabsArray,
1430        updatedTabsLiContent = refreshData.updatedTabsLiContent || [],
1431        updatedTabsPostProcessors = refreshData.updatedTabsPostProcessors || [],
1432        propNames = refreshData.propNames,
1433        ignoreTabPanes = refreshData.ignoreTabPanes,
1434        options = refreshData.options,
1435        $currTabLis = refreshData.$currTabLis,
1436        $navTabs = refreshData.$navTabs,
1437        $currTabContentPanesContainer = ignoreTabPanes ? null : refreshData.$currTabContentPanesContainer,
1438        $currTabContentPanes = ignoreTabPanes ? null : refreshData.$currTabContentPanes,
1439        isInitTabsRequired = false;
1440  
1441    // make sure each tab in the updated tabs array has a corresponding DOM element
1442    updatedTabsArray.forEach(function (tab, idx) {
1443      var $li = $currTabLis.find('a[href="#' + tab[propNames.paneId] + '"]'),
1444          isTabIdxPastCurrTabs = (idx >= $currTabLis.length),
1445          $pane;
1446  
1447      if (!$li.length) { // new tab
1448        isInitTabsRequired = true;
1449  
1450        // add the tab, add its pane (if necessary), and refresh the scroller
1451        options.tabLiContent = updatedTabsLiContent[idx];
1452        options.tabPostProcessor = updatedTabsPostProcessors[idx];
1453        $li = tabElements.getNewElTabLi(tab, propNames, options);
1454        tabUtils.storeDataOnLiEl($li, updatedTabsArray, idx);
1455  
1456        if (isTabIdxPastCurrTabs) { // append to end of current tabs
1457          $li.appendTo($navTabs);
1458        } else {                        // insert in middle of current tabs
1459          $li.insertBefore($currTabLis.eq(idx));
1460        }
1461  
1462        if (!ignoreTabPanes && tab[propNames.content] !== undefined) {
1463          $pane = tabElements.getNewElTabPane(tab, propNames, options);
1464          if (isTabIdxPastCurrTabs) { // append to end of current tabs
1465            $pane.appendTo($currTabContentPanesContainer);
1466          } else {                        // insert in middle of current tabs
1467            $pane.insertBefore($currTabContentPanes.eq(idx));
1468          }
1469        }
1470  
1471      }
1472  
1473    });
1474  
1475    return isInitTabsRequired;
1476  }
1477  
1478  function checkForTabPropertiesUpdated(refreshData) {
1479    var tabLiData = refreshData.tabLi,
1480        ignoreTabPanes = refreshData.ignoreTabPanes,
1481        $li = tabLiData.$li,
1482        $contentPane = tabLiData.$contentPane,
1483        origTabData = tabLiData.origTabData,
1484        newTabData = tabLiData.newTabData,
1485        propNames = refreshData.propNames,
1486        isInitTabsRequired = false;
1487  
1488    // update tab title if necessary
1489    if (origTabData[propNames.title] !== newTabData[propNames.title]) {
1490      $li.find('a[role="tab"]')
1491          .html(origTabData[propNames.title] = newTabData[propNames.title]);
1492  
1493      isInitTabsRequired = true;
1494    }
1495  
1496    // update tab disabled state if necessary
1497    if (origTabData[propNames.disabled] !== newTabData[propNames.disabled]) {
1498      if (newTabData[propNames.disabled]) { // enabled -> disabled
1499        $li.addClass('disabled');
1500        $li.find('a[role="tab"]').attr('data-toggle', '');
1501      } else { // disabled -> enabled
1502        $li.removeClass('disabled');
1503        $li.find('a[role="tab"]').attr('data-toggle', 'tab');
1504      }
1505  
1506      origTabData[propNames.disabled] = newTabData[propNames.disabled];
1507      isInitTabsRequired = true;
1508    }
1509  
1510    // update tab active state if necessary
1511    if (refreshData.options.forceActiveTab) {
1512      // set the active tab based on the tabs array regardless of the current
1513      // DOM state, which could have been changed by the user clicking a tab
1514      // without those changes being reflected back to the tab data
1515      $li[newTabData[propNames.active] ? 'addClass' : 'removeClass']('active');
1516  
1517      $contentPane[newTabData[propNames.active] ? 'addClass' : 'removeClass']('active');
1518  
1519      origTabData[propNames.active] = newTabData[propNames.active];
1520  
1521      isInitTabsRequired = true;
1522    }
1523  
1524    // update tab content pane if necessary
1525    if (!ignoreTabPanes && origTabData[propNames.content] !== newTabData[propNames.content]) {
1526      $contentPane.html(origTabData[propNames.content] = newTabData[propNames.content]);
1527      isInitTabsRequired = true;
1528    }
1529  
1530    return isInitTabsRequired;
1531  }
1532  
1533  function checkForTabRemoved(refreshData) {
1534    var tabLiData = refreshData.tabLi,
1535        ignoreTabPanes = refreshData.ignoreTabPanes,
1536        $li = tabLiData.$li,
1537        idxToMakeActive;
1538  
1539    if (tabLiData.newIdx !== -1) { // tab was not removed--it has a valid index
1540      return false;
1541    }
1542  
1543    // if this was the active tab, make the closest enabled tab active
1544    if ($li.hasClass('active')) {
1545  
1546      idxToMakeActive = tabUtils.getIndexOfClosestEnabledTab(refreshData.$currTabLis, tabLiData.currDomIdx);
1547      if (idxToMakeActive > -1) {
1548        refreshData.$currTabLis
1549          .eq(idxToMakeActive)
1550          .addClass('active');
1551  
1552        if (!ignoreTabPanes) {
1553          refreshData.$currTabContentPanes
1554            .eq(idxToMakeActive)
1555            .addClass('active');
1556        }
1557      }
1558    }
1559  
1560    $li.remove();
1561  
1562    if (!ignoreTabPanes) {
1563      tabLiData.$contentPane.remove();
1564    }
1565  
1566    return true;
1567  }
1568  
1569  function checkForTabsOrderChanged(refreshData) {
1570    var $currTabLis = refreshData.$currTabLis,
1571        updatedTabsArray = refreshData.updatedTabsArray,
1572        propNames = refreshData.propNames,
1573        ignoreTabPanes = refreshData.ignoreTabPanes,
1574        newTabsCollection = [],
1575        newTabPanesCollection = ignoreTabPanes ? null : [];
1576  
1577    if (!tabUtils.didTabOrderChange($currTabLis, updatedTabsArray, propNames)) {
1578      return false;
1579    }
1580  
1581    // the tab order changed...
1582    updatedTabsArray.forEach(function (t) {
1583      var paneId = t[propNames.paneId];
1584  
1585      newTabsCollection.push(
1586          $currTabLis
1587            .find('a[role="tab"][href="#' + paneId + '"]')
1588            .parent('li')
1589          );
1590  
1591      if (!ignoreTabPanes) {
1592        newTabPanesCollection.push($('#' + paneId));
1593      }
1594    });
1595  
1596    refreshData.$navTabs.append(newTabsCollection);
1597  
1598    if (!ignoreTabPanes) {
1599      refreshData.$currTabContentPanesContainer.append(newTabPanesCollection);
1600    }
1601  
1602    return true;
1603  }
1604  
1605  function checkForTabsRemovedOrUpdated(refreshData) {
1606    var $currTabLis = refreshData.$currTabLis,
1607        updatedTabsArray = refreshData.updatedTabsArray,
1608        propNames = refreshData.propNames,
1609        isInitTabsRequired = false;
1610  
1611  
1612    $currTabLis.each(function (currDomIdx) {
1613      var $li = $(this),
1614          origTabData = $li.data('tab'),
1615          newIdx = tabUtils.getTabIndexByPaneId(updatedTabsArray, propNames.paneId, origTabData[propNames.paneId]),
1616          newTabData = (newIdx > -1) ? updatedTabsArray[newIdx] : null;
1617  
1618      refreshData.tabLi = {
1619        $li: $li,
1620        currDomIdx: currDomIdx,
1621        newIdx: newIdx,
1622        $contentPane: tabElements.getElTabPaneForLi($li),
1623        origTabData: origTabData,
1624        newTabData: newTabData
1625      };
1626  
1627      if (checkForTabRemoved(refreshData)) {
1628        isInitTabsRequired = true;
1629        return; // continue to next $li in .each() since we removed this tab
1630      }
1631  
1632      if (checkForTabPropertiesUpdated(refreshData)) {
1633        isInitTabsRequired = true;
1634      }
1635    });
1636  
1637    return isInitTabsRequired;
1638  }
1639  
1640  function listenForDropdownMenuTabs($scroller, stc) {
1641    var $ddMenu;
1642  
1643    // for dropdown menus to show, we need to move them out of the
1644    // scroller and append them to the body
1645    $scroller
1646      .on(CONSTANTS.EVENTS.DROPDOWN_MENU_SHOW, handleDropdownShow)
1647      .on(CONSTANTS.EVENTS.DROPDOWN_MENU_HIDE, handleDropdownHide);
1648  
1649    function handleDropdownHide(e) {
1650      // move the dropdown menu back into its tab
1651      $(e.target).append($ddMenu.off(CONSTANTS.EVENTS.CLICK));
1652    }
1653  
1654    function handleDropdownShow(e) {
1655      var $ddParentTabLi = $(e.target),
1656          ddLiOffset = $ddParentTabLi.offset(),
1657          $currActiveTab = $scroller.find('li[role="presentation"].active'),
1658          ddMenuRightX,
1659          tabsContainerMaxX,
1660          ddMenuTargetLeft;
1661  
1662      $ddMenu = $ddParentTabLi
1663                  .find('.dropdown-menu')
1664                  .attr('data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED, true);
1665  
1666      // if the dropdown's parent tab li isn't already active,
1667      // we need to deactivate any active menu item in the dropdown
1668      if ($currActiveTab[0] !== $ddParentTabLi[0]) {
1669        $ddMenu.find('li.active').removeClass('active');
1670      }
1671  
1672      // we need to do our own click handling because the built-in
1673      // bootstrap handlers won't work since we moved the dropdown
1674      // menu outside the tabs container
1675      $ddMenu.on(CONSTANTS.EVENTS.CLICK, 'a[role="tab"]', handleClickOnDropdownMenuItem);
1676  
1677      $('body').append($ddMenu);
1678  
1679      // make sure the menu doesn't go off the right side of the page
1680      ddMenuRightX = $ddMenu.width() + ddLiOffset.left;
1681      tabsContainerMaxX = $scroller.width() - (stc.$slideRightArrow.outerWidth() + 1);
1682      ddMenuTargetLeft = ddLiOffset.left;
1683  
1684      if (ddMenuRightX > tabsContainerMaxX) {
1685        ddMenuTargetLeft -= (ddMenuRightX - tabsContainerMaxX);
1686      }
1687  
1688      $ddMenu.css({
1689        'display': 'block',
1690        'top': ddLiOffset.top + $ddParentTabLi.outerHeight() - 2,
1691        'left': ddMenuTargetLeft
1692      });
1693  
1694      function handleClickOnDropdownMenuItem() {
1695        /* jshint validthis: true */
1696        var $selectedMenuItemAnc = $(this),
1697            $selectedMenuItemLi = $selectedMenuItemAnc.parent('li'),
1698            $selectedMenuItemDropdownMenu = $selectedMenuItemLi.parent('.dropdown-menu'),
1699            targetPaneId = $selectedMenuItemAnc.attr('href');
1700  
1701        if ($selectedMenuItemLi.hasClass('active')) {
1702          return;
1703        }
1704  
1705        // once we select a menu item from the dropdown, deactivate
1706        // the current tab (unless it's our parent tab), deactivate
1707        // any active dropdown menu item, make our parent tab active
1708        // (if it's not already), and activate the selected menu item
1709        $scroller
1710          .find('li.active')
1711          .not($ddParentTabLi)
1712          .add($selectedMenuItemDropdownMenu.find('li.active'))
1713          .removeClass('active');
1714  
1715        $ddParentTabLi
1716          .add($selectedMenuItemLi)
1717          .addClass('active');
1718  
1719        // manually deactivate current active pane and activate our pane
1720        $('.tab-content .tab-pane.active').removeClass('active');
1721        $(targetPaneId).addClass('active');
1722      }
1723  
1724    }
1725  }
1726  
1727  function refreshDataDrivenTabs($container, options) {
1728    var instanceData = $container.data().scrtabs,
1729        scroller = instanceData.scroller,
1730        $navTabs = $container.find('.scrtabs-tab-container .nav-tabs'),
1731        $currTabContentPanesContainer = $container.find('.tab-content'),
1732        isInitTabsRequired = false,
1733        refreshData = {
1734          options: options,
1735          updatedTabsArray: instanceData.tabs,
1736          updatedTabsLiContent: instanceData.tabsLiContent,
1737          updatedTabsPostProcessors: instanceData.tabsPostProcessors,
1738          propNames: instanceData.propNames,
1739          ignoreTabPanes: instanceData.ignoreTabPanes,
1740          $navTabs: $navTabs,
1741          $currTabLis: $navTabs.find('> li'),
1742          $currTabContentPanesContainer: $currTabContentPanesContainer,
1743          $currTabContentPanes: $currTabContentPanesContainer.find('.tab-pane')
1744        };
1745  
1746    // to preserve the tab positions if we're just adding or removing
1747    // a tab, don't completely rebuild the tab structure, but check
1748    // for differences between the new tabs array and the old
1749    if (checkForTabAdded(refreshData)) {
1750      isInitTabsRequired = true;
1751    }
1752  
1753    if (checkForTabsOrderChanged(refreshData)) {
1754      isInitTabsRequired = true;
1755    }
1756  
1757    if (checkForTabsRemovedOrUpdated(refreshData)) {
1758      isInitTabsRequired = true;
1759    }
1760  
1761    if (isInitTabsRequired) {
1762      scroller.initTabs();
1763    }
1764  
1765    return isInitTabsRequired;
1766  }
1767  
1768  function refreshTargetElementInstance($container, options) {
1769    if (!$container.data('scrtabs')) { // target element doesn't have plugin on it
1770      return;
1771    }
1772  
1773    // force a refresh if the tabs are static html or they're data-driven
1774    // but the data didn't change so we didn't call initTabs()
1775    if ($container.data('scrtabs').isWrapperOnly || !refreshDataDrivenTabs($container, options)) {
1776      $('body').trigger(CONSTANTS.EVENTS.FORCE_REFRESH);
1777    }
1778  }
1779  
1780  function scrollToActiveTab() {
1781    /* jshint validthis: true */
1782    var $targetElInstance = $(this),
1783        scrtabsData = $targetElInstance.data('scrtabs');
1784  
1785    if (!scrtabsData) {
1786      return;
1787    }
1788  
1789    scrtabsData.scroller.scrollToActiveTab();
1790  }
1791  
1792  var methods = {
1793    destroy: function() {
1794      var $targetEls = this;
1795  
1796      return $targetEls.each(destroyPlugin);
1797    },
1798  
1799    init: function(options) {
1800      var $targetEls = this,
1801          targetElsLastIndex = $targetEls.length - 1,
1802          settings = $.extend({}, $.fn.scrollingTabs.defaults, options || {});
1803  
1804      // ---- tabs NOT data-driven -------------------------
1805      if (!settings.tabs) {
1806  
1807        // just wrap the selected .nav-tabs element(s) in the scroller
1808        return $targetEls.each(function(index) {
1809          var dataObj = {
1810                isWrapperOnly: true
1811              },
1812              $targetEl = $(this).data({ scrtabs: dataObj }),
1813              readyCallback = (index < targetElsLastIndex) ? null : function() {
1814                $targetEls.trigger(CONSTANTS.EVENTS.TABS_READY);
1815              };
1816  
1817          wrapNavTabsInstanceInScroller($targetEl, settings, readyCallback);
1818        });
1819  
1820      }
1821  
1822      // ---- tabs data-driven -------------------------
1823      return $targetEls.each(function (index) {
1824        var $targetEl = $(this),
1825            readyCallback = (index < targetElsLastIndex) ? null : function() {
1826              $targetEls.trigger(CONSTANTS.EVENTS.TABS_READY);
1827            };
1828  
1829        buildNavTabsAndTabContentForTargetElementInstance($targetEl, settings, readyCallback);
1830      });
1831    },
1832  
1833    refresh: function(options) {
1834      var $targetEls = this,
1835          settings = $.extend({}, $.fn.scrollingTabs.defaults, options || {});
1836  
1837      return $targetEls.each(function () {
1838        refreshTargetElementInstance($(this), settings);
1839      });
1840    },
1841  
1842    scrollToActiveTab: function() {
1843      return this.each(scrollToActiveTab);
1844    }
1845  };
1846  
1847  function destroyPlugin() {
1848    /* jshint validthis: true */
1849    var $targetElInstance = $(this),
1850        scrtabsData = $targetElInstance.data('scrtabs'),
1851        $tabsContainer;
1852  
1853    if (!scrtabsData) {
1854      return;
1855    }
1856  
1857    if (scrtabsData.enableSwipingElement === 'self') {
1858      $targetElInstance.removeClass(CONSTANTS.CSS_CLASSES.ALLOW_SCROLLBAR);
1859    } else if (scrtabsData.enableSwipingElement === 'parent') {
1860      $targetElInstance.closest('.scrtabs-tab-container').parent().removeClass(CONSTANTS.CSS_CLASSES.ALLOW_SCROLLBAR);
1861    }
1862  
1863    scrtabsData.scroller
1864      .off(CONSTANTS.EVENTS.DROPDOWN_MENU_SHOW)
1865      .off(CONSTANTS.EVENTS.DROPDOWN_MENU_HIDE);
1866  
1867    // if there were any dropdown menus opened, remove the css we added to
1868    // them so they would display correctly
1869    scrtabsData.scroller
1870      .find('[data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED + ']')
1871      .css({
1872        display: '',
1873        left: '',
1874        top: ''
1875      })
1876      .off(CONSTANTS.EVENTS.CLICK)
1877      .removeAttr('data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED);
1878  
1879    if (scrtabsData.scroller.hasTabClickHandler) {
1880      $targetElInstance
1881        .find('a[data-toggle="tab"]')
1882        .off('.scrtabs');
1883    }
1884  
1885    if (scrtabsData.isWrapperOnly) { // we just wrapped nav-tabs markup, so restore it
1886      // $targetElInstance is the ul.nav-tabs
1887      $tabsContainer = $targetElInstance.parents('.scrtabs-tab-container');
1888  
1889      if ($tabsContainer.length) {
1890        $tabsContainer.replaceWith($targetElInstance);
1891      }
1892  
1893    } else { // we generated the tabs from data so destroy everything we created
1894      if (scrtabsData.scroller && scrtabsData.scroller.initTabs) {
1895        scrtabsData.scroller.initTabs = null;
1896      }
1897  
1898      // $targetElInstance is the container for the ul.nav-tabs we generated
1899      $targetElInstance
1900        .find('.scrtabs-tab-container')
1901        .add('.tab-content')
1902        .remove();
1903    }
1904  
1905    $targetElInstance.removeData('scrtabs');
1906  
1907    while(--$.fn.scrollingTabs.nextInstanceId >= 0) {
1908      $(window).off(CONSTANTS.EVENTS.WINDOW_RESIZE + $.fn.scrollingTabs.nextInstanceId);
1909    }
1910  
1911    $('body').off(CONSTANTS.EVENTS.FORCE_REFRESH);
1912  }
1913  
1914  
1915  $.fn.scrollingTabs = function(methodOrOptions) {
1916  
1917    if (methods[methodOrOptions]) {
1918      return methods[methodOrOptions].apply(this, Array.prototype.slice.call(arguments, 1));
1919    } else if (!methodOrOptions || (typeof methodOrOptions === 'object')) {
1920      return methods.init.apply(this, arguments);
1921    } else {
1922      $.error('Method ' + methodOrOptions + ' does not exist on $.scrollingTabs.');
1923    }
1924  };
1925  
1926  $.fn.scrollingTabs.nextInstanceId = 0;
1927  
1928  $.fn.scrollingTabs.defaults = {
1929    tabs: null,
1930    propPaneId: 'paneId',
1931    propTitle: 'title',
1932    propActive: 'active',
1933    propDisabled: 'disabled',
1934    propContent: 'content',
1935    ignoreTabPanes: false,
1936    scrollToTabEdge: false,
1937    disableScrollArrowsOnFullyScrolled: 
1937true,
1938    forceActiveTab: false,
1939    reverseScroll: false,
1940    widthMultiplier: 1,
1941    tabClickHandler: null,
1942    cssClassLeftArrow: 'glyphicon glyphicon-chevron-left',
1943    cssClassRightArrow: 'glyphicon glyphicon-chevron-right',
1944    leftArrowContent: '',
1945    rightArrowContent: '',
1946    tabsLiContent: null,
1947    tabsPostProcessors: null,
1948    enableSwiping: true,
1949    enableRtlSupport: false,
1950    bootstrapVersion: 3
1951  };
1952  
1953
1954
1955}(jQuery, window));
1956
1957/* st-demo */
1958;(function() {
1959  'use strict';
1960
1961
1962  $(activate);
1963
1964
1965  function activate() {
1966
1967    $('.nav-tabs')
1968      .scrollingTabs()
1969      .on('ready.scrtabs', function() {
1970        $('.tab-content').show();
1971      });
1972
1973  }
1974}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.