﻿/*!
 * FooBar - The Unobtrusive Notification Bar That Doesn’t Suck!
 * http://bit.ly/getfoobar
 *
 * Copyright 2011, Steven Usher & Brad Vincent
 * http://themergency.com
 * http://themergency.com/foobar-a-jquery-notification-plugin/
 *
 * Date: 1 October 2011
 * Version : 1.8
 */

(function( $ ) {

  $.foobar = function( options ) { // $.foobar the main constructor for the plugin
    if ( typeof options == "string" ) { // if just a string is passed into the constructor assume it to be the only message to display
      options = { "messages": [options] };
    }
    var settings = $.extend( true, {}, defaults, options ); // extend the default options with the user provided options
    bar.apply( settings );
  };

  $.foobarGoogleCallback = function() { // this is the function called once the google api has been loaded into the page
    bar.loadFeeds();
  };

  /* This block of code is here to enable more detailed debugging options */
  if ( !window.console ) { console = {}; }
  console.log = console.log || function() {};
  console.warn = console.warn || function() {};
  console.error = console.error || function() {};
  console.info = console.info || function() {};

  if ( !JSON ) { JSON = {}; }
  JSON.stringify = JSON.stringify || function() {};
  /* End debug helpers */

  var defaults = {
    "height": 30, // The height of the bar in pixels
    "collapsedButtonHeight": 30, // The height of the button in pixels, when the bar is collapsed
    "positioning": "fixed", // How the bar is positioned within the page (inline | fixed)
    "adjustPageHeight": true,
    "backgroundColor": "IndianRed", // The CSS background color of the bar
    "border": "solid 3px #FFF", // The CSS border styling for the bottom of the bar
    "enableShadow": true, // Sets whether to display the shadow below the bar and close button.
    "ignoreHtmlMarginTop": false, // If you have a plugin that hides the default WP admin bar or similar web parts and the foobar is not correctly
    // positioning itself at the top of the page set this value to true to force the foobar to render correctly.

    "buttonTheme": "triangle-arrow",

    "display": "expanded", // The initial state of the FooBar (expanded, collapsed, delayed, onscroll)
    "displayDelay": 0, // Used in conjunction with the "delayed" and "onscroll" display values to determine the amount of time to wait before showing the bar

    "speed": 200, // The speed at which to scroll the bar into view
    "easing": "swing",  //The type of easing used when expanding or collapsing the bar

    "messages": [], // The messages to display in the bar
    //If only 1 message it will be displayed permanently otherwise the messagesDelay value is used to cycle through the array.

    "messageSizes" : [],

    "messagesDelay": 4000, // The delay between switching of messages if more than 1 is supplied
    "messagesFadeDelay": 500, // The time it takes to transition to the next message    
    "messagesScrollSpeed": 50, // The pixels per second to scroll extra length messages into view
    "messagesScrollDelay": 2000, // The delay between initially displaying a long message and the beginning of scrolling it
    "messagesScrollDirection": "left", // The direction to scroll the text of long messages (left, right)
    "enableRandomMessage": false, // Sets whether or not to randomly select messages to be displayed
    "enableMessageScroll": true, // Sets whether or not to allow extra length messages to be scrolled into view
    "messagePadding" : "2px",

    "enableCookie": false, // stores the state of the bar in a client-side cookie (open or closed)
    "positionClose": "right", // Position of the close button (left | right | hidden)
    "positionSocial": "left", // Position of the social links (left | right | hidden)

    "rightHtml": null,  //custom HTML that is displayed in the right section of the bar
    "leftHtml": null,   //custom HTML that is displayed in the left section of the bar
    "leftWidth" : null, //custom width of the left section of the bar
    "rightWidth" : null,//custom width of the right section of the bar
    "centerWidth" : null,//custom width of the center section of the bar that contains the messages

    "messageClass": "", // The CSS class to apply to the messages
    "socialClass": "", // The CSS class to apply to the social links

    "fontFamily": "Verdana", // The font family used for the messages
    "fontSize": "10pt", // The font size used for the messages
    "fontColor": "White", // The font color used for the messages
    "fontShadow": null, // The shadow applied to the font used for messages (only supported by browsers that support CSS3)

    "aFontFamily": "Verdana", // The font family of any links in the messages
    "aFontSize": "10pt", // The font size of any links in the messages
    "aFontColor": "LightYellow", // The font color of any links in the messages
    "aFontDecoration": "underline", // The text decoration of any links in the messages
    "aFontShadow": null, // The shadow applied to the font of any links in the messages (only supported by browsers that support CSS3)

    "aHoverFontFamily": null, // The font family of any links in the messages
    "aHoverFontSize": null, // The font size of any links in the messages
    "aHoverFontColor": null, // The font color of any links in the messages
    "aHoverFontDecoration": null, // The text decoration of any links in the messages
    "aHoverFontShadow": null, // The shadow applied to the font of any links in the messages (only supported by browsers that support CSS3)

    "googleAPIKey": "", // Your Google API key, that is needed in order to pull RSS feed results

    "social": {
      "text": "Follow us:", // The text displayed in the social area
      "fontFamily": "Verdana", // The font family of the text in the social area
      "fontSize": "10pt", // The font size of the text in the social area
      "fontColor": "White", // The font color of the text in the social area
      "fontShadow": null, // The shadow applied to the font of the text in the social area (only supported by browsers that support CSS3)
      "profiles": []
    },

    "rss": {
      "googleAPIKey": "", // [DEPRECATED] Use the root "googleAPIKey" option instead.
      "enabled": false, // If messages can be populated using an RSS feed
      "url": "http://my-domain.com/rss", // The URL to the RSS feed
      "maxResults": 5, //The maximum number of RSS feed resuls to display as messages
      "linkText": "Read More", // The text displayed for the link back to the original post
      "linkTarget": "_blank" // The target for rss links (_blank | _self | _parent | _top | 'framename')
    },

    "twitter": {
      "enabled": false, // If tweets can be loaded
      "user": null, // the user whose tweets you want to show
      "maxTweets": 5 // max number of tweets to fetch
    }
  };

  var bar = {
    settings: {},

    wrapper: null,
    container: null,
    shadow: null,
    left: null,
    center: null,
    message: null,
    right: null,
    closeButtonContainer: null,
    closeButton: null,
    openButtonContainer: null,
    openButton: null,

    initialized: false,
    isOpen: false,
    htmlMarginTop: 0,
    messageTimeoutId: null,
    messageIndex: 0,
    currentMessageIndex: -1,
    messageHover: false,
    shadowHeight: 5, // Sets the additional height added to the bar to allow for the shadow. When the shadow is disabled this is set to 0.

    initialize: function() {
      /// <summary>Creates the initial foobar html and appends it to the DOM, setting the various bar. properties as it goes</summary>

      if ( $( '.foobar-wrapper' ).length === 0 ) { // if the foobar doesn't exist create it
        bar.wrapper = $( '<div></div>' ).addClass( 'foobar-wrapper' );
        bar.container = $( '<div></div>' ).addClass( 'foobar-container' );
        bar.shadow = $( '<div></div>' ).addClass( 'foobar-shadow' );
        bar.left = $( '<div></div>' ).addClass( 'foobar-container-left' );
        bar.center = $( '<div></div>' ).addClass( 'foobar-container-center' );
        bar.message = $( '<span></span>' ).addClass( 'foobar-message' );
        bar.right = $( '<div></div>' ).addClass( 'foobar-container-right' );
        bar.closeButtonContainer = $( '<div></div>' ).addClass( 'foobar-close-button-container' );
        bar.closeButton = $( '<a></a>' ).attr( 'href', '#close-foobar' ).addClass( 'foobar-close-button' ).text( ' ' );
        bar.openButtonContainer = $( '<div></div>' ).addClass( 'foobar-open-button-container' );
        bar.openButton = $( '<a></a>' ).attr( 'href', '#open-foobar' ).addClass( 'foobar-open-button' ).text( ' ' );
        // append the bar to the DOM
        $( 'body' ).prepend( bar.wrapper );
        bar.wrapper.append( bar.container ).append( bar.shadow ).append( bar.openButtonContainer );
        bar.container.append( bar.closeButtonContainer ).append( bar.left ).append( bar.center.append( bar.message ) ).append( bar.right );
        bar.closeButtonContainer.append( bar.closeButton );
        bar.openButtonContainer.append( bar.openButton );
      } else { // else get the already existing elements
        bar.wrapper = $( '.foobar-wrapper' );
        bar.container = $( '.foobar-container' );
        bar.shadow = $( '.foobar-shadow' );
        bar.left = $( '.foobar-container-left' );
        bar.center = $( '.foobar-container-center' );
        bar.message = $( '.foobar-message' );
        bar.right = $( '.foobar-container-right' );
        bar.closeButtonContainer = $( '.foobar-close-button-container' );
        bar.closeButton = $( '.foobar-close-button' ).text( ' ' );
        bar.openButtonContainer = $( '.foobar-open-button-container' );
        bar.openButton = $( '.foobar-open-button' ).text( ' ' );
      }
      bar.initialized = true;
    },

    setCookie: function( name, value, days ) {
      /// <summary>Sets a cookie using the supplied parameters</summary>
      /// <param name="name">The name of the cookie</param>
      /// <param name="value">The value of the cookie</param>
      /// <param name="days">The number of days before the cookie expires</param>

      var expires = '';
      if ( days ) {
        var date = new Date();
        date.setTime( date.getTime() + (days * 24 * 60 * 60 * 1000) );
        expires = '; expires='+date.toGMTString();
      }
      document.cookie = name + '=' + value + expires + '; path=/';
    },

    getCookie: function( name ) {
      /// <summary>Gets a cookie by name and returns it's value</summary>
      /// <param name="name">The name of the cookie</param>

      var nameEq = name + '=';
      var ca = document.cookie.split( ';' );
      for ( var i = 0; i < ca.length; i++ ) {
        var c = ca[i];
        while ( c.charAt( 0 ) == ' ' ) c = c.substring( 1, c.length );
        if ( c.indexOf( nameEq ) === 0 ) return c.substring( nameEq.length, c.length );
      }
      return null;
    },

    deleteCookie: function( name ) {
      /// <summary>Deletes a cookie by name</summary>
      /// <param name="name">The name of the cookie</param>

      bar.setCookie( name, '', -1 );
    },

    isNotNullOrEmpty: function( value ) {
      /// <summary>Checks if the supplied value is a string and is null or empty</summary>
      /// <param name="value">The value to check</param>

      return ( typeof value == 'string' && value !== null && value !== '' );
    },

    getRandomMessageIndex: function() {
      /// <summary>Gets a random message index to be displayed. This will return a random index between 0 and the maximum messages array index.</summary>

      if ( bar.settings.messages.length <= 1 ) { return 0; }

      var from = 0, to = bar.settings.messages.length - 1;
      var random = Math.floor( Math.random() * (to - from + 1) + from );

      // Recursively get an index until it's not the same as the current index.
      if ( random == bar.currentMessageIndex ) { return bar.getRandomMessageIndex(); }
      return random;
    },

    clearMessageTimeout: function() {
      /// <summary>Clears the timeout used to cycle through the messages</summary>

      if ( typeof bar.messageTimeoutId != 'undefined' && bar.messageTimeoutId !== null ) {
        clearTimeout( bar.messageTimeoutId );
      }
      bar.messageTimeoutId = null;
    },

    messageCycleReset : function() {
      /// <summary>Resets the messages cycle back to its starting values</summary>

      if ( bar.messageHover ) { //pause if the user is hovering on the message
        bar.messageTimeoutId = setTimeout( bar.messageCycleReset, bar.settings.messagesDelay );
        return;
      }
      //set the message back to its starting position
      $( '#foobar-message-' + bar.currentMessageIndex ).animate( { 'margin-left': 0 }, 500, function() {
        bar.currentMessageIndex = -1; //reset the current index so it will cycle again
        bar.messageCycle();
      } );
    },

    messageCycleFade : function() {
      /// <summary>Fades out the current message and resets it back to its starting values</summary>

      if ( bar.messageHover ) { //pause if the user is hovering on the message
        bar.messageTimeoutId = setTimeout( bar.messageCycleFade, bar.settings.messagesDelay );
        return;
      }

      //fade the current message out
      var $current = $( '#foobar-message-' + bar.currentMessageIndex );
      $current.animate( { 'opacity': 0 }, bar.settings.messagesFadeDelay / 2, function() {
        $current.css( { 'margin-left': 0 } ).hide(); //once the message has faded out reset it's margin and hide it
        bar.messageCycle(); //and cycle to next message
      } );
    },

    parseHtml: function( html ) {
      /// <summary>Parse HTML to see if elements from the dom must be included</summary>

      if ( ! bar.isNotNullOrEmpty( html ) ) return html;

      //check if the html contains the keyword "{{include:}}"
      if ( html && html.match( '{{include:(.*?)}}' ) ) {
        var regEx = new RegExp( '{{include:(.*?)}}' );
        var match = regEx.exec( html );
        while ( match !== null ) {
          //get the element's html
          var matchedHtml = $( match[1] ).html();
          html = html.replace( match[0], matchedHtml );
          match = regEx.exec( html );
        }
      }

      return html;
    },

    messageCycle: function() {
      /// <summary>This method now controls all aspects of displaying of messages including the side scrolling animate</summary>

      if ( !bar.isOpen || bar.settings.messages.length === 0 || bar.currentMessageIndex == bar.messageIndex ) { return; }
      if ( bar.messageHover ) { //pause on the message
        bar.messageTimeoutId = setTimeout( bar.messageCycle, bar.settings.messagesDelay );
        return;
      }
      bar.clearMessageTimeout();

      if ( $( '.foobar-message-wrapper' ).length === 0 ) {
        //first thing we do is append the messages to the dom to calculate their true widths
        for ( var mi = 0; mi < bar.settings.messages.length; mi++ ) {
          var messageHtml = bar.parseHtml( bar.settings.messages[mi] );
          var $message = $( '<div id="foobar-message-' + mi + '" class="foobar-message-wrapper">' + messageHtml + '</div>' );
          $message.css( { 'position' : 'absolute', 'display' : 'block', 'width' : 'auto' } );
          $message.css( { 'left' : 0, 'top' : mi * 100 + 100 } );
          $message.css( { 'padding' : bar.settings.messagePadding } );

          if ( !bar.isNotNullOrEmpty( bar.settings.messageClass ) ) { //style links if we need to
            var $a = $message.find( 'a' );
            bar.styleLinks( $a );
            bar.styleLinksHover( $a );
          }
          bar.message.append( $message ); //append it to the dom
          bar.settings.messageSizes[mi] = { 'width' : $message.width(), 'height' : $message.height() };
          $message.hover( bar.pauseMessages, bar.resumeMessages );
        }
      }
      bar.message.children().hide();
      var $currentMessage = $( '#foobar-message-' + bar.messageIndex ); //get the current message and show it
      $currentMessage.css( {
        'position': 'static',
        'visibility': 'visible',
        'margin-left': 0,
        'opacity': 100
      } ).show();

      bar.currentMessageIndex = bar.messageIndex; //set the current index

      //get the current center bar size to find out if we must scroll and to calculate the padding for
      //vertical centering (using padding to align as line-height trick was interfering with custom html messages)
      var cWidth = bar.center.width();
      var cHeight = bar.center.height();

      //get current message size that we calculated previously
      var mWidth = bar.settings.messageSizes[bar.messageIndex].width;
      var mHeight = bar.settings.messageSizes[bar.messageIndex].height;

      if ( bar.settings.enableRandomMessage ) {
        bar.messageIndex = bar.getRandomMessageIndex();
      } else if ( bar.messageIndex >= (bar.settings.messages.length - 1) ) {
        bar.messageIndex = 0;
      } else {
        bar.messageIndex++;
      }

      if ( mHeight < cHeight ) {
        var padding = Math.round( (cHeight - mHeight) / 2 );
        $currentMessage.css( { 'height': mHeight } ); //force the height
        bar.message.css( { 'height': cHeight } ); //force the container height
        bar.center.css( { 'padding-top': padding } ); //add some top padding to align the message vertically
      }

      if ( bar.settings.enableMessageScroll && mWidth > cWidth ) { //we need to scroll the message
        var diff = mWidth - cWidth; //how far do we need to scroll it
        var speed = Math.round( diff / bar.settings.messagesScrollSpeed ) * 1000; //how fast will it scroll
        $currentMessage.css( { 'width': mWidth } ); //force the width
        bar.message.css( { 'width': cWidth } ); //force the container width

        $currentMessage.delay( bar.settings.messagesScrollDelay ).animate( { 'margin-left': '-' + diff }, speed, 'linear', function() {
          if ( bar.messageIndex == bar.currentMessageIndex ) { //delay and then reset
            bar.messageTimeoutId = setTimeout( bar.messageCycleReset, bar.settings.messagesDelay );
          } else { //delay and then continue with the message cycling
            bar.messageTimeoutId = setTimeout( bar.messageCycleFade, bar.settings.messagesDelay );
          }
        } );
      } else if ( bar.messageIndex != bar.currentMessageIndex ) { //we need to proceed to the next message
        bar.messageTimeoutId = setTimeout( bar.messageCycleFade, bar.settings.messagesDelay );
      }
    },

    styleLinks: function( links ) {
      /// <summary>Applies the links CSS style settings to the provided jQuery object array</summary>

      if ( bar.isNotNullOrEmpty( bar.settings.aFontFamily ) ) { links.css( 'font-family', bar.settings.aFontFamily ); }
      if ( bar.isNotNullOrEmpty( bar.settings.aFontSize ) ) { links.css( 'font-size', bar.settings.aFontSize ); }
      if ( bar.isNotNullOrEmpty( bar.settings.aFontColor ) ) { links.css( 'color', bar.settings.aFontColor ); }
      if ( bar.isNotNullOrEmpty( bar.settings.aFontDecoration ) ) { links.css( 'text-decoration', bar.settings.aFontDecoration ); }
      if ( bar.isNotNullOrEmpty( bar.settings.aFontShadow ) ) { links.css( 'text-shadow', bar.settings.aFontShadow ); }
    },

    styleLinksHover: function( links ) {
      /// <summary>Binds the hover events of each of the objects in the provided jQuery array</summary>

      links.unbind( 'mouseenter mouseleave' ).bind( {
        mouseenter: function() {
          if ( bar.isNotNullOrEmpty( bar.settings.aHoverFontFamily ) ) { $( this ).css( 'font-family', bar.settings.aHoverFontFamily ); }
          if ( bar.isNotNullOrEmpty( bar.settings.aHoverFontSize ) ) { $( this ).css( 'font-size', bar.settings.aHoverFontSize ); }
          if ( bar.isNotNullOrEmpty( bar.settings.aHoverFontColor ) ) { $( this ).css( 'color', bar.settings.aHoverFontColor ); }
          if ( bar.isNotNullOrEmpty( bar.settings.aHoverFontDecoration ) ) { $( this ).css( 'text-decoration', bar.settings.aHoverFontDecoration ); }
          if ( bar.isNotNullOrEmpty( bar.settings.aHoverFontShadow ) ) { $( this ).css( 'text-shadow', bar.settings.aHoverFontShadow ); }
        },
        mouseleave: function() {
          bar.styleLinks( $( this ) );
        }
      } );
    },

    isGoogleLoaded: function() {
      /// <summary>Checks if the Google API is loaded</summary>

      var googleApi = false;
      $( 'head > script[type="text/javascript"]' ).each( function() {
        var src = $( this ).attr( 'src' );
        var check = 'http://www.google.com/jsapi?key=';
        if ( bar.isNotNullOrEmpty( src ) && src.length >= check.length && src.substr( 0, check.length ) === check ) {
          googleApi = true;
        }
      } );
      return ( googleApi && !(typeof google == 'undefined') );
    },

    isFeedsLoaded: function() {
      /// <summary>Checks if the Google Feeds API is loaded</summary>

      return ( bar.isGoogleLoaded() && !(typeof google.feeds == 'undefined') );
    },

    loadGoogle: function() {
      /// <summary>Loads the Google API</summary>

      var script = document.createElement( "script" );
      script.src = 'http://www.google.com/jsapi?key=' + bar.settings.googleAPIKey + '&callback=jQuery.foobarGoogleCallback';
      script.type = 'text/javascript';
      document.getElementsByTagName( 'head' )[0].appendChild( script );
    },

    loadFeeds: function() {
      /// <summary>Loads the Google Feeds API</summary>

      google.load( 'feeds', '1', { 'callback': bar.loadFeedsAndTweets } );
    },

    loadFeedsAndTweets: function() {
      /// <summary>loads the feeds or tweets, or both</summary>

      // Handle the tweets for the bar
      if ( bar.settings.twitter.enabled && bar.isNotNullOrEmpty( bar.settings.twitter.user ) ) {
        bar.loadTwitter();
      }
      // Handle the rss for the bar
      if ( bar.settings.rss.enabled && bar.isNotNullOrEmpty( bar.settings.rss.url ) ) {
        bar.loadRss();
      }
    },

    loadRss: function() {
      /// <summary>loads the rss feeds content and appends it to the messages array</summary>

      var feed = new google.feeds.Feed( bar.settings.rss.url );
      feed.setNumEntries( bar.settings.rss.maxResults );
      feed.load( function( result ) {
        bar.clearMessageTimeout();

        if ( !result.error ) {
          for ( var i = 0; i < result.feed.entries.length; i++ ) {
            var msg = result.feed.entries[i].title;
            if ( bar.isNotNullOrEmpty( bar.settings.rss.linkText ) ) {
              msg += ' <a href="' + result.feed.entries[i].link + '" target="' + bar.settings.rss.linkTarget + '">' + bar.settings.rss.linkText + '</a>';
            }
            bar.settings.messages.push( msg );
          }
        } else {
          console.error( result.error.message );
        }
        bar.message.stop().css( { 'left': 0, 'opacity': 100 } );
        bar.messageCycle();
      } );
    },

    loadTwitter: function() {
      /// <summary>loads the tweets content and appends it to the messages array</summary>

      var twitterRssUrl = 'http://api.twitter.com/1/statuses/user_timeline.rss?trim_user=0&screen_name=' + bar.settings.twitter.user;
      var feed = new google.feeds.Feed( twitterRssUrl );
      feed.setNumEntries( bar.settings.twitter.maxTweets );
      feed.load( function( result ) {
        bar.clearMessageTimeout();

        if ( !result.error ) {
          for ( var i = 0; i < result.feed.entries.length; i++ ) {
            var tweet = bar.formatTweetHtml( result.feed.entries[i].title );
            bar.settings.messages.push( tweet );
          }
        } else {
          console.error( result.error.message );
        }
        bar.message.stop().css( { 'left': 0, 'opacity': 100 } );
        bar.messageCycle();
      } );
    },

    formatTweetHtml: function( tweet ) {
      /// <summary>Formats the tweet body text. See http://www.dustindiaz.com/basement/ify.html for more info.</summary>

      //if we are dealing with content from a twitter RSS feed then strip out the screen name
      var tweetPrefix = bar.settings.twitter.user + ': ';
      if ( tweet.indexOf( tweetPrefix ) === 0 ) {
        tweet = tweet.substring( tweetPrefix.length );
      }
      tweet = ' ' + tweet;

      // The tweets arrive as plain text, so we replace all the textual URLs with hyperlinks
      tweet = tweet.replace( /\b(((https*\:\/\/)|www\.)[^\"\']+?)(([!?,.\)]+)?(\s|$))/g, function( link, m1, m2, m3, m4 ) {
        var http = m2.match( /w/ ) ? 'http://' : '';
        return '<a class="twtr-hyperlink" target="_blank" href="' + http + m1 + '">' + m1 + '</a>' + m4;
      } );

      // Replace the mentions
      tweet = tweet.replace( /\B[@＠]([a-zA-Z0-9_]{1,20})/g, function( m, username ) {
        return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">@' + username + '</a>';
      } );

      // Replace the lists
      tweet = tweet.replace( /\B[@＠]([a-zA-Z0-9_]{1,20}\/\w+)/g, function( m, userlist ) {
        return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/' + userlist + '">@' + userlist + '</a>';
      } );

      // Replace the hashtags
      tweet = tweet.replace( /(^|\s+)#(\w+)/gi, function( m, before, hash ) {
        return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
      } );

      return tweet;
    },
    
    toggle: function( e ) {
      /// <summary>Toggles the foobar (either expands or collapses) using the animation settings</summary>
      if ( bar.isOpen ) {
        bar.collapse();
      } else {
        bar.expand();
      }
    },

    expand: function( e ) {
      /// <summary>Expands the foobar using the animation settings</summary>
      /// <param name="e">If used as an event handler the first parameter passed through is the event arguments</param>

      if ( typeof e != 'undefined' && e !== null && typeof e.preventDefault == 'function' ) {
        e.preventDefault();
      }
      if ( !bar.isOpen ) {
        bar.openButton.animate( { 'height': 0 }, bar.settings.speed );
        bar.openButtonContainer.animate( { 'height': 0 }, bar.settings.speed, function() {
          bar.container.animate( { 'height': bar.settings.height }, bar.settings.speed, bar.settings.easing ).css( {'border-bottom': bar.settings.border } );
          bar.wrapper.animate( { 'height': bar.settings.height + bar.shadowHeight }, bar.settings.speed, bar.settings.easing );
          bar.message.add( bar.left ).add( bar.right ).show();
          if ( bar.settings.adjustPageHeight ) {
            if ( bar.settings.positioning == 'fixed' ) {
              $( 'html' ).animate( { 'margin-top': '+=' + (bar.settings.height + bar.shadowHeight) } );
            } else {
              $( 'html' ).css( { 'margin-top': bar.htmlMarginTop + 'px' } );
            }
          }
          bar.wrapper.focus();
          bar.isOpen = true;
          bar.messageCycle();
        } );
        if ( bar.settings.enableCookie ) {
          bar.setCookie( 'foobar-state', true, 1 );
        }
      }
    },

    collapse: function( e ) {
      /// <summary>Collapses the foobar using the animation settings</summary>
      /// <param name="e">If used as an event handler the first parameter passed through is the event arguments</param>

      if ( typeof e != 'undefined' && e !== null && typeof e.preventDefault == 'function' ) {
        e.preventDefault();
      }
      if ( bar.isOpen ) {
        bar.container.animate( { 'height': 0 }, bar.settings.speed ).css( {'border': 'none' } );
        if ( bar.settings.adjustPageHeight ) {
          if ( bar.settings.positioning == 'fixed' ) {
            $( 'html' ).animate( { 'margin-top': '-=' + (bar.settings.height + bar.shadowHeight) } );
          } else {
            $( 'html' ).css( { 'margin-top': bar.htmlMarginTop + 'px' } );
          }
        }
        bar.wrapper.animate( { 'height': 5 }, bar.settings.speed, function() {
          bar.message.add( bar.left ).add( bar.right ).hide();
          bar.openButtonContainer.animate( { 'height': bar.settings.collapsedButtonHeight + 11 }, bar.settings.speed, bar.settings.easing );
          bar.openButton.animate( { 'height': bar.settings.collapsedButtonHeight }, bar.settings.speed, bar.settings.easing );
        } );
        bar.isOpen = false;
        if ( bar.settings.enableCookie ) {
          bar.setCookie( 'foobar-state', false, 1 );
        }
      }
    },

    setExpanded: function() {
      /// <summary>Sets the bar to the expanded state, no animations occur when using this function</summary>

      bar.container.height( bar.settings.height ).css( {'border-bottom': bar.settings.border } );
      bar.wrapper.height( bar.settings.height + bar.shadowHeight );
      bar.openButtonContainer.add( bar.openButton ).height( 0 );
      if ( bar.settings.adjustPageHeight ) {
        if ( bar.settings.positioning == 'fixed' ) {
          $( 'html' ).css( { 'margin-top': (bar.htmlMarginTop <= 0) ? (bar.settings.height + bar.shadowHeight) + 'px' : (bar.htmlMarginTop + (bar.settings.height + bar.shadowHeight)) + 'px' } );
        } else {
          $( 'html' ).css( { 'margin-top': bar.htmlMarginTop + 'px' } );
        }
      }
      bar.isOpen = true;
      bar.message.add( bar.left ).add( bar.right ).show();
      if ( bar.settings.enableCookie ) {
        bar.setCookie( 'foobar-state', true, 1 );
      }
    },

    setCollapsed: function() {
      /// <summary>Sets the bar to the collapsed state, no animations occur when using this function</summary>

      bar.container.height( 0 ).css( {'border-bottom': 0 } );
      bar.wrapper.height( bar.shadowHeight );
      bar.openButtonContainer.height( bar.settings.collapsedButtonHeight + 11 );
      bar.openButton.height( bar.settings.collapsedButtonHeight );
      if ( bar.settings.adjustPageHeight ) {
        if ( bar.settings.positioning == 'fixed' ) {
          $( 'html' ).css( { 'margin-top': (bar.htmlMarginTop <= 0) ? 0 + 'px' : (bar.htmlMarginTop - (bar.settings.height + bar.shadowHeight)) + 'px' } );
        } else {
          $( 'html' ).css( { 'margin-top': bar.htmlMarginTop + 'px' } );
        }
      }
      bar.isOpen = false;
      bar.message.add( bar.left ).add( bar.right ).hide();
      if ( bar.settings.enableCookie ) {
        bar.setCookie( 'foobar-state', false, 1 );
      }
    },

    pauseMessages : function() {
      /// <summary>Sets the bar.messageHover property to true indication that the user is hovering over a message</summary>

      bar.messageHover = true;
    },

    resumeMessages: function() {
      /// <summary>Sets the bar.messageHover property to false indication that the user has stopped hovering over a message</summary>

      bar.messageHover = false;
    },

    apply: function( settings ) {
      /// <summary>The heart of the plugin, this function takes all the settings into account and applies them to the foobar</summary>
      /// <param name="settings">The combined user & default settings</param>

      bar.settings = settings; // Store it, love it, use it

      if ( !bar.initialized ) {
        // Calc the current HTML elements margin top to determine where to render the bar. (This looks for anything like the WP admin bar which alters the margin top CSS value)
        var $html = $( 'html' );
        bar.htmlMarginTop = parseInt( $html.css( 'margin-top' ) );
        bar.htmlMarginTop = bar.settings.ignoreHtmlMarginTop || isNaN( bar.htmlMarginTop ) ? 0 : bar.htmlMarginTop;

        bar.initialize();
      } else { //clear the sides
        bar.left.add( bar.right ).empty();
      }

			//#### TO BE REMOVED IN 2.0 ####
			if (settings.googleAPIKey === '' && settings.rss.googleAPIKey !== '') {
				settings.googleAPIKey = settings.rss.googleAPIKey;
      }
      //#### TO BE REMOVED IN 2.0 ####

      // check that the messagesScrollDirection is either left or right and if not set to default
      if ( settings.messagesScrollDirection != 'left' && settings.messagesScrollDirection != 'right' ) {
        settings.messagesScrollDirection = defaults.messagesScrollDirection;
      }

      // set the height for all the bar elements
      bar.wrapper.height( bar.settings.height + bar.shadowHeight );
      bar.openButtonContainer.height( bar.settings.collapsedButtonHeight + 11 ); // 11 to allow for shadow around open tab
      bar.openButton.height( bar.settings.collapsedButtonHeight );
      bar.container.add( bar.right ).add( bar.left ).add( bar.center ).add( bar.message )
        .add( bar.closeButtonContainer ).add( bar.closeButton )
        .height( bar.settings.height );

      // set the background color of the bar
      bar.container.add( bar.openButton ).css( { 'background-color': bar.settings.backgroundColor } );

      // set the border for the bar
      bar.container.css( { 'border-bottom': bar.settings.border } );
      bar.openButton.css( { 'border': bar.settings.border, 'border-top': 'none' } );

      // set the position of the div that contains the open button
      bar.openButtonContainer.css( { 'top': bar.htmlMarginTop + 'px' } );

      // apply the css class for the button theme
      bar.openButton.add( bar.closeButton ).removeClass();
      bar.openButton.addClass( 'foobar-open-button' );
      bar.closeButton.addClass( 'foobar-close-button' );
      bar.openButton.add( bar.closeButton ).addClass( settings.buttonTheme );

      // display the shadow or not
      if ( bar.settings.enableShadow ) {
        bar.shadowHeight = 5;
        bar.shadow.show();
        bar.openButtonContainer.addClass( 'shadow' );
      } else {
        bar.shadowHeight = 0;
        bar.shadow.hide();
        bar.openButtonContainer.removeClass( 'shadow' );
      }
      // apply the message styles either class or inline styles
      if ( bar.isNotNullOrEmpty( bar.settings.messageClass ) ) {
        bar.message.addClass( bar.settings.messageClass );
      } else {
        if ( bar.isNotNullOrEmpty( bar.settings.fontFamily ) ) { bar.message.css( 'font-family', bar.settings.fontFamily ); }
        if ( bar.isNotNullOrEmpty( bar.settings.fontSize ) ) { bar.message.css( 'font-size', bar.settings.fontSize ); }
        if ( bar.isNotNullOrEmpty( bar.settings.fontColor ) ) { bar.message.css( 'color', bar.settings.fontColor ); }
        if ( bar.isNotNullOrEmpty( bar.settings.fontShadow ) ) { bar.message.css( 'text-shadow', bar.settings.fontShadow ); }
      }

      // Create and position the social bar
      if ( bar.settings.positionSocial == 'left' || bar.settings.positionSocial == 'right' ) {
        if ( bar.settings.social.profiles.length > 0 ) {
          var $social = $( '.foobar-social' ).length > 0 ? $( '.foobar-social' ) : $( '<ul></ul>' ).addClass( 'foobar-social' );
          $social.empty();

          var $text = $( '<li></li>' ).text( bar.settings.social.text ).css( { 'height': bar.settings.height, 'line-height': bar.settings.height + 'px' } );
          if ( bar.isNotNullOrEmpty( bar.settings.socialClass ) ) {
            $text.addClass( bar.settings.socialClass );
          } else {
            $text.css( { 'padding-right': '10px', 'padding-left': '10px' } );
            if ( bar.isNotNullOrEmpty( bar.settings.social.fontFamily ) ) { $text.css( 'font-family', bar.settings.social.fontFamily ); }
            if ( bar.isNotNullOrEmpty( bar.settings.social.fontSize ) ) { $text.css( 'font-size', bar.settings.social.fontSize ); }
            if ( bar.isNotNullOrEmpty( bar.settings.social.fontColor ) ) { $text.css( 'color', bar.settings.social.fontColor ); }
            if ( bar.isNotNullOrEmpty( bar.settings.social.fontShadow ) ) { $text.css( 'text-shadow', bar.settings.social.fontShadow ); }
          }
          $social.append( $text );

          var pdef = { 'name': null, 'url': null, 'image': null, 'target': '_blank' }; // default template for social profiles
          $.each( bar.settings.social.profiles, function( i, p ) {
            var profile = $.extend( {}, pdef, p );
            if ( profile.name !== null && profile.url !== null && profile.image !== null ) {
              var $a = $( '<a></a>' ).attr( 'href', profile.url ).attr( 'title', profile.name ).attr( 'target', profile.target )
                .css( { 'background': "url('" + profile.image + "') no-repeat center center", 'height': bar.settings.height } );

              $social.append( $( '<li></li>' ).css( { 'height': bar.settings.height } ).append( $a ) );
            }
          } );

          if ( bar.settings.positionSocial == 'right' ) {
            $social.css( 'float', 'right' );
            bar.right.append( $social );
          } else if ( bar.settings.positionSocial == 'left' ) {
            $social.css( 'float', 'left' );
            bar.left.append( $social );
          }
        } else { $( '.foobar-social' ).remove(); }
      } else { $( '.foobar-social' ).remove(); }

      //add custom HTML to the right bar
      if ( bar.isNotNullOrEmpty( bar.settings.rightHtml ) ) {
        bar.right.append( bar.parseHtml( bar.settings.rightHtml ) );
      }

      //add custom HTML to the left bar
      if ( bar.isNotNullOrEmpty( bar.settings.leftHtml ) ) {
        bar.left.append( bar.parseHtml( bar.settings.leftHtml ) );
      }

      // Set the positioning of the bar
      if ( bar.settings.positioning == 'fixed' ) {
        bar.wrapper.css( { 'position': 'fixed', 'top': bar.htmlMarginTop + 'px', 'left': '0px' } );
        bar.openButtonContainer.css( { 'position': 'fixed' } );
      } else {
        bar.wrapper.css( { 'position': 'relative', 'top': 0, 'left': 0 } );
        bar.openButtonContainer.css( { 'position': 'absolute' } );
      }

      // Set the position of the close button
      if ( bar.settings.positionClose == 'right' ) {
        if ( !bar.isNotNullOrEmpty( bar.settings.leftWidth ) ) { bar.left.css( 'width', '25%' ); }
        if ( !bar.isNotNullOrEmpty( bar.settings.rightWidth ) ) { bar.right.css( 'width', '20%' ); }
        bar.closeButtonContainer.css( 'float', 'right' );
        bar.openButtonContainer.css( {'left': 'auto', 'right': '0px'} );
      } else if ( bar.settings.positionClose == 'left' ) {
        if ( !bar.isNotNullOrEmpty( bar.settings.leftWidth ) ) { bar.left.css( 'width', '20%' ); }
        if ( !bar.isNotNullOrEmpty( bar.settings.rightWidth ) ) { bar.right.css( 'width', '25%' ); }
        bar.closeButtonContainer.css( 'float', 'left' );
        bar.openButtonContainer.css( {'right': 'auto', 'left': '0px'} );
      } else {
        bar.closeButtonContainer.hide();
        bar.openButtonContainer.hide();
        if ( !bar.isNotNullOrEmpty( bar.settings.leftWidth ) ) { bar.left.css( 'width', '20%' ); }
        if ( !bar.isNotNullOrEmpty( bar.settings.rightWidth ) ) { bar.right.css( 'width', '25%' ); }
      }

      if ( bar.isNotNullOrEmpty( bar.settings.leftWidth ) ) { bar.left.css( 'width', bar.settings.leftWidth ); }
      if ( bar.isNotNullOrEmpty( bar.settings.rightWidth ) ) { bar.right.css( 'width', bar.settings.rightWidth ); }
      if ( bar.isNotNullOrEmpty( bar.settings.centerWidth ) ) { bar.center.css( 'width', bar.settings.centerWidth ); }

      var cookie = bar.getCookie( 'foobar-state' );
      if ( cookie === null || !bar.settings.enableCookie ) {
        if ( !bar.settings.enableCookie ) { bar.deleteCookie( 'foobar-state' ); }
        switch ( bar.settings.display ) {
          case 'onscroll':
            bar.setCollapsed();
            $( window ).one( 'scroll', function() {
              setTimeout( bar.expand, bar.settings.displayDelay );
            } );
            break;
          case 'delayed':
            bar.setCollapsed();
            setTimeout( bar.expand, bar.settings.displayDelay );
            break;
          case 'collapsed':
            bar.setCollapsed();
            break;
          case 'expanded':
          default:
            bar.setExpanded();
            break;
        }
      } else {
        cookie == "true" ? bar.setExpanded() : bar.setCollapsed();
      }

      bar.openButton.unbind().click( bar.expand );
      bar.closeButton.unbind().click( bar.collapse );

      //finally, setup the messages

      //clear all previously created messages
      $( '.foobar-message-wrapper' ).remove();
      bar.messageHover = false;
      bar.messageIndex = 0;
      bar.currentMessageIndex = -1;

      var handleMessages = true, handleFeeds = false;

      // Handle the tweets for the bar
      if ( bar.settings.twitter.enabled && bar.isNotNullOrEmpty( bar.settings.twitter.user ) ) {
        handleMessages = false;
        if ( bar.isGoogleLoaded() && bar.isFeedsLoaded() ) {
          bar.loadTwitter();
        } else {
          handleFeeds = true;
        }
      }

      // Handle the rss for the bar
      if ( bar.settings.rss.enabled && bar.isNotNullOrEmpty( bar.settings.googleAPIKey ) && bar.isNotNullOrEmpty( bar.settings.rss.url ) ) {
        handleMessages = false;
        if ( bar.isGoogleLoaded() && bar.isFeedsLoaded() ) {
          bar.loadRss();
        } else {
          handleFeeds = true;
        }
      }

      if ( handleFeeds ) {
        if ( bar.isFeedsLoaded() ) {
          bar.loadFeeds();
        } else {
          bar.loadGoogle();
        }
      }

      if ( handleMessages ) {
        bar.message.stop( true, false ).css( { 'opacity': 100 } );
        bar.messageCycle();
      }
    }
  };

})( jQuery );
