/*
 * Action tracker for jQuery, can track any data on any event.
 * The submitted data has the following format:
 * [
 *   {time: <timestamp1>, data: {key1: <value1>, key2: <value2>, ...}},
 *   {time: <timestamp2>, data: {...}},
 *   ...
 * ]
 *
 * Examples:
 *
 * Most basic invocation:
 * $("a.tracked").track();
 *
 * Track values of "href", "data-foo" and "data-bar" attributes:
 * $("a.tracked").track({
 *   source: "href data-foo data-bar"
 * });
 *
 * Use a custom callback to prepare the data:
 * $("a.tracked").track({
 *   source: function() {
 *     var $this = $(this);
 *     return {
 *       count: $this.data("count") + 1,
 *       text: $this.text()
 *     };
 *   }
 * });
 */
(function($) {
    
  var defaultOptions = {
    events: "click",      // Which events to track
    source: "id",         // Which attributes to get the data from, can be a function
    live: false,          // Whether to submit data directly (NOT RECOMMENDED)
    foreignClick: true,   // Whether to submit data upon click on not tracked links
    viewPortLeave: true,  // Whether to submit data upon mouse leaving the viewport 
    target: "tracker.php" // Where to submit the data to
  };
  var trackingData = [];

  $.fn.extend({
    track: function(options) {
    
      var options = $.extend(true, {}, defaultOptions, options);
  
      function submitData() {
      
        if (trackingData.length > 0) {
        
          $.post(options.target, JSON.stringify(trackingData));
          trackingData = [];
        }
      }
      
      if (!options.live) {
      
        // Trigger data submission upon page refresh/close
        // On refresh: Firefox (3.6, 4.0), IE (6, 7, 8, 9), Chrome (5, 6, 7), Safari (4, 5)
        // On close: Firefox (3.6, 4.0), IE (6, 7, 8, 9), Chrome (5, 6, 7), Safari (4, 5)
        window.onbeforeunload = submitData;
      
        if (window.opera) {
        
          window.opera.setOverrideHistoryNavigationMode("compatible");
          window.history.navigationMode = "compatible";

          // At least submit tracking data after leaving the page via link
          window.onunload = function() {
          
            if (trackingData.length > 0) {
            
              $.ajax({
                async: false, // Asynchronous requests won't get through
                type: "POST",
                url: options.target,
                data: JSON.stringify(trackingData)
              });
            }
          };
        }
      }
      
      this.bind(options.events, function() {
      
        var $this = $(this);
        var data = {};
      
        if (typeof(options.source) == "function") {
        
          data = options.source.call(this);
        } else {
        
          var sources = options.source.split(" ");
        
          for (var i = 0; i < sources.length; ++i) {
          
            var attribute = sources[i];
            var key = attribute;
          
            if (attribute.indexOf("data-") == 0) {
            
              key = attribute.substring("data-".length);
            }
            
            data[key] = $this.attr(attribute);
          }
        }
      
        var item = {
          time: (new Date()).getTime() / 1000, // ms => s
          data: data
        };
        
        if (options.live) {
        
          submitData();
        } else {
        
          trackingData.push(item);
        }
      });
      
      if (options.foreignClick) {
      
        // Trigger data submission upon click on non-tracked links
        $("a[href!=#]").not(this).click(submitData);
      }
      
      if (options.viewPortLeave) {
      
        // Trigger data submission upon mouse leaving the viewport
        $(document.body).mouseleave(submitData);
      }
    }
  });
})(jQuery);
