(function($){

	// to track if the mouse button is pressed
	var isMouseDown    = false;

	// to track the current element being dragged
	var currentElement = null;

	// callback holders
	var dropCallbacks = {};
	var dragCallbacks = {};
	
	// bubbling status
	var bubblings = {};

	// global position records
	var lastMouseX;
	var lastMouseY;
	var lastElemTop;
	var lastElemLeft;
	
	// track element dragStatus
	var dragStatus = {};	

	// if user is holding any handle or not
	var holdingHandler = false;

	// returns the mouse (cursor) current position
	$.getMousePosition = function(e){
		var posx = 0;
		var posy = 0;

		if (!e) var e = window.event;

		if (e.pageX || e.pageY) {
			posx = e.pageX;
			posy = e.pageY;
		}
		else if (e.clientX || e.clientY) {
			posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			posy = e.clientY + document.body.scrollTop  + document.documentElement.scrollTop;
		}

		return { 'x': posx, 'y': posy };
	};

	// updates the position of the current element being dragged
	$.updatePosition = function(e) {
		var pos = $.getMousePosition(e);

		var spanX = (pos.x - lastMouseX);
		var spanY = (pos.y - lastMouseY);

		$(currentElement).css("top",  (lastElemTop + spanY));
		$(currentElement).css("left", (lastElemLeft + spanX));
	};

	// when the mouse is moved while the mouse button is pressed
	$(document).mousemove(function(e){
		if(isMouseDown && dragStatus[currentElement.id] != 'false'){
			// update the position and call the registered function
			$.updatePosition(e);
			if(dragCallbacks[currentElement.id] != undefined){
				dragCallbacks[currentElement.id](e, currentElement);
			}

			return false;
		}
	});

	// when the mouse button is released
	$(document).mouseup(function(e){
		if(isMouseDown && dragStatus[currentElement.id] != 'false'){
			isMouseDown = false;
			if(dropCallbacks[currentElement.id] != undefined){
				dropCallbacks[currentElement.id](e, currentElement);
			}

			return false;
		}
	});

	// register the function to be called while an element is being dragged
	$.fn.ondrag = function(callback){
		return this.each(function(){
			dragCallbacks[this.id] = callback;
		});
	};

	// register the function to be called when an element is dropped
	$.fn.ondrop = function(callback){
		return this.each(function(){
			dropCallbacks[this.id] = callback;
		});
	};
	
	// disable the dragging feature for the element
	$.fn.dragOff = function(){
		return this.each(function(){
			dragStatus[this.id] = 'off';
		});
	};
	
	// enable the dragging feature for the element
	$.fn.dragOn = function(){
		return this.each(function(){
			dragStatus[this.id] = 'on';
		});
	};
	
	// set a child element as a handler
	$.fn.setHandler = function(handlerId){
		return this.each(function(){
			var draggable = this;
			
			// enable event bubbling so the user can reach the handle
			bubblings[this.id] = true;
			
			// reset cursor style
			$(draggable).css("cursor", "");
			
			// set current drag status
			dragStatus[draggable.id] = "handler";

			// change handle cursor type
			$("#"+handlerId).css("cursor", "move");	
			
			// bind event handler
			$("#"+handlerId).mousedown(function(e){
				holdingHandler = true;
				$(draggable).trigger('mousedown', e);
			});
			
			// bind event handler
			$("#"+handlerId).mouseup(function(e){
				holdingHandler = false;
			});
		});
	};

	// set an element as draggable - allowBubbling enables/disables event bubbling
	$.fn.easydrag = function(allowBubbling){

		return this.each(function(){

			// if no id is defined assign a unique one
			if(undefined == this.id || !this.id.length) {this.id = "easydrag"+(new Date().getTime());}
			
			// save event bubbling status
			bubblings[this.id] = allowBubbling ? true : false;

			// set dragStatus 
			dragStatus[this.id] = "on";
			
			// change the mouse pointer
			$(this).css("cursor", "move");

			// when an element receives a mouse press
			$(this).mousedown(function(e){
				
				// just when "on" or "handler"
				if((dragStatus[this.id] == "off") || (dragStatus[this.id] == "handler" && !holdingHandler)) {
					return bubblings[this.id];
				}

				// set it as absolute positioned
				$(this).css("position", "absolute");

				// set z-index
				$(this).css("z-index", parseInt( new Date().getTime()/1000 ));

				// update track variables
				isMouseDown    = true;
				currentElement = this;

				// retrieve positioning properties
				var pos    = $.getMousePosition(e);
				lastMouseX = pos.x;
				lastMouseY = pos.y;

				lastElemTop  = this.offsetTop;
				lastElemLeft = this.offsetLeft;

				$.updatePosition(e);

				return bubblings[this.id];
			});
		});
	};

})(jQuery);

(function($){
   $.fn.selectNavigation = function(options) {
      return this.each(function(){
        if(this.nodeName.toLowerCase()!='select') {
          return false;
        }        
        
        var $opts = $.extend({
          'autoClose':true,
          'close':'close',
          'title':'',
          'target':'self',
          'detailTitle':false,
          'animate':true,
          'animationSpeed':'fast',
          'detailColumns':3,
          'MaxPerColumn':12,
          'columWidth':200,
          'draggable':true,
          'behaviour':'hover'
        },options);
        
        var $select = $(this);
        var data = {};
        var $parentID = (!$select.attr('id')) ? 
          $select.attr('name') : $select.attr('id');
        
        var labelText  =  $("label[for='"+$parentID+"']").text();  
        
        if(!$opts.title || $opts.title=='') {
          $opts.title = lablelText;          
        }
        
        if(labelText=="") {lablelText = $opts.title;}
        
        var maxSize = $opts.columnWidth + ($opts.columnWidth*$opts.detailColums);
        
        
        $('option',$select).each(function() {
          var text = $(this).text();
          var val = $(this).val();          
          if($(this).parents('optgroup').size()>0) {
            var p = $(this).parents('optgroup')[0];
            var id = $(p).attr('id');
            if(!id) {id = _strStrictify($(p).attr('label'));}
            if(!data[id]) {
              data[id] = [];
              var vv = ($(p).attr('rel')) ? $(p).attr('rel') : false; 
              data[id][data[id].length] = {'vtarget':vv,'value':false,'text':$(p).attr('label')};
            }
            data[id][data[id].length] = {'value':val,'text':text};
          } else {
            var id =  _strStrictify(val);
            var vv = ($(this).attr('rel')) ? $(this).attr('rel') : false;
            data[id] = {'vtarget':vv,'value':val,'text':text};
          }
        });
        
        
        var $selectorBox = $('<div id="Selector_'+$parentID+'" class="select-navigation"</div>').appendTo('body');
        var $selectorBar = $('<div id="Selector_Bar_'+$parentID+'" class="select-bar"></div>').appendTo($selectorBox);
        if($opts.title) {
          $('<h3 class="select-title"><span>'+$opts.title+'</span></h3>').appendTo($selectorBar);          
        }
        if($opts.close && $opts.close!="") {
          var close = $('<div class="select-close" title="'+
          $opts.close+'"><span>'+
          $opts.close+'</span></div>').appendTo($selectorBar);
          $(close).click(function(){$selectorBox.hide();});
          
        }
        var $contentBox = $('<div class="select-wrapper"></div>').appendTo($selectorBox);
        var $selectorMain = $('<div class="select-navigation-list"></div>').appendTo($contentBox);
        var $selectorList = $('<ul></ul>').appendTo($selectorMain);
        var $detailBox = $('<div class="select-detail-navigation"></div>').appendTo($contentBox);
        
        var cnt = 0;
        for(a in data) {
          var cType =  _getTypeOf(data[a]);
          cnt++;
          var cElm = false;
          if(cType=='object') {
            cElm = $('<li rel="'+data[a]['value']+'"></li>').appendTo($selectorList);
            cElm.append('<span>'+data[a]['text']+'</span>');
            if(data[a]['vtarget']) {cElm.attr('link',data[a]['vtarget']);}
            if($opts.behaviour=='hover') {
              cElm.mouseover(function(){
                $('li',$selectorList).removeClass('select-active');
                 _hideDetail(false);
              });
              if(data[a]['vtarget']) {
                cElm.click(function(){
                 location.href = $(this).attr('link');
              });
              }
            
            } else {
              cElm.click(function(){
                $('li',$selectorList).removeClass('select-active');
                 _hideDetail(true);
              });
            }
          } else if(cType=='array') {
            cElm = $('<li rel="'+a+'"></li>').appendTo($selectorList);
            cElm.append('<span>'+data[a][0]['text']+'</span>');
            if(data[a][0]['vtarget']) {cElm.attr('link',data[a][0]['vtarget']);}
            //alert(data[a][0]['vtarget']);
            if($opts.behaviour=='hover') {
              cElm.mouseover(function(){$this = this;_displayDetail($this);});
              if(data[a][0]['vtarget']) {
                cElm.click(function(){
                 location.href = $(this).attr('link');
              });
              }
              
            } else {
              cElm.click(function(){$this = this;_displayDetail($this);});
            }
          }
          if(cElm) {
            $(cElm).mouseover(function(){$(this).addClass('sn-hover')})
            .mouseout(function(){$(this).removeClass('sn-hover')});
             var cPos = (cElm).position();             
             $('span',cElm).css({'backgroundPosition':'0 -'+cPos.top+'px'});            
          }
        }     
        
        $("label[for='"+$parentID+"']").hide();
        $select.hide();
        if($opts.title) {
          $select.after('<div id="SelectorHandler_'+$parentID+'">'+labelText+'</div>');
          var cPos = $('#SelectorHandler_'+$parentID).offset();
          // notfalls hier noch -x ans top hängen...
          $selectorBox.css({'left':cPos.left,'top':cPos.top});
          $('#SelectorHandler_'+$parentID).click(function(){
            $selectorBox.toggle();
          }).css({'cursor':'pointer'});          
        }
        if($opts.draggable) {
          $selectorBox.easydrag();
          if($('#Selector_Bar_'+$parentID).size()==1) {
            $selectorBox.setHandler('Selector_Bar_'+$parentID);
          }
        }
        $selectorBox.css({'width':$opts.columWidth}).hide();
        var form = $select.parents('form');
        if(form.size() > 0) {
          $(form).submit(function(){return false;});
          $('button, input',form).hide();
        }
        
        if($opts.autoClose) {
          $('body').click(function(event) {
            var p = $(event.target).parents('#Selector_'+$parentID+',#SelectorHandler_'+$parentID);            
            if(p.size()> 0) {return;}
            if($(event.target).is('#SelectorHandler_'+$parentID)) {return;} 
            if($(event.target).is('#Selector_'+$parentID)) {return;}
            $selectorBox.hide();
          });
        }
        
        function _handleLink($li) {
          var url = $($li).attr('rel');
          if(url.substr(0,1)=='#') {
            try {
              $(url)[0].scrollIntoView(true);
            } catch(e) {
              self.location.href= url;
            }
            if($opts.autoClose) {
             $selectorBox.hide();
            }          
          } else {
            eval($opts.target+'.location.href="'+url+'"');
          }                    
        }
        
        function _hideDetail(doLink) {
          $detailBox.removeAttr('rel');
          var currScale = $('ul',$detailBox).size();
          $detailBox.html('');
          if($opts.animate && currScale > 0) {
            $selectorBox.animate(
              {'width':$opts.columWidth}, 
              $opts.animationSpeed,
              "",
              function(){
                if(doLink) {
                  _handleLink($this);
                }
              }
            );
          } else {
            $selectorBox.css({'width':$opts.columWidth});
            if(doLink) {
              _handleLink($this);
            }
          }
        }              
      
        
        function _displayDetail(elm) {
          $('li',$selectorList).removeClass('select-active');
              $(elm).addClass('select-active');
              var identifier = $(elm).attr('rel');
              if($detailBox.attr('rel') == identifier) {return;}
              $detailBox.attr('rel',identifier);
              $detailBox.html('<div class="box"><div class="box"><div class="box"><div class="box"><div class="box"></div></div></div></div></div>');
              var htmlInject = $('div:eq(4)',$detailBox); 
              var dT = ($opts.detailTitle) ? $opts.detailTitle : $(elm).text();
              $detailBox.prepend('<h4>'+dT+'</h4>');
              var cCon = cConBox = false;
              var total = data[identifier].length;
              var counter = 0;
              for(c=0; c<$opts.detailColumns; c++) {
                  var min = (c==0) ? 1 : ($opts.MaxPerColumn*c)+1;
                  var max = (total > min+$opts.MaxPerColumn) ? (min+$opts.MaxPerColumn) : total;  
                  if(counter < (total-1)) {
                    cConBox = $('<div class="select-detail-column"></div>').appendTo(htmlInject);
                    cCon = $('<ul></ul>').appendTo(cConBox);
                    for(var i=min; i<max; i++) {
                      var sElm = $('<li rel="'+data[identifier][i]['value']+'">'+
                      data[identifier][i]['text']+'</li>').appendTo(cCon);
                      sElm.click(function(){$this = this;_handleLink($this);})
                      .mouseover(function(){$(this).addClass('sn-hover')})
                      .mouseout(function(){$(this).removeClass('sn-hover')});
                      counter++;
                    }
                  }
              }
              var newScale = $('ul',$detailBox).size();
              if($opts.animate) {
                $selectorBox.animate(
                    {'width':$opts.columWidth+(newScale*$opts.columWidth)}, 
                    $opts.animationSpeed
                  );                
              } else {
                $selectorBox.css({'width':$opts.columWidth+(newScale*$opts.columWidth)});
              }
        }

        function _strStrictify(str) {
          str = str.toString()
            .replace(/^\s*(.*)/, "$1")
            .replace(/(.*?)\s*$/, "$1")
            .replace(/\s+/g,'_');
          return str;
        };
        
        function _getTypeOf(obj) {
          if ( typeof(obj) == 'object' ) {
            if (obj.length) {
              return 'array';
            } else {
              return 'object';
            }
          } else {
            return typeof(obj);
          }
        }
 });
 };
})(jQuery);