PageSourceSearch

https://fedgar.com/javascript/jquery.easytabs.js

js fedgar.com collected 2026-10-05 03:41:10 UTC 25,631 bytes, 679 lines download raw bytes

1/*
2 * jQuery EasyTabs plugin 3.1.1
3 *
4 * Copyright (c) 2010-2011 Steve Schwartz (JangoSteve)
5 *
6 * Dual licensed under the MIT and GPL licenses:
7 *   http://www.opensource.org/licenses/mit-license.php
8 *   http://www.gnu.org/licenses/gpl.html
9 *
10 * Date: Tue Jan 26 16:30:00 2012 -0500
11 */
12( function($) {
13
14  $.easytabs = function(container, options) {
15
16        // Attach to plugin anything that should be available via
17        // the $container.data('easytabs') object
18    var plugin = this,
19        $container = $(container),
20
21        defaults = {
22          animate: true,
23          panelActiveClass: "active",
24          tabActiveClass: "active",
25          defaultTab: "li:first-child",
26          animationSpeed: "normal",
27          tabs: "> ul > li",
28          updateHash: true,
29          cycle: false,
30          collapsible: false,
31          collapsedClass: "collapsed",
32          collapsedByDefault: true,
33          uiTabs: false,
34          transitionIn: 'fadeIn',
35          transitionOut: 'fadeOut',
36          transitionInEasing: 'swing',
37          transitionOutEasing: 'swing',
38          transitionCollapse: 'slideUp',
39          transitionUncollapse: 'slideDown',
40          transitionCollapseEasing: 'swing',
41          transitionUncollapseEasing: 'swing',
42          containerClass: "",
43          tabsClass: "",
44          tabClass: "",
45          panelClass: "",
46          cache: true,
47          panelContext: $container
48        },
49
50        // Internal instance variables
51        // (not available via easytabs object)
52        $defaultTab,
53        $defaultTabLink,
54        transitions,
55        lastHash,
56        skipUpdateToHash,
57        animationSpeeds = {
58          fast: 200,
59          normal: 400,
60          slow: 600
61        },
62
63        // Shorthand variable so that we don't need to call
64        // plugin.settings throughout the plugin code
65        settings;
66
67    // =============================================================
68    // Functions available via easytabs object
69    // =============================================================
70
71    plugin.init = function() {
72
73      plugin.settings = settings = $.extend({}, defaults, options);
74
75      // Add jQuery UI's crazy class names to markup,
76      // so that markup will match theme CSS
77      if ( settings.uiTabs ) {
78        settings.tabActiveClass = 'ui-tabs-selected';
79        settings.containerClass = 'ui-tabs ui-widget ui-widget-content ui-corner-all';
80        settings.tabsClass = 'ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all';
81        settings.tabClass = 'ui-state-default ui-corner-top';
82        settings.panelClass = 'ui-tabs-panel ui-widget-content ui-corner-bottom';
83      }
84
85      // If collapsible is true and defaultTab specified, assume user wants defaultTab showing (not collapsed)
86      if ( settings.collapsible && options.defaultTab !== undefined && options.collpasedByDefault === undefined ) {
87        settings.collapsedByDefault = false;
88      }
89
90      // Convert 'normal', 'fast', and 'slow' animation speed settings to their respective speed in milliseconds
91      if ( typeof(settings.animationSpeed) === 'string' ) {
92        settings.animationSpeed = animationSpeeds[settings.animationSpeed];
93      }
94
95      $('a.anchor').remove().prependTo('body');
96
97      // Store easytabs object on container so we can easily set
98      // properties throughout
99      $container.data('easytabs', {});
100
101      plugin.setTransitions();
102
103      plugin.getTabs();
104
105      addClasses();
106
107      setDefaultTab();
108
109      bindToTabClicks();
110
111      initHashChange();
112
113      initCycle();
114
115      // Append data-easytabs HTML attribute to make easy to query for
116      // easytabs instances via CSS pseudo-selector
117      $container.attr('data-easytabs', true);
118    };
119
120    // Set transitions for switching between tabs based on options.
121    // Could be used to update transitions if settings are changes.
122    plugin.setTransitions = function() {
123      transitions = ( settings.animate ) ? {
124          show: settings.transitionIn,
125          hide: settings.transitionOut,
126          speed: settings.animationSpeed,
127          collapse: settings.transitionCollapse,
128          uncollapse: settings.transitionUncollapse,
129          halfSpeed: settings.animationSpeed / 2
130        } :
131        {
132          show: "show",
133          hide: "hide",
134          speed: 0,
135          collapse: "hide",
136          uncollapse: "show",
137          halfSpeed: 0
138        };
139    };
140
141    // Find and instantiate tabs and panels.
142    // Could be used to reset tab and panel collection if markup is
143    // modified.
144    plugin.getTabs = function() {
145      var $matchingPanel;
146
147      // Find the initial set of elements matching the setting.tabs
148      // CSS selector within the container
149      plugin.tabs = $container.find(settings.tabs),
150
151      // Instantiate panels as empty jquery object
152      plugin.panels = $(),
153
154      plugin.tabs.each(function(){
155        var $tab = $(this),
156            $a = $tab.children('a'),
157
158            // targetId is the ID of the panel, which is either the
159            // `href` attribute for non-ajax tabs, or in the
160            // `data-target` attribute for ajax tabs since the `href` is
161            // the ajax URL
162            targetId = $tab.children('a').data('target');
163
164        $tab.data('easytabs', {});
165
166        // If the tab has a `data-target` attribute, and is thus an ajax tab
167        if ( targetId !== undefined && targetId !== null ) {
168          $tab.data('easytabs').ajax = $a.attr('href');
169        } else {
170          targetId = $a.attr('href');
171        }
172        targetId = targetId.match(/#([^\?]+)/)[0].substr(1);
173
174        $matchingPanel = settings.panelContext.find("#" + targetId);
175
176        // If tab has a matching panel, add it to panels
177        if ( $matchingPanel.length ) {
178
179          // Store panel height before hiding
180          $matchingPanel.data('easytabs', {
181            position: $matchingPanel.css('position'),
182            visibility: $matchingPanel.css('visibility')
183          });
184
185          // Don't hide panel if it's active (allows `getTabs` to be called manually to re-instantiate tab collection)
186          $matchingPanel.not(settings.panelActiveClass).hide();
187
188          plugin.panels = plugin.panels.add($matchingPanel);
189
190          $tab.data('easytabs').panel = $matchingPanel;
191
192        // Otherwise, remove tab from tabs collection
193        } else {
194          plugin.tabs = plugin.tabs.not($tab);
195        }
196      });
197    };
198
199    // Select tab and fire callback
200    plugin.selectTab = function($clicked, callback) {
201      var url = window.location,
202          hash = url.hash.match(/^[^\?]*/)[0],
203          $targetPanel = $clicked.parent().data('easytabs').panel,
204          ajaxUrl = $clicked.parent().data('easytabs').ajax;
205
206      // Tab is collapsible and active => toggle collapsed state
207      if( settings.collapsible && ! skipUpdateToHash && ($clicked.hasClass(settings.tabActiveClass) || $clicked.hasClass(settings.collapsedClass)) ) {
208        plugin.toggleTabCollapse($clicked, $targetPanel, ajaxUrl, callback);
209
210      // Tab is not active and panel is not active => select tab
211      } else if( ! $clicked.hasClass(settings.tabActiveClass) || ! $targetPanel.hasClass(settings.panelActiveClass) ){
212        activateTab($clicked, $targetPanel, ajaxUrl, callback);
213
214      // Cache is disabled => reload (e.g reload an ajax tab).
215      } else if ( ! settings.cache ){
216        activateTab($clicked, $targetPanel, ajaxUrl, callback);
217      }
218
219    };
220
221    // Toggle tab collapsed state and fire callback
222    plugin.toggleTabCollapse = function($clicked, $targetPanel, ajaxUrl, callback) {
223      plugin.panels.stop(true,true);
224
225      if( fire($container,"easytabs:before", [$clicked, $targetPanel, settings]) ){
226        plugin.tabs.filter("." + settings.tabActiveClass).removeClass(settings.tabActiveClass).children().removeClass(settings.tabActiveClass);
227
228        // If panel is collapsed, uncollapse it
229        if( $clicked.hasClass(settings.collapsedClass) ){
230
231          // If ajax panel and not already cached
232          if( ajaxUrl && (!settings.cache || !$clicked.parent().data('easytabs').cached) ) {
233            $container.trigger('easytabs:ajax:beforeSend', [$clicked, $targetPanel]);
234
235            $targetPanel.load(ajaxUrl, function(response, status, xhr){
236              $clicked.parent().data('easytabs').cached = true;
237              $container.trigger('easytabs:ajax:complete', [$clicked, $targetPanel, response, status, xhr]);
238            });
239          }
240
241          // Update CSS classes of tab and panel
242          $clicked.parent()
243            .removeClass(settings.collapsedClass)
244            .addClass(settings.tabActiveClass)
245            .children()
246              .removeClass(settings.collapsedClass)
247              .addClass(settings.tabActiveClass);
248
249          $targetPanel
250            .addClass(settings.panelActiveClass)
251            [transitions.uncollapse](transitions.speed, settings.transitionUncollapseEasing, function(){
252              $container.trigger('easytabs:midTransition', [$clicked, $targetPanel, settings]);
253              if(typeof callback == 'function') callback();
254            });
255
256        // Otherwise, collapse it
257        } else {
258
259          // Update CSS classes of tab and panel
260          $clicked.addClass(settings.collapsedClass)
261            .parent()
262              .addClass(settings.collapsedClass);
263
264          $targetPanel
265            .removeClass(settings.panelActiveClass)
266            [transitions.collapse](transitions.speed, settings.transitionCollapseEasing, function(){
267              $container.trigger("easytabs:midTransition", [$clicked, $targetPanel, settings]);
268              if(typeof callback == 'function') callback();
269            });
270        }
271      }
272    };
273
274
275    // Find tab with target panel matching value
276    plugin.matchTab = function(hash) {
277      return plugin.tabs.find("[href='" + hash + "'],[data-target='" + hash + "']").first();
278    };
279
280    // Find panel with `id` matching value
281    plugin.matchInPanel = function(hash) {
282      return ( hash && hash !== "#" ? plugin.panels.filter(':has(' + hash + ')').first() : [] );
283    };
284
285    // Select matching tab when URL hash changes
286    plugin.selectTabFromHashChange = function() {
287      var hash = window.location.hash.match(/^[^\?]*/)[0],
288          $tab = plugin.matchTab(hash),
289          $panel;
290
291      if ( settings.updateHash ) {
292
293        // If hash directly matches tab
294        if( $tab.length ){
295          skipUpdateToHash = true;
296          plugin.selectTab( $tab );
297
298        } else {
299          $panel = plugin.matchInPanel(hash);
300
301          // If panel contains element matching hash
302          if ( $panel.length ) {
303            hash = '#' + $panel.attr('id');
304            $tab = plugin.matchTab(hash);
305            skipUpdateToHash = true;
306            plugin.selectTab( $tab );
307
308          // If default tab is not active...
309          } else if ( ! $defaultTab.hasClass(settings.tabActiveClass) && ! settings.cycle ) {
310
311            // ...and hash is blank or matches a parent of the tab container or
312            // if the last tab (before the hash updated) was one of the other tabs in this container.
313            if ( hash === '' || plugin.matchTab(lastHash).length || $container.closest(hash).length ) {
314              skipUpdateToHash = true;
315              plugin.selectTab( $defaultTabLink );
316            }
317          }
318        }
319      }
320    };
321
322    // Cycle through tabs
323    plugin.cycleTabs = function(tabNumber){
324      if(settings.cycle){
325        tabNumber = tabNumber % plugin.tabs.length;
326        $tab = $( plugin.tabs[tabNumber] ).children("a").first();
327        skipUpdateToHash = true;
328        plugin.selectTab( $tab, function() {
329          setTimeout(function(){ plugin.cycleTabs(tabNumber + 1); }, settings.cycle);
330        });
331      }
332    };
333
334    // Convenient public methods
335    plugin.publicMethods = {
336      select: function(tabSelector){
337        var $tab;
338
339        // Find tab container that matches selector (like 'li#tab-one' which contains tab link)
340        if ( ($tab = plugin.tabs.filter(tabSelector)).length === 0 ) {
341
342          // Find direct tab link that matches href (like 'a[href="#panel-1"]')
343          if ( ($tab = plugin.tabs.find("a[href='" + tabSelector + "']")).length === 0 ) {
344
345            // Find direct tab link that matches selector (like 'a#tab-1')
346            if ( ($tab = plugin.tabs.find("a" + tabSelector)).length === 0 ) {
347
348              // Find direct tab link that matches data-target (lik 'a[data-target="#panel-1"]')
349              if ( ($tab = plugin.tabs.find("[data-target='" + tabSelector + "']")).length === 0 ) {
350
351                // Find direct tab link that ends in the matching href (like 'a[href$="#panel-1"]', which would also match http://example.com/currentpage/#panel-1)
352                if ( ($tab = plugin.tabs.find("a[href$='" + tabSelector + "']")).length === 0 ) {
353
354                  $.error('Tab \'' + tabSelector + '\' does not exist in tab set');
355                }
356              }
357            }
358          }
359        } else {
360          // Select the child tab link, since the first option finds the tab container (like <li>)
361          $tab = $tab.children("a").first();
362        }
363        plugin.selectTab($tab);
364      }
365    };
366
367    // =============================================================
368    // Private functions
369    // =============================================================
370
371    // Triggers an event on an element and returns the event result
372    var fire = function(obj, name, data) {
373      var event = $.Event(name);
374      obj.trigger(event, data);
375      return event.result !== false;
376    }
377
378    // Add CSS classes to markup (if specified), called by init
379    var addClasses = function() {
380      $container.addClass(settings.containerClass);
381      plugin.tabs.parent().addClass(settings.tabsClass);
382      plugin.tabs.addClass(settings.tabClass);
383      plugin.panels.addClass(settings.panelClass);
384    };
385
386    // Set the default tab, whether from hash (bookmarked) or option,
387    // called by init
388    var setDefaultTab = function(){
389      var hash = window.location.hash.match(/^[^\?]*/)[0],
390          $selectedTab = plugin.matchTab(hash).parent(),
391          $panel;
392
393      // If hash directly matches one of the tabs, active on page-load
394      if( hash !== '' && $selectedTab.length === 1 ){
395        $defaultTab = $selectedTab;
396        settings.cycle = false;
397
398      } else {
399        $panel = plugin.matchInPanel(hash);
400
401        // If one of the panels contains the element matching the hash,
402        // make it active on page-load
403        if ( hash !== '' && $panel.length ) {
404          hash = '#' + $panel.attr('id');
405          $defaultTab = plugin.matchTab(hash).parent();
406
407        // Otherwise, make the default tab the one that's active on page-load
408        } else {
409          $defaultTab = plugin.tabs.parent().find(settings.defaultTab);
410          if ( $defaultTab.length === 0 ) {
411            $.error("The specified default tab ('" + settings.defaultTab + "') could not be found in the tab set.");
412          }
413        }
414      }
415
416      $defaultTabLink = $defaultTab.children("a").first();
417
418      activateDefaultTab($selectedTab);
419    };
420
421    // Activate defaultTab (or collapse by default), called by setDefaultTab
422    var activateDefaultTab = function($selectedTab) {
423      var defaultPanel,
424          defaultAjaxUrl;
425
426      if ( settings.collapsible && $selectedTab.length === 0 && settings.collapsedByDefault ) {
427        $defaultTab
428          .addClass(settings.collapsedClass)
429          .children()
430            .addClass(settings.collapsedClass);
431
432      } else {
433
434        defaultPanel = $( $defaultTab.data('easytabs').panel );
435        defaultAjaxUrl = $defaultTab.data('easytabs').ajax;
436
437        if ( defaultAjaxUrl && (!settings.cache || !$defaultTab.data('easytabs').cached) ) {
438          $container.trigger('easytabs:ajax:beforeSend', [$defaultTabLink, defaultPanel]);
439          defaultPanel.load(defaultAjaxUrl, function(response, status, xhr){
440            $defaultTab.data('easytabs').cached = true;
441            $container.trigger('easytabs:ajax:complete', [$defaultTabLink, defaultPanel, response, status, xhr]);
442          });
443        }
444
445        $defaultTab.data('easytabs').panel
446          .show()
447          .addClass(settings.panelActiveClass);
448
449        $defaultTab
450          .addClass(settings.tabActiveClass)
451          .children()
452            .addClass(settings.tabActiveClass);
453      }
454    };
455
456    // Bind tab-select funtionality to namespaced click event, called by
457    // init
458    var bindToTabClicks = function() {
459      plugin.tabs.children("a").bind("click.easytabs", function(e) {
460
461        // Stop cycling when a tab is clicked
462        settings.cycle = false;
463
464        // Hash will be updated when tab is clicked,
465        // don't cause tab to re-select when hash-change event is fired
466        skipUpdateToHash = false;
467
468        // Select the panel for the clicked tab
469        plugin.selectTab( $(this) );
470
471        // Don't follow the link to the anchor
472        e.preventDefault();
473      });
474    };
475
476    // Activate a given tab/panel, called from plugin.selectTab:
477    //
478    //   * fire `easytabs:before` hook
479    //   * get ajax if new tab is an uncached ajax tab
480    //   * animate out previously-active panel
481    //   * fire `easytabs:midTransition` hook
482    //   * update URL hash
483    //   * animate in newly-active panel
484    //   * update CSS classes for inactive and active tabs/panels
485    //
486    // TODO: This could probably be broken out into many more modular
487    // functions
488    var activateTab = function($clicked, $targetPanel, ajaxUrl, callback) {
489      plugin.panels.stop(true,true);
490
491      if( fire($container,"easytabs:before", [$clicked, $targetPanel, settings]) ){
492        var $visiblePanel = plugin.panels.filter(":visible"),
493            $panelContainer = $targetPanel.parent(),
494            targetHeight,
495            visibleHeight,
496            heightDifference,
497            showPanel,
498            hash = window.location.hash.match(/^[^\?]*/)[0];
499
500        if (settings.animate) {
501          targetHeight = getHeightForHidden($targetPanel);
502          visibleHeight = $visiblePanel.length ? setAndReturnHeight($visiblePanel) : 0;
503          heightDifference = targetHeight - visibleHeight;
504        }
505
506        // Set lastHash to help indicate if defaultTab should be
507        // activated across multiple tab instances.
508        lastHash = hash;
509
510        // TODO: Move this function elsewhere
511        showPanel = function() {
512          // At this point, the previous panel is hidden, and the new one will be selected
513          $container.trigger("easytabs:midTransition", [$clicked, $targetPanel, settings]);
514
515          // Gracefully animate between panels of differing heights, start height change animation *after* panel change if panel needs to contract,
516          // so that there is no chance of making the visible panel overflowing the height of the target panel
517          if (settings.animate && settings.transitionIn == 'fadeIn') {
518            if (heightDifference < 0)
519              $panelContainer.animate({
520                height: $panelContainer.height() + heightDifference
521              }, transitions.halfSpeed ).css({ 'min-height': '' });
522          }
523
524          if ( settings.updateHash && ! skipUpdateToHash ) {
525            //window.location = url.toString().replace((url.pathname + hash), (url.pathname + $clicked.attr("href")));
526            // Not sure why this behaves so differently, but it's more straight forward and seems to have less side-effects
527            window.location.hash = '#' + $targetPanel.attr('id');
528          } else {
529            skipUpdateToHash = false;
530          }
531
532          $targetPanel
533            [transitions.show](transitions.speed, settings.transitionInEasing, function(){
534              $panelContainer.css({height: '', 'min-height': ''}); // After the transition, unset the height
535              $container.trigger("easytabs:after", [$clicked, $targetPanel, settings]);
536              // callback only gets called if selectTab actually does something, since it's inside the if block
537              if(typeof callback == 'function'){
538                callback();
539              }
540          });
541        };
542
543        if ( ajaxUrl && (!settings.cache || !$clicked.parent().data('easytabs').cached) ) {
544          $container.trigger('easytabs:ajax:beforeSend', [$clicked, $targetPanel]);
545          $targetPanel.load(ajaxUrl, function(response, status, xhr){
546            $clicked.parent().data('easytabs').cached = true;
547            $container.trigger('easytabs:ajax:complete', [$clicked, $targetPanel, response, status, xhr]);
548          });
549        }
550
551        // Gracefully animate between panels of differing heights, start height change animation *before* panel change if panel needs to expand,
552        // so that there is no chance of making the target panel overflowing the height of the visible panel
553        if( settings.animate && settings.transitionOut == 'fadeOut' ) {
554          if( heightDifference > 0 ) {
555            $panelContainer.animate({
556              height: ( $panelContainer.height() + heightDifference )
557            }, transitions.halfSpeed );
558          } else {
559            // Prevent height jumping before height transition is triggered at midTransition
560            $panelContainer.css({ 'min-height': $panelContainer.height() });
561          }
562        }
563
564        // Change the active tab *first* to provide immediate feedback when the user clicks
565        plugin.tabs.filter("." + settings.tabActiveClass).removeClass(settings.tabActiveClass).children().removeClass(settings.tabActiveClass);
566        plugin.tabs.filter("." + settings.collapsedClass).removeClass(settings.collapsedClass).children().removeClass(settings.collapsedClass);
567        $clicked.parent().addClass(settings.tabActiveClass).children().addClass(settings.tabActiveClass);
568
569        plugin.panels.filter("." + settings.panelActiveClass).removeClass(settings.panelActiveClass);
570        $targetPanel.addClass(settings.panelActiveClass);
571
572        if( $visiblePanel.length ) {
573          $visiblePanel
574            [transitions.hide](transitions.speed, settings.transitionOutEasing, showPanel);
575        } else {
576          $targetPanel
577            [transitions.uncollapse](transitions.speed, settings.transitionUncollapseEasing, showPanel);
578        }
579      }
580    };
581
582    // Get heights of panels to enable animation between panels of
583    // differing heights, called by activateTab
584    var getHeightForHidden = function($targetPanel){
585
586      if ( $targetPanel.data('easytabs') && $targetPanel.data('easytabs').lastHeight ) {
587        return $targetPanel.data('easytabs').lastHeight;
588      }
589
590      // this is the only property easytabs changes, so we need to grab its value on each tab change
591      var display = $targetPanel.css('display'),
592
593          // Workaround, because firefox returns wrong height if element itself has absolute positioning
594          height = $targetPanel
595            .wrap($('<div>', {position: 'absolute', 'visibility': 'hidden', 'overflow': 'hidden'}))
596            .css({'position':'relative','visibility':'hidden','display':'block'})
597            .outerHeight();
598
599      $targetPanel.unwrap();
600
601      // Return element to previous state
602      $targetPanel.css({
603        position: $targetPanel.data('easytabs').position,
604        visibility: $targetPanel.data('easytabs').visibility,
605        display: display
606      });
607
608      // Cache height
609      $targetPanel.data('easytabs').lastHeight = height;
610
611      return height;
612    };
613
614    // Since the height of the visible panel may have been manipulated due to interaction,
615    // we want to re-cache the visible height on each tab change, called
616    // by activateTab
617    var setAndReturnHeight = function($visiblePanel) {
618      var height = $visiblePanel.outerHeight();
619
620      if( $visiblePanel.data('easytabs') ) {
621        $visiblePanel.data('easytabs').lastHeight = height;
622      } else {
623        $visiblePanel.data('easytabs', {lastHeight: height});
624      }
625      return height;
626    };
627
628    // Setup hash-change callback for forward- and back-button
629    // functionality, called by init
630    var initHashChange = function(){
631
632      // enabling back-button with jquery.hashchange plugin
633      // http://benalman.com/projects/jquery-hashchange-plugin/
634      if(typeof $(window).hashchange === 'function'){
635        $(window).hashchange( function(){
636          plugin.selectTabFromHashChange();
637        });
638      } else if ($.address && typeof $.address.change === 'function') { // back-button with jquery.address plugin http://www.asual.com/jquery/address/docs/
639        $.address.change( function(){
640          plugin.selectTabFromHashChange();
641        });
642      }
643    };
644
645    // Begin cycling if set in options, called by init
646    var initCycle = function(){
647      var tabNumber;
648      if (settings.cycle) {
649        tabNumber = plugin.tabs.index($defaultTab);
650        setTimeout( function(){ plugin.cycleTabs(tabNumber + 1); }, settings.cycle);
651      }
652    };
653
654
655    plugin.init();
656
657  };
658
659  $.fn.easytabs = function(options) {
660    var args = arguments;
661
662    return this.each(function() {
663      var $this = $(this),
664          plugin = $this.data('easytabs');
665
666      // Initialization was called with $(el).easytabs( { options } );
667      if (undefined === plugin) {
668        plugin = new $.easytabs(this, options);
669        $this.data('easytabs', plugin);
670      }
671
672      // User called public method
673      if ( plugin.publicMethods[options] ){
674        return plugin.publicMethods[options](Array.prototype.slice.call( args, 1 ));
675      }
676    });
677  };
678
679})(jQuery);

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.