/*
 * DaiShu CMS 5 Editable Zone
 *
 * Copyright (c) 2010 Scorpion Design, Inc.
 */
(function( $ ) {

$.widget( "cms.flyout", {
	options: {
		item: '.ifly',
		panel: 'div.iflylist',
		hover: 'iflyOver',
		right: 'iflyRight',
		loading: 'iflyLoading',
		url: '/CobaltAjax.ashx?M=GetPage',
		fit: 'none none',
		collision: 'flip fit',
		maxitems: 16
	},

	// Create the flyout.
	_create: function() {;
		// Note the top level properties.
		this.element[0]._start = true;

		// Get the mobile menu panel, if available, and set the starting state of the first panel.
		this.elements = {};
		if ( this.options.mobile ) {
			this.elements.panel = this.element.find('div.imenupanel');
			this.elements.top = this.elements.panel.children(':first');
			this._mobilePosition( this.elements.top[0] );
			this._stack = [];
		}

		// Set up the child items.
		this._createItems( this.element );

		// Bind the hover event.  Mouseover will be used for both mouse on and mouse out of every item and sub item.
		// Mouseleave means we left the entire flyout, so kill everything.
		this.element.bind('mouseover mouseleave',$.proxy(this._fly, this));

		// Set up the mobile click event.
		if ( this.options.mobile ) {
			if ( !this._handleClick ) {
				this._handleClick = $.proxy(this._click,this);
			}
			$(document).bind('click',this._handleClick);
		}
	},

	// Build out the properties for the flyout items.
	_createItems: function( start ) {
		// Build a closure to set up the flyout items.
		var flyout = this;

		var fn = function(i){
			// Set the depth of this node relative to it's nearest parent item.
			this._p = 0;
			var p = this;
			while ( p && !p._item && !p._start && p.parentNode ) {
				this._p--;
				p = p.parentNode;
			}
			this._top = p && p._start;

			// Note that this is an item.
			this._item = true;

			// If this is a top-level element, set its properties.
			if ( this._top ) {
				this._my = flyout.options.my || this.getAttribute('_my') || 'left top';
				this._at = flyout.options.at || this.getAttribute('_at') || 'left bottom';
				var offset = this.getAttribute('_offset');
				if ( offset ) {
					try {
						this._offset = eval( '('+offset+')' );
					} catch(ex) {
						this._offset = {};
					}
				} else {
					this._offset = {};
				}
			}

			// If we have a selector for child panels.
			if (typeof flyout.options.panel === 'string') {

				// Record the child panel position.
				for (var i=0;i<this.childNodes.length;i++) {
					// Get the next child.
					var child = $(this.childNodes[i]);

					// If this child is a flyout panel.
					if ( ( flyout.options.mobile && child[0] === flyout.elements.panel[0] ) || child.is(flyout.options.panel) ) {
						// Record the child index.
						this._child = i;

						// Note that this is a panel.
						this.childNodes[i]._panel = true;

						// Record the default opacity.
						if ( flyout.options.mobile ) {
							child.css({display:'none'});
						} else {
							this._opacity = $.toFloat(child.css('opacity'))||1;
							child.css({opacity:0});
						}

						// We're done with this item.
						return;
					}
				}

				// Check to see if we need to load the child elements with ajax.
				if ( this._child === undefined ) {
					var child = $.toInt( this.getAttribute('_childpages') );
					if ( child > 0 ) {
						this._child = 'ajax';
					}
				}
			}
		};

		// If we don't have any flyout elements, load them.
		if ( start.find(this.options.item).each(fn).length === 0 ) {
			this._loadChildren(start[0]);
		}
	},

	// Fly the element according to the mouse event.
	_fly: function(e) {
		// Mouseleave means we left the entire menu and all children.
		if (e.type === 'mouseleave')
		{
			this._killfade = false;
			if ( this.options.mobile ) {
				this._over = false;
			} else {
				this._flyOut();
			}
			return;
		} else if ( $.cms.flyout.disabled ) {
			return;
		}

		// Get the nearest actual flyout item.
		var item = e.target;
		while ( item && !item._item ) {
			if ( item._start ) {
				return;
			}
			item = item.parentNode;
		}
		if (!item) {
			return;
		}

		if ( this.options.mobile ) {
			this._over = true;
			this._event = [e, item];
		} else {
			this._flyOver(e, item);
		}
	},

	// Only fly on click.
	_click: function(e) {
		var link = $.getLinkTarget(e),
			href = link && link.attr('href'),
			m = href && /^javascript:(\w+)\('([^']+)'/i.exec(href),
			action = m && m[2];

		if ( ( this._on && !this._over ) || ( this._on && action === 'Start' ) ){
			// We've clicked somewhere else on the document, or we've clicked the "Start" link a second time.
			this._killfade = false;
			this._flyOut();
			return false;
		} else if ( this._over && this._on && ( action === 'Back' || ( e && e.target && /\bileft\b/.test(e.target.className) ) ) ) {
			if ( this._events ) {
				this._events.pop();
				this._event = this._events[ this._events.length-1 ];
			}
			if ( this._event ) {
				this._flyOver.apply( this, this._event );
			}
			return false;
		} else if ( href && action !== 'Start' ) {
			// If we've clicked on another link, let the default event handle it.
			return;
		} else if ( this._over && this._event ) {
			// Save the event in the stack, since we're activating it.
			if ( !this._events ) {
				this._events = [];
			}
			this._events.push(this._event);

			// Handle the event.
			this._flyOver.apply( this, this._event );
			return false;
		}
	},

	// Get the flyout parent of the item.
	_getParent: function(item) {
		switch (item._p) {
			case 0:
				return item;
			case -1:
				return item.parentNode;
			case -2:
				return item.parentNode.parentNode;
			case -3:
				return item.parentNode.parentNode.parentNode;
			case -4:
				return item.parentNode.parentNode.parentNode.parentNode;
			default:
				return null;
		}
	},

	// We may need to navigate through a hierarchy to get to the correct nodes.
	_getChildNodes: function(parent) {
		var depth = parent._depth,
			nodes = parent.childNodes,
			pos;

		if ( depth ) {
			while ( (pos=depth.shift()) !== undefined ) {
				nodes = nodes[pos].childNodes;
			}
		}

		return nodes;
	},

	// Does the specified parent already have an active flyout?
	_getActive: function(parent) {
		if ( parent && parent._active && $.isArray( parent._active ) ) {
			var el = parent;
			for (var i=0; i<parent._active.length; i++ ) {
				// Get the position in the hierarchy and get the item and the next level down.
				var pos = parent._active[i];
				el = el.childNodes[pos];
			}
			return el === parent ? null : el;
		} else {
			return null;
		}
	},

	// Get the index of the item in relation to its item parent.
	_getChildPosition: function(item, parent) {
		var active = [];

		do {
			// Iterate through the sibling nodes.
			for ( var i=0; i<item.parentNode.childNodes.length; i++ ) {
				if ( item.parentNode.childNodes[i] === item ) {
					// If we find a match, add the index at the front of the array.
					active.unshift(i);
					break;
				}
			}
			// Move up to the parent element.
			item = item.parentNode;

		} while ( item && item.parentNode && item != parent );

		return active;
	},

	// Show a specific item's children.
	_flyOver: function(e, item) {
		// Get the current flyout's parent and any already-active element.		
		var parent = this._getParent(item);
		if (!parent) return;
		var active = this._getActive(parent);

		// If we already had a timeout to show a child element, clear it.
		if (parent._timeout) {
			clearTimeout(parent._timeout);
			parent._timeout = null;
		}

		// If we're in the middle of any fading, kill it.
		this._killfade = true;

		// If we have an active element.
		if (active) {
			if (active === item) {
				// If we've re-moused over the main item, ensure any active child items are "moused off"
				if (!e.target._panel) {
					this._hideChildren(e, this._getActive(item));
				}
				return;
			} else {
				// If we're really close to the right edge of a flown panel, show the next element on a timeout,
				// just in case the user didn't intend to mouse off of it.
				if (!item._top) {
					var dim = $(item).dimensions();
					var distance = (dim.left + dim.width) - e.clientX;

					// If we're really close to the edge, the user may be trying to move the mouse onto an already-flown child menu.
					if (distance>=0 && distance<50) {
						// Build a closure to handle the show item on a timeout.
						var fn = function(flyout, item) {
							return function(){
								var parent = flyout._getParent(item);
								var active = flyout._getActive(parent);
								flyout._showItem(null, item, parent, active);
								item = null;
								parent = null;
							};
						}(this, item);

						// Set the new timeout.
						parent._timeout = setTimeout(fn, 250);

						// Exit now.
						return;
					}
				}
				this._showItem(e, item, parent, active);
			}
		}
		else {
			this._showItem(e, item, parent);
		}
	},

	// Hide all active children from the bottom up.
	_hideChildren: function(e, item) {
		// If we have an item.
		if (item) {
			// Recurse the method first to hide any active child elements.
			this._hideChildren(e, this._getActive(item));
			// Then hide this item.
			this._hideItem(e, item);
		}
	},

	// Display the item on screen.
	_showItem: function(e, item, parent, active) {
		// If we don't have the flyout parent, get it.
		if (!parent) {
			parent = this._getParent(item);
			if (!parent) return;
			active = this._getActive(parent);
		}

		// If we are set to show another element on a timeout, kill it.
		if (parent._timeout) {
			clearTimeout(parent._timeout);
			parent._timeout = null;
		}

		// If we have another active item, hide it.
		if ( active && active != item ) {
			this._hideChildren(e, active);
		}

		// Add the hover class.
		if (this.options.hover) {
			$(item).addClass(this.options.hover)
				.children('a')
					.addClass(this.options.hover);
		}

		// If we have a hover event defined, fire it.
		if (this.options.onhover) {
			// If the event expicitly returns false, stop here.
			if ( this.options.onhover.apply(this, [{type:'mouseover'}, null, item, parent]) === false ) {
				return false;
			}
		}

		// Set this as the active item for its parent.
		parent._active = this._getChildPosition(item, parent);

		// If we have a child panel, we'll need to show it.
		if ( item._child !== undefined ) {
			if ( item._child === 'ajax' ) {
				if ( item._killajax ) {
					// We've moved back onto the element, so we no longer need to kill it.
					item._killajax = null;
				} else {
					// Load the child by ajax and then show it.
					this._loadChildren(item);
				}
			} else {
				// Get a reference to the flyout panel.
				var child;
				if ( this.options.mobile && item._top ) {
					child = $(item.childNodes[item._child].childNodes[0]);
				} else {
					child = $(item.childNodes[item._child]);
				}

				// Are in the process of fading the element?
				var fading;
				if ( this.options.mobile ) {
					var left = $.toFloat(child.css('left'));
					fading = left > 0 || left < 320;
				} else {
					var opacity = $.toFloat(child.css('opacity'));
					fading = ( opacity > 0.00 && opacity < item._opacity );
				}

				// A top level item will have a 1/10th of a second delay before showing the child items.
				// (Unless we just moused over it in the middle of fading out.)
				if ( item._top && !fading ) {
					var fn = function(flyout, item, child){
						return function(){
							flyout._showChild(item, child);
							flyout = null;
							item = null;
							child = null;
						};
					}(this, item, child);
					item._timeout = setTimeout(fn, 100);
					return;
				}
				this._showChild(item, child);
			}
		}

		// If we have a parent item, ensure it is visible as well (this will kill any cascading fade-outs).
		if ( item.parentNode && item.parentNode !== this.element[0] && item.parentNode.parentNode._item ) {
			this._showItem(e, item.parentNode.parentNode);
		}

	},

	// Load child elements with an ajax call.
	_loadChildren: function(item) {
		var pageid = $.toInt( item.getAttribute('_pageid') );
		if ( pageid > 0 ) {
			// Mark the element as loading.
			var el = $(item);
			el.addClass(this.options.loading);

			// Load the pages with an ajax call.
			$.cms.flyout.ajax.load(this, item, pageid);
		}
	},

	// Render the child elements.
	_renderChildren: function(item, results) {
		var el = $(item);
		el.removeClass(this.options.loading);

		// If we have valid results.
		if ( results && results.ChildPages && results.ChildPages.length ) {
			// Start the panel.
			var cls = this.options.panel.split('.').pop();
			var sb = ['<div class="'+cls+'">'];
			cls = this.options.item.split('.').pop();

			if ( this.options.mobile ) {
				sb.push('<div class="');
				sb.push(cls);
				sb.push(' ileft"><a href="javascript:void(\'Back\');"><span>Back</span></a></div>');
			}

			// Add each of the child items with the appropriate properties.
			for ( var i=0; i<results.ChildPages.length; i++ ) {
				var page = results.ChildPages[i];
				var children = page.ChildPages.length;
				sb.push('<div class="');
				sb.push(cls);
				if ( children > 0 ) {
					sb.push(' ');
					sb.push(this.options.right);
				}
				sb.push('" _pageid="');
				sb.push(page.PageID);
				if ( children > 0 ) {
					sb.push('" _childpages="');
					sb.push(children);
				}
				sb.push('"><a href="');
				sb.push(page.URL||page.Path);
				if ( page.URLProperties && page.URLProperties.indexOf('target,_blank') >= 0 ) {
					sb.push('" target="_blank');
				}
				sb.push('">');
				sb.push(page.PageName);
				sb.push('</a></div>');
			}
			sb.push('</div>');

			// Add and initialize the new pages.
			var child = $( sb.join("") ).appendTo(el);
			item._child = item.childNodes.length - 1;

			if ( this.options.mobile ) {
				this._mobilePosition( child[0] );
			} else {
				item._opacity = $.toFloat(child.css('opacity'))||1;
				child.css({opacity:0});
			}
			this._createItems(el);

			if ( item._killajax ) {
				// We moused off this item, so we shouldn't show it.
				item._killajax = null;
			} else {
				// Make the child flyout visible.
				var child = $(item.childNodes[item._child]);
				this._showChild(item, child);
			}
		}
	},

	// Set the mobile item starting position.
	_mobilePosition: function(menu){
		var w = 320 - $.toInt( $.curCSS(menu,'borderLeftWidth') )
					- $.toInt( $.curCSS(menu,'borderRightWidth') )
					- $.toInt( $.curCSS(menu,'paddingLeft') )
					- $.toInt( $.curCSS(menu,'paddingRight') );
		menu.style.display = 'none';
		menu.style.top = '0px';
		menu.style.left = '320px';
		menu.style.width = w+'px';
	},

	// Show a child flyout.
	_showChild: function(item, child) {
		var w, h, doc, dim, css;
		if ( this.options.mobile ) {
			// Note that the menu is on.
			this._on = true;

			// Activate the panel and child so they have layout.
			this.elements.panel[0].style.display = 'block';
			child[0].style.display = 'block';

			// If this element is already in the stack, we're good.
			if ( this._stack.length && this._stack.indexOf(child[0]) >= 0 ) {
				return;
			}

			// Get the dimensions of the element and the document.
			dim = this.element.dimensions();
			doc = $(document.body).dimensions();

			// Set the height of the main panel.
			h = Math.max( doc.height - dim.top - dim.height,
						child.height(),
						this._stack.length ? $(this._stack[this._stack.length-1]).height() : 0 );

			// Make sure the panel is correctly positioned (this is primarily for non-mobile browsers viewing this menu).
			w = $(window).width() - $.toInt($(document.body).css('marginRight'));
			css = {
				top: dim.top+dim.height,
				left: ( w > 320 ? Math.floor( ( w - 320 ) / 2 ) : 0 ),
				height: h
			};
			this.elements.panel.css(css);
		} else {
			css = $.placement(child.css({display:'block'}), {
				my: item._my,
				at: item._at,
				of: item,
				offset: item._offset||this.options.offset||{top:-2},
				collision: item._top ? 'none none' : this.options.collision
			});
		}

		// Set it.
		if ( css && child.length ) {
			if ( !this.options.mobile && $.browser.msie && $.browser.version <= 7 ) {
				// MSIE requires named width to prevent child elements with hasLayout to not honor width:auto.
				var w = child[0]._width;
				if (!w) {
					child[0]._width = w = child.width();
				}
				css.width = w;
			}

			// Get the animation css.
			if ( this.options.mobile ) {
				// Add the element to the stack.
				this._stack.push( child[0] );

				// Slide the first element to the left.
				var left = ( 1 - this._stack.length ) * 320;
				this.elements.top.stop().animate({ left: left }, 600);
			} else {
				// If the current opacity is 0, start at just above 0.
				var opacity = $.toFloat(child.css('opacity'));
				if ( opacity === 0.00 ) {
					css.opacity = 0.01;
				}
				// Fade in the element.
				child.stop().css(css).animate({ opacity: item._opacity }, 150);
			}
		}

		// If we haven't already set up this flyout for scrolling.
		if ( !child[0]._scroll ) {
			// If we have more child items than will fit, hide the ones at the end.
			var items;
			if ( this.options.maxitems > 0 && (items = child.children()).length > this.options.maxitems ) {
				items.slice( this.options.maxitems, items.length ).hide();
				child[0]._scroll = true;
			}

			// If we had to set up scrolling.
			if ( child[0]._scroll ) {
				var w = child.width();
				child.css({width:w}).bind('mousewheel', this._scrollChild);
				$('<div class="ui-more-items"><img class="more-up" src="/Shared/images/spacer.gif"><img class="more-down" src="/Shared/images/spacer.gif"></div>')
					.appendTo(child)
					.bind('mouseover mouseout', this._scrollTimer);
			}
		}
	},

	// Scroll child elements.
	_scrollChild: function(e) {
		var items = $(this).children(':not(div.ui-more-items)');
		var first = items.index( items.filter(':visible:first') );
		var last = items.index( items.filter(':visible:last') );
		var delta = e.delta;

		// Scrolling down.
		while ( delta < 0 && last < items.length - 1 ) {
			items.eq(first++).hide();
			items.eq(++last).show();
			delta++;
		}

		// Scrolling up.
		while ( delta > 0 && first > 0 ) {
			items.eq(last--).hide();
			items.eq(--first).show();
			delta--;
		}

		// Don't bubble the mousewheel event.
		return false;
	},

	_scrollTimer: function(e) {
		// Clear any existing timer.
		if ( this._interval ) {
			clearInterval( this._interval );
			this._interval = null;
		}

		// If we've moused out, do nothing.
		if ( e.type === 'mouseout' ) {
			return;
		}
		
		// What direction to scroll?
		var el = $(e.target);
		var direction;
		if ( el.is('img.more-up') ) {
			direction = 1;
		} else if ( el.is('img.more-down') ) {
			direction = -1;
		} else {
			return;
		}

		// Build a closure to handle auto-scroll.
		var fn = function(el, delta){
			return function(){
				$.cms.flyout.prototype._scrollChild.apply(el, [{delta:delta}]);
			};
		}(this.parentNode, direction);

		// Set the interval.
		this._interval = setInterval(fn, 75);

		// And fire it once immediately.
		fn();
	},

	// Hide all items.
	_flyOut: function(speed) {
		// If we've been asked to kill the fadeout, stop here.
		if (this._killfade) {
			this._killfade = false;
			return;
		}

		// Start from the currently active base element and walk all the way down the tree.
		var elem = null, active = this._getActive(this.element[0]);
		while ( active ) {
			elem = active;
			active = this._getActive(elem);
		}

		if ( elem ) {
			// Fade the child-most item with a callback to recurse this method until everything is gone.
			var fn = function(flyout){
				return function(speed){
					flyout._flyOut(speed);
					flyout = null;
				};
			}(this);
			this._hideItem( null, elem, true, fn, speed||(this.options.mobile?600:800) );
		} else if ( this.options.mobile ) {
			this.elements.panel.css({display:'none'});
			this._on = false;
			this._stack = [];

			// If we still have events, we're hovered over the main button.  Keep the first event alive.
			if ( this._events && this._events.length ) {
				this._event = this._events[0];
			} else {
				this._event = null;
			}
			this._events = null;
		}
	},

	// Hide a specific item.
	_hideItem: function(e, item, fade, callback, speed) {
		// If there was fade-in timeout set, clear it.
		if (item._timeout) {
			clearTimeout(item._timeout);
			item._timeout = null;
		}

		// Note that the parent no longer has an active child.
		var parent = this._getParent(item);
		if (parent) {
			parent._active = null;
		}

		// Build a hover class closure as needed.
		var fn = function( item, cls, callback ) {
			return function(speed){
				// Remove the class.
				if (cls) {
					$(item).removeClass(cls)
						.children('a')
							.removeClass(cls);
				}

				// Fire any callback.
				if (callback) {
					callback(speed);
					callback = null;
				}
				// Null out the closure element.
				item = null;
			};
		}(item, this.options.hover, callback);

		// If we have a hover event defined, fire it.
		if (this.options.onhover) {
			// If the event expicitly returns false, stop here.
			if ( this.options.onhover.apply(this, [{type:'mouseout'}, speed, item, parent]) === false ) {
				return false;
			}
		}

		if ( item._ajaxip ) {
			item._killajax = true;
		} else if ( typeof item._child !== 'number' ) {
			// If we don't have a child item, there's no thing more to do here.
			fn();
			return;
		}

		// Get the child and stop any active animation.
		var child;
		if ( this.options.mobile && item._top ) {
			child = $(item.childNodes[item._child].childNodes[0]);
		} else {
			child = $(item.childNodes[item._child]);
		}

		if ( this.options.mobile ) {
			if ( this._stack.indexOf( child[0] ) >= 0 ) {
				// If we're clearing an item in the stack, remove it.
				this._stack.removeItem( child[0] );
			} else {
				// If we couldn't find the item in the stack, hide it immediately.
				child[0].style.display = 'none';
			}

			// Animate the stack.
			var finished = function(_child){
					return function(){
						_child.style.display = 'none';
						_child = null;
					};
				}(child[0]);
			this.elements.top.stop().animate({left: ( 1 - this._stack.length ) * 320 },300,'linear',finished);

			// Fire the callback on a timeout.
			setTimeout(fn,300);
		} else {
			// If we've defined a fade and we're not in the middle of a fade-in, fade it out.
			if ( fade && $.toFloat(child.css('opacity')) === item._opacity ) {
				var txt = $(item).children('a').text();
				var opacity = $.toFloat(child.css('opacity'));
				if ( opacity === 1.00 ) {
					child.css({opacity:0.99});
				}
				child.stop().animate({opacity:0},speed||150,'linear',function(e){
					// Hide the child element.
					$(this).css({display:'none'});
					fn(300);
				});
			} else {
				// Otherwise, hide it immediately.
				child.stop().css({display:'none', opacity:0});
				fn();
			}
		}
	},

	// Dispose of flyout resources.
	destroy: function() {

		// Unbind the hover event.
		this.element.unbind('mouseover mouseleave');

		// Clear out the node array to prevent memory leaks.
		$.Widget.prototype.destroy.apply( this, arguments );
	}

});

// Static flyout methods.
$.extend($.cms.flyout, {
	disabled: false,

	// Migrate flyout attributes to the parent element.
	setParent: function() {
		var el = $(this);

		// If this element is a link, it cannot hold child flyouts, so migrate it to the parent.
		if ( el.is('a') ) {
			// Migrate the page attributes to the parent.
			var p = el.parent(),
				direction = el.attr('_direction'),
				corner = el.attr('_corner'),
				icobalt = el.attr('icobalt');

			p.addClass('ifly')
				.attr('icobalt',icobalt||'')
				.attr('_pageid',el.attr('_pageid')||'')
				.attr('_childpages',el.attr('_childpages')||'')
				.attr('_exclude',el.attr('_exclude')||'')
				.attr('_maxpages',el.attr('_maxpages')||'')
				.attr('_offset',el.attr('_offset')||'')
				.attr('_maxlevels',el.attr('_maxlevels')||'');

			// Note if we're flying to the right.
			if ( direction === 'Right' || corner === '{top:true,left:false}' ) {
				p.attr('_my', 'left top' );
				p.attr('_at', 'right top' );
			}

			// Remove the attributes from the child.
			el.removeAttr('icobalt')
				.removeAttr('_pageid')
				.removeAttr('_childpages')
				.removeAttr('_exclude')
				.removeAttr('_corner')
				.removeAttr('_direction')
				.removeAttr('_maxitems')
				.removeAttr('_maxpages')
				.removeAttr('_offset')
				.removeAttr('_maxlevels');
		} else {
			// Otherwise, identify the element directly.
			el.addClass('ifly');
		}
	},

	ajax: {
		// Are we in the middle of an ajax call?
		ip: false,

		// The stack of future ajax flyouts to run.
		stack: [],

		// Load a specific child flyout.
		load: function(flyout, item, pageid) {
			if ( item._ajaxip ) {
				// We're already loading this item's children by ajax.
				return;
			} else {
				// Note we're loading this element by ajax.
				item._ajaxip = true;
			}

			if ( !this.ip ) {
				// If we don't already have a (global) flyout running, call it directly.
				this._load(flyout, item, pageid);
			} else {
				// Otherwise, build a closure and add it to the stack.
				var fn = function(fo, i, p){
					return function(){
						$.cms.flyout.ajax._load(fo, i, p);
						fo = null;
						i = null;
						p = null;
					};
				}(flyout, item, pageid);
				this.stack.push(fn);
			}
		},

		// Run the ajax load process.
		_load: function(flyout, item, pageid) {
			// Fire the ajax call.
			this.ip = true;
			var url = $.getAjaxUrl( flyout.options.url, {P: pageid} );
			var fn = function(fo, i){
				return function(results){
					$.cms.flyout.ajax._render(fo, i, results);
					fo = null;
					i = null;
				};
			}(flyout, item);

			$.ajax({
				url: url,
				dataType: 'json',
				success: fn,
				error: fn
			});
		},

		// Handle the ajax call stack and render the results.
		_render: function(flyout, item, results) {
			// If we have ajax calls in the stack, pull the first one out and call it.
			if ( this.stack.length ) {
				this.stack.shift()();
			}

			item._ajaxip = null;
			this.ip = false;

			// Render these results.
			flyout._renderChildren(item, results);
		}
	}
});

})( jQuery );

