;
// $Id: drupal.js,v 1.41.2.4 2009/07/21 08:59:10 goba Exp $

var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {} };

/**
 * Set the variable that indicates if JavaScript behaviors should be applied
 */
Drupal.jsEnabled = document.getElementsByTagName && document.createElement && document.createTextNode && document.documentElement && document.getElementById;

/**
 * Attach all registered behaviors to a page element.
 *
 * Behaviors are event-triggered actions that attach to page elements, enhancing
 * default non-Javascript UIs. Behaviors are registered in the Drupal.behaviors
 * object as follows:
 * @code
 *    Drupal.behaviors.behaviorName = function () {
 *      ...
 *    };
 * @endcode
 *
 * Drupal.attachBehaviors is added below to the jQuery ready event and so
 * runs on initial page load. Developers implementing AHAH/AJAX in their
 * solutions should also call this function after new page content has been
 * loaded, feeding in an element to be processed, in order to attach all
 * behaviors to the new content.
 *
 * Behaviors should use a class in the form behaviorName-processed to ensure
 * the behavior is attached only once to a given element. (Doing so enables
 * the reprocessing of given elements, which may be needed on occasion despite
 * the ability to limit behavior attachment to a particular element.)
 *
 * @param context
 *   An element to attach behaviors to. If none is given, the document element
 *   is used.
 */
Drupal.attachBehaviors = function(context) {
  context = context || document;
  if (Drupal.jsEnabled) {
    // Execute all of them.
    jQuery.each(Drupal.behaviors, function() {
      this(context);
    });
  }
};

/**
 * Encode special characters in a plain-text string for display as HTML.
 */
Drupal.checkPlain = function(str) {
  str = String(str);
  var replace = { '&': '&amp;', '"': '&quot;', '<': '&lt;', '>': '&gt;' };
  for (var character in replace) {
    var regex = new RegExp(character, 'g');
    str = str.replace(regex, replace[character]);
  }
  return str;
};

/**
 * Translate strings to the page language or a given language.
 *
 * See the documentation of the server-side t() function for further details.
 *
 * @param str
 *   A string containing the English string to translate.
 * @param args
 *   An object of replacements pairs to make after translation. Incidences
 *   of any key in this array are replaced with the corresponding value.
 *   Based on the first character of the key, the value is escaped and/or themed:
 *    - !variable: inserted as is
 *    - @variable: escape plain text to HTML (Drupal.checkPlain)
 *    - %variable: escape text and theme as a placeholder for user-submitted
 *      content (checkPlain + Drupal.theme('placeholder'))
 * @return
 *   The translated string.
 */
Drupal.t = function(str, args) {
  // Fetch the localized version of the string.
  if (Drupal.locale.strings && Drupal.locale.strings[str]) {
    str = Drupal.locale.strings[str];
  }

  if (args) {
    // Transform arguments before inserting them
    for (var key in args) {
      switch (key.charAt(0)) {
        // Escaped only
        case '@':
          args[key] = Drupal.checkPlain(args[key]);
        break;
        // Pass-through
        case '!':
          break;
        // Escaped and placeholder
        case '%':
        default:
          args[key] = Drupal.theme('placeholder', args[key]);
          break;
      }
      str = str.replace(key, args[key]);
    }
  }
  return str;
};

/**
 * Format a string containing a count of items.
 *
 * This function ensures that the string is pluralized correctly. Since Drupal.t() is
 * called by this function, make sure not to pass already-localized strings to it.
 *
 * See the documentation of the server-side format_plural() function for further details.
 *
 * @param count
 *   The item count to display.
 * @param singular
 *   The string for the singular case. Please make sure it is clear this is
 *   singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
 *   Do not use @count in the singular string.
 * @param plural
 *   The string for the plural case. Please make sure it is clear this is plural,
 *   to ease translation. Use @count in place of the item count, as in "@count
 *   new comments".
 * @param args
 *   An object of replacements pairs to make after translation. Incidences
 *   of any key in this array are replaced with the corresponding value.
 *   Based on the first character of the key, the value is escaped and/or themed:
 *    - !variable: inserted as is
 *    - @variable: escape plain text to HTML (Drupal.checkPlain)
 *    - %variable: escape text and theme as a placeholder for user-submitted
 *      content (checkPlain + Drupal.theme('placeholder'))
 *   Note that you do not need to include @count in this array.
 *   This replacement is done automatically for the plural case.
 * @return
 *   A translated string.
 */
Drupal.formatPlural = function(count, singular, plural, args) {
  var args = args || {};
  args['@count'] = count;
  // Determine the index of the plural form.
  var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);

  if (index == 0) {
    return Drupal.t(singular, args);
  }
  else if (index == 1) {
    return Drupal.t(plural, args);
  }
  else {
    args['@count['+ index +']'] = args['@count'];
    delete args['@count'];
    return Drupal.t(plural.replace('@count', '@count['+ index +']'));
  }
};

/**
 * Generate the themed representation of a Drupal object.
 *
 * All requests for themed output must go through this function. It examines
 * the request and routes it to the appropriate theme function. If the current
 * theme does not provide an override function, the generic theme function is
 * called.
 *
 * For example, to retrieve the HTML that is output by theme_placeholder(text),
 * call Drupal.theme('placeholder', text).
 *
 * @param func
 *   The name of the theme function to call.
 * @param ...
 *   Additional arguments to pass along to the theme function.
 * @return
 *   Any data the theme function returns. This could be a plain HTML string,
 *   but also a complex object.
 */
Drupal.theme = function(func) {
  for (var i = 1, args = []; i < arguments.length; i++) {
    args.push(arguments[i]);
  }

  return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args);
};

/**
 * Parse a JSON response.
 *
 * The result is either the JSON object, or an object with 'status' 0 and 'data' an error message.
 */
Drupal.parseJson = function (data) {
  if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) {
    return { status: 0, data: data.length ? data : Drupal.t('Unspecified error') };
  }
  return eval('(' + data + ');');
};

/**
 * Freeze the current body height (as minimum height). Used to prevent
 * unnecessary upwards scrolling when doing DOM manipulations.
 */
Drupal.freezeHeight = function () {
  Drupal.unfreezeHeight();
  var div = document.createElement('div');
  $(div).css({
    position: 'absolute',
    top: '0px',
    left: '0px',
    width: '1px',
    height: $('body').css('height')
  }).attr('id', 'freeze-height');
  $('body').append(div);
};

/**
 * Unfreeze the body height
 */
Drupal.unfreezeHeight = function () {
  $('#freeze-height').remove();
};

/**
 * Wrapper around encodeURIComponent() which avoids Apache quirks (equivalent of
 * drupal_urlencode() in PHP). This function should only be used on paths, not
 * on query string arguments.
 */
Drupal.encodeURIComponent = function (item, uri) {
  uri = uri || location.href;
  item = encodeURIComponent(item).replace(/%2F/g, '/');
  return (uri.indexOf('?q=') != -1) ? item : item.replace(/%26/g, '%2526').replace(/%23/g, '%2523').replace(/\/\//g, '/%252F');
};

/**
 * Get the text selection in a textarea.
 */
Drupal.getSelection = function (element) {
  if (typeof(element.selectionStart) != 'number' && document.selection) {
    // The current selection
    var range1 = document.selection.createRange();
    var range2 = range1.duplicate();
    // Select all text.
    range2.moveToElementText(element);
    // Now move 'dummy' end point to end point of original range.
    range2.setEndPoint('EndToEnd', range1);
    // Now we can calculate start and end points.
    var start = range2.text.length - range1.text.length;
    var end = start + range1.text.length;
    return { 'start': start, 'end': end };
  }
  return { 'start': element.selectionStart, 'end': element.selectionEnd };
};

/**
 * Build an error message from ahah response.
 */
Drupal.ahahError = function(xmlhttp, uri) {
  if (xmlhttp.status == 200) {
    if (jQuery.trim($(xmlhttp.responseText).text())) {
      var message = Drupal.t("An error occurred. \n@uri\n@text", {'@uri': uri, '@text': xmlhttp.responseText });
    }
    else {
      var message = Drupal.t("An error occurred. \n@uri\n(no information available).", {'@uri': uri, '@text': xmlhttp.responseText });
    }
  }
  else {
    var message = Drupal.t("An HTTP error @status occurred. \n@uri", {'@uri': uri, '@status': xmlhttp.status });
  }
  return message;
}

// Global Killswitch on the <html> element
if (Drupal.jsEnabled) {
  // Global Killswitch on the <html> element
  $(document.documentElement).addClass('js');
  // 'js enabled' cookie
  document.cookie = 'has_js=1; path=/';
  // Attach all behaviors.
  $(document).ready(function() {
    Drupal.attachBehaviors(this);
  });
}

/**
 * The default themes.
 */
Drupal.theme.prototype = {

  /**
   * Formats text for emphasized display in a placeholder inside a sentence.
   *
   * @param str
   *   The text to format (plain-text).
   * @return
   *   The formatted text (html).
   */
  placeholder: function(str) {
    return '<em>' + Drupal.checkPlain(str) + '</em>';
  }
};
;
// Validate all required fields before submission.
// All fields are tested for validity when they lose focus, so
// don't worry about non-required fields.
//
// If any of the fields possess the "error" class, we know
// there's something wrong. Abort the submission.

var DriggValidateBeforeSubmit = function () {
	$(".error").removeClass("error");
	$(".messages.error").remove();
	DriggValidateBodyFull( true );
	DriggValidateTitleFull( true );

        if( ! Drupal.settings.drigg.allow_empty_url){
	  DriggValidateURL( $("#edit-url"), "Story's URL", 'sURL', true);
        }

	if ( $(".error").length > 0 ) {
		return false;
	}
	return true;
}

// cname: The error "class name"
// error: The error message to display
//
// Generate an error message in a standard error message box.
// The error has an error name (cname) and an error message.
// The cname is assigned as the class name to the error message.
// In that way, everything that can generate an error has a distinct
// error message location. That message is generated, cleared, or
// modified based on the cname.

function DriggAddError( cname, error ) {
	var msgs = $(".messages.error ul");

	// If the error display area doesn't exist, create it.
	// Arbitrarily, it is created after the div with class "help"
	if ( msgs.length == 0 ) {
		$(".messages.error").remove();
		$("form#node-form").before('<div class="messages error"><ul></ul></div>' );
		msgs = $(".messages.error ul");
	}
	// Remove any current messages with a class of <cname>
	msgs.find("."+cname).remove();

	// Create a new error item identified by <cname>
	msgs.append('<li class="' + cname + '">' + error + '</li>');
}


// cname: The "error class" identifier
//
// Remove the error message identified by <cname>.
// If there are no more error messages, remove the error
// message box.

function DriggRemoveError( cname ) {
	var msgs = $(".messages.error ul");

	msgs.find("." + cname).remove();
	if( msgs.children().length == 0 ) {
		$(".messages.error").remove();
	}
}

// Validate the "Story description" text area. Do this by
// calling DriggValidateBodyFull. The "false" indicates 
// this is not a full check-- don't generate an error if
// the field is empty.

var DriggValidateBody = function() {
	DriggValidateBodyFull( false );
}

// check: Full check flag
//
// Validate the "Story description" text area. If <check> is true,
// generate an error if the field is empty. Otherwise, only generate
// an error if the field contains characters, but does not contain
// the minimum required number of characters.
//
// Returns "true" if the field validates, "false" otherwise.

var DriggValidateBodyFull = function( check ) {
	var body = $("#edit-body");

	if ( check || body.val().length > 0 )
	{
		check = true;
	}
	else {
		DriggRemoveError("bodyErr");
		body.removeClass("error");
	}

	if ( check ) {
		if ( Drupal.settings.drigg.body_minimum_length != 0 && $("#edit-body").val().length < Drupal.settings.drigg.body_minimum_length ) {
			DriggAddError("bodyErr", "The description must be at least " + 
				Drupal.settings.drigg.body_minimum_length + " characters long.");
			$("#edit-body").addClass("error");
			return false;
		} else if ( Drupal.settings.drigg.body_maximum_length != 0 && $("#edit-body").val().length > Drupal.settings.drigg.body_maximum_length ) {
			DriggAddError("bodyErr", "The description cannot be more than " + 
				Drupal.settings.drigg.body_maximum_length + " characters long.");
			$("#edit-body").addClass("error");
			return false;
		} else {
			DriggRemoveError("bodyErr");
			$("#edit-body").removeClass("error");
			return true;
		}
	}
	else {
		return false;
	}
}

// Validate the "Story's title" text input. Do this by
// calling DriggValidateTitleFull. The "false" indicates 
// this is not a full check-- don't generate an error if
// the field is empty.

var DriggValidateTitle = function( ) {
	DriggValidateTitleFull( false );
}

// check: Full check flag
//
// Validate the "Story's title" text input. If <check> is true,
// generate an error if the field is empty. Otherwise, only generate
// an error if the field contains characters, but does not contain
// the minimum required number of characters.
//
// Returns "true" if the field validates, "false" otherwise.

var DriggValidateTitleFull = function( check ) {
	var title = $("#edit-title");
	if ( check || title.val().length > 0 )
	{
		check = true;
	}
	else {
		DriggRemoveError("titleErr");
		title.removeClass("error");
	}
	if ( check )
	{
		if ( Drupal.settings.drigg.title_minimum_length != 0 && $("#edit-title").val().length < Drupal.settings.drigg.title_minimum_length ) {
			DriggAddError("titleErr", "The title must be at least "
					+ Drupal.settings.drigg.title_minimum_length + " characters.");
			$("#edit-title").addClass("error");
			return false;
		} else if ( Drupal.settings.drigg.title_maximum_length != 0 && $("#edit-title").val().length > Drupal.settings.drigg.title_maximum_length ) {
			DriggAddError("titleErr", "The title cannot be longer than "
					+ Drupal.settings.drigg.title_maximum_length + " characters.");
			$("#edit-title").addClass("error");
			return false;
		} else {
			DriggRemoveError("titleErr");
			$("#edit-title").removeClass("error");
			return true;
		}
	}
	else {
		return false;
	}
}

// Validate the "Trackback URL" text input. Do this by
// calling DriggValidateURL. (SEE DriggValidateURL for
// a complete description of the arguments.) 

var DriggValidatePingURL = function() {
	DriggValidateURL( this, 'Trackback URL', 'tbURL', false);
}

// Validate the "Story's URL" text input. Do this by
// calling DriggValidateURL. (SEE DriggValidateURL for
// a complete description of the arguments.) 

var DriggValidateStoryURL = function() {

        var entry = $(this);
        var url = entry.val();

        // If an empty URL is allowed, then don't check and take the error out
        if( Drupal.settings.drigg.allow_empty_url &&  url.length == 0 ) {
          entry.removeClass("error");
          DriggRemoveError('sURL');
          return;
        }

        DriggValidateURL( this, "Story's URL", 'sURL', false);
}

// elem: The text input to validate
// prefix: A string to display in the error box
// errtype: A unique identifier (<cname> in DriggCreateError
//          and DriggRemoveError) 
// check: Full check flag
//
// Validates the text contained in <elem> to make sure the URL
// is valid. This is done via the server, so we pass it in
// with AJAX.
//
// If the entry is not a valid URL, generate an error of class
// <errtype>. The error message begins with "prefix," which
// should help distinguish the error for the user.
//
// If <check> is false, don't bother reporting if the field is
// empty. This helps make things a little nicer when navigating
// the form.

var DriggValidateURL = function(elem, prefix, errtype, check) {
	var entry = $(elem);
	var args = {};
	args.operation = 'validate_url';
	args.url = entry.val();

        if(Drupal.settings.drigg.editing_node){
          args.editing_node=Drupal.settings.drigg.editing_node;
        }

	if ( args.url == '' )
	{
		if ( !/required/.test(entry[0].className) ) {
			check = false;
		}
		DriggRemoveError(errtype);
		entry.removeClass("error");
	}
	else {
		check = true;
	}

	if ( check ) {
		if ( args.url == '' ) {
			DriggAddError(errtype, prefix + ' is required');
			entry.addClass("error");
	
			return false;
		}

		$.ajax({
			data     : args,
                	url      : Drupal.settings.drigg.base_path+"drigg/ajax/handle",
			type     : 'get',
			datatype : 'html',
			success  : function (result) {
				if ( result == 'success' ) {
					entry.removeClass("error");
					DriggRemoveError(errtype);
				}
				else {
					DriggAddError(errtype, prefix + ': ' + result);
					entry.addClass("error");
				}
			},
			error: function( xhr, msg ) {
				entry.addClass("error");
			},
			complete: function( xhr, stat ) {
			}	
		});
	
	}
}

// This is executed on document load.
//
// If this appears to be a drigg node form, wire up the text
// entries for validation.

$(function(){
  if ( $("#edit-drigg-node-form").length > 0 ) {
    $("#edit-trackback-ping-url").blur(DriggValidatePingURL);
                //if(! Drupal.settings.drigg.editing_node ){
    $("#edit-url").blur(DriggValidateStoryURL);
                //}
    $("#edit-title").blur(DriggValidateTitle);
    $("#edit-body").blur(DriggValidateBody);
    //$("#node-form").submit(DriggValidateBeforeSubmit);
  }
});

;
// Check for related links.
//
// Build an AJAX request from the title and body (description).
// This should return some HTML containing related links.
//
// Only use the title and body if they are valid, and contain
// data.

var DriggRelatedLinks = function () {
        var entry = $(this);
        var args = {};
	var title = $("#edit-title");

	if ( !DriggValidateBodyFull(false) ) {
		$("#related_links_box").html('');
		return;
	}
	if ( DriggValidateTitleFull(false) ) {
		args.text = title.val() + " ";
	}
	else {
		args.text = '';
	}

        args.operation = 'related_links';
        args.text += entry.val();
        $.ajax({
                data     : args,
                url      : Drupal.settings.drigg.base_path+"drigg/ajax/rl",
                type     : 'get',
                datatype : 'html',
                success  : function (result) {
			$("#related_links_box").html(result);
                },
                error: function( xhr, msg ) {
                },
                complete: function( xhr, stat ) {
                }
        });

}


$(function(){
	if ( $("#edit-drigg-node-form").length > 0 ) {
		$("#edit-body").blur(DriggRelatedLinks);
	}
});

;
///////////////////////////////////////////////////////////////////////////////
// NOTE 1: The radiobutton-based forms are handled a bit oddly. The handling
// of these forms is based on the assumption that the radiobuttons are not
// displayed. All click events are handled through the radiobutton's label.
//
// Classes are assigned to the labels based on their state-- clickable,
// not clickable, or already-clicked. The CSS associated with these classes
// change the appearance of these labels based on which classes are assigned.
// Therefore, the Javascript herein concerns itself with changing the classes
// assigned to labels, and leaves the display to CSS.
//
// The "select"-based dropdown boxes are not handled in any unorthodox manner.
// They're pretty straightforward.
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// NOTE 2: Thanks to the brain-dead nature of Internet Explorer CSS selectors,
// all classes must be aggragated into a single class.
//
// An element in Internet Explorer can have multiple classes, but you can't build
// CSS selectors with multiple classes. For instance, assume an element E1 with
// class="green blue red", and an element E2 with class="red". The CSS selector
// S1 ".green .red" will select *BOTH* E1 and E2 in Internet Explorer. This is
// a fairly disasterous bug, in that it screws up our combinational logic.
//
// To get around that, aggregate the classes into a single "super-class."
// This super-class is built from the other assigned classes. So, there are three
// types of classes:
//
//    Direction: "karma_up" | "karma_down"
//    Clicked:   "karma_clicked"
//    Clickable: "karma_clickable" | "karma_not_clickable"
//
// Three distinct bits = eight possible combinations:
//
//       karma_up_clicked_clickable
//       karma_up_not_clicked_clickable
//       karma_up_clicked_not_clickable
//       karma_up_not_clicked_not_clickable
//       karma_down_clicked_clickable
//       karma_down_not_clicked_clickable
//       karma_down_clicked_not_clickable
//       karma_down_not_clicked_not_clickable
//
// SEE karma_aggregate_classes for combinational logic
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// This is called when a form is submitted.
var KarmaSendForm = function() {
  var form = $(this);
  var args = {};
  var destination = form.find("input[name=js_destination]").val();
  var karma_url=Drupal.settings.extra_voting_forms.base_path + 'extra_voting_forms/handle';
  var karma_timeout=Drupal.settings.extra_voting_forms.jquery_timeout;

  // If 'destination' is defined, we need to load the destination page 
  // rather than submit the vote.
  // This is used to redirect to a login page, for example.
  if (  destination ) {
    // top.location = Drupal.settings.extra_voting_forms.base_path + Drupal.settings.extra_voting_forms.login_page + '?destination=' + destination;
    top.location = '/' + Drupal.settings.extra_voting_forms.login_page + '?destination=' + destination;
    return false;
  }

  // The class "tmpClicked" is a simple state variable. It is added as soon 
  // as a label is clicked, and removed only once the vote has succeeded or 
  // failed. In this way, pending votes are tracked.

  // If an element exists with both the "karma_clicked" and the "tmpClicked", 
  // then the user has cast the same vote again. Ignore it. 
  else if ( form.find('.tmpClicked').length > 0 ) {
    var tmpclicked = $(form.find('.tmpClicked')[0] );
    tmpclicked.removeClass('tmpClicked');
    if ( karma_clicked_indicator( tmpclicked ) == 'clicked' ) {
      return false;
    }
    else {
      tmpclicked.addClass('tmpClicked');
    }
  }
  // Process the vote.
  // Marshall all AJAX arguments

  args.karma_vote   = form.find(":selected").val() || form.find(":checked").val();
  args.oid     = form.find("input[name=oid]").val();
  args.otype     = form.find("input[name=otype]").val();
  args.form_type     = 'ajax';

  form.addClass("karma_sending");
  if (Drupal.settings.extra_voting_forms.hide_karma_form) {
    if (form.hasClass("karma_form_promoted")) {
      form.removeClass("karma_form_promoted");
      var karma_form_promoted_REMOVED=1;
    }
    if (form.hasClass("karma_form")) {
      form.removeClass("karma_form");
      var karma_form_REMOVED=1;
    }
  }

  // Send the request  
  $.ajax({

    data    : args,
    url     : karma_url,
    type    : 'POST',
    dataType: 'json',
    timeout : karma_timeout,
    success : function(json){
      var ts;
      for(var x in json) {args[x] = json[x];}

      if (karma_form_promoted_REMOVED==1) {
        form.addClass("karma_form_promoted");
      }
      if (karma_form_REMOVED==1) {
        form.addClass("karma_form");
      }

      form.removeClass("karma_sending");

      // If there's an error, remove "tmpClicked", as the request is no 
      // longer pending. 
      if (json.error) {
        form.find(".tmpClicked").removeClass("tmpClicked");
        alert(json.error);
      } else {

        // On success, disable the appropriate forms (SEE KarmaStripForm 
        // for more details).
        // Also, ensure only the currently-clicked button has the state 
        // "karma_clicked". Remove "tmpClicked", as the request is 
        // no longer pending.
        var tmpclicked = form.find(".tmpClicked");
        form.addClass("karma_success");
        form.find("label").each( function() { karma_clicked( $(this), "not_clicked"); } )
        tmpclicked.each( function() { { karma_clicked( $(this), "clicked")  } } );
        KarmaStripForm(form,args);
      }
    },
    error : function(xhr,msg){
      // If there's an error, remove "tmpClicked", as the request is no longer pending. 

      form.find(".tmpClicked").removeClass("tmpClicked");
      if (xhr.status==404||!msg) {
        alert("Karma Error: Communication (0)");
      } else {
        alert("Karma Error: "+msg);
      }
    },
    complete: function(xhr,stat) {
      (function(form,args){
        window.setTimeout(function(){
          form.removeClass("karma_sending");

          if (karma_form_promoted_REMOVED==1) {
            form.addClass("karma_form_promoted");
          }
          if (karma_form_REMOVED==1) {
            form.addClass("karma_form");
          }
        },karma_timeout);

      })(form,args);
    }
  });
  return false;
}


///////////////////////////////////////////////////////////////////////////////
// Given a form, disable the appropriate related forms.
var KarmaStripForm  = function(form,args) {

  // If the site is set to allow only a single vote, disable the target form.

  if (Drupal.settings.extra_voting_forms.only_one_vote) {
    // Disable the drop-down select elements by setting their "disabled" attribute
    form.find("select").attr("disabled","disabled");

    // For our radio-button based forms, the label is the clickable element. Select all labels in the target form.
    form.find("label")
      .each(function() { karma_clickable( $(this), 'not_clickable' ); } )

      // Finally, remove the 'click' event, so the label no longer responds to user input.
      .unbind('click');
  }

  // Find all forms associated with this node. Disable all other forms. This has the effect of
  // disabling *all* forms in an only_one_vote environment, or disabling all forms but the target
  // form in other environments.

  var fid = form.attr("id");

  // Modify the forms *only* if there is a valid karma score (in args.karma_aggregate)
  if ( args.karma_aggregate != "Undefined") {
    for ( var prefix in ( { 'w': 1, 'x': 1, 'y': 1, 'z': 1} ) ) {
      var did = prefix + args.oid;

      // Update all the karma scores for this node
      $("#" + did + " span.karma_score").text(args.karma_aggregate);
      var did = prefix + args.oid;
      if ( did != fid ) {

        // Disable all drop-down select elements
        $("#" + did + " *").attr("disabled", "true");

        // Disable all radiobutton-based forms, via their clickable labels.
        $("#" + did + " label")
           .each(function() { karma_clickable( $(this), 'not_clickable' ); } )
          // Finally, remove the 'click' event, so the label no longer responds to user input.
          .unbind('click');
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// Add click events to the appropriate form elements. This occurs at document load time.
var KarmaRenderForm = function() {
  var form   = $(this);
  var submit = form.find("input[type=submit]");

  // Skip this form if it's disabled.
  if (! submit.attr("disabled")) {

    // Remove checks on radiobuttons.
    form.find(":checked").attr("checked", false );

    // Add a simple "submit" event when a drop-down is selected.
    form.find("select").change(function(){form.submit();});

    // For radiobutton-based forms, add a slightly more complex click event
    // to the *label* of the radiobutton. The radiobuttons themselves are
    // not visible, so the *label* becomes the active element.

    form.find("label").click(function(){

      // If this radiobutton is not checked, do nothing. Basically, there are *two* click
      // events on each label. The first is generated when the label is clicked, but before
      // the radiobutton has been checked. The second is when the radiobutton is checked, which
      // occurs immediately after the first click event propogates.
      //
      // NOTE: "checked" here means "received the checkmark," and does not refer to validiation.

      form.find("#"+$(this).attr("for")).attr("checked", true );
      if($('.karma_sending').length > 0) {
  
        return;
      }
      // Add the "tmpClicked" class, to provide a state variable indicating a vote
      // submission is in progress for this label.

      $(this).addClass("tmpClicked");

      // Submit the form. This calls "KarmaSendForm".
      form.submit();
    });
  }
}

///////////////////////////////////////////////////////////////////////////////
function karma_up_down_indicator( element ) {
  if ( element.attr('class').substring(0,8) == "karma_up" ) {
    return "karma_up";
  }
  else {
    return "karma_down";
  }
}

///////////////////////////////////////////////////////////////////////////////
function karma_clickable_indicator( element ) {
  if ( element.attr('class').substr(-13) == "not_clickable" ) {
    return "not_clickable";
  }
  else {
    return "clickable";
  }
}

///////////////////////////////////////////////////////////////////////////////
function karma_clicked_indicator( element ) {
  if ( element.attr('class').substring(0,16) == "karma_up_clicked" ||
       element.attr('class').substring(0,18) == "karma_down_clicked" ) {

    return "clicked";
  }
  else {
    return "not_clicked";
  }
}

///////////////////////////////////////////////////////////////////////////////
function karma_clicked( element, clicked ) {
  var up_down = karma_up_down_indicator( element );
  var clickable = karma_clickable_indicator( element );
  element.removeAttr('class').attr( 'class', up_down + '_' + clicked + '_' + clickable );
  
}

///////////////////////////////////////////////////////////////////////////////
function karma_clickable( element, clickable ) {
  var up_down = karma_up_down_indicator( element );
  var clicked = karma_clicked_indicator( element );
  element.removeAttr('class').addClass( up_down + '_' + clicked + '_' + clickable );
}
///////////////////////////////////////////////////////////////////////////////
/*
var karma_comment_show = function() {
  var button = $(this);
  var parent = button.parent();

  if ( $(".karma_comment_show" ).find(this).length > 0 ) {
    button.removeClass("karma_comment_show");
    button.addClass("karma_comment_hide");
    parent.find("div").each( karma_restore_display );
  }
  else if ( $(".karma_comment_hide" ).find(this).length > 0 ) {
    button.removeClass("karma_comment_hide");
    button.addClass("karma_comment_show");
    parent.find("div").css('display', 'none');
  }
}

var karma_save_display = function() {
  var item = $(this);
  item.attr( "karma-save-display", item.css( 'display' ));
}

var karma_restore_display = function() {
  var item = $(this);
  var disp = item.attr( "karma-save-display" );
  if ( disp ) {
    item.css( "display", disp );
  }
}

var karma_hide_comment = function( val ) {
  var clicked = $(this);
  if ( val && typeof val != 'number' )
  {
    clicked = $(val);
  }
  var comment = clicked.parents( ".comment" );
  if ( comment.find(".karma_down.karma_clicked").length > 0 )
  {
    if ( !( comment.find(".karma_comment_show").length > 0) && !( comment.find(".karma_comment_hide" ).length > 0 )) {
      comment.find('div').each(karma_save_display);
      comment.find('.title').after("<span class='karma_comment_show'>&nbsp;&nbsp;</span>")
      comment.find('.karma_comment_show').click(karma_comment_show);
    }
    comment.find('div').css('display','none');
  }
}
*/

///////////////////////////////////////////////////////////////////////////////
// This is activated when the document is loaded.
$(function(){

  // Modify all karma_forms (even the promoted ones).
  $("form.karma_form,form.karma_form_promoted")

    // Call "KarmaSendForm" when the form is submitted. KarmaSendForm handles
    // all the AJAX magic.
    .submit(KarmaSendForm)

    // Send every form to "KarmaRenderForm" (which attaches click events to the form elements)
    .each(KarmaRenderForm);

  //$(".karma_clicked.karma_down").each(karma_hide_comment);
});
;

