/*
 *  
 * jQuery extensions by plentySystems
 * 
 *
 * 
 * HowTo ...
 * 
 * ...definine a custom function:
 * 
 * jQuery.fn.FUNCTIONNAME = function()
 *                          {
 *                              return jQuery(this).each(
 *                                  function()
 *                                  {
 *                                      // "jQuery(this)" is the jquery object
 *                                      jQuery(this).doSomething();
 *                                  });
 *                          };
 * 
 */


/*
 * Lets element(s) blink
 */
jQuery.fn.blink =   function(iBlinkCount)
                    {
                        return jQuery(this).each(
                            function()
                            {
                                // speed in ms
                                var speed = 250;
                                
                                // count of blinks
                                if(iBlinkCount > 0)
                                {
                                    var count = iBlinkCount;
                                }
                                else
                                {
                                    var count = 3;
                                }
                                
                                for(var i=0;i<count;i++)
                                {
                                    this.fadeOut(speed).fadeIn(speed);
                                }
                            });
                    };

/*
 * Move element(s) to center (chainable), centers both on param unset
 */
jQuery.fn.center =  function (from)
                    {
                        return jQuery(this).each(
                            function()
                            {
                                // top pos
                                var top     = (jQuery(window).height() - jQuery(this).height()) / 2 + jQuery(window).scrollTop() + "px";
                                // left pos
                                var left    = (jQuery(window).width() - jQuery(this).width()) / 2 + jQuery(window).scrollLeft() + "px";
                                
                                switch(from)
                                {
                                // adjust top
                                case 'top':
                                    jQuery(this).css("top", top);
                                    break;
                                    // adjust left
                                case 'left':
                                    jQuery(this).css("left", left);
                                    break;
                                    // default adjust both
                                default:
                                    jQuery(this).css("top", top);
                                jQuery(this).css("left", left);
                                break;
                                }
                            });
                    };

/*
 * Applies a for the document unique id to element(s)
 */
jQuery.fn.setUniqueId = function(prefix)
                        {
                            return jQuery(this).each(
                                function()
                                {
                                    var sUniqueId = '';
                                    var sUniqueId = '';
                                    var iMultiplier = 3;
                                    var iRandomNum  = 0;
                                    
                                    var elExists = true;
                                    
                                    // set default id
                                    if(!prefix)
                                    {
                                        prefix = 'id_';
                                    }
                                    
                                    do
                                    {
                                        // get more room for new IDs on repeat
                                        iMultiplier++;
                                        // get random number
                                        iRandomNum = Math.ceil(Math.random() * iMultiplier);
                                        
                                        // build new id
                                        sUniqueId = prefix + iRandomNum;
                                    }
                                    // repeat if id already exists
                                    while(jQuery('#'+sUniqueId).exists());
                                    
                                    // set id to element
                                    this.attr('id', sUniqueId);
                                });
                        };

/**
 * Extending the jQuery namespace by defining a new function jQuery.plentyRequest() for AJAX calls
 * (estafilarakis)
 */
jQuery.extend({
    plentyRequest : plentyAjaxRequest2
});

/**
 * 
 * Custom effect for tabs
 */
jQuery.tools.tabs.addEffect(    "plenty_tabs",
                        function(tabIndex, done)
                        {
                            this.getPanes()
                                // slide up all panes
                                .slideUp("slow")
                                // get the selected tab
                                .eq(tabIndex)
                                // slide down the selected
                                .slideDown("slow");
                            
                            // effect is done
                            done.call();
                        });

/*
 * Custom datepicker
 */
jQuery.fn.plentyDateInput =     function() 
                                {
                                    return this.each(   function()
                                                        {
                                                             jQuery(this).dateinput({
                                                                                format: "dd.mm.yyyy ",
                                                                                selectors: true,                
                                                                                min: -1
                                                                                });
                                                        });
                                };


/**
 * New plugin 'plentyPortlet'.
 * 
 * The HTML structur should be:
 * <div class="PlentyGuiHeaderPane">
 *     <div class="PlentyGuiHeader">...</div>
 *     <div class="PlentyGuiContent">...</div>
 * </div>
 * 
 * The javascript call to build the portlet is:
 * jQuery(".PlentyGuiHeaderPane").plentyPortlet();
 * 
 * The plugin adds a button with the CSS class 'PlentyGuiPortletButton'
 * to open and close the content of the portlet.
 * 
 * @author estafilarakis@plentySystems
 * 
 */
jQuery.fn.plentyPortlet = function(conf) {
    if(typeof(conf) != "object")
    {
        conf = { opened : false };
    }
    return this.each(function(){
        var button = jQuery('<div class="PlentyGuiPortletButton">&nbsp;</div>');
        var portlet = jQuery(this);
        var portletContent = portlet.find('> .PlentyGuiContent');
        if(conf.opened == true || conf.opened == "true")
        {
            portletContent.addClass("PortletFirstOpened");
        }
        portlet.
            addClass('PlentyGuiPortlet').
            find('> .PlentyGuiContent').
                addClass('PlentyGuiPortletContent').
                end().
            find('> .PlentyGuiHeader').
                addClass('PlentyGuiPortletHeader').
                before(button);
        button.toggle(
                function(){
                    jQuery(this).addClass('PlentyGuiPortletOpened');
                    portletContent.slideDown();
                    if(typeof(conf.onOpen) == 'function')
                    {
                        conf.onOpen(portlet.find('> .PlentyGuiPortletContent'));
                    }
                },
                function(){
                    jQuery(this).removeClass('PlentyGuiPortletOpened');
                    portletContent.slideUp();
                    if(typeof(conf.onClose) == 'function')
                    {
                        conf.onClose(portlet.find('> .PlentyGuiPortletContent'));
                    }
                }
        );
        if(conf.opened == true || conf.opened == "true")
        {
            button.click();
        }
    });
}

/**
 * New Plugin 'plentyOverlay'.
 * 
 * This plugin combines the Tools-Plugin 'overlay' with the UI-Plugin 'draggable'.
 * So you get a draggable overlay.
 * 
 * @author estafilarakis@plentySystems
 * 
 */
jQuery.fn.plentyOverlay = function(conf){
    return this.each(function(){
        // Bilde Overlay (über den Trigger)
        jQuery(this).
            overlay({   onLoad : conf.onLoad,
                        onBeforeLoad : conf.onBeforeLoad,
                        oneInstance : (conf.oneInstance ? conf.oneInstance : false),
                        left : conf.left ? conf.left : '20%',
                        top : conf.top ? conf.top : '10%',
                        closeOnClick : (conf.closeOnClick ? true : false)
                    });
        // Mache das Overlay verschiebbar
        jQuery(jQuery(this).attr("rel")).
            draggable({ appendTo : 'body',
                        //containment : 'window',
                        scroll : true,
                        stack : '.plenty_overlay',
                        handle : '> .title_bar'
                    });
    });
}

/**
 * New Plugin 'plentyDateInput'
 * 
 * This Plugin uses the jQuery Tools dateinput-Plugin and fixes
 * the problems with multiple dateinput instances in different
 * AJAX loaded contents (e.g. portlets).
 * 
 * @author estafilarakis@plentySystems
 * 
 */
jQuery.fn.plentyDateInput = function(conf){
    return this.each(function(){
        jQuery(this).
            bind("focus",function(){
                jQuery(this).after(jQuery("#calroot"));
            }).
            dateinput(conf);
    });
}

/**
 * New Plugin 'plentyQFAMS'
 * 
 * @author estafilarakis@plentySystems
 */
jQuery.fn.plentyQFAMS = function(conf)
{
    return this.each(function(){
        var leftSelect = jQuery(this).find(".QFAMSLeftSelect").eq(0);
        var rightSelect = jQuery(this).find(".QFAMSRightSelect").eq(0);
        var addFunction = function()
        {
            var newOptions = leftSelect.find("option:selected");
            /*if(newOptions.length > 0)
            {
                rightSelect.find("option.emptyOption").remove();
            }*/
            rightSelect.append(newOptions);
        }
        var removeFunction = function()
        {
            var newOptions = rightSelect.find("option:selected");//.filter(":not(.emptyOption)");
            leftSelect.append(newOptions);
            /*if(rightSelect.find("option").length == 0)
            {
                rightSelect.append("<option value=\"\" class=\"emptyOption\">&nbsp;</option>");
            }*/
        }
        /*if(rightSelect.find("option").length==0)
        {
            rightSelect.append("<option value=\"\" class=\"emptyOption\">&nbsp;</option>");
        }*/
        leftSelect.dblclick(addFunction);
        rightSelect.dblclick(removeFunction);
        jQuery(this).
            find(".QFAMSAddIcon").
                click(addFunction).
                end().
            find(".QFAMSRemoveIcon").
                click(removeFunction).
                end();
    });
}

/**
 * New Plugin 'plentyFarbtastic'
 * 
 * @author estafilarakis@plentySystems
 * 
 * @uses The jQuery farbtastic plugin 
 */
jQuery.fn.plentyFarbtastic = function()
{
    /*
     * Load the farbtastic script if not allready loaded.
     */
    jQuery.getPlentyScriptOnce("/tpl/global/jquery/farbtastic/farbtastic.js");
    
    /*
     * Split hexstring color to RGB array
     */
    function getRGB(color)
    {
        if(color.length == 7 && color.substring(0,1) == "#")
        {
            return [parseInt('0x'+color.substring(1,3))/255, parseInt('0x'+color.substring(3,5))/255, parseInt('0x'+color.substring(5,7))/255];
        }
        else if(color.length == 4 && color.substring(0,1) == "#")
        {
            return [parseInt('0x'+color.substring(1,2))/15, parseInt('0x'+color.substring(2,3))/15, parseInt('0x'+color.substring(3,4))/15];
        }
        return [255,255,255];
    }
    
    /*
     * Returns the foreground color for the given background color.
     */
    function getFgColor(bgColor)
    {
        var r = bgColor[0], g = bgColor[1], b = bgColor[2];
        var min = Math.min(r, Math.min(g, b));
        var max = Math.max(r, Math.max(g, b));
        return (min+max)/2 > 0.5 ? '#000' : '#fff';
    }
    
    return this.each(function(){
        var picker = jQuery("<div class=\"plentyFarbtastic\" id=\""+jQuery(this).attr("id")+"_farbtastic\"></div>");
        
        jQuery(this).
            after(picker.hide()).
            focusin(function(){
                picker.farbtastic(this).show();
            }).
            focusout(function(){
                picker.farbtastic(this).hide();
            }).
            css({
                backgroundColor: jQuery(this).val(),
                color: getFgColor(getRGB(jQuery(this).val()))
            });
    });
}

/**
 * Loads a Javascript file with the given URL syncron.
 */
jQuery.getPlentyScript = function(url){
    jQuery.ajax({
          url: url,
          dataType: 'script',
          async : false
        });
}

/*jQuery.fn.scrollTo =  function()
                        {
                            var x = $(this).offset().top - 100;
                            $("html,body").animate({scrollTop: x}, 500);
                            return jQuery(this);
                        }*/

jQuery.fn.removeClassByPrefix = function(sPrefix)
{
    return this.each(function(){
        
        var regx = new RegExp('\\b' + sPrefix + '.*?\\b', 'g');
        this.className = this.className.replace(regx, '');
        //return this;
        
    });
}
    

/**
 * Removes a tab added with jQuery.addNewTab() and reinitializes the tabs object.
 */
jQuery.removeTab = function()
{
    var tabTitle = $(this).closest("li").find("a.PlentyGuiTabTitle");
    var tabId = tabTitle.attr("id");
    var paneId = tabTitle.attr("href").substr(1,tabTitle.attr("href").length);
    
    var tabsSelectorId = tabTitle.closest("div.PlentyGuiTabs").attr("id");
    var tabsObj = $("#"+tabsSelectorId+" > .tabs");
    var options = tabsObj.data("options");
    
    $("#"+tabsSelectorId).find("div#"+paneId).remove();
    $(this).closest("li").remove();
    
    options.initialIndex = 0;
    tabsObj.tabs("> .pane",options);
    tabsObj.find("a.current").click();
}

/**
 * Adds a new tab in an existing tabs object and reinitializes it.
 * 
 * @param string    selector    The ID of the tabs object (PlentyGuiTabs->getID()).
 * @param string    id          The ID of the new tab title. This value will be submited to the AJAX class called with 'ajaxUrl'.
 * @param string    ajaxUrl     The URL to be called. This call should return the tab title and the tab pane for the new tab.
 * @param boolean   click       A flag indicating whether to imediatly open the new tab or not.
 * 
 * @return void
 */
jQuery.addNewTab = function(selector,id,ajaxUrl,click)
{
    var tab = $(selector);
    var selectorId = selector;
    var liId = id+"_liElement";
    var liElem = $("#"+liId);
    var ulElem = $("#"+selector+" > .tabs");
    
    /*
     * Wenn das Tabs Element nicht existiert, dann braucht nichts weiter gemacht werden.
     */
    if(jQuery("#"+selector).length <= 0)
	{
		return false;
	}
    
    if(typeof click != "boolean")
    {
        click = false;
    }
    
    /*
     * Only if the tab does not already exist.
     */
    if(liElem.length == 0)
    {
        var closeBtn = $("<img/>");
        liElem = $("<li></li>");
        
        /*
         * The new close button.
         */
        closeBtn.
            css({'position':'absolute', 'top':'10px', 'right':'5px'}).
            addClass("link").
            addClass("PlentyGuiIcon").
            addClass("TabCloseButton").
            attr("title","Close Tab").
            attr("id",liId+"_closeButton").
            attr("src","/images/icons/silk/tab_close.gif").
            bind("click",$.removeTab);
        
        /*
         * The new tab title of the tabs object.
         */
        liElem.
            css("position","relative").
            attr("id",liId);
        ulElem.append(liElem);
        
        /*
         * The callback function, which adds the new tab and reinitializes the tabs object.
         */
        var addCloseBtn = function()
        {
            var options = $("#"+selector+" > .tabs").data("options");
            options.initialIndex = ulElem.find('li').index(liElem);
            
            /*
             * Add the close button
             */
            liElem.append(closeBtn);
            
            /*
             * Reinitialize the tabs object.
             */
            ulElem.tabs("> .pane",options);
            
            /*
             * Open the new tab, if requested.
             */
            if(click)
            {
                ulElem.find("a.current").click();
            }
        }
        
        /*
         * The AJAX call.
         */
        ajaxUrl = ajaxUrl + '&Params[result_id][0]=' + liId + '&Params[result_id][1]=' + selectorId + '&Params[add_result_id][1]=1'+'&TabTitleId='+id;
        plentyAjaxRequest2(ajaxUrl,addCloseBtn);
    }
    else
    {
        /*
         * The requested tab already exists, so only open it.
         */
        $("#"+selector+" > .tabs").data("tabs").click(ulElem.find('li').index(liElem));
    }
}


jQuery.fn.iconselect = function(conf){
    return this.each(function(){
            $(this).change(function(){
                var option = $(this).find("option:selected");
                var url = option.css("background-image");
                if( url )
                {
                    $(this).css("background-image",url);
                }
                else
                {
                    $(this).css("background-image","none");
                }
            });
            $(this).css("background-repeat","no-repeat").
                    css("height","19px").
                    css("padding-left","25px");
            $(this).change();
        });
}


/**
 * Display and hide the accordion tabs for checkout.
 * 
 * @param string    accordion   The ID of the accordion container
 *
 * @return void
 */
checkoutAccordions = function(accordion, options){
	var settings = {
		paneClass	:	"AccordionPane",	//	The CSS class of the accordion panes
		titleClass	:	"AccordionTitle",	//	The CSS class of the accordion titles
		
		aPrefix		:	"PlentyOrder",		//	Prefix of the accordion elements ID
		cAPostfix	:	"Content",			//	Postfix of the accordion panes ID
		tAPostfix	:	"Title",			//	Postfix of the accordion tabs ID
		
		ePrefix		:	"Plenty",			//	Prefix of the error panes and error elements ID
		eEPostfix	:	"ErrorPane",		//	Postfix of the error panes ID
		dEPostfix	:	"Data",				//	Postfix of the error elements ID
		
		noOpenTwice	:	[]					//	An array with panes, which should never be opend twice
	};
	
	var current = null;
	
	if(typeof options == "object")
	{
		jQuery.extend(settings,options);
	}
	
	var accPanes  = $(accordion + " > ." + settings.paneClass);
	var accTitles = $(accordion + " > ." + settings.titleClass);
	
	var isUniqueStep = function(step)
	{
		return jQuery.isArray(settings.noOpenTwice) && settings.noOpenTwice.join(' ').indexOf(step) != -1
	}
	
	var canOpen = function(step)
	{
		if(isUniqueStep(step))
		{
			return !getTitle(step).hasClass("AlreadyOpened");
		}
		else
		{
			return true;
		}
	}
	
	/*
	 * Get the step pane as jQuery object.
	 */
	var getContent = function(step)
	{
		return $("#" + settings.aPrefix + step + settings.cAPostfix);
	}
	
	/*
	 * Get the step title as jQuery object.
	 */
	var getTitle = function(step)
	{
		return $("#" + settings.aPrefix + step + settings.tAPostfix);
	}

	/*
	 * Get the error pane as jQuery object.
	 */
	var getError = function(step)
	{
		return $("#" + settings.ePrefix + step + settings.eEPostfix);
	}
	
	/*
	 * Get the error data pane as jQuery object.
	 */
	var getErrorData = function(step)
	{
		return $("." + settings.ePrefix + step + settings.dEPostfix +  ".PlentyError");
	}
	
	/**
	 * Open step pane.
	 * 
	 * @param string    step    	The ID of the active accordion ("WebLogin")
	 *
	 * @return self
	 */
	this.open = function(step)
	{
		var obj = this;
		if(step == current)
		{
			return this;
		}
		
		if(canOpen(step))
		{
			accPanes.slideUp();
			accTitles.removeClass("CurrentAccordionTitle");
			getContent(step).slideDown();
			getTitle(step).addClass("AlreadyOpened CurrentAccordionTitle");
			if(isUniqueStep(step))
			{
				getTitle(step).addClass("UniqueAccordionTitle");
			}
			
			if(current != null)
			{
				var curInd = accTitles.index(getTitle(current));
				var newInd = accTitles.index(getTitle(step));
				if(curInd <= newInd && canOpen(current))
				{
					getTitle(current).
						css({cursor:'pointer'}).
						addClass("AlreadyOpened").
						bind("click",{step:current},function(event){
							obj.open(event.data.step);
						});
				}
				else
				{
					accTitles.filter(function(ind){ return ind >= newInd;}).
						css({cursor:'default'}).
						removeClass("AlreadyOpened").
						unbind("click");
				}
			}
			
			current = step;
		}
		
		return this;
	}

	/**
	 * Show error pane for given step. If errors is a coma separated string of ids, these elements get the CSS class "PlentyError".
	 * 
	 * @param	string	step	The step of the accordion to show the errors.
	 * @param	string	errors	A coma separated list of IDs to add the CSS class "PlentyError". If not given, 
	 * 
	 * @return self
	 */ 
	this.showErrors = function(step,errors)
	{
		this.hideErrors(step);
		getError(step).show();
		if(typeof errors == "string")
		{
			$(errors).addClass("PlentyError");
		}
		else
		{
			getErrorData(step).addClass("PlentyError");
		}
		return this;
	}

	/**
	 * Hide error pane for given step. Also the CSS class "PlentyError" will be removed form all error data elements.
	 * 
	 * @param	string	step	The step of the accordion to hide the errors
	 * 
	 * @return self
	 */
	this.hideErrors = function(step)
	{
		getError(step).hide();
		getErrorData(step).removeClass("PlentyError");
		return this;
	}
}

WebBasketOverlay = function(options)
{
	var settings = {
		mainClass	:	"PlentyWebBasketOverlayMainPane",	//	The CSS class of the accordion titles
		contentClass:	"PlentyWebBasketOverlayContent",
		closeClass	:	"PlentyWebBasketOverlayClose"
	};
	
	if(typeof options == "object")
	{
		jQuery.extend(settings,options);
	}
	
	var overlay  = jQuery("div."+settings.mainClass);
	var closeBtn = jQuery("div."+settings.mainClass+" > a."+settings.closeClass);
	
	overlay.hide();
	closeBtn.bind("click", function(){overlay.hide()});
	
	this.open = function(url)
	{
		overlay.show();
		url = url+"&result_id="+settings.contentClass;
		plentyAjaxRequest2(url);
	}
	
	this.close = function(reload)
	{
		overlay.hide();
	}
}

/**
 * Class of PlentyElementCache
 * 
 * @returns {PlentyElementCache}
 */
function PlentyElementCache()
{
    // Element sets are registered here
    this.oRegistry = new Object();
    // Singleton
    PlentyElementCache.oInstance = null;
}

/**
 * 
 * @returns {PlentyElementCache}
 * @todo if this class is in heavy use, we could think about a cache limit
 */
PlentyElementCache.getInstance = function()
{
    if(PlentyElementCache.oInstance == null)
    {
        PlentyElementCache.oInstance = new PlentyElementCache();
    }
    
    return PlentyElementCache.oInstance;
};

/**
 * 
 * @returns {void}
 */
PlentyElementCache.destroy = function()
{
    PlentyElementCache.oInstance = null;
};

/**
 * 
 * @param {String} sJQuerySelector
 * @param {String} sCacheNamespace
 * @returns {Object} jQuery element match
 */
PlentyElementCache.prototype.get = function(sJQuerySelector, sCacheNamespace)
{
    if(     sJQuerySelector.length > 0
        &&  this.exists(    sJQuerySelector,
                            sCacheNamespace) == false)
    {
        // Allocate element set to the selector
        this.set(   sJQuerySelector,
                    sCacheNamespace);
    }
    
    if(     $.isset(sCacheNamespace)
        &&  sCacheNamespace.length  > 0)
    {
        return this.oRegistry[sCacheNamespace][sJQuerySelector];
    }
    
    return this.oRegistry[sJQuerySelector];
}

/**
 * Check if matching elements are cached for a selector or a selector in a namespace
 * 
 * @param {String} sJQuerySelector
 * @param {String} sCacheNamespace
 * @returns {Boolean}
 */
PlentyElementCache.prototype.exists = function(sJQuerySelector, sCacheNamespace)
{
    if(     $.isset(sCacheNamespace)
        &&  sCacheNamespace.length > 0)
    {
        return      $.isset(this.oRegistry[sCacheNamespace])
                &&  $.isset(this.oRegistry[sCacheNamespace][sJQuerySelector]);
    }
    else
    {
        return      $.isset(this.oRegistry[sJQuerySelector]);
    }
}

/**
 * Sets a set of matched elements to the cache
 * 
 * @param {String} sJQuerySelector
 * @param {String} sCacheNamespace
 * @returns {void}
 */
PlentyElementCache.prototype.set = function(sJQuerySelector, sCacheNamespace)
{
    if(     $.isset(sCacheNamespace)
        &&  sCacheNamespace.length  > 0)
    {
        if($.isset(this.oRegistry[sCacheNamespace]) == false)
        {
            this.oRegistry[sCacheNamespace] = new Object();
        }
        
        this.oRegistry[sCacheNamespace][sJQuerySelector] = jQuery(sJQuerySelector);
    }
    else
    {
        this.oRegistry[sJQuerySelector] = jQuery(sJQuerySelector);
    }
}

/**
 * Unsets a set of matched elements in the cache.
 * 
 * Unset either for a selector, a selector in a namespace or a whole namespace (by leaving the first param blank)
 * 
 * @param {String} sJQuerySelector
 * @param {String} sCacheNamespace
 */
PlentyElementCache.prototype.unset = function(sJQuerySelector, sCacheNamespace)
{
    if(     $.isset(sCacheNamespace)
        &&  sCacheNamespace.length  > 0)
    {
        if(     $.isset(sJQuerySelector)
            &&  sJQuerySelector.length  > 0)
        {
            // unset selector in namespace
            delete this.oRegistry[sCacheNamespace][sJQuerySelector];
        }
        else
        {
            // unset whole namespace
            delete this.oRegistry[sCacheNamespace];
        }
    }
    else
    {
        if(     $.isset(sJQuerySelector)
            &&  sJQuerySelector.length > 0)
        {
            // unset selector
            delete this.oRegistry[sJQuerySelector];
        }
    }
}

/**
 * 
 * Class registers paths to javascript files, so that the same script isn't loaded twice.
 */
function PlentyScriptRegistry()
{
    // Registry object, script's src tag's values are registered here
    this.oRegistry = new Object();
    PlentyScriptRegistry.oInstance = null;
}

/**
 * Singleton
 * 
 * @returns {PlentyScriptRegistry}
 */
PlentyScriptRegistry.getInstance = function()
{
    if(PlentyScriptRegistry.oInstance == null)
    {
        PlentyScriptRegistry.oInstance = new PlentyScriptRegistry();
    }
    
    return PlentyScriptRegistry.oInstance;
};

/**
 * Register script
 * 
 * @param {String} sSource The path to the script
 */
PlentyScriptRegistry.prototype.register = function(sSource)
{
    if(sSource.length > 0)
    {
        this.oRegistry[sSource] = true;
    }
}

/**
 * Is a script registered with that path?
 * 
 * @param {String} sSource The path to the script
 */
PlentyScriptRegistry.prototype.isRegistered = function(sSource)
{
    return      $.isset(this.oRegistry[sSource])
            &&  this.oRegistry[sSource]         == true
}

/*
 * jQuery extensions.
 * 
 * Functions will be available via "$." or "jQuery." respectively.
 */
jQuery.extend({
    /**
     * Takes use of the PlentyElementCache class.
     * 
     * All elements that are found via this function are cached by the selector as long as the page is not reloaded.
     * 
     * @param {String} sJQuerySelector
     * @param {String} sCacheNamespace
     * @returns {Object} A set of matching elements for the selector
     */
    c: function(sJQuerySelector, sCacheNamespace) {
        return  PlentyElementCache
                    .getInstance()
                        .get(sJQuerySelector, sCacheNamespace);
    },
    /**
     * Direct access to the PlentyElementCache instance.
     * 
     * Use this, for example, to unset parts of the cache.
     * 
     * @returns {PlentyElementCache}
     */
    getCache: function() {
        return  PlentyElementCache
                    .getInstance();
    },
    /**
     * Loads a script via ajax just once
     * 
     * @param {String} sSource
     */
    getPlentyScriptOnce: function(sSource) {
        if( PlentyScriptRegistry
                .getInstance()
                    .isRegistered(sSource) === false)
        {
            // Load script via synchronous ajax
            jQuery.getPlentyScript(sSource);
            // Register script source
            PlentyScriptRegistry
                .getInstance()
                    .register(sSource);
        }
    },
    /**
     * Loads a CSS file via ajax
     * 
     * To load a CSS file just once, use $.getPlentyCssOnce()
     * 
     * @param {String} sSource
     */
    getPlentyCss: function(sSource)
    {
        if(     $.isset(sSource)
            &&  sSource.length > 0)
        {
            $('head').append('<link>');
            css = $('head').children(':last');
            css.attr({
                rel:  'stylesheet',
                type: 'text/css',
                href: sSource
            });
        }
    },
    /**
     * Loads a CSS file just once
     * 
     * @param {String} sSource
     */
    getPlentyCssOnce: function(sSource)
    {
        if( PlentyScriptRegistry
                .getInstance()
                    .isRegistered(sSource) === false)
        {
            // Register
            PlentyScriptRegistry
                .getInstance()
                    .register(sSource);
            // Get
            $.getPlentyCss(sSource);
        }
    },
    /**
     * Returns true if param is not undefined and not null
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    isset: function(mixed)
    {
        return      mixed   !== undefined
                &&  mixed   !== null;
    },
    /**
     * Returns true if param is null
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_null: function(mixed)
    {
        return mixed == null;
    },
    /**
     * Returns true if param is not undefined and not null
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_undefined: function(mixed)
    {
        return mixed === undefined;
    },
    /**
     * Returns true if param is an object
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_object: function(mixed)
    {
        return typeof mixed == 'object';
    },
    /**
     * Returns true if param is a number (even if the number is a string value)
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_numeric: function(mixed)
    {
        return typeof mixed == 'number' || !isNaN(mixed);
    },
    /**
     * Returns true if param is of type string
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_string: function(mixed)
    {
        return typeof mixed == 'string';
    },
    /**
     * Returns true if param is of type boolean
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_boolean: function(mixed)
    {
        return typeof mixed == 'boolean';
    },
    /**
     * Returns true if param is a function
     * 
     * @param {mixed}
     * @returns {Boolean}
     */
    is_function: function(mixed)
    {
        return typeof mixed == 'function';
    },
    /**
     * @param {String}
     * @return {Integer}
     */
    strlen: function(sValue)
    {
        // Is numeric?
        if($.is_numeric(sValue))
        {
            // Is numeric, parse to string
            sValue = String(sValue);
        }
        
        // Is string?
        if($.is_string(sValue))
        {
            // Is string, return length
            return sValue.length;
        }
        
        // No string, return zero
        return 0;
    },
    URLEncode:function(c)
    {
    	var o='';
    	var x=0;
    	c=c.toString();
    	var r=/(^[a-zA-Z0-9_.]*)/;
    	
    	while(x<c.length)
    	{
    		var m=r.exec(c.substr(x));
    		
	      	if(m!=null && m.length>1 && m[1]!='')
	      	{
	      		o+=m[1];x+=m[1].length;
	      	}
	      	else
	      	{
	      		if(c[x]==' ')
	      		{
	      			o += '+';
	      		}
	      		else
	      		{
	      			var d = c.charCodeAt(x);
	      			var h = d.toString(16);
	      			
	      			o+='%'+(h.length<2?'0':'')+h.toUpperCase();
	      		}
	      		
	      		x++;
	      	}
      	}
    	
    	return o;
    },
 	URLDecode:function(s)
 	{
		var o = s;
		var binVal, t;
		var r = /(%[^%]{2})/;
	  
		while(	(m=r.exec(o)) != null	&&
    		  	m.length > 1			&&
    		  	m[1]!='')
		{
    		b = parseInt(m[1].substr(1),16);
    		t = String.fromCharCode(b);
			o = o.replace(m[1], t);
		}

		return o;
	}
});

/**
 * Loads and Saves the (current) scrolling position in the first li child of tabs.
 * 
 * @param string    selector          The ID of the main tabs div.
 */
ScrollingPosition = function(selector)
{
	var dataChild = "#" + selector + " .tabs li:first-child";
	var posY = null;
	
	/**
	 * Loads the scrolling position from the id.
	 * 
	 * @return void
	 */	
	this.load = function()
	{
		posY = $(dataChild).data('scrollTopPosition');
		$('html').scrollTop(posY);
	}
	
	/**
	 * Saves the scrolling position from the id.
	 * 
	 * @param int    y          The position to be saved (optional).
	 * 
	 * @return void
	 */	
	this.save = function(y)
	{
		posY = (	y === undefined
				?	$('html').scrollTop()
				:	y
			);
		
		$(dataChild).data('scrollTopPosition', posY);
		$('html').scrollTop(0);
	}
	
	/**
	 * Get the name of the Tab where position is stored
	 * 
	 * @return string dataTab
	 */
	this.getDataChild = function()
	{
		return dataChild;
	}
	
	if (posY == null)
	{
		this.save(0);
	}
}
