home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (tabs.js)

This example Drupal source code file (tabs.js) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

callback, containers, else, false, function, hide, if, php, position, self, show, this, url, var

The tabs.js Drupal example source code

// $Id: tabs.js,v 1.4.2.1 2010/03/10 20:08:58 merlinofchaos Exp $

/**
 * @file tabs.js
 * jQuery UI Tabs (Tabs 3)
 *
 * This is nothing more than the pure jquery UI tabs implementation.
 * It has been implemented under the Drupal.Views.Tabs namespace to
 * avoid conflicts with alternatve versions of jquery, jquery UI.
 */

Drupal.Views = Drupal.Views || {};

Drupal.Views.Tabs = function(el, options) {

  this.source = el;

  this.options = $.extend({

    // basic setup
    initial: 0,
    event: 'click',
    disabled: [],
    // TODO bookmarkable: $.ajaxHistory ? true : false,
    unselected: false,
    toggle: options.unselected ? true : false,

    // Ajax
    spinner: 'Loading…',
    cache: false,
    hashPrefix: 'tab-',

    // animations
    /*fxFade: null,
    fxSlide: null,
    fxShow: null,
    fxHide: null,*/
    fxSpeed: 'normal',
    /*fxShowSpeed: null,
    fxHideSpeed: null,*/

    // callbacks
    add: function() {},
    remove: function() {},
    enable: function() {},
    disable: function() {},
    click: function() {},
    hide: function() {},
    show: function() {},
    load: function() {},

    // CSS classes
    navClass: 'ui-tabs-nav',
    selectedClass: 'ui-tabs-selected',
    disabledClass: 'ui-tabs-disabled',
    containerClass: 'ui-tabs-container',
    hideClass: 'ui-tabs-hide',
    loadingClass: 'ui-tabs-loading'

  }, options);

  this.tabify(true);

  // save instance for later
  var uuid = 'instance-' + Drupal.Views.Tabs.prototype.count++;
  Drupal.Views.Tabs.instances[uuid] = this;
  this.source.data('UI_TABS_UUID', uuid);

};

// static
Drupal.Views.Tabs.instances = {};

$.extend(Drupal.Views.Tabs.prototype, {
  animating: false,
  count: 0,
  tabify: function(init) {

    this.$tabs = $('a:first-child', this.source);
    this.$containers = $([]);

    var self = this, o = this.options;

    this.$tabs.each(function(i, a) {
      // inline tab
      if (a.hash && a.hash.replace('#', '')) { // safari 2 reports '#' for an empty hash
        self.$containers = self.$containers.add(a.hash);
      }
      // remote tab
      else {
        var id = a.title && a.title.replace(/\s/g, '_') || o.hashPrefix + (self.count + 1) + '-' + (i + 1), url = a.href;
        a.href = '#' + id;
        a.url = url;
        self.$containers = self.$containers.add(
          $('#' + id)[0] || $('<div id="' + id + '" class="' + o.containerClass + '"></div>')
            .insertAfter( self.$containers[i - 1] || self.source )
        );
      }
    });

    if (init) {

      // Try to retrieve initial tab from fragment identifier in url if present,
      // otherwise try to find selected class attribute on <li>.
      this.$tabs.each(function(i, a) {
        if (location.hash) {
          if (a.hash == location.hash) {
            o.initial = i;
            // prevent page scroll to fragment
            //if (($.browser.msie || $.browser.opera) && !o.remote) {
            if ($.browser.msie || $.browser.opera) {
              var $toShow = $(location.hash), toShowId = $toShow.attr('id');
              $toShow.attr('id', '');
              setTimeout(function() {
                $toShow.attr('id', toShowId); // restore id
              }, 500);
            }
            scrollTo(0, 0);
            return false; // break
          }
        } else if ( $(a).parents('li:eq(0)').is('li.' + o.selectedClass) ) {
          o.initial = i;
          return false; // break
        }
      });

      // attach necessary classes for styling if not present
      $(this.source).is('.' + o.navClass) || $(this.source).addClass(o.navClass);
      this.$containers.each(function() {
        var $this = $(this);
        $this.is('.' + o.containerClass) || $this.addClass(o.containerClass);
      });

      // highlight tab accordingly
      var $lis = $('li', this.source);
      this.$containers.addClass(o.hideClass);
      $lis.removeClass(o.selectedClass);
      if (!o.unselected) {
        this.$containers.slice(o.initial, o.initial + 1).show();
        $lis.slice(o.initial, o.initial + 1).addClass(o.selectedClass);
      }

      // trigger load of initial tab is remote tab
      if (this.$tabs[o.initial].url) {
        this.load(o.initial + 1, this.$tabs[o.initial].url);
        if (o.cache) {
          this.$tabs[o.initial].url = null; // if loaded once do not load them again
        }
      }

      // disabled tabs
      for (var i = 0, position; position = o.disabled[i]; i++) {
        this.disable(position);
      }

    }

    // setup animations
    var showAnim = {}, hideAnim = {}, showSpeed = o.fxShowSpeed || o.fxSpeed,
      hideSpeed = o.fxHideSpeed || o.fxSpeed;
    if (o.fxSlide || o.fxFade) {
      if (o.fxSlide) {
        showAnim['height'] = 'show';
        hideAnim['height'] = 'hide';
      }
      if (o.fxFade) {
        showAnim['opacity'] = 'show';
        hideAnim['opacity'] = 'hide';
      }
    } else {
      if (o.fxShow) {
        showAnim = o.fxShow;
      } else { // use some kind of animation to prevent browser scrolling to the tab
        showAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox
        showSpeed = 1; // as little as 1 is sufficient
      }
      if (o.fxHide) {
        hideAnim = o.fxHide;
      } else { // use some kind of animation to prevent browser scrolling to the tab
        hideAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox
        hideSpeed = 1; // as little as 1 is sufficient
      }
    }

    // callbacks
    var click = o.click, hide = o.hide, show = o.show;

    // reset some styles to maintain print style sheets etc.
    var resetCSS = { display: '', overflow: '', height: '' };
    if (!$.browser.msie) { // not in IE to prevent ClearType font issue
      resetCSS['opacity'] = '';
    }

    // hide a tab, animation prevents browser scrolling to fragment
    function hideTab(clicked, $hide, $show) {
      $hide.animate(hideAnim, hideSpeed, function() { //
        $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
        hide(clicked, $show, $hide[0]);
        if ($show) {
          showTab(clicked, $hide, $show);
        }
      });
    }

    // show a tab, animation prevents browser scrolling to fragment
    function showTab(clicked, $hide, $show) {
      // show next tab
      if (!(o.fxSlide || o.fxFade || o.fxShow)) {
        $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab containers
      }
      $show.animate(showAnim, showSpeed, function() {
        $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
        if ($.browser.msie) {
          $hide[0].style.filter = '';
          $show[0].style.filter = '';
        }
        show(clicked, $show[0], $hide[0]);
        self.animating = false;
      });

    }

    // switch a tab
    function switchTab(clicked, $hide, $show) {
      /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
        $.ajaxHistory.update(clicked.hash);
      }*/
      $(clicked).parents('li:eq(0)').addClass(o.selectedClass)
        .siblings().removeClass(o.selectedClass);
      hideTab(clicked, $hide, $show);
    }

    // tab click handler
    function tabClick(e) {

      //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
      var $li = $(this).parents('li:eq(0)'), $hide = self.$containers.filter(':visible'), $show = $(this.hash);

      // if tab may be closed
      if (o.toggle && !$li.is('.' + o.disabledClass) && !self.animating) {
        if ($li.is('.' + o.selectedClass)) {
          $li.removeClass(o.selectedClass);
          hideTab(this, $hide);
          this.blur();
          return false;
        } else if (!$hide.length) {
          $li.addClass(o.selectedClass);
          showTab(this, $hide, $show);
          this.blur();
          return false;
        }
      }

      // If tab is already selected or disabled, animation is still running or click callback
      // returns false stop here.
      // Check if click handler returns false last so that it is not executed for a disabled tab!
      if ($li.is('.' + o.selectedClass + ', .' + o.disabledClass)
        || self.animating || click(this, $show[0], $hide[0]) === false) {
        this.blur();
        return false;
      }

      self.animating = true;

      // show new tab
      if ($show.length) {

        // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
        /*if ($.browser.msie && o.bookmarkable) {
          var showId = this.hash.replace('#', '');
          $show.attr('id', '');
          setTimeout(function() {
            $show.attr('id', showId); // restore id
          }, 0);
        }*/

        if (this.url) { // remote tab
          var a = this;
          self.load(self.$tabs.index(this) + 1, this.url, function() {
            switchTab(a, $hide, $show);
          });
          if (o.cache) {
            this.url = null; // if loaded once do not load them again
          }
        } else {
          switchTab(this, $hide, $show);
        }

        // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
        /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
        var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
        setTimeout(function() {
          scrollTo(scrollX, scrollY);
        }, 0);*/

      } else {
        throw Drupal.t('jQuery UI Tabs: Mismatching fragment identifier.');
      }

      this.blur(); // prevent IE from keeping other link focussed when using the back button

      //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
      return false;

    }

    // attach click event, avoid duplicates from former tabifying
    this.$tabs.unbind(o.event, tabClick).bind(o.event, tabClick);

  },
  add: function(url, text, position) {
    if (url && text) {
      var o = this.options;
      position = position || this.$tabs.length; // append by default
      if (position >= this.$tabs.length) {
        var method = 'insertAfter';
        position = this.$tabs.length;
      } else {
        var method = 'insertBefore';
      }
      if (url.indexOf('#') == 0) { // ajax container is created by tabify automatically
        var $container = $(url);
        // try to find an existing element before creating a new one
        ($container.length && $container || $('<div id="' + url.replace('#', '') + '" class="' + o.containerClass + ' ' + o.hideClass + '"></div>'))
          [method](this.$containers[position - 1]);
      }
      $('<li><a href="' + url + '"><span>' + text + '</span></a></li>')
        [method](this.$tabs.slice(position - 1, position).parents('li:eq(0)'));
      this.tabify();
      o.add(this.$tabs[position - 1], this.$containers[position - 1]); // callback
    } else {
      throw Drupal.t('jQuery UI Tabs: Not enough arguments to add tab.');
    }
  },
  remove: function(position) {
    if (position && position.constructor == Number) {
      this.$tabs.slice(position - 1, position).parents('li:eq(0)').remove();
      this.$containers.slice(position - 1, position).remove();
      this.tabify();
    }
    this.options.remove(); // callback
  },
  enable: function(position) {
    var $li = this.$tabs.slice(position - 1, position).parents('li:eq(0)'), o = this.options;
    $li.removeClass(o.disabledClass);
    if ($.browser.safari) { // fix disappearing tab after enabling in Safari... TODO check Safari 3
      $li.animate({ opacity: 1 }, 1, function() {
        $li.css({ opacity: '' });
      });
    }
    o.enable(this.$tabs[position - 1], this.$containers[position - 1]); // callback
  },
  disable: function(position) {
    var $li = this.$tabs.slice(position - 1, position).parents('li:eq(0)'), o = this.options;
    if ($.browser.safari) { // fix opacity of tab after disabling in Safari... TODO check Safari 3
      $li.animate({ opacity: 0 }, 1, function() {
          $li.css({ opacity: '' });
      });
    }
    $li.addClass(this.options.disabledClass);
    o.disable(this.$tabs[position - 1], this.$containers[position - 1]); // callback
  },
  click: function(position) {
    this.$tabs.slice(position - 1, position).trigger('click');
  },
  load: function(position, url, callback) {
    var self = this,
      o = this.options,
      $a = this.$tabs.slice(position - 1, position).addClass(o.loadingClass),
      $span = $('span', $a),
      text = $span.html();

    // shift arguments
    if (url && url.constructor == Function) {
      callback = url;
    }

    // set new URL
    if (url) {
      $a[0].url = url;
    }

    // load
    if (o.spinner) {
      $span.html('<em>' + o.spinner + '</em>');
    }
    setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
      $($a[0].hash).load(url, function() {
        if (o.spinner) {
          $span.html(text);
        }
        $a.removeClass(o.loadingClass);
        // This callback is needed because the switch has to take place after loading
        // has completed.
        if (callback && callback.constructor == Function) {
          callback();
        }
        o.load(self.$tabs[position - 1], self.$containers[position - 1]); // callback
      });
    }, 0);
  }
});

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal tabs.js source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.