/*
	 title: Baloooon(beta)
	 copy: copyright 2009 nori (norimania@gmail.com)
	 license: MIT
	 author: 5509 - http://moto-mono.net
	 date: 2009-07-18
	 version: 0.44
 */

(function($j){
	
	// $j.Baloooon
	// excecute [new Baloooon] via jQuery($j)
	// @ 2009-06-24
	$j.Baloooon = function(options){
		new Baloooon(options);
	}
		  
	// Baloooon
	// make the custom tooltip easily with jQuery
	// @ 2009-06-25
	var Baloooon = function(options){
	
		// @ version & name
		this.version = '0.44';
		this.name = 'Baloooon';
	
		// Options, these are customizable
		this.options = $j.extend({
			triggerEvent: 'mouseover', // bind event (mouseover, click, dblclick)
			triggerOutEvent: 'mouseout', // bind event (mouseout, click, dblclick)
			defaultEventCancel: false,
			object: '', // jQuery object
			keyScope: 'same', // jQuery object
			type: '', // 'text', 'all attr(not className)'
			set: '', // [[key1,reg1],[key2,reg2].....]
			positionLock: false, // boolean
			flexiblePosition: false, // boolean
			defaultPos: 'above', // if(flexiblePosition) you can select either above or below
			top: 20, // setting position
			left: 30, // setting position
			bottom: 20, // setting position
			maxWidth: 'auto', // Baloooon max-width
			height: 'fixed', // or flexible
			overflow: false, // or true
			duration: 300, // fadeIn duration
			loadMethod: 'json', // json, attr, ajax, inline, iframe
			path: '', // JSON URI that might be fullpath
			id: '', // this Baloooon id
			setClass: 'baloooon' // this Baloooon className
		},options || {});
		
		this.container = $j(document.createElement('div'));
		this.container.attr({
			id: this.options.id+'_baloooon',
			className: this.options.setClass
		}).append(Array(
			'<div class="baloooon_close"></div>',
			'<div class="baloooon_below"></div>',
			'<div class="baloooon_top">',
				'<div class="baloooon_left"></div>',
				'<div class="baloooon_middle"></div>',
				'<div class="baloooon_right"></div>',
			'</div>',
			'<div class="baloooon_content">',
				'<div class="baloooon_loader"></div>',
				'<div class="baloooon_content_main" style="display: none">',
					'<div class="baloooon_top">',
						'<div class="baloooon_left"></div>',
						'<div class="baloooon_middle"></div>',
						'<div class="baloooon_right"></div>',
					'</div>',
					'<div class="baloooon_content_box"></div>',
					'<div class="baloooon_bottom">',
						'<div class="baloooon_left"></div>',
						'<div class="baloooon_middle"></div>',
						'<div class="baloooon_right"></div>',
					'</div>',
				'</div>',
				'<div class="baloooon_footer">',
					'<div class="baloooon_title"></div>',
				'</div>',
			'</div>',
			'<div class="baloooon_bottom">',
				'<div class="baloooon_left"></div>',
				'<div class="baloooon_middle"></div>',
				'<div class="baloooon_right"></div>',
			'</div>',
			'<div class="baloooon_above"></div>'
		).join('')).hide();
		this.currentSize = {
			width: 0,
			height: 0
		};
		this.textBox = {};
		this.JSONloaded = false;
		this.isOverflow = false;
		$j('body').append(this.container);
		
		// execute the Baloooon
		this.init();
	}
		
	// baloooon basic methods
	// @ 2009-06-25
	Baloooon.prototype = {
	
		// Baloooon.init
		// activate Baloooon
		// @ 2009-07-18
		init: function(){
			var that = this,
				trgO = this.options,
				contemporaryObject;

			if(!trgO.positionLock) $j('.baloooon_close',this.container).hide();
			
			trgO.object.bind(trgO.triggerEvent,function(e){
														
				//if(!$j(this).attr(trgO.type) || $j(this).attr(trgO.type).length>1) return false;
			
				var scope = trgO.keyScope=='same' ? $j(this) : that.options.keyScope,
					key = trgO.type=='text' ? scope.text() : scope.attr(trgO.type);
									
				if(key=='title') $j(this).removeAttr('title');
				
				that.setPos(e);
				that.container.animate(
					{
						opacity: 'toggle'
					},
					{
						duration: trgO.duration,
						queue: false,
						complete: function(){
							that.container.css({
								opacity: 1
							});
						}
					}
				);
				that.getCurrentSize();
				
				switch(trgO.loadMethod){
					case 'json':
						that.loadJSON(key,e);
						break;
					case 'attr':
						that.loadAttrContent(key,e);
						break;
					case 'ajax':
						that.loadAJAXContent(key,e);
						break;
					case 'inline':
						that.loadINLINEContent(key,e);
						break;
					case 'iframe':
						that.loadIFRAMEContent(key,e);
						break;
					default:
						return false;
				}
				
				if(trgO.defaultEventCancel){
					return false;
				}
				
			});
			
			if(trgO.triggerEvent=='mouseover'){
				trgO.object.bind('mouseout',function(){
					if(!that.options.positionLock) that.container.hide();
					$j('.baloooon_content_box',that.container).height('auto');
				}).bind('mousemove',function(e){
					if(!that.options.positionLock) that.setPos(e);
				});
			}
			
			$j('.baloooon_close',this.container).click(function(){
				that.container.fadeOut();
			});
			
		},
		
		// Baloooon.getCurrentSize
		// get current (Baloooon) size
		// @ 2009-07-18
		getCurrentSize: function(){
			this.currentSize = {
				width: this.container.width(),
				height: this.container.height()
			}
		},
		
		// Baloooon.setPos
		// set Baloooon position
		// @ 2009-07-18
		setPos: function(e){
			var d = document,
				options = this.options,
			
				clientH = d.documentElement.clientHeight,
				//clientW = d.documentElement.clientWidth,
				currentY = d.body.scrollTop || d.documentElement.scrollTop,
				containerH = this.container.height(),
				top = parseInt(options.top),
				bottom = parseInt(options.bottom),
				
				containerTopHeight = $j('>div.baloooon_top',this.container).height(),
				containerBottomHeight = $j('>div.baloooon_bottom',this.container).height(),
				
				boxTopHeight = $j('div.baloooon_content_main div.baloooon_top',this.container).height(),
				boxBottomHeight = $j('div.baloooon_content_main div.baloooon_bottom',this.container).height(),
				
				posValue = options.flexiblePosition ? (clientH/2)<(e.pageY-currentY) ? -(containerH+top) : bottom : options.defaultPos=='above' ? -(containerH+top) : bottom,
				
				// below or above by posValue
				classValue = posValue>0 ? '.baloooon_below' : '.baloooon_above',
				maxContainerHeight = posValue>0 ? currentY+clientH-e.pageY - bottom : e.pageY-currentY - top,
				
				// contents might be overflow container
				overflow = containerH>maxContainerHeight ? true : false,
				//isOverflow = false,
				
				contentBox = $j('div.baloooon_content_box',this.container);
				maxContentBoxHeight = maxContainerHeight-(containerTopHeight+containerBottomHeight+boxTopHeight+boxBottomHeight);
				
			// if isOverflow set true, isOverflow is being true
			this.isOverflow = (!this.isOverflow && overflow) ? true : this.isOverflow ? true : false;
			this.container.removeClass(posValue>0 ? 'above' : 'below').addClass(posValue>0 ? 'below' : 'above');
			
			$j('.baloooon_below,.baloooon_above',this.container).hide();
			$j(classValue,this.container).show();
			
			if(options.overflow){
				if(this.isOverflow){
					contentBox.height(maxContentBoxHeight-50);
				}else{
					this.container.height('auto');
					contentBox.height('auto');
				}
			}

			this.container.css({
				//maxWidth: clientW - 50,
				top: e.pageY + posValue,
    			left: e.pageX - parseInt(this.options.left)
    		});
    		    		
		},
		
		// Baloooon.loadAttrContent
		// loading contents by attr key
		// @ 2009-07-18
		loadAttrContent: function(key,e){
			this.replaceContent(key,e);
		},
		
		// Baloooon.loadJSON
		// loading json by triggerkey
		// @ 2009-07-18
		loadJSON: function(key,e){
			var that = this, triggerKey = this.triggerKey(key);
			if(!this.JSONloaded){
				$j.getJSON(
					that.options.path,
					function(json){
						that.JSONloaded = true;
						for(var d in json){
							that.textBox[d] = json[d];
						}
						that.replaceContent(that.textBox[triggerKey],e);
					}
				);
			}else{
				this.replaceContent(that.textBox[triggerKey],e);
			}
		},
		
		// Baloooon.loadAJAXContent
		// loading Ajax content by href key
		// @ 2009-07-18
		loadAJAXContent: function(key,e){
			var that = this;
			$j.ajax({
				url: key,
				cache: true,
				error: function(){
					that.replaceContent('XMLHTTPRequest was failed',e);
				},
				success: function(html){
					that.replaceContent(html,e);
				}
			});
		},
		
		// Baloooon.loadINLINEContent
		// loading inline content by href key
		// @ 2009-07-18
		loadINLINEContent: function(key,e){
			if($j(key) && $j(key).length>0){
				this.replaceContent($j(key).html(),e);
			}else{
				this.replaceContent('There is no Object['+key+']',e);
			}
		},
		
		// Baloooon.IFRAMEContent
		// loading iframe content by href key
		// @ 2009-07-18
		loadIFRAMEContent: function(key,e){
			var iframe = $j(document.createElement('iframe'));
		},
		
		// Baloooon.replaceContent
		// replace old contents with new contents in Baloooon
		// @ 2009-07-18
		replaceContent: function(content,e){
			$j('.baloooon_loader',this.container).hide();
			$j('.baloooon_content_box',this.container).html(content);
			$j('.baloooon_content_main',this.container).show();
			this.setPos(e);
			//this.changeSize();
		},
		
		// Baloooon.triggerKey
		// return triggerKey using JSON loading
		// @ 2009-07-18
		triggerKey: function(key){
			for(var i=0;i<this.options.set.length;i++){
				if(key.match(this.options.set[i][1])) return this.options.set[i][0];
			}
		}
	}
	
})(jQuery);