/*
 * jQuery SmoothDivScroll 1.1
 *
 * Copyright (c) 2010 Thomas Kahn
 * Licensed under the GPL license.
 *
 * http://www.maaki.com/thomas/SmoothDivScroll/
 *
 * Depends:
 * jquery.ui.widget.js
 *
 */
(function($) {

	$.widget("thomaskahn.smoothDivScroll", {
		// Default options
		options: {
			scrollingHotSpotLeft: "div.scrollingHotSpotLeft",
			scrollingHotSpotRight: "div.scrollingHotSpotRight",
			scrollableArea: "div.scrollableArea",
			scrollWrapper: "div.scrollWrapper",
			hiddenOnStart: false,
			ajaxContentURL: "",
			countOnlyClass: "",
			scrollStep: 15,
			scrollInterval: 10,
			mouseDownSpeedBooster: 3,
			autoScroll: "",
			autoScrollDirection: "right",
			autoScrollStep: 5,
			autoScrollInterval: 10,
			visibleHotSpots: "",
			hotSpotsVisibleTime: 5,
			startAtElementId: ""
		},
		_create: function() {

			// Set variables
			var self = this, o = this.options, el = this.element;

			el.data("scrollWrapper", el.find(o.scrollWrapper));
			el.data("scrollingHotSpotRight", el.find(o.scrollingHotSpotRight));
			el.data("scrollingHotSpotLeft", el.find(o.scrollingHotSpotLeft));
			el.data("scrollableArea", el.find(o.scrollableArea));
			el.data("speedBooster", 1);
			el.data("motherElementOffset", el.offset().left);
			el.data("scrollXPos", 0);
			el.data("hotSpotWidth", el.find(o.scrollingHotSpotLeft).width());
			el.data("scrollableAreaWidth", 0);
			el.data("startingPosition", 0);
			el.data("rightScrollInterval", null);
			el.data("leftScrollInterval", null);
			el.data("autoScrollInterval", null);
			el.data("hideHotSpotBackgroundsInterval", null);
			el.data("previousScrollLeft", 0);
			el.data("pingPongDirection", "right");
			el.data("getNextElementWidth", true);
			el.data("swapAt", null);
			el.data("startAtElementHasNotPassed", true);
			el.data("swappedElement", null);
			el.data("originalElements", el.data("scrollableArea").children(o.countOnlyClass));
			el.data("visible", true);
			el.data("initialAjaxContentLoaded", false);
			el.data("enabled", true);

			// If the user wants to have visible hotspots, here is where it's taken care of
			if (o.autoScroll !== "always") {
				switch (o.visibleHotSpots) {
					case "always":
						self.showHotSpotBackgrounds();
						break;
					case "onstart":
						self.showHotSpotBackgrounds();
						el.data("hideHotSpotBackgroundsInterval", setTimeout(function() {
							self.hideHotSpotBackgrounds("slow");
						}, (o.hotSpotsVisibleTime * 1000)));
						break;
					default:
						break;
				}
			}
			/*****************************************
			SET UP EVENTS FOR SCROLLING RIGHT
			*****************************************/
			// Check the mouse X position and calculate the relative X position inside the right hotspot
			el.data("scrollingHotSpotRight").bind("mousemove", function(e) {
				var x = e.pageX - (this.offsetLeft + el.data("motherElementOffset"));
				el.data("scrollXPos", Math.round((x / el.data("hotSpotWidth")) * o.scrollStep));
				if (el.data("scrollXPos") === Infinity) {
					el.data("scrollXPos", 0);
				}
			});

			// mouseover right hotspot - scrolling
			el.data("scrollingHotSpotRight").bind("mouseover", function() {

				// Clear autoscrolling, if it should only run on start
				if ((o.autoScroll === "onstart" && el.data("autoScrollInterval") !== null)) {
					clearInterval(el.data("autoScrollInterval"));
					el.data("autoScrollInterval", null);
					self._trigger("autoScrollIntervalStopped");
				}

				// Start the scrolling interval
				el.data("rightScrollInterval", setInterval(function() {

					if (el.data("scrollXPos") > 0 && el.data("enabled")) {
						el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + (el.data("scrollXPos") * el.data("speedBooster")));

						self._showHideHotSpots();
					}

				}, o.scrollInterval));

				// Callback
				self._trigger("mouseOverRightHotSpot");

			});

			// mouseout right hotspot
			el.data("scrollingHotSpotRight").bind("mouseout", function() {
				clearInterval(el.data("rightScrollInterval"));
				el.data("scrollXPos", 0);
			});

			// mousedown right hotspot (add scrolling speed booster)
			el.data("scrollingHotSpotRight").bind("mousedown", function() {
				el.data("speedBooster", o.mouseDownSpeedBooster);
			});

			// mouseup anywhere (stop boosting the scrolling speed)
			$("body").bind("mouseup", function() {
				el.data("speedBooster", 1);
			});

			/*****************************************
			SET UP EVENTS FOR SCROLLING LEFT
			*****************************************/
			// Check the mouse X position and calculate the relative X position inside the left hotspot
			el.data("scrollingHotSpotLeft").bind("mousemove", function(e) {
				var x = el.data("scrollingHotSpotLeft").innerWidth() - (e.pageX - el.data("motherElementOffset"));
				el.data("scrollXPos", Math.round((x / el.data("hotSpotWidth")) * o.scrollStep));
				if (el.data("scrollXPos") === Infinity) {
					el.data("scrollXPos", 0);
				}

			});

			// mouseover left hotspot
			el.data("scrollingHotSpotLeft").bind("mouseover", function() {

				// Clear autoscrolling, if it should only run on start

				if ((o.autoScroll === "onstart" && el.data("autoScrollInterval") !== null)) {
					clearInterval(el.data("autoScrollInterval"));
					el.data("autoScrollInterval", null);
					self._trigger("autoScrollIntervalStopped");
				}

				el.data("leftScrollInterval", setInterval(function() {
					if (el.data("scrollXPos") > 0 && el.data("enabled")) {
						el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - (el.data("scrollXPos") * el.data("speedBooster")));

						self._showHideHotSpots();
					}

				}, o.scrollInterval));

				// Callback
				self._trigger("mouseOverLeftHotSpot");
			});

			// mouseout left hotspot
			el.data("scrollingHotSpotLeft").bind("mouseout", function() {
				clearInterval(el.data("leftScrollInterval"));
				el.data("scrollXPos", 0);
			});

			// mousedown left hotspot (add scrolling speed booster)
			el.data("scrollingHotSpotLeft").bind("mousedown", function() {
				el.data("speedBooster", o.mouseDownSpeedBooster);
			});

			/*****************************************
			SET UP EVENT FOR RESIZING THE BROWSER WINDOW
			*****************************************/
			$(window).bind("resize", function() {
				// If the scrollable area is not hidden on start, show/hide the hotspots
				if (!(o.hiddenOnStart)) {
					self._showHideHotSpots();
				}

				self._trigger("windowResized");
			});

			/*****************************************
			FETCHING AJAX CONTENT ON INITIALIZATION
			*****************************************/
			// If there's an ajaxContentURL in the options, 
			// fetch the content
			if (o.ajaxContentURL.length > 0) {
				self.replaceContent(o.ajaxContentURL);
			}
			else {
				self.recalculateScrollableArea();
			}

			// Should it be hidden on start?
			if (o.hiddenOnStart) {
				self.hide();
			}

			/*****************************************
			AUTOSCROLLING
			*****************************************/
			// If the user has set the option autoScroll, the scollable area will
			// start scrolling automatically. If the content is fetched using AJAX
			// the autoscroll is not started here but in recalculateScrollableArea.
			// Otherwise recalculateScrollableArea won't have the time to calculate
			// the width of the scrollable area before the autoscrolling starts.
			if ((o.autoScroll.length > 0) && !(o.hiddenOnStart) && (o.ajaxContentURL.length <= 0)) {
				self.startAutoScroll();
			}

		},
		/**********************************************************
		Hotspot functions
		**********************************************************/
		showHotSpotBackgrounds: function(fadeSpeed) {
			// Alter the CSS (SmoothDivScroll.css) if you want to customize
			// the look'n'feel of the visible hotspots
			var self = this, el = this.element;

			// Fade in the hotspot backgrounds
			if (fadeSpeed !== undefined) {
				// Before the fade-in starts, we need to make sure the opacity
				// is zero
				el.data("scrollingHotSpotLeft").css("opacity", "0.0");
				el.data("scrollingHotSpotRight").css("opacity", "0.0");

				el.data("scrollingHotSpotLeft").addClass("scrollingHotSpotLeftVisible");
				el.data("scrollingHotSpotRight").addClass("scrollingHotSpotRightVisible");

				// Fade in the left hotspot
				el.data("scrollingHotSpotLeft").fadeTo(fadeSpeed, 0.35);

				// Fade in the right hotspot
				el.data("scrollingHotSpotRight").fadeTo(fadeSpeed, 0.35);
			}
			// Don't fade, just show them
			else {
				// The left hotspot
				el.data("scrollingHotSpotLeft").addClass("scrollingHotSpotLeftVisible");
				el.data("scrollingHotSpotLeft").removeAttr("style");

				// The right hotspot
				el.data("scrollingHotSpotRight").addClass("scrollingHotSpotRightVisible");
				el.data("scrollingHotSpotRight").removeAttr("style");
			}
			self._showHideHotSpots();
		},
		hideHotSpotBackgrounds: function(fadeSpeed) {
			var el = this.element;

			// Fade out the hotspot backgrounds
			if (fadeSpeed !== undefined) {
				// Fade out the left hotspot
				el.data("scrollingHotSpotLeft").fadeTo(fadeSpeed, 0.0, function() {
					el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible");
				});

				// Fade out the right hotspot
				el.data("scrollingHotSpotRight").fadeTo(fadeSpeed, 0.0, function() {
					el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible");
				});
			}
			// Don't fade, just hide them
			else {
				el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible");
				el.data("scrollingHotSpotLeft").removeAttr("style");

				el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible");
				el.data("scrollingHotSpotRight").removeAttr("style");
			}

		},
		// Function for showing and hiding hotspots depending on the
		// offset of the scrolling
		_showHideHotSpots: function() {
			var self = this, el = this.element, o = this.options;

			// If autoscrolling is set to always, there should be no hotspots
			if (o.autoScroll !== "always") {
				// If the scrollable area is shorter than the scroll wrapper, both hotspots
				// should be hidden
				if (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth())) {
					el.data("scrollingHotSpotLeft").hide();
					el.data("scrollingHotSpotRight").hide();
				}
				// When you can't scroll further left the left scroll hotspot should be hidden
				// and the right hotspot visible.
				else if (el.data("scrollWrapper").scrollLeft() === 0) {
					el.data("scrollingHotSpotLeft").hide();
					el.data("scrollingHotSpotRight").show();
					// Callback
					self._trigger("scrollLeftLimitReached");
					// Clear interval
					clearInterval(el.data("leftScrollInterval"));
					el.data("leftScrollInterval", null);
				}
				// When you can't scroll further right
				// the right scroll hotspot should be hidden
				// and the left hotspot visible
				else if (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth() + el.data("scrollWrapper").scrollLeft())) {
					el.data("scrollingHotSpotLeft").show();
					el.data("scrollingHotSpotRight").hide();
					// Callback
					self._trigger("scrollRightLimitReached");
					// Clear interval
					clearInterval(el.data("rightScrollInterval"));
					el.data("rightScrollInterval", null);
				}
				// If you are somewhere in the middle of your
				// scrolling, both hotspots should be visible
				else {
					el.data("scrollingHotSpotLeft").show();
					el.data("scrollingHotSpotRight").show();
				}
			}
			else {
				el.data("scrollingHotSpotLeft").hide();
				el.data("scrollingHotSpotRight").hide();
			}
		},
		/**********************************************************
		Moving to a certain element
		**********************************************************/
		moveToElement: function(moveTo, elementNumber) {
			var self = this, el = this.element, o = this.options, tempScrollableAreaWidth = 0, foundStartAtElement = false;

			switch (moveTo) {
				case "first":
					el.data("scrollXPos", 0);
					self._trigger("movedToFirstElement");
					break;
				case "start":
					// Check to see where the start-at element is at the moment.
					// This can vary if endlessloop is used for autoscroll since it
					// swaps elements around.

					el.data("scrollableArea").children(o.countOnlyClass).each(function() {

						if ((o.startAtElementId.length > 0) && (($(this).attr("id")) === o.startAtElementId)) {
							el.data("startingPosition", tempScrollableAreaWidth);
							foundStartAtElement = true;
						}
						tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true);
					});

					el.data("scrollXPos", el.data("startingPosition"));
					self._trigger("movedToStartElement");
					break;
				case "last":
					el.data("scrollXPos", el.data("scrollableAreaWidth"));
					self._trigger("movedToLastElement");
					break;
				case "number":
					if (!(isNaN(elementNumber))) {
						// Get the total width of all preceding elements					
						el.data("scrollableArea").children(o.countOnlyClass).each(function(index) {
							if (index === (elementNumber - 1)) {
								el.data("scrollXPos", tempScrollableAreaWidth);
							}
							tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true);
						});
					}
					self._trigger("movedToElementNumber", null, { "elementNumber": elementNumber });
					break;
				default:
					break;
			}

			el.data("scrollWrapper").scrollLeft(el.data("scrollXPos"));
			self._showHideHotSpots();
		},
		/**********************************************************
		Adding or replacing content
		**********************************************************/
		addContent: function(ajaxContentURL, addWhere) {
			var self = this, el = this.element;

			$.get(ajaxContentURL, function(data) {
				// Add the loaded content first or last in the scrollable area
				if (addWhere === "first") {
					el.data("scrollableArea").children(":first").before(data);
				}
				else {
					el.data("scrollableArea").children(":last").after(data);
				}

				// Recalculate the total width of the elements inside the scrollable area
				self.recalculateScrollableArea();

				// Determine which hotspots to show
				self._showHideHotSpots();
			});
		},
		replaceContent: function(ajaxContentURL) {
			var self = this, el = this.element;

			el.data("scrollableArea").load(ajaxContentURL, function() {
				// Recalculate the total width of the elements inside the scrollable area
				self.recalculateScrollableArea();
				self.moveToElement("first");
				self._showHideHotSpots();
				el.data("startingPosition", 0);
			});
		},
		/**********************************************************
		Recalculate the scrollable area
		**********************************************************/
		recalculateScrollableArea: function() {

			var tempScrollableAreaWidth = 0, foundStartAtElement = false, o = this.options, el = this.element, self = this;

			// Add up the total width of all the items inside the scrollable area
			// and check to see if there's a specific element to start at
			el.data("scrollableArea").children(o.countOnlyClass).each(function() {
				// Check to see if the current element in the loop is the one where the scrolling should start
				if ((o.startAtElementId.length > 0) && (($(this).attr("id")) === o.startAtElementId)) {
					el.data("startingPosition", tempScrollableAreaWidth);
					foundStartAtElement = true;
				}
				tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true);
			});
		
			
			// If the element with the ID specified by startAtElementId
			// is not found, reset it
			if (!(foundStartAtElement)) {
				el.data("startAtElementId", "");
			}

			// Set the width of the scrollable area
			el.data("scrollableAreaWidth", tempScrollableAreaWidth);
			el.data("scrollableArea").width(el.data("scrollableAreaWidth"));

			// Move to the starting position
			el.data("scrollWrapper").scrollLeft(el.data("startingPosition"));
			el.data("scrollXPos", el.data("startingPosition"));

			// If the content of the scrollable area is fetched using AJAX
			// during initialization, it needs to be done here. After it has
			// been loaded a flag variable is set to indicate that the content
			// has been loaded already and shouldn
			if (!(el.data("initialAjaxContentLoaded"))) {
				if ((o.autoScroll.length > 0) && !(o.hiddenOnStart) && (o.ajaxContentURL.length > 0)) {
					self.startAutoScroll();
					el.data("initialAjaxContentLoaded", true);
				}
			}

		},
		/**********************************************************
		Stopping, starting and doing the autoscrolling
		**********************************************************/
		stopAutoScroll: function() {
			var self = this, el = this.element;

			clearInterval(el.data("autoScrollInterval"));
			el.data("autoScrollInterval", null);

			// Check to see which hotspots should be active
			// in the position where the scroller has stopped
			self._showHideHotSpots();

			self._trigger("autoScrollStopped");

		},
		startAutoScroll: function() {
			var self = this, el = this.element, o = this.options;

			self._showHideHotSpots();

			// Stop any running interval
			clearInterval(el.data("autoScrollInterval"));
			el.data("autoScrollInterval", null);

			// Callback
			self._trigger("autoScrollStarted");

			// Start interval
			el.data("autoScrollInterval", setInterval(function() {

				// If the scroller is not visible or
				// if the scrollable area is shorter than the scroll wrapper
				// any running autoscroll interval should stop.
				if (!(el.data("visible")) || (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth()))) {
					// Stop any running interval
					clearInterval(el.data("autoScrollInterval"));
					el.data("autoScrollInterval", null);
				}
				else {
					// Store the old scrollLeft value to see if the scrolling has reached the end
					el.data("previousScrollLeft", el.data("scrollWrapper").scrollLeft());


					switch (o.autoScrollDirection) {
						case "right":
							el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + o.autoScrollStep);
							if (el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) {
								self._trigger("autoScrollRightLimitReached");
								clearInterval(el.data("autoScrollInterval"));
								el.data("autoScrollInterval", null);
								self._trigger("autoScrollIntervalStopped");
							}
							break;

						case "left":
							el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - o.autoScrollStep);
							if (el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) {
								self._trigger("autoScrollLeftLimitReached");
								clearInterval(el.data("autoScrollInterval"));
								el.data("autoScrollInterval", null);
								self._trigger("autoScrollIntervalStopped");
							}
							break;

						case "backandforth":
							if (el.data("pingPongDirection") === "right") {
								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + (o.autoScrollStep));
							}
							else {
								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - (o.autoScrollStep));
							}

							// If the scrollLeft hasnt't changed it means that the scrolling has reached
							// the end and the direction should be switched
							if (el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) {
								if (el.data("pingPongDirection") === "right") {
									el.data("pingPongDirection", "left");
									self._trigger("autoScrollRightLimitReached");
								}
								else {
									el.data("pingPongDirection", "right");
									self._trigger("autoScrollLeftLimitReached");
								}
							}
							break;

						case "endlessloopright":
							// Get the width of the first element. When it has scrolled out of view,
							// the element swapping should be executed. A true/false variable is used
							// as a flag variable so the swapAt value doesn't have to be recalculated
							// in each loop.

							if (el.data("getNextElementWidth")) {
								if ((o.startAtElementId.length > 0) && (el.data("startAtElementHasNotPassed"))) {
									el.data("swapAt", $("#" + o.startAtElementId).outerWidth(true));
									el.data("startAtElementHasNotPassed", false);
								}
								else {
									el.data("swapAt", el.data("scrollableArea").children(":first").outerWidth(true));
								}

								el.data("getNextElementWidth", false);
							}

							// Do the autoscrolling
							el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + o.autoScrollStep);

							// Check to see if the swap should be done
							if (el.data("swapAt") <= el.data("scrollWrapper").scrollLeft()) {
								el.data("swappedElement", el.data("scrollableArea").children(":first").detach());
								el.data("scrollableArea").append(el.data("swappedElement"));
								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - el.data("swappedElement").outerWidth(true));
								el.data("getNextElementWidth", true);
							}
							break;
						case "endlessloopleft":
							// Get the width of the first element. When it has scrolled out of view,
							// the element swapping should be executed. A true/false variable is used
							// as a flag variable so the swapAt value doesn't have to be recalculated
							// in each loop.

							if (el.data("getNextElementWidth")) {
								if ((o.startAtElementId.length > 0) && (el.data("startAtElementHasNotPassed"))) {
									el.data("swapAt", $("#" + o.startAtElementId).outerWidth(true));
									el.data("startAtElementHasNotPassed", false);
								}
								else {
									el.data("swapAt", el.data("scrollableArea").children(":first").outerWidth(true));
								}

								el.data("getNextElementWidth", false);
							}

							// Do the autoscrolling
							el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - o.autoScrollStep);

							// Check to see if the swap should be done
							if (el.data("scrollWrapper").scrollLeft() === 0) {
								el.data("swappedElement", el.data("scrollableArea").children(":last").detach());
								el.data("scrollableArea").prepend(el.data("swappedElement"));
								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + el.data("swappedElement").outerWidth(true));
								el.data("getNextElementWidth", true);
							}
							break;
						default:
							break;

					}
				}
			}, o.autoScrollInterval));

		},
		restoreOriginalElements: function() {
			var self = this, el = this.element;

			// Restore the original content of the scrollable area
			el.data("scrollableArea").html(el.data("originalElements"));
			self.recalculateScrollableArea();
			self.moveToElement("first");
		},
		show: function() {
			var el = this.element;
			el.data("visible", true);
			el.show();
		},
		hide: function() {
			var el = this.element;
			el.data("visible", false);
			el.hide();
		},
		enable: function() {
			var el = this.element;

			// Set enabled to true
			el.data("enabled", true);
		},
		disable: function() {
			var el = this.element;

			// Clear all running intervals
			clearInterval(el.data("autoScrollInterval"));
			clearInterval(el.data("rightScrollInterval"));
			clearInterval(el.data("leftScrollInterval"));
			clearInterval(el.data("hideHotSpotBackgroundsInterval"));

			// Set enabled to false
			el.data("enabled", false);
		},
		destroy: function() {
			var el = this.element;

			// Clear all running intervals
			clearInterval(el.data("autoScrollInterval"));
			clearInterval(el.data("rightScrollInterval"));
			clearInterval(el.data("leftScrollInterval"));
			clearInterval(el.data("hideHotSpotBackgroundsInterval"));

			// Remove all element specific events
			el.data("scrollingHotSpotRight").unbind("mouseover");
			el.data("scrollingHotSpotRight").unbind("mouseout");
			el.data("scrollingHotSpotRight").unbind("mousedown");

			el.data("scrollingHotSpotLeft").unbind("mouseover");
			el.data("scrollingHotSpotLeft").unbind("mouseout");
			el.data("scrollingHotSpotLeft").unbind("mousedown");

			// Restore the original content of the scrollable area
			el.data("scrollableArea").html(el.data("originalElements"));

			// Remove the width of the scrollable area
			el.data("scrollableArea").removeAttr("style");
			el.data("scrollingHotSpotRight").removeAttr("style");
			el.data("scrollingHotSpotLeft").removeAttr("style");

			el.data("scrollWrapper").scrollLeft(0);
			el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible");
			el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible");
			el.data("scrollingHotSpotRight").hide();
			el.data("scrollingHotSpotLeft").hide();

			// Call the base destroy function
			$.Widget.prototype.destroy.apply(this, arguments);

		}
	});
})(jQuery);/**
 * Freeow!
 * Stylish, Growl-like message boxes
 *
 * Copyright (c) 2011 PJ Dietz
 * Version: 1.0.0
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * http://pjdietz.com/jquery-plugins/freeow/
 */
 
/*global setTimeout, jQuery */

(function ($) {

    "use strict";
     
    var Freeow;
    
    Freeow = function (title, message, options) {
        
        var startStyle, i, u;
        
        // Merge the options.
        this.options = $.extend({}, $.fn.freeow.defaults, options);
                    
        // Build the element with the template function.
        this.element = $(this.options.template(title, message));
        
        // Set its initial style to the startStyle or hideStyle.
        if (this.options.startStyle) {
            startStyle = this.options.startStyle;
        }
        else {
            startStyle = this.options.hideStyle;
        }
        this.element.css(startStyle);

        // Store a reference to it in the data.
        this.element.data("freeow", this);
        
        // Add to the element.
        for (i = 0, u = this.options.classes.length; i < u; i += 1) {
            this.element.addClass(this.options.classes[i]);
        }
            
        // Bind the event handler.
        this.element.click(this.options.onClick);
        this.element.hover(this.options.onHover);
        
        // Default. Set to true in show() if there's an autoHideDelay.
        this.autoHide = false;
        
    };
    
    Freeow.prototype = {
        
        attach: function (container) {
            $(container).prepend(this.element);
            this.show();
        }, 
        
        show: function () {
            
            var opts, self, fn, delay;
            
            opts = { 
                duration: this.showDuration
            };
            
            // If an auto hide delay is set, create a callback function and
            // set it to fire after the auto hide time expires.
            if (this.options.autoHide && this.options.autoHideDelay > 0) {
      
                this.autoHide = true;
                
                self = this;
                delay = this.options.autoHideDelay;
                fn = function () { 
                    if (self.autoHide) {
                        self.hide(); 
                    }
                };
                
                opts.complete = function () {
                    setTimeout(fn, delay);
                };
                    
            }    
            
            // Animate to the "show" style.
            // Optionally, set the auto hide function to fire on a delay.
            this.element.animate(this.options.showStyle, opts);

        },
        
        hide: function () {
            
            var self = this; // Keep "this" from current scope;
                     
            this.element.animate(this.options.hideStyle, {
                duration: this.options.hideDuration,
                complete: function () {
                    self.destroy();
                }    
            });

        },
        
        destroy: function () {
            
            // Remove the Freeow instance from the element's data.
            this.element.data("freeow", undefined);
            
            // Remove the element from the DOM.
            this.element.remove();
            
        }
        
    };
    
    // Extend jQuery ----------------------------------------------------------- 

    if (typeof $.fn.freeow === "undefined") {
     
        $.fn.extend({  
            
            freeow: function (title, message, options) {
            
                return this.each(function () {
                    
                    var f;
                    
                    f = new Freeow(title, message, options);
                    f.attach(this);
                            
                }); // return this.each()
            
            } // freeow()
        
        }); // $.fn.extend()
     
        // Configuration Defaults. 
        $.fn.freeow.defaults = {
            
            autoHide: true,
            autoHideDelay: 3000,
            classes: [],
            startStyle: null,
            showStyle: {opacity: 1.0},
            showDuration: 250,
            hideStyle: {opacity: 0.0},
            hideDuration: 500,
            
            onClick: function (event) {
                $(this).data("freeow").hide();
            },
            
            onHover: function (event) {
                $(this).data("freeow").autoHide = false;
            },
               
            template: function (title, message) {
                
                var e;
                
                e = [
                    '<div>',
                    '<div class="background">',
                    '<div class="content">',
                    '<h2>' + title + '</h2>',
                    '<p>' + message + '</p>',
                    '</div>',
                    '</div>',
                    '<span class="icon"></span>',
                    '<span class="close"></span>',
                    '</div>'
                ].join("");
                
                return e;
            }
             
        }; // $.fn.freeow.defaults
        
    } // if undefined        
            
}(jQuery));

/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true */(function($) {
	$.fn.extend({
		searchBoxActions: function(options) {

			var defaults = {
				minLength: 2,
				limit: 20
			};
			
			var options = $.extend(defaults, options);				
			
			return this.each(function(){
				var $this = $(this);

				var searchBox = $('#regular_search', $this);
				var searchByToggle = $('.stateKeyToggle', $this);
				var clearSearchLink = $('.clearSearchLink', $this);
				var keywordSearchBox = $('#keyword_search', $this);
				var keywordInput = $("#keyword_input", $this);
				var stateInput = $("input#state", $this);
				var cityInput = $("input#city", $this);
				var categoryInput = $("input#category", $this);
				
				$('.roundedInputContainer', $this).corner('round');
				
				searchByToggle.click(function(){
					keywordSearchBox.toggle();
					searchBox.toggle();
					
					if(keywordSearchBox.css('display') == 'block') {
						keywordInput.focus();
					} else {
						stateInput.focus();
					}
					
					return false;
				});
				
				clearSearchLink.click(function(){
					keywordSearchBox.hide();
					searchBox.show();
					
					keywordInput.val('');
					cityInput.clearInput();
					stateInput.clearInput();
					categoryInput.clearInput();
					
					return false;
				})
				
				stateInput.richTextSearchBox("/ajax/getstates", {
					inputName: 'state',
					minLength: 1,
					defaultValue: options.state.value,
					defaultText: options.state.text,
					nextItemId: '#city',
					autoFocus: true
				});
				
				categoryInput.richTextSearchBox("/ajax/getcategories", {
					minLength: 1,
					defaultValue: options.category.value,
					defaultText: options.category.text,
					inputName: 'category_id'
				})
				.click(function(){
					keywordInput.val('');
				});
				
				cityInput.richTextSearchBox("/ajax/getcities", {
					inputName: 'cit',
					useCache: false,
					dependencyId: 'state',
					defaultValue: options.city.value,
					defaultText: options.city.text,
					nextItemId: '#category'
				});
				
				
				if(options.keyword != '') {
					keywordSearchBox.toggle();
					searchBox.toggle();
					keywordInput.focus();
				}
			})
		}
	})
})(jQuery);
(function($) {
	$.fn.extend({
		richTextSearchBox: function(urlOrData, options) {
			var isUrl = typeof urlOrData == 'string';
			
			options = $.extend({}, $.richTextSearchBox.defaults, {
				source: isUrl ? urlOrData : null,
				data: isUrl ? null : urlOrData,
				delay: isUrl ? $.richTextSearchBox.defaults.delay : 10,
				max: options && ! options.scroll ? 10 : 150
			}, options);
			
			return this.each(function() {
				new $.richTextSearchBox(this, options);
			})

		},
		setOptions: function(options) {
			return this.trigger("setOptions", options);
		},
		flushCache: function() {
			return this.trigger("flushCache");
		},
		clearInput: function(options) {
			return this.trigger("clearInput", options);
		}
	});
	
	$.richTextSearchBox = function(input, options) {
		var $this = $(input);
		
		$this.attr('autocomplete', 'off').addClass(options.inputClass);
		
		var KEY = {
			UP: 38,
			DOWN: 40,
			DEL: 46,
			TAB: 9,
			RETURN: 13,
			ESC: 27,
			COMMA: 188,
			PAGEUP: 33,
			PAGEDOWN: 34,
			BACKSPACE: 8
		}
		
		var timeout;
		var previousValue = "";
		var hasFocus = 0;
		var lastKeyPressCode;
		var itemlist = $.richTextSearchBox.Items(options, $this, selectCurrent);
		var cache = $.richTextSearchBox.Cache(options);
		
		var blockSubmit;

		var stateInput = $("#search_box input#state");
		options.uniqueId = generateUniqueId();
		
		$.browser.opera && ($this.form).bind("submit.richTestSearchBox", function() {
			if (blockSubmit) {
				blockSubmit = false;
				return false;
			}
		});
		
		$this.next('.roundedInputArrow').bind('click', function(){
			if (itemlist.visible()) {
				$this.prev('div .active').removeClass('active').addClass('inactive');
				hideResults();
			} else {
				clearTimeout(timeout);
				timeout = setTimeout(
					updateList, 
					options.delay
				);
			}
		});

		$this.bind('keydown', function(event){
			
			lastKeyPressCode = event.keyCode;
			switch(event.keyCode) {
				case KEY.UP:
					event.preventDefault();
					if (itemlist.visible()) {
						itemlist.prev();
					} else {
						$this.click();
					}

				break; case KEY.DOWN:							
					event.preventDefault();
					if (itemlist.visible()) {
						itemlist.next();
					} else {
						$this.click();
					}

				break; 
				case KEY.TAB:
				case KEY.RETURN:
					if (selectCurrent()) {
						event.preventDefault();
						blockSubmit = true;

						return false;
					}

				break; case KEY.ESC:
					itemlist.hide();
				
				break; default:
					clearTimeout(timeout);
					timeout = setTimeout(
						updateList, 
						options.delay
					);
			}

		})
		.focus(function(){
			hasFocus++;
		})
		.blur(function(){
			hasFocus = 0;
			hideResults();
		})
		.click(function(){
			if(hasFocus++ > 1 && !itemlist.visible()) {
				updateList();
			}
		})
		.bind("flushCache", function() {
			cache.flush();
		})
		.bind("clearInput", function() {
			$.extend(options, arguments[1]);
			
			if(options.inputName && $('input[name=' + options.inputName + ']').length > 0) {
				$('input[name=' + options.inputName + ']').val('');
			}
			
			$this.val('');
			updateInput();
		})
		.bind("setOptions", function() {
			$.extend(options, arguments[1]);
			
			if ("data" in arguments[1]) {
				cache.populate();
			}
		});
		
		function generateUniqueId() {
			var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
			
			var n = 0,
				i = 0,
				uniqueId = '',
				len = 9;
			
			for (var x = 1; x < len; x++) {
				n = Math.random() * 62;
				i = Math.round(n);
				uniqueId += str.charAt(i);
			}
			
			return uniqueId;
		}
		
		function selectCurrent(selectedListItem){
			var select = itemlist.selected();
			if(!select)
				return false;

			$this.val(select.text());
			updateInput(select);
			
			$this.prev('div .active').removeClass('active').addClass('inactive');
			hideResults();
			return true;
		}
		
		function hideResults() {
			
			clearTimeout(timeout);
			
			$this.trigger('flushCache');
		}
		
		function updateInput(selectedListItem) {
			if(options.inputName.length == 0){
				return false;
			}
			
			if(options.inputCreated == true) {
				if(selectedListItem && selectedListItem.length > 0) {
					$('input[name=' + options.inputName + ']').val(selectedListItem.attr('dataValue'));
					selectedListItem.removeClass('hasFocus');
				}
			} else {
				var hiddenInput = $("<input></input>")
				.attr('type', 'hidden')
				.attr('name', options.inputName);
				
				if(selectedListItem && selectedListItem.length > 0) {
					selectedListItem.removeClass('hasFocus');
					hiddenInput.val(selectedListItem.attr('dataValue'))
				}

				$(input).before(hiddenInput);
				options.inputCreated = true;
			}
		}
		
		function updateList(defaultValue) {
			options.key = $this.val();
			
			options.cacheKey = stateInput.val() + "-" + options.key;
			if (options.useCache && options.cache[options.cacheKey]) {
				if (!cache.uniqueData(options.cacheKey)) {
					return false;
				}
				
				itemlist.build(options.cache[options.cacheKey], options.key);
				itemlist.show();
				
			} else {
				var additionalData = '';
				var value = '-';
				
				if(options.dependencyId != '' && $('input#' + options.dependencyId)
						&& ($('input#' + options.dependencyId).length > 0)) {
					additionalData = $('input#' + options.dependencyId).val();
				}
				
				if (options.key != '') {
					value = options.key;
				}
					
				// search for data
				$.ajax({
					mode: 'abort',
					port: 'richTextSearchBox' + options.uniqueId,
					type: options.type,
					url: options.source + "/" + value + "/" + additionalData,
					dataType: options.dataType,
					success: function(data){						
						if (data == null || !cache.uniqueData(data)) {
							return false;
						}
						
						itemlist.emptyList();
						
						// Cache results
						//cache.add(options.cacheKey, data);
						
						// Setup list results and show
						itemlist.build(data, options.key);
						if (defaultValue == 'default'){
							selectCurrent();
						} else {
							itemlist.show();
						}
					},
					error: function(){
						itemlist.emptyList();
					}
				});
			}
			
			previousValue = options.key;
		}
		
		updateInput();
		
		if(options.defaultText != '') {
			$this.val(options.defaultText);					
		}
		
		if(options.defaultValue != '') {
			options.key = options.defaultValue;
			
			// If there is default text and value, cancel the lookup
			// and update the field
			if (options.defaultText != '') {
				$('input[name=' + options.inputName + ']').val(options.defaultValue);
			} else {
				updateList('default');
			}
		}
		
		if(options.autoFocus) {
			$this.focus();
		}
	};

	// List Item Functions
	$.richTextSearchBox.Items = function(options, input, select) {	
		var CLASSES = {
			ACTIVE: 'hasFocus',
			ITEMLINK: 'richTextSearchListItemLink',
			LIST: 'richTextSearchList',
			LISTITEM: 'richTextSearchListItem'
		}
		
		var listItems,
			active = false,
			data,
			term = "",
			needsInit = true,
			element,
			list;	
		
		function init(){
			if(!needsInit)
				return;
			
			element = $('<div></div>')
			.addClass(options.resultClass)
			.addClass('inactive');
			
			list = $('<ul></ul>')
			.addClass(CLASSES.LIST)
			.addClass(options.uniqueId)
			.css('position', 'absolute');

			$('ul.' + options.uniqueId + ' a.' + CLASSES.ITEMLINK + '.' + CLASSES.ACTIVE)
			.live('click', function(){
				select($(this));
				
				if (options.nextItemId != '' && $(options.nextItemId) && $(options.nextItemId).length > 0) {
					$(options.nextItemId).focus().select();
				}
				
				return false;
			});
			
			$('ul.' + options.uniqueId + ' a.' + CLASSES.ITEMLINK).live('mouseover', function(){
				$('ul.' + options.uniqueId + ' a.' + CLASSES.ITEMLINK).removeClass(CLASSES.ACTIVE);
				$(this).addClass(CLASSES.ACTIVE);
			})
			
			needsInit = false;
		}
		
		function moveSelect(direction){
			var activeItem = $('.active ul li a.' + CLASSES.ITEMLINK + '.' + CLASSES.ACTIVE);
			var selectIndex = $('.active ul li a.' + CLASSES.ITEMLINK + '.' + CLASSES.ACTIVE).parent().prevAll().length;

			$(".active ul li a").removeClass(CLASSES.ACTIVE);
			//alert(selectIndex);
			if (activeItem.length == 0) {
				if (direction) {
					$('.active a.' + CLASSES.ITEMLINK + ':first').addClass('hasFocus');
				} else {
					$('.active a.' + CLASSES.ITEMLINK + ':last').addClass('hasFocus');
				}
			} else {
				if (direction) {
					if($(".active ul li:nth-child(" + (++selectIndex) + ")").length == 0) {
						$('.active a.' + CLASSES.ITEMLINK + ':last').addClass('hasFocus');
					} else {
						$(".active ul li:nth-child(" + ++selectIndex + ") a").addClass('hasFocus');
					}
				} else {
					if($(".active ul li:nth-child(" + (selectIndex) + ")").length == 0) {
						$('.active a.' + CLASSES.ITEMLINK + ':last').addClass('hasFocus');
					} else {
						$(".active ul li:nth-child(" + selectIndex + ") a").addClass('hasFocus');
					}
				}
			}
		}
		
		function buildList(){		
			listCount = 0;
			
			// @todo: change how this loops through so it only does "limit" amount of times
			//for(var i=0; i < options.maxLength; i++) {
			$.each(data, function(index, obj){
				if(listCount < options.limit) {					
					$('<li></li>')
					.attr('class', CLASSES.LISTITEM)
					.html(
						$("<a></a>")
						.attr('href', '#')
						.attr('class', CLASSES.ITEMLINK)
						.attr('dataValue', obj.value)
						.html(obj.text)
					)
					.appendTo(list);
					
					listCount++;
				}
			});
			
			listItems = list.find('a');
			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);

			input.before(element.html(list).removeClass('inactive').addClass('active'));
			
		}
		
		return {
			CLASSES: CLASSES,
			build: function(d, q) {
				init();
				data = d;
				term = q;
				buildList();
			},
			hide: function() {
				element.removeClass('active').addClass('inactive');
				active = false;
			},
			show: function() {
				element.removeClass('inactive').addClass('active');
				active = true;
			},
			next: function() {
				moveSelect(true)
			},
			prev: function() {
				moveSelect(false)
			},
			visible: function() {
				return $('.active .' + options.uniqueId)
				 && $('.active .' + options.uniqueId).length > 0;
			},
			selected: function() {
				//var selected = listItems && listItems.filter("." + CLASSES.ACTIVE);
				var selected = $('.active .' + options.uniqueId + ' .' + CLASSES.ITEMLINK + '.' + CLASSES.ACTIVE)
					.removeClass(CLASSES.ACTIVE);
				return (selected && selected.length > 0) ? selected : false;
			},
			emptyList: function() {
				//list && list.empty();
				$('li.' + CLASSES.LISTITEM).remove();
			},
			unbind: function() {
				element && element.remove();
			}
		}
	};

	$.richTextSearchBox.defaults = {
		uniqueId: '',
		key: '',
		source: '',
		defaultText: '',
		defaultValue: '',
		limit: 20,
		delay: 300,
		minLength: 2,
		type: 'GET',
		dataType: 'json',
		cache: [],
		cacheLength: 10,
		data: null,
		useCache: true,
		dependencyId: '',
		visible: false,
		autoFocus: false,
		
		LIST_ITEM_CLASS: 'richTextSearchListItem',
		
		inputClass: 'richTextSearchBox',
		resultClass: 'richTextSearchResults'
	};
	
	$.richTextSearchBox.Cache = function(options) {
		var data = {},
		length = 0,
		current;
		
		function populate(value){
			current = value;
		}
		
		function add(q, value){
			q = q.toLowerCase();
			
			if(length > options.cacheLength) {
				flush();
			}
			
			if(!data[q]){
				length++;
			}
			
			data[q] = value;
			options.cache = data;
			populate(value);
		}
		
		function flush(){
			var data = {},
			length = 0;
		}
		
		setTimeout(populate, 25);
		
		return {
			flush: flush,
			populate: populate,
			add: add,
			uniqueData: function(data) {
				return (data != '') && (current != data);
			}
		};
		
	}
})(jQuery);
/*!
 * jQuery corner plugin: simple corner rounding
 * Examples and documentation at: http://jquery.malsup.com/corner/
 * version 2.12 (23-MAY-2011)
 * Requires jQuery v1.3.2 or later
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * Authors: Dave Methvin and Mike Alsup
 */

/**
 *  corner() takes a single string argument:  $('#myDiv').corner("effect corners width")
 *
 *  effect:  name of the effect to apply, such as round, bevel, notch, bite, etc (default is round). 
 *  corners: one or more of: top, bottom, tr, tl, br, or bl.  (default is all corners)
 *  width:   width of the effect; in the case of rounded corners this is the radius. 
 *           specify this value using the px suffix such as 10px (yes, it must be pixels).
 */
;(function($) { 

var style = document.createElement('div').style,
    moz = style['MozBorderRadius'] !== undefined,
    webkit = style['WebkitBorderRadius'] !== undefined,
    radius = style['borderRadius'] !== undefined || style['BorderRadius'] !== undefined,
    mode = document.documentMode || 0,
    noBottomFold = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8),

    expr = $.browser.msie && (function() {
        var div = document.createElement('div');
        try { div.style.setExpression('width','0+0'); div.style.removeExpression('width'); }
        catch(e) { return false; }
        return true;
    })();

$.support = $.support || {};
$.support.borderRadius = moz || webkit || radius; // so you can do:  if (!$.support.borderRadius) $('#myDiv').corner();

function sz(el, p) { 
    return parseInt($.css(el,p))||0; 
};
function hex2(s) {
    s = parseInt(s).toString(16);
    return ( s.length < 2 ) ? '0'+s : s;
};
function gpc(node) {
    while(node) {
        var v = $.css(node,'backgroundColor'), rgb;
        if (v && v != 'transparent' && v != 'rgba(0, 0, 0, 0)') {
            if (v.indexOf('rgb') >= 0) { 
                rgb = v.match(/\d+/g); 
                return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
            }
            return v;
        }
        if (node.nodeName.toLowerCase() == 'html')
            break;
        node = node.parentNode; // keep walking if transparent
    }
    return '#ffffff';
};

function getWidth(fx, i, width) {
    switch(fx) {
    case 'round':  return Math.round(width*(1-Math.cos(Math.asin(i/width))));
    case 'cool':   return Math.round(width*(1+Math.cos(Math.asin(i/width))));
    case 'sharp':  return width-i;
    case 'bite':   return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
    case 'slide':  return Math.round(width*(Math.atan2(i,width/i)));
    case 'jut':    return Math.round(width*(Math.atan2(width,(width-i-1))));
    case 'curl':   return Math.round(width*(Math.atan(i)));
    case 'tear':   return Math.round(width*(Math.cos(i)));
    case 'wicked': return Math.round(width*(Math.tan(i)));
    case 'long':   return Math.round(width*(Math.sqrt(i)));
    case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
    case 'dogfold':
    case 'dog':    return (i&1) ? (i+1) : width;
    case 'dog2':   return (i&2) ? (i+1) : width;
    case 'dog3':   return (i&3) ? (i+1) : width;
    case 'fray':   return (i%2)*width;
    case 'notch':  return width; 
    case 'bevelfold':
    case 'bevel':  return i+1;
    case 'steep':  return i/2 + 1;
    case 'invsteep':return (width-i)/2+1;
    }
};

$.fn.corner = function(options) {
    // in 1.3+ we can fix mistakes with the ready state
    if (this.length == 0) {
        if (!$.isReady && this.selector) {
            var s = this.selector, c = this.context;
            $(function() {
                $(s,c).corner(options);
            });
        }
        return this;
    }

    return this.each(function(index){
        var $this = $(this),
            // meta values override options
            o = [$this.attr($.fn.corner.defaults.metaAttr) || '', options || ''].join(' ').toLowerCase(),
            keep = /keep/.test(o),                       // keep borders?
            cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]),  // corner color
            sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]),  // strip color
            width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10, // corner width
            re = /round|bevelfold|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dogfold|dog|invsteep|steep/,
            fx = ((o.match(re)||['round'])[0]),
            fold = /dogfold|bevelfold/.test(o),
            edges = { T:0, B:1 },
            opts = {
                TL:  /top|tl|left/.test(o),       TR:  /top|tr|right/.test(o),
                BL:  /bottom|bl|left/.test(o),    BR:  /bottom|br|right/.test(o)
            },
            // vars used in func later
            strip, pad, cssHeight, j, bot, d, ds, bw, i, w, e, c, common, $horz;
        
        if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
            opts = { TL:1, TR:1, BL:1, BR:1 };
            
        // support native rounding
        if ($.fn.corner.defaults.useNative && fx == 'round' && (radius || moz || webkit) && !cc && !sc) {
            if (opts.TL)
                $this.css(radius ? 'border-top-left-radius' : moz ? '-moz-border-radius-topleft' : '-webkit-border-top-left-radius', width + 'px');
            if (opts.TR)
                $this.css(radius ? 'border-top-right-radius' : moz ? '-moz-border-radius-topright' : '-webkit-border-top-right-radius', width + 'px');
            if (opts.BL)
                $this.css(radius ? 'border-bottom-left-radius' : moz ? '-moz-border-radius-bottomleft' : '-webkit-border-bottom-left-radius', width + 'px');
            if (opts.BR)
                $this.css(radius ? 'border-bottom-right-radius' : moz ? '-moz-border-radius-bottomright' : '-webkit-border-bottom-right-radius', width + 'px');
            return;
        }
            
        strip = document.createElement('div');
        $(strip).css({
            overflow: 'hidden',
            height: '1px',
            minHeight: '1px',
            fontSize: '1px',
            backgroundColor: sc || 'transparent',
            borderStyle: 'solid'
        });
    
        pad = {
            T: parseInt($.css(this,'paddingTop'))||0,     R: parseInt($.css(this,'paddingRight'))||0,
            B: parseInt($.css(this,'paddingBottom'))||0,  L: parseInt($.css(this,'paddingLeft'))||0
        };

        if (typeof this.style.zoom != undefined) this.style.zoom = 1; // force 'hasLayout' in IE
        if (!keep) this.style.border = 'none';
        strip.style.borderColor = cc || gpc(this.parentNode);
        cssHeight = $(this).outerHeight();

        for (j in edges) {
            bot = edges[j];
            // only add stips if needed
            if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
                strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
                d = document.createElement('div');
                $(d).addClass('jquery-corner');
                ds = d.style;

                bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);

                if (bot && cssHeight != 'auto') {
                    if ($.css(this,'position') == 'static')
                        this.style.position = 'relative';
                    ds.position = 'absolute';
                    ds.bottom = ds.left = ds.padding = ds.margin = '0';
                    if (expr)
                        ds.setExpression('width', 'this.parentNode.offsetWidth');
                    else
                        ds.width = '100%';
                }
                else if (!bot && $.browser.msie) {
                    if ($.css(this,'position') == 'static')
                        this.style.position = 'relative';
                    ds.position = 'absolute';
                    ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
                    
                    // fix ie6 problem when blocked element has a border width
                    if (expr) {
                        bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
                        ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"');
                    }
                    else
                        ds.width = '100%';
                }
                else {
                    ds.position = 'relative';
                    ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' : 
                                        (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';                
                }

                for (i=0; i < width; i++) {
                    w = Math.max(0,getWidth(fx,i, width));
                    e = strip.cloneNode(false);
                    e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
                    bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
                }
                
                if (fold && $.support.boxModel) {
                    if (bot && noBottomFold) continue;
                    for (c in opts) {
                        if (!opts[c]) continue;
                        if (bot && (c == 'TL' || c == 'TR')) continue;
                        if (!bot && (c == 'BL' || c == 'BR')) continue;
                        
                        common = { position: 'absolute', border: 'none', margin: 0, padding: 0, overflow: 'hidden', backgroundColor: strip.style.borderColor };
                        $horz = $('<div/>').css(common).css({ width: width + 'px', height: '1px' });
                        switch(c) {
                        case 'TL': $horz.css({ bottom: 0, left: 0 }); break;
                        case 'TR': $horz.css({ bottom: 0, right: 0 }); break;
                        case 'BL': $horz.css({ top: 0, left: 0 }); break;
                        case 'BR': $horz.css({ top: 0, right: 0 }); break;
                        }
                        d.appendChild($horz[0]);
                        
                        var $vert = $('<div/>').css(common).css({ top: 0, bottom: 0, width: '1px', height: width + 'px' });
                        switch(c) {
                        case 'TL': $vert.css({ left: width }); break;
                        case 'TR': $vert.css({ right: width }); break;
                        case 'BL': $vert.css({ left: width }); break;
                        case 'BR': $vert.css({ right: width }); break;
                        }
                        d.appendChild($vert[0]);
                    }
                }
            }
        }
    });
};

$.fn.uncorner = function() { 
    if (radius || moz || webkit)
        this.css(radius ? 'border-radius' : moz ? '-moz-border-radius' : '-webkit-border-radius', 0);
    $('div.jquery-corner', this).remove();
    return this;
};

// expose options
$.fn.corner.defaults = {
    useNative: true, // true if plugin should attempt to use native browser support for border radius rounding
    metaAttr:  'data-corner' // name of meta attribute to use for options
};
    
})(jQuery);

