/**
 * @file ajaxView.js
 *
 * Handles AJAX fetching of views, including filter submission and response.
 */
(function ($) {

/**
 * Attaches the AJAX behavior to Views exposed filter forms and key View links.
 */
Drupal.behaviors.ViewsAjaxView = {};
Drupal.behaviors.ViewsAjaxView.attach = function() {
  if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) {
    // Retrieve the path to use for views' ajax.
    var ajax_path = Drupal.settings.views.ajax_path;

    // If there are multiple views this might've ended up showing up multiple times.
    if (ajax_path.constructor.toString().indexOf("Array") != -1) {
      ajax_path = ajax_path[0];
    }

    $.each(Drupal.settings.views.ajaxViews, function(i, settings) {
      var view = '.view-dom-id-' + settings.view_dom_id;
      var element_settings = {
        url: ajax_path,
        submit: settings,
        setClick: true,
        event: 'click',
        selector: view,
        progress: { type: 'throbber' }
      };

      // Process exposed filter forms.
      $('form#views-exposed-form-' + settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-'))
      .filter(':not(.views-processed)')
      .each(function () {
        var button = $('input[type=submit], input[type=image]', this);
        button = button[0];

        var ajax = new Drupal.ajax($(button).attr('id'), button, element_settings);
      })
      .addClass('views-processed')

      $(view).filter(':not(.views-processed)')
        // Don't attach to nested views. Doing so would attach multiple behaviors
        // to a given element.
        .filter(function() {
          // If there is at least one parent with a view class, this view
          // is nested (e.g., an attachment). Bail.
          return !$(this).parents('.view').size();
        })
        .each(function() {
          // Set a reference that will work in subsequent calls.
          var target = this;
          $(this)
            .addClass('views-processed')
            // Process pager, tablesort, and attachment summary links.
            .find('ul.pager > li > a, th.views-field a, .attachment .views-summary a')
            .each(function () {
              var viewData = {};
              // Construct an object using the settings defaults and then overriding
              // with data specific to the link.
              $.extend(
                viewData,
                settings,
                Drupal.Views.parseQueryString($(this).attr('href')),
                // Extract argument data from the URL.
                Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path)
              );

              // For anchor tags, these will go to the target of the anchor rather
              // than the usual location.
              $.extend(viewData, Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path));

              element_settings.submit = viewData;
              var ajax = new Drupal.ajax(false, this, element_settings);
            }); // .each function () {
      }); // $view.filter().each
    }); // .each Drupal.settings.views.ajaxViews
  } // if
};
})(jQuery);
;

/**
 * Event handler that listens for flags selected by the user.
 */
jQuery(document).bind('flagGlobalAfterLinkUpdate', function(event, data) {
  var refresh = new viewsFlagRefresh(data.flagName);
  jQuery.each(Drupal.settings.views.ajaxViews, function(index, settings) {
    refresh.ajax(settings);
  });
});

/**
 * Constructor for the viewsFlagRefresh object, sets the name of the flag that
 * was selected.
 * 
 * @param flagName
 *   The name of the flag selected by the user.
 */
function viewsFlagRefresh(flagName) {
  this.flagName = flagName;
}

/**
 * Class method that returns the Views module's AJAX path.
 * 
 * @reutrn
 *   The Views module's AJAX path.
 */
viewsFlagRefresh.ajaxPath = function() {
  var ajaxPath = Drupal.settings.views.ajax_path;
  if (ajaxPath.constructor.toString().indexOf("Array") != -1) {
    ajaxPath = ajaxPath[0];
  }
  return ajaxPath;
}

/**
 * Returns the widget settings associated with the view. Returns false if the
 * view isn't set to refresh on this flag.
 * 
 * @param viewName
 *   The name of the view we are running the check against.
 * @param viewDisplayId
 *   The display ID of the view we are running the check against.
 * @return
 *   A string containing the theme hook used to theme the view as it is being
 *   refreshed, a boolean false if the view should not be refreshed at all.
 */
viewsFlagRefresh.prototype.widgetSettings = function(viewName, viewDisplayId) {
  var settings = Drupal.settings.viewsFlagRefresh.flags;
  for (var flagName in settings) {
    functionName = viewName + '-' + viewDisplayId;
    if (flagName == this.flagName && functionName in settings[flagName]) {
      return settings[flagName][functionName];
    }
  }
  return false;
}

/**
 * Returns a key / value pair to be uses as settings by the ajax methods.
 * 
 * @param view
 *   a jQuery object containing the view.
 * @param settings
 *   The View's AJAX settings.
 * @param theme
 *   The theme object.
 * @return
 *   The AJAX settings.
 */
viewsFlagRefresh.ajaxSettings = function(view, settings, theme) {
  return {
    url: viewsFlagRefresh.ajaxPath(),
    type: 'GET',
    data: settings,
    success: function(response, status) {
      // Handle ajax response.
      jQuery(view).data('viewsFlagRefresh_ajax').success(response, status);

      // Invokes theme hook.
      jQuery(view).filter(function() { return !jQuery(this).parents('.view').size(); }).each(function() {
        theme.target = this;
        theme.hookInvoke('themeHookPost');
      });
    },
    error: function() {
      // Invokes theme hook, handles errors gracefully.
      theme.hookInvoke('themeHookPost');
      Drupal.Views.Ajax.handleErrors(xhr, viewsFlagRefresh.ajaxPath());
    },
    dataType: 'json'
  };
}

/**
 * Refreshes a view via AJAX if it is configured to do so.
 * 
 * @param settings
 *   The view's AJAX settings passed through Drupal.settings.views.ajaxViews.
 */
viewsFlagRefresh.prototype.ajax = function(settings) {
  // Bails if the view shouldn't be refreshed when this flag is selected.
  var widgetSettings = this.widgetSettings(settings.view_name, settings.view_display_id);
  if (!widgetSettings) {
    return;
  }

  // Calculates the selector for the view.
  var view = '.view-dom-id-' + settings.view_dom_id;
  if (!jQuery(view).size()) {
    view = '.view-id-' + settings.view_name + '.view-display-id-' + settings.view_display_id;
  }

  // Locates the view, AJAX refreshes the content.
  jQuery(view).filter(function() { return !jQuery(this).parents('.view').size(); }).each(function() {
    var target = this;

    // create ajax object to handle the response.
    if (jQuery(target).data('viewsFlagRefresh_ajax') == null) {
      // TODO: Find a way to handle ajax commands without creating an ajax object.
      var element_settings = {
        url: viewsFlagRefresh.ajaxPath(),
        submit: {},
        setClick: true,
        event: 'click',
        selector: view,
        progress: { type: 'throbber' }
      };

      jQuery(target).data('viewsFlagRefresh_ajax', new Drupal.ajax(false, view, element_settings));
    }
    
    // Instantiates the theme object, invokes the widget's theme hook.
    var theme = new viewsFlagRefresh.theme(target, widgetSettings);
    theme.hookInvoke('themeHook');
    
    // Gets AJAX settings, either refreshes the view or submits the exposed
    // filter form. This latter refreshes the view and maintains the filters.
    var ajaxSettings = viewsFlagRefresh.ajaxSettings(view, settings, theme);
    var exposedForm = jQuery('form#views-exposed-form-' + settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-'));
    if (exposedForm.size()) {
      setTimeout(function() { jQuery(exposedForm).ajaxSubmit(ajaxSettings); }, theme.timeout);
    }
    else {
      setTimeout(function() { jQuery.ajax(ajaxSettings); }, theme.timeout);
    }
  });
}

/**
 * Contructor for our pseudo theme system class.
 * 
 * @param target
 *   A jQuery object containing the content being refreshed.
 * @param settings
 *   The widget settings.
 */
viewsFlagRefresh.theme = function(target, settings) {
  this.timeout  = 0;
  this.target   = target;
  this.settings = settings;
}

/**
 * Invokes a hook in the theme object.
 * 
 * @param hookType
 *   The hook type being invoked, i.e. 'themeHook', 'themeHookPost'.
 * @return
 *   A boolean flagging whether the hook exists.
 */
viewsFlagRefresh.theme.prototype.hookInvoke = function(hookType) {
  if (hookType in this.settings && this.settings[hookType] in this) {
    this[this.settings[hookType]]();
    return true;
  }
  return false;
}

/**
 * Adds a throbber image to the view content while it is being refreshed.
 */
viewsFlagRefresh.theme.prototype.throbber = function() {
  // Hide the content of the view.
  jQuery(this.target).css('visibility', 'hidden');
  
  // Captures parent, as the view is usually in something such as a block.
  var container = jQuery(this.target).parent();
  
  // Adds our throbber to the middle of the view.
  // NOTE: The throbber image is 32px wide.
  var pos = jQuery(container).position();
  this.throbberElement = jQuery('<img src="' + Drupal.settings.viewsFlagRefresh.imagePath + '/throbber.gif" class="views_flag_refresh-throbber" />')
    .css('left', pos.left + (jQuery(container).outerWidth() / 2) - 16)
    .css('top', pos.top + (jQuery(container).outerHeight() / 2) - 16)
    .insertAfter(this.target);
}

/**
 * Cleans up the throbber image.
 * 
 * @param target
 *   A jQuery object containing the view being refreshed.
 */
viewsFlagRefresh.theme.prototype.throbberPost = function() {
  jQuery(this.throbberElement).remove();
};
(function ($) {

$(document).ready(function() {

  // Accepts a string; returns the string with regex metacharacters escaped. The returned string
  // can safely be used at any point within a regex to match the provided literal string. Escaped
  // characters are [ ] { } ( ) * + ? - . , \ ^ $ # and whitespace. The character | is excluded
  // in this function as it's used to separate the domains names.
  RegExp.escapeDomains = function(text) {
    return (text) ? text.replace(/[-[\]{}()*+?.,\\^$#\s]/g, "\\$&") : '';
  }

  // Attach onclick event to document only and catch clicks on all elements.
  $(document.body).click(function(event) {
    // Catch the closest surrounding link of a clicked element.
    $(event.target).closest("a,area").each(function() {

      var ga = Drupal.settings.googleanalytics;
      // Expression to check for absolute internal links.
      var isInternal = new RegExp("^(https?):\/\/" + window.location.host, "i");
      // Expression to check for special links like gotwo.module /go/* links.
      var isInternalSpecial = new RegExp("(\/go\/.*)$", "i");
      // Expression to check for download links.
      var isDownload = new RegExp("\\.(" + ga.trackDownloadExtensions + ")$", "i");
      // Expression to check for the sites cross domains.
      var isCrossDomain = new RegExp("^(https?|ftp|news|nntp|telnet|irc|ssh|sftp|webcal):\/\/.*(" + RegExp.escapeDomains(ga.trackCrossDomains) + ")", "i");

      // Is the clicked URL internal?
      if (isInternal.test(this.href)) {
        // Is download tracking activated and the file extension configured for download tracking?
        if (ga.trackDownload && isDownload.test(this.href)) {
          // Download link clicked.
          var extension = isDownload.exec(this.href);
          _gaq.push(["_trackEvent", "Downloads", extension[1].toUpperCase(), this.href.replace(isInternal, '')]);
        }
        else if (isInternalSpecial.test(this.href)) {
          // Keep the internal URL for Google Analytics website overlay intact.
          _gaq.push(["_trackPageview", this.href.replace(isInternal, '')]);
        }
      }
      else {
        if (ga.trackMailto && $(this).is("a[href^=mailto:],area[href^=mailto:]")) {
          // Mailto link clicked.
          _gaq.push(["_trackEvent", "Mails", "Click", this.href.substring(7)]);
        }
        else if (ga.trackOutbound && this.href) {
          if (ga.trackDomainMode == 2 && isCrossDomain.test(this.href)) {
            // Top-level cross domain clicked. document.location is handled by _link internally.
            _gaq.push(["_link", this.href]);
          }
          else if (ga.trackOutboundAsPageview) {
            // Track all external links as page views after URL cleanup.
            // Currently required, if click should be tracked as goal.
            _gaq.push(["_trackPageview", '/outbound/' + this.href.replace(/^(https?|ftp|news|nntp|telnet|irc|ssh|sftp|webcal):\/\//i, '').split('/').join('--')]);
          }
          else {
            // External link clicked.
            _gaq.push(["_trackEvent", "Outbound links", "Click", this.href]);
          }
        }
      }
    });
  });
});

})(jQuery);
;

