text: /**
   * portal - jQuery EasyUI
   * 
   * Licensed under the GPL:
   *   http://www.gnu.org/licenses/gpl.txt
   *
   * Copyright 2010 stworthy [ stworthy@gmail.com ] 
   * 
   * Dependencies:
   *   draggable
   *   panel
   * 
   */
  (function($){
  	/**
  	 * initialize the portal
  	 */
  	function init(target){
  		$(target).addClass('portal');
  		var table = $('<table border="0" cellspacing="0" cellpadding="0"><tr></tr></table>').appendTo(target);
  		var tr = table.find('tr');
  		
  		var columnWidths = [];
  		var totalWidth = 0;
  		$(target).find('>div').each(function(){	// each column panel
  			var column = $(this);
  			totalWidth += column.outerWidth();
  			columnWidths.push(column.outerWidth());
  			
  			var td = $('<td class="portal-column-td"></td>').appendTo(tr)
  			column.addClass('portal-column').appendTo(td);
  			column.find('>div').each(function(){	// each portal panel
  				var p = $(this).addClass('portal-p').panel({
  					doSize:false,
  					cls:'portal-panel'
  				});
  				makeDraggable(target, p);
  			});
  		});
  		for(var i=0; i<columnWidths.length; i++){
  			columnWidths[i] /= totalWidth;
  		}
  		
  		$(target).bind('_resize', function(){
  			var opts = $.data(target, 'portal').options;
  			if (opts.fit == true){
  				setSize(target);
  			}
  			return false;
  		});
  		
  		return columnWidths;
  	}
  	
  	function setSize(target){
  		var t = $(target);
  		var opts = $.data(target, 'portal').options;
  		if (opts.fit){
  			var p = t.parent();
  			opts.width = p.width();
  			opts.height = p.height();
  		}
  		if (!isNaN(opts.width)){
  			if ($.boxModel == true){
  				t.width(opts.width - (t.outerWidth() - t.width()));
  			} else {
  				t.width(opts.width);
  			}
  		} else {
  			t.width('auto');
  		}
  		if (!isNaN(opts.height)){
  			if ($.boxModel == true){
  				t.height(opts.height - (t.outerHeight() - t.height()));
  			} else {
  				t.height(opts.height);
  			}
  		} else {
  			t.height('auto');
  		}
  		
  		var hasScroll = t.find('>table').outerHeight() > t.height();
  		var width = t.width();
  		var columnWidths = $.data(target, 'portal').columnWidths;
  		var leftWidth = 0;
  		
  		// calculate and set every column size
  		for(var i=0; i<columnWidths.length; i++){
  			var p = t.find('div.portal-column:eq('+i+')');
  			var w = Math.floor(width * columnWidths[i]);
  			if (i == columnWidths.length - 1){
  				w = width - leftWidth - (hasScroll == true ? 28 : 10);
  			}
  			if ($.boxModel == true){
  				p.width(w - (p.outerWidth()-p.width()));
  			} else {
  				p.width(w);
  			}
  			leftWidth += p.outerWidth();
  			
  			// resize every panel of the column
  			p.find('div.portal-p').panel('resize', {width:p.width()});
  		}
  	}
  	
  	/**
  	 * set draggable feature for the specified panel
  	 */
  	function makeDraggable(target, panel){
  		var spacer;
  		panel.panel('panel').draggable({
  			handle:'>div.panel-header>div.panel-title',
  			proxy:function(source){
  				var p = $('<div class="portal-proxy">proxy</div>').insertAfter(source);
  				p.width($(source).width());
  				p.height($(source).height());
  				p.html($(source).html());
  				p.find('div.portal-p').removeClass('portal-p');
  				return p;
  			},
  			onStartDrag:function(e){
  				$(this).hide();
  				spacer = $('<div class="portal-spacer"></div>').insertAfter(this);
  				setSpacerSize($(this).outerWidth(), $(this).outerHeight());
  			},
  			onDrag:function(e){
  				var p = findPanel(e, this);
  				if (p){
  					if (p.pos == 'up'){
  						spacer.insertBefore(p.target);
  					} else {
  						spacer.insertAfter(p.target);
  					}
  					setSpacerSize($(p.target).outerWidth());
  				} else {
  					var c = findColumn(e);
  					if (c){
  						if (c.find('div.portal-spacer').length == 0){
  							spacer.appendTo(c);
  							setSize(target);
  							setSpacerSize(c.width());
  						}
  					}
  				}
  			},
  			onStopDrag:function(e){
  				$(this).css('position', 'static');
  				$(this).show();
  				spacer.hide();
  				$(this).insertAfter(spacer);
  				spacer.remove();
  				setSize(target);
  				var opts = $.data(target, 'portal').options;
  				opts.onStopDrag.call();
  			}
  		});
  		
  		/**
  		 * find which panel the cursor is over
  		 */
  		function findPanel(e, source){
  			var result = null;
  			$(target).find('div.portal-p').each(function(){
  				var pal = $(this).panel('panel');
  				if (pal[0] != source){
  					var pos = pal.offset();
  					if (e.pageX > pos.left && e.pageX < pos.left + pal.outerWidth()
  							&& e.pageY > pos.top && e.pageY < pos.top + pal.outerHeight()){
  						if (e.pageY > pos.top + pal.outerHeight() / 2){
  							result = {
  								target:pal,
  								pos:'down'
  							};
  						} else {
  							result = {
  								target:pal,
  								pos:'up'
  							}
  						}
  					}
  				}
  			});
  			return result;
  		}
  		
  		/**
  		 * find which portal column the cursor is over
  		 */
  		function findColumn(e){
  			var result = null;
  			$(target).find('div.portal-column').each(function(){
  				var pal = $(this);
  				var pos = pal.offset();
  				if (e.pageX > pos.left && e.pageX < pos.left + pal.outerWidth()){
  					result = pal;
  				}
  			});
  			return result;
  		}
  		
  		/**
  		 * set the spacer size
  		 */
  		function setSpacerSize(width, height){
  			if ($.boxModel == true){
  				spacer.width(width - (spacer.outerWidth() - spacer.width()));
  				if (height){
  					spacer.height(height - (spacer.outerHeight() - spacer.height()));
  				}
  			} else {
  				spacer.width(width);
  				if (height){
  					spacer.height(height);
  				}
  			}
  		}
  	}
  	
  	function json2str(o) {
  		 var arr = [];
  		 var fmt = function(s) {
  			 if (typeof s == 'object' && s != null) return json2str(s);
  			return /^(string|number)$/.test(typeof s) ? "'" + s + "'" : s;
  		}
  		for (var i in o) arr.push("'" + i + "':" + fmt(o[i]));
  		return '{' + arr.join(',') + '}';
  	}
  	
  	
  	$.fn.portal = function(options, param){
  		if (typeof options == 'string'){
  			return $.fn.portal.methods[options](this, param);
  		}
  		
  		options = options || {};
  		return this.each(function(){
  			var state = $.data(this, 'portal');
  			if (state){
  				$.extend(state.options, options);
  			} else {
  				state = $.data(this, 'portal', {
  					options: $.extend({}, $.fn.portal.defaults, $.fn.portal.parseOptions(this), options),
  					columnWidths: init(this)
  				});
  			}
  			if (state.options.border){
  				$(this).removeClass('portal-noborder');
  			} else {
  				$(this).addClass('portal-noborder');
  			}
  			setSize(this);
  		});
  	};
  	
  	$.fn.portal.methods = {
  		options: function(jq){
  			return $.data(jq[0], 'portal').options;
  		},
  		resize: function(jq, param){
  			return jq.each(function(){
  				if (param){
  					var opts = $.data(this, 'portal').options;
  					if (param.width) opts.width = param.width;
  					if (param.height) opts.height = param.height;
  				}
  				setSize(this);
  			});
  		},
  		getPanels: function(jq, columnIndex){
  			var c = jq;	// the panel container
  			if (columnIndex){
  				c = jq.find('div.portal-column:eq(' + columnIndex + ')');
  			}
  			var panels = [];
  			c.find('div.portal-p').each(function(){
  				panels.push($(this));
  			});
  			return panels;
  		},
  		add: function(jq, param){	// param: {panel,columnIndex}
  			return jq.each(function(){
  				var c = $(this).find('div.portal-column:eq(' + param.columnIndex + ')');
  				var p = param.panel.addClass('portal-p');
  				p.panel('open');
  				p.panel('panel').addClass('portal-panel').appendTo(c);
  				makeDraggable(this, p);
  				
  				p.panel('resize', {width:c.width()});
  			});
  		},
  		remove: function(jq, panel){
  			return jq.each(function(){
  				var panels = $(this).portal('getPanels');
  				for(var i=0; i<panels.length; i++){
  					var p = panels[i];
  					if (p[0] == $(panel)[0]){
  						p.panel('destroy');
  					}
  				}
  			});
  		},
  		getLayout: function(jq){
  			 var portal = $(jq).portal('getPanels');
  			 var portalColumnTd = $(jq).find('.portal-column-td');
  			 var portalLayout="{";
  			 var t = 0;
  			 for(var i = 0; i < portalColumnTd.length;i++){
  				var panel="\""+i+"\":[";
  				var colomLength = $(portalColumnTd[i]).find('.portal-panel').length;
  				for(var j = 0; j < colomLength;j++){
  					panel += json2str(portal[t].panel('options'))+",";
  					t++;
  				}
  				panel = panel.substring(0,panel.length-1)+"]";
  				portalLayout += panel+",";
  			 }
  			 if(portalLayout.substring(0,portalLayout.length-1) == ""){
  				portalLayout = portalLayout+"}";
  			 }else{
  				portalLayout = portalLayout.substring(0,portalLayout.length-1)+"}";
  			 }
  			 return portalLayout;
  		},
  		getPanelForTitle:function(jq,title){
  			 var portal = $(jq).portal('getPanels');
  			 var panel;
  			 $.each(portal,function(i,v){
  				if(v.panel('options').title == title) {
  					panel = v; 
  					return;
  				}
  			 });
  			 return panel;
  		}
  	};
  	
  	$.fn.portal.parseOptions = function(target){
  		var t = $(target);
  		return {
  			width: (parseInt(target.style.width) || undefined),
  			height: (parseInt(target.style.height) || undefined),
  			border: (t.attr('border') ? t.attr('border') == 'true' : undefined),
  			fit: (t.attr('fit') ? t.attr('fit') == 'true' : undefined)
  		};
  	};
  	
  	$.fn.portal.defaults = {
  		width:'auto',
  		height:'auto',
  		border:true,
  		fit:false,
  		onStopDrag:function(){
  			
  		}
  	};
  })(jQuery);