/** 
 * jQuery netinsight Plugin.
 * @author mderting
 * @version 1.0.0 $Revision: 551 $ ($Author: $)
 */
(function( $ ) {

  /**
   * Default Settings.
   * @see http://docs.jquery.com/Plugins/Authoring#Defaults_and_Options
   */
  var settings = {
    // A list of rel attribute values to attach link event tracking to.
    allowedRels: ['bookmark', 'contact', 'enclosure', 'external', 'featured', 'search', 'share'],
    // A list of key/value pairs to map rel attribute values to their report-friendly name.
    allowedRelsAliases: { 'enclosure' : 'download' },
    // A list of landmark role attribute values to use to report the link location to.
    allowedLandmarks : ['application', 'banner', 'complementary', 'contentinfo', 'form', 'main', 'navigation', 'search'],
     // A list of form class attribute values to attach form event tracking to.
    allowedFormClassNames: ['search-form', 'netinsight', 'poll', 'validity'],
    // A list of form class attribute values to attach flash event tracking to.
    allowedShareClassNames: ['share'],
    mapClassNamesToRels: false,
    debug: false
  };

  /**
   * jQuery netinsight Plugin Methods.
   * @see http://docs.jquery.com/Plugins/Authoring#Plugin_Methods
   */
  var methods = {
    init: function( options ) {
      
      if ($.fn.netinsight.jQueryVersionRequired > jQuery.fn.jquery) {
        $.error( 'jQuery netinsight Plugin requires jQuery version ' +  $.fn.netinsight.jQueryVersionRequired + ' or above' );
        return false;
      }

      this.each(function() {
        var data, dataId, opts,
            $this = $(this);

        $this.netinsight('options', options);
        data = $this.data('netinsight') || {};
        opts = data.options;

        // If the plugin hasn't been initialized yet
        if ( !data.netinsight ) {
          dataId = +new Date;

          data = {
            netinsight: netinsight,
            options: opts,
            id: dataId
          };

          $this.data('netinsight', data);
          
          // create element
          var netinsight = $('<div />', {
            'class': 'netinsight'
          });

          netinsight.data( 'netinsight', {target: $this, id: dataId} );

          // creates a jQuery selector based on the allowedLandmarks options
          var landmarksSelector = $(opts.allowedLandmarks).map(function() {
            return "[role~=" + this + "]";
          }).get().join(',');

          // creates a jQuery selector based on the allowedFormClassNames options
          var formsSelector = $(opts.allowedFormClassNames).map(function() {
            return "form." + this;
          }).get().join(',');
          
          // if mapClassNamesToRels option is true, then append values defined in the link's class attribute 
          // to the link's rel attribute.
          if (opts.mapClassNamesToRels) {
            // creates a jQuery selector based on the allowedRels options
            var classNamesSelector = $(opts.allowedRels).map(function() {
              return "a." + this;
            }).get().join(',');

            $(classNamesSelector, $this).each(function() {
              var $thisLink = $(this),
                  classNames = $thisLink.attr('class').toLowerCase().split(' '),
                  rels = $thisLink.attr('rel').toLowerCase().split(' ');

              // Modify classNames array so that only values within allowedRels are included.
              // Additionally, only include if the className is not already present in the rel attribute.
              classNames = $.grep(classNames, function(val) {
            	return ($.inArray(val, opts.allowedRels) > -1 && $.inArray(val, rels) === -1);
              });

              // Append classNames to the rel attribute.
              $thisLink.attr('rel', function(index, val) {
                return val + " " + classNames.join(' ');
              });
            });
          }

          var shareSelector = $(opts.allowedShareClassNames).map(function() {
            return "div." + this;
          }).get().join(',');

          $(shareSelector, $this).each(function() {
            var isExternal,
                titlePrefix = "",
                $thisDiv = $(this);

            linkData = $thisDiv.data( 'netinsight' ) || {};

            var landmarks = $thisDiv.parents(landmarksSelector).map(function() {
              return $(this).attr('role');
            }).get().join(',');

            isExternal = 1;

            if ($.isEmptyObject(linkData)) {
              var linkEventData = {
                linkLocation: landmarks,
                linkType: "share",
                linkTitle: $thisDiv.attr('id'),
                link: "",
                lk: isExternal
              }
            }

            $thisDiv.data('netinsight', linkEventData);
          });

          // creates a jQuery selector based on the allowedRels options
          var relsSelector = $(opts.allowedRels).map(function() {
            return "a[rel~=" + this + "]";
          }).get().join(',');


          // Loop through each link that matches the relsSelector and attach data to the netinsight namespace.
          // Then bind the click event within the netinsight namespace to methods.trackLinkEvent.
          // @see http://docs.jquery.com/Plugins/Authoring#Events
          // @see http://docs.jquery.com/Plugins/Authoring#Data
          $(relsSelector, $this).each(function() {
            var isExternal, 
                titlePrefix = "",
                $thisLink = $(this),
                linkData = $thisLink.data( 'netinsight' ) || {},
                rels = $thisLink.attr('rel').toLowerCase().split(' ');

            rels = $.grep(rels, function(val) {
              return ($.inArray(val, opts.allowedRels) > -1);
            });

            if ($.inArray("external", rels) > -1) {
              titlePrefix = $thisLink.url().attr('host').replace('www.', '') + " | ";
              isExternal = 1;
            }

            // Replace rels with their report-friendly alias.
            $.each(opts.allowedRelsAliases, function(key, val) {
              var relsIndex = $.inArray(key, rels);
              if (relsIndex > -1) {
                rels[relsIndex] = val;
              }
            });

            var landmarks = $thisLink.parents(landmarksSelector).map(function() {
              return $(this).attr('role');
            }).get().join(',');

            var title;
            var linkText = $thisLink.text().toLowerCase().replace(/\n|\s\s|\t/g,'');
            
            // if there is link text use it, otherwise fallback to the link's title attribute.
            if (linkText.length > 1) {
            	title = titlePrefix + $thisLink.text().toLowerCase().replace(/\n|\s\s|\t/g,'');
            } else {
            	title = $thisLink.attr('title').toLowerCase();
            }
            
            var linkEventData = {
              linkLocation: landmarks,
              linkType: rels.join(','),
              linkTitle: title,
              linkFormat: ($thisLink.attr('type') != "" && $thisLink.attr('type') !== undefined) ? $thisLink.attr('type') : undefined,
              link: ($thisLink.attr('href') != "" && $thisLink.attr('href') !== undefined) ? $thisLink.attr('href') : undefined,
              lk: isExternal
            }

            // extend (merge) default values and those set with:
            //   html5 data-netinsight JSON
            //   values set via html5 data-netinsight attribute override those set in linkEventData.
            // @see http://api.jquery.com/jQuery.extend/
            $.extend(linkEventData, linkData);

            $thisLink.data('netinsight', linkEventData);

          }).bind('click.netinsight', methods.trackLinkEvent);

          // Loop though form elements.
          $(formsSelector, $this).each(function() {
            var $thisForm = $(this),
                formData = $thisForm.data( 'netinsight' ) || {};
            
            var autoFieldEventData = {
              quesion : $('.auto', $thisForm).map(function() { return $(this).attr('name'); }),
              answer : $('.auto', $thisForm).map(function() { return $(this).val(); })
            }
            
            var formEventData = {
              form: $thisForm.attr('id'),
              question: (autoFieldEventData.quesion.length !== 0) ? (autoFieldEventData.quesion).get().join(',') : undefined,
              answer: (autoFieldEventData.answer.length !== 0) ? (autoFieldEventData.answer).get().join(',') : undefined
            }
            
            // extend (merge) default values and those set with:
            //   html5 data-netinsight JSON
            //   values set via html5 data-netinsight attribute override those set in formEventData.
            // @see http://api.jquery.com/jQuery.extend/
            $.extend(formEventData, formData);

            $thisForm.data('netinsight', formEventData);

            $(':input', $thisForm).each(function() {
              var $thisField = $(this),
                  fieldData = $thisField.data( 'netinsight' ) || {};
            });

            // Delegate Event Handlers to track form start.
            $thisForm.delegate(':input', 'focus.netinsight', methods.trackFormEventStart);
            $thisForm.delegate(':button,:checkbox,:checkbox+label,:radio,:radio+label', 'mousedown.netinsight', methods.trackFormEventStart);
            
            // Delegate Event Handler to track form progress for multistep forms.
            $thisForm.delegate(':input.flow', 'change.netinsight', methods.trackFormEventProgress);

          });
        } // !data.netinsight

      });

      return this;
    },
    destroy: function( ) {

      this.each(function() {

        var $this = $(this),
            data = $this.data( 'netinsight' );

        // Remove created elements, unbind namespaced events, and remove data
        $(document).unbind( '.netinsight' );
        data.netinsight.remove();
        $this.unbind( '.netinsight' )
        .removeData( 'netinsight' );
      });

      return this;
    },
    options: function( options ) {

      this.each(function() {
        var $this = $(this),
            // don't use our getData() function here
            // because we want an object regardless
            data = $this.data( 'netinsight' ) || {},
            opts = data.options || {};

        // deep extend (merge) default settings, per-call options, and options set with:
        // html5 data-netinsight options JSON and $('selector').netinsight( 'options', {} );
        opts = $.extend( true, {}, $.fn.netinsight.defaults, opts, options || {} );
        data.options = opts;
        $.data( this, 'netinsight', data );
      });

      return this;
    },
    trackFlashEvent: function(id, link) {
      var $this = $("#"+id);

      debug('link data is: ' + JSON.stringify($this.data('netinsight')));

      var netinsightData = $this.data('netinsight');
      if (netinsightData !== undefined && link) {
        netinsightData.link = link;

        if ($.url(link).attr("host") != $.url(window.location.href).attr("host")) {
          netinsightData.linkType += ",external";
        }

        $.each(netinsightData, function(key, value) {
          if (value !== undefined) {
            ntptAddPair(key, value);
            debug("add pair : " + key + " : " + value);
          } else {
            ntptDropPair(key);
            debug("drop pair : " + key);
          }
        });
        ntptEventTag('ev=link');
      }
    },
    trackLinkEvent: function(event) {
      var $this = $(event.target).closest('a');

      debug('link data is: ' + JSON.stringify($this.data('netinsight')));

      if ($this.data('netinsight') !== undefined) {
        $.each($this.data('netinsight'), function(key, value) {
          if (value !== undefined) {
            ntptAddPair(key, value);
            debug("add pair : " + key + " : " + value);
          } else {
            ntptDropPair(key);
            debug("drop pair : " + key);
          }
        });
        ntptEventTag('ev=link');
      }
    },
    trackFormEventStart: function(event) {
      var $this = $(event.target),
          $thisForm = $this.closest('form');

      if ($thisForm.data('netinsight') !== undefined) {
        $.each($thisForm.data('netinsight'), function(key, value) {
          if (value !== undefined) {
            ntptAddPair(key, value);
            debug("add pair : " + key + " : " + value);
          }
        });
        
        ntptAddPair('formAction', 'start');
        ntptEventTag('ev=form');
        debug("add pair : formAction : start");
        debug("ev : form");
      }

      // only call trackFromEventStart once.
      ntptDropPair('formAction');
      ntptDropPair('question');
      ntptDropPair('answer');
      $thisForm.undelegate(':input', 'focus.netinsight', methods.trackFormEventStart);
      $thisForm.undelegate(':button,:checkbox,:checkbox+label,:radio,:radio+label', 'mousedown.netinsight', methods.trackFormEventStart);
    },
    trackFormEventProgress: function(event) {
      var $this = $(event.target),
          $thisForm = $this.closest('form'),
          question = $this.attr('name'),
          answer = getLabel($this);
      
      if ($thisForm.data('netinsight') !== undefined) {
        $.each($thisForm.data('netinsight'), function(key, value) {
          if (value !== undefined) {
            ntptAddPair(key, value);
            debug("add pair : " + key + " : " + value);
          } else {
            ntptDropPair(key);
            debug("drop pair : " + key);
          }
        });

        ntptAddPair('question', question);
        ntptAddPair('answer', answer);
        ntptAddPair('formAction', 'questionAnswered');
        ntptEventTag('ev=form');
        debug("formAction : questionAnswered");
        debug("question : " + question);
        debug("answer : " + answer);
        debug("ev : form");
      }
    },
    trackFormEventQuestionServed: function(val) {
	  var $this = $(this),
	      $thisForm = $this.closest('form'),
	      question = ($this.attr('name') != "" && $this.attr('name') !== undefined) ? $this.attr('name') : $this.attr('id');
	    
	  if ($thisForm.data('netinsight') !== undefined) {
	    $.each($thisForm.data('netinsight'), function(key, value) {
	      if (value !== undefined) {
	        ntptAddPair(key, value);
	        debug("add pair : " + key + " : " + value);
	      } else {
	        ntptDropPair(key);
	        debug("drop pair : " + key);
	      }
	    });
	
	    ntptAddPair('formAction', 'questionServed');
	    ntptAddPair('question', question);
	    ntptEventTag('ev=form');
	    debug("formAction : questionServed");
	    debug("question : " + question);
	    debug("ev : form");
	  }
	}
  };

  var protoSlice = Array.prototype.slice;

  $.fn.netinsight = function( method ) {
    if ( methods[method] ) {
      return methods[method].apply( this, protoSlice.call( arguments, 1 ) );
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.fn.netinsight' );
    }
  };

  $.extend($.fn.netinsight, {
    defaults: settings,
    version: '1.0.0',
    jQueryVersionRequired: '1.4.3',
    fireEvent: function() {
      var a = [].slice.call(arguments);
      var p = $(a.slice(1)).map(function() {
        return  "'" + this + "'";
      }).get().join(',');
      eval("methods." + a[0] + "(" + p + ")");
      return;
    }
  });

  function getData(el) {
    var netinsight, opts,
        $this = $(el),
        data = $this.data( 'netinsight' ) || {};

    if (!data.netinsight) { return false; }

    return data;
  }

  function getLabel(el) {
    var $this = $(el),
        $thisForm = $this.closest('form'),
        label;

    if ($this.attr('type') == "checkbox" || $this.attr('type') == "radio") {
      label = $('label[for=' + $this.attr('id') + ']', $thisForm);
    } else {
      label = $(':selected', $this);
    }
    if (!label) { return false; }

    return label.text().toLowerCase().replace(/\n|\s\s|\t/g,'');

  }

  /**
   * Prints to Firebug console, if available. To enable:
   *   $.fn.netinsight.defaults.debug = true;
   */
  function debug(message) {
    if (typeof console != 'undefined' && typeof console.debug != 'undefined' && $.fn.netinsight.defaults.debug) {
      console.debug(message);
    }
  };

})( jQuery );

window.netinsight = $.fn.netinsight;
