


/*******************************************************************************
 * Portions of this script are based on ideas found in the DragDropHelpers library
 * The DragDropHelpers Library is available at http://www.useragentman.com
 * 
 *******************************************************************************/






(function() {

	var cachedShowVisualCues = null;
	var dragEffectNode = null;
	var currentlyDragged = null;
	var dragEffectOffset = {};
	var dragMousePosition = null;
	var dragData = {};
	var dragInProgress = false;
	var currentDragStart = 0;
	
	
	/* Helper Functions  */
	
	/*
	 * figures out if a visual cue needs to be inserted.
	 * Drawn from the DragDropHelpers library  
	 */
	function doesShowVisualCues(e) {
		var chromeString, info;
		
		if (cachedShowVisualCues === null) {
			
			if (e.dataTransfer.setDragImage) {
				chromeString = navigator.userAgent.match(/Chrome\/[0-9]\.[0-9]/);
				
				if (chromeString) {
				
					info = chromeString[0].split('/');
					
					if (parseFloat(info[1]) < 7) {
					    cachedShowVisualCues = false;
					}
					else {
						cachedShowVisualCues = true;
					}
				}
				else {
					cachedShowVisualCues = true;
				}
				
			}
			else {
				cachedShowVisualCues = false;
			}
		} else {
		    chromeString = navigator.userAgent.match(/Chrome\/[0-9]\.[0-9]/);
		    if(chromeString) {
			info = chromeString[0].split('/');
			if (parseFloat(info[1]) < 7) {
			    if($(e.target).hasClass('.item') || BBX.traverse.getAncestorByClass(e.target, 'item')) {
				cachedShowVisualCues = true;
			    } else {
				cachedShowVisualCues = false;
			    }
			}
		    }



		}
		    
		return cachedShowVisualCues;
	}
	
	
	
	/*
	A helper function for IE.  Will call the dragDrop method of the object we want to drag.
	The dragDrop method fires the dragstart event, and things should go ok from there.
	
	*/
	
	function dragStartHelper(e) {
		if(!dragInProgress) {
			var evt;
			if(e.event) {
				evt = e.event;
			} else {
				evt = e;
			}
		
			
			if (evt.button === 1) {
				DOMAssistant.cancelBubble(e);
				BBX.log('class: ' + this.getAttribute('class'));
			
				$(this).removeEvent('mousemove',dragStartHelper);
	//			this.detachEvent('onmousemove',dragStartHelper);
				this.dragDrop();
				
			    
			}
			
			return false;
		}
	}
	
	
	/* 
		This function determines if we can drop into the content area, (#content).  If we can, the body will have the class 'mydash';
	*/
	function canDropOnContent() {
		var b = $('body').first();
		if(b) {
			if(b.hasClass('mydash')) {
				return true;
			}
		}
		return false;
	}
	
	/*
		Tetermines if we can drop the currently dragging item onto the content area, (using the event.dataTransfer info)
	*/
	function canDropThisOnContent(evt) {
		
		if(canDropOnContent() && o.getDraggedItemType(evt) == 'site')	{
			return true;
		}
		
		return false;
	}
	
	/*
		Determines if we can drop a friend into a specific place in the friendlist, or just onto the list in general.  
		
	*/
	function canDropFriendInPlace(elm) {
		var p = $(BBX.traverse.getAncestorByTag(elm,'ol'));
		if(p.hasClass('customorder')) {
			BBX.log(p);
			BBX.log('returning true, we have custom order');
			return true;
		}
		BBX.log('no custom order, returning false');
		return false;
	
	}
	
	
	
	/*
		Displays a loading message in a grid box.  
	*/
	
	function setLoading(elm) {
		elm = $(elm);
		elm.replaceContent(elm.create('span',{className:'loadingmsg'},false,BBX.langdata.getData('loading')));
	
	}
	
	
	/*
	
		Determines if enough time has passed since the beginning of the drag event to truly call this a drag.
		
	*/
	
	function isDragNotClick() {
	
		var now = new Date(), minDragLength = 100;
		if((now.getTime() - currentDragStart) > minDragLength ) {
			return true;
		}
		
		return false;
	
	
	}
	
	
	/* End Helper Functions */
	
	
	
	function autoscroll(e) {
			
		if(dragMousePosition === null) {
			return;
		}
	
	
		var move = 25, scrolled, sy, mpos, cly, evt;
		if(typeof window.dragscroll !== 'undefined') {
			clearTimeout(window.dragscroll);
		}
	
		scrolled = BBX.measure.scrolled();
		sy = scrolled[1];
		
		if(e === null || typeof e == 'undefined') {
			mpos = dragMousePosition.y;
			evt = null;
			cly = mpos-sy;
		} else {
			evt = e.event;
			mpos = e.pageY;
			cly = e.clientY;
		}		
			
		
		if(cly < 50) {
			if(sy > 0) {
				if(!doesShowVisualCues(evt)) {
					currentlyDragged.setStyle('top', (parseInt(currentlyDragged.getStyle('top'),10)-move)+'px');
				}
				window.scrollBy(0,-move);
			}
			window.dragscroll = setTimeout(function() {autoscroll(null);},10);
		} else {
			var y = BBX.measure.viewport()[1];
			var cy = y-cly;
			if(cy < 50) {
				if(!doesShowVisualCues(evt)) {
					currentlyDragged.setStyle('top', (parseInt(currentlyDragged.getStyle('top'),10)+move)+'px');
				}
				window.scrollBy(0,move);
				window.dragscroll = setTimeout(function () {autoscroll(null);},10);
			}
		}
		
	}
	
	
	
	
	function dragStartHandler(e) {
		// set dragData to an empty object, (we would do this in cleanupDrag but Chrome/Win fires dragEnd before drop and we need the data to survive the dragEnd event, so we'll reset it here.
		
		if(dragInProgress) {
			return;
		}
		
		dragData = {};
		dragInProgress = true;
		
		var now = new Date();
		currentDragStart = now.getTime();
		
		var that = this;
		
		var evt = e.event, t = $(this);
		
		if(t.hasClass('item') || t.hasClass('receivedbox')) {
			o.setData('X-BBX-site',this.getAttribute('data-bbx-sid'));
		} else if(t.hasClass('friend')) {
			// if this is a friendlist that has been filtered don't allow dragging.
			var par = $(t.parentNode);
			if(par.hasClass('filtered')) {
				return;
			}
		
			o.setData('X-BBX-friend', this.getAttribute('data-bbx-nickname'));
		}
		
		evt.dataTransfer.setData('Text','BonzoBox');
		evt.dataTransfer.effectAllowed = 'all';

		
		
		if (!doesShowVisualCues(evt)) {
			dragEffectNode = $(this.cloneNode(true));
			document.body.appendChild(dragEffectNode);
			dragEffectNode.setStyle({"position":"absolute","display":"none",'opacity':"0.5"});
			dragEffectNode.addClass('dragEffect');
			dragEffectNode.setStyle({'width':parseInt(this.offsetWidth, 10) + 'px'});
			dragEffectNode.cssSelect('.interactive').remove();
			
			var pagePos = BBX.measure.positionOnPage(this);
			dragEffectOffset =  {'x':(e.pageX - pagePos[0]), 'y':(e.pageY - pagePos[1])};
			
			/* 
			we're overwriting here because the generated 'ghost' of the object we're dragging is blocking the
			dragover, dragenter, and dragleave events because it sits between the cursor and the drop locations
			
			// TODO:  Test in IE to see if this is a problem.  If not, only overwrite dragEffectOffset on webkit-based browsers
			*/
			dragEffectOffset = {'x': -3, 'y': -3};	 
		}
	
	
		$(this).setStyle('opacity','0.8');
		$('body').first().addEvent('dragover',dragHelper);
		
		currentlyDragged = this;
	
	}


	function dragHandler(e) {
		var evt = e.event;
	
		if (!doesShowVisualCues(evt)) {
			dragEffectNode.setStyle({
			'display':'block',
			'left':(e.pageX - dragEffectOffset.x) + 'px',
			'top':(e.pageY - dragEffectOffset.y) + 'px'	
		
			});
		}
	}
	

	// we need to use dragHelper on the dragover event to handle some repositioning because in Firefox, (and maybe others), the drag event has no mouse co-ordinates.
	function dragHelper(e) {
		dragMousePosition = {'x':e.pageX,'y':e.pageY};
		autoscroll(e);
	}
	
	
	/*
	A function to remove all droptarget classes
	
	*/
	function cleanDropTargetClasses() {
		$('.currentDropTarget').removeClass('currentDropTarget');
		$('.beforeCurrentDropTarget').removeClass('beforeCurrentDropTarget');
		$('.currentFriendShareDropTarget').removeClass('currentFriendShareDropTarget');
		$('.currentFriendMoveDropTarget').removeClass('currentFriendMoveDropTarget');

	
	}
	
	
	/*  We need to have a cleanup function instead of dumping everything in the dragEndHandler function because it seems that if a drop event fires then the dragend event doesn't fire.  */
	function cleanupDrag(evt) {
		BBX.log('cleaning up drag');
		if(!doesShowVisualCues(evt)) {
//			dragEffectNode.remove();
			var p = dragEffectNode.parentNode;
			if(p) {
				p.removeChild(dragEffectNode);	// this needs to be native, NOT DOMAssistant remove(), in order to keep IE in line.
			}
		}
		
		cleanDropTargetClasses();
				
		currentlyDragged.setStyle('opacity','1');
		currentlyDragged.setStyle('filter','none'); // for IE.

		if (currentlyDragged.dragDrop) {
//			currentlyDragged.attachEvent('onmousemove',function() { dragStartHelper.apply(currentlyDragged,[window.event]); });
			currentlyDragged.addEvent('mousemove',dragStartHelper);
		}
		
		$('body').first().removeEvent('dragover',dragHelper);
		
		dragMousePosition = null;
//		dragData = {};		// now being done at the very beginning of dragStartHandler
		dragInProgress = false;
		
	
	}
	
	function dragEndHandler(e) {
		BBX.log('dragend');
		var evt = e.event;
		
		cleanupDrag(evt);
		
		e.preventDefault();
		DOMAssistant.cancelBubble(e);
		return false;
	}
	
//
//	function folksDragOverHandler(e) {
//		var evt = e.event;
//
//		dragHelper(e);
//		if(canDropOnContent()) {
//			evt.dataTransfer.dropEffect = 'copy';
//
//			e.preventDefault();
//			return false;
//		}
//
//	}
	
	function folksDragEnterHandler(e) {
		var t = $(this);
		var src = e.target;
	
		if(!((t.hasChild(src) || this == src) && t.hasClass('currentDropTarget'))) {	
			cleanDropTargetClasses();
		}

		
		if(canDropThisOnContent(e.event) && !t.hasChild(currentlyDragged) && t.cssSelect('.item').length > 0) {
		
			
			t.addClass('currentDropTarget');
			e.event.dataTransfer.dropEffect = 'move';
			
			if(($('#content>ol>li').indexOf(this)) % BBX.folks.layout.findGridRowLength() !== 0) {
				if(t.prev()) {
					t.prev().addClass('beforeCurrentDropTarget');
				}
			}			
		}
		
		
		
		
		
		e.preventDefault();
		BBX.log('default prevented');
	}
	
	function folksDragLeaveHandler(e) {
		var t = $(this), src;
		src = e.target; 

				
		if(src == this && !t.hasChild(currentlyDragged) && t.cssSelect('.item').length > 0 && canDropOnContent()) {
			t.removeClass('currentDropTarget');
			if(t.prev()) {
				t.prev().removeClass('beforeCurrentDropTarget');
			}
		}
		
	}
	
	
	/* Handles the drop when moving a site around in the dashboard.  */
	
	function gridDropHandler(e) {
		var evt = e.event, t = $(this);
		BBX.log('dropped!');
		BBX.log(e.event);
		
		
		// if we're dropping onto a location on my dashboard.
		if(canDropThisOnContent(evt) && t.nodeName.toLowerCase() == 'li' && isDragNotClick()) {
			// we'll need to find out where we are on the page and which page we're on
			
			var lis, pagepos, page, position, usid, data = '', url;
			
			lis = $('#content>ol>li');
			
			// if we're not dropping where the item came from
			if(lis.indexOf(this) != lis.indexOf(currentlyDragged.parentNode)) {
				pagepos = lis.indexOf(this);
				page = BBX.folks.layout.findPageNum();
				usid = currentlyDragged.getAttribute('data-bbx-myusid');
				sid = currentlyDragged.getAttribute('data-bbx-sid');
				
				
				position = ((page - 1) * lis.length) + pagepos;
				
				BBX.log('pagepos: '+pagepos + ", page: " + page + ", position: "+position+", usid: "+usid);
				
				if(usid !== undefined && usid > 0) {
					data += 'usid=' + encodeURIComponent(usid);
					url = '/move';
				} else {
					data += 'sid=' + encodeURIComponent(sid);
					url = '/add';
				}
				
				
				data += '&pos=' + encodeURIComponent(position);
				data += '&output=json';
				
				
				// rearrange boxes if we have to.
				var oldpar = currentlyDragged.parentNode, addedTo;
				if(t.cssSelect('.item').length > 0) {	// if the place we're going to already has a box in it.
					var lst = $(this.parentNode);
					
					var newbox = oldpar.removeChild(currentlyDragged);
					
					// make sure we're not dragging in from somewhere else before we remove the old parent.
					if(lis.indexOf(oldpar) > -1) {
						$(oldpar).remove();
					}
					addedTo = t.create('li',{},false);
					prepareGridDrop(addedTo);	// add event handlers to this new LI.
					lst.insertBefore(addedTo,lst.cssSelect('>li')[pagepos]);
					
					// unless we're moving from somewhere within the grid we'll need to rebuild the box, not simply move the DOM node.
					if(lis.indexOf(oldpar) > -1) {
						addedTo.replaceContent(newbox);
					} else {
						setLoading(addedTo);
						// remove the last element from lis
						$(lis[lis.length - 1]).remove();
						
					}
				
				} else {
					
					BBX.folks.sitemanager.unsetEmpty(this);	// removes events for empty boxes
					
					
					
					// make sure we're not dragging in from somewhere else before we mess with the old parent.
					if(lis.indexOf(oldpar) > -1) {
						t.replaceContent(currentlyDragged);
						BBX.folks.sitemanager.setEmpty(oldpar);
					} else {
						currentlyDragged.remove();
						setLoading(t);
					}
					
					addedTo = t;
				
				}
				
				// send off the AJAX call.
				addedTo.ajax({"method":"POST",'params':data, 'url':url, 'callback':gridDropCallback});
				addedTo.addClass('newholder');
				
				
				
			
			}
		} else if(!isDragNotClick()) {
			$(currentlyDragged).cssSelect('.taggedlink').triggerEvent('click');
		}
		
		
		cleanupDrag(evt);
		
	
		e.preventDefault();
		return false;
	}
	
	
	/*
	
		The callback for the ajax call that happens when the saving an item to the grid.  Will rebuild the grid box.
		
	*/
	
	function gridDropCallback(resp) {
	
		var t = $(this), folk, r = eval(resp);
		BBX.log('griddropcallback');
		BBX.log(this);
		if(r.saved) {
			BBX.log('in if');
			folk = BBX.folks.sitemanager.buildFolksEntry(r);
			BBX.log('have folk');
			BBX.log(folk);
			BBX.folks.drawer.init(folk);
			BBX.folks.sitemanager.init(folk);
			BBX.folks.share.init(folk);
			o.makeDraggable(folk);
			BBX.log('init complete');
			t.replaceContent(folk);
			BBX.log('done');
		}
		
		t.removeClass('newholder');
		
	
	
	}
	
		/*
	function prepareGridDrop(elm)
	@param (DOM node) elm : The element to prepare, (should be an LI)
	
	Adds the required event handlers to the element passed in.
	
	*/
	function prepareGridDrop(elm) {
//		BBX.log(elm);
		elm = $(elm);
		elm.addEvent('dragenter',folksDragEnterHandler);
		elm.addEvent('dragleave',folksDragLeaveHandler);
		elm.addEvent('dragover',dragoverHandler);
		elm.addEvent('drop',gridDropHandler);
	
	}
	
	
	
	
	
	/* Event handlers for dropping things onto the friendlist, (including friends from elsewhere in the list when reordering) */
	
	function canDropFriend(evt, elm) {
		elm = $(elm);

		
		if(o.getDraggedItemType(evt) == 'friend') {
			if(elm.getAttribute('data-bbx-nickname') != o.getData('X-BBX-friend'))	{
				return true;
			}
		}
		return false;
	}
	
	
	function friendShareDragenterHandler(e) {
		var evt = e.event;
		var t = $(this);
		
		// clean up any dropTarget styles on the grid
		$('.currentDropTarget').removeClass('currentDropTarget');
		$('.beforeCurrentDropTarget').removeClass('beforeCurrentDropTarget');
		
		if(o.getDraggedItemType(evt) == 'site') {
		// dropping a site onto a friend
			t.addClass('currentFriendShareDropTarget');
			e.preventDefault();
		} else if(canDropFriend(evt, t)) {
		// dropping a friend onto a friend, (for reordering & adding friends)
			if(canDropFriendInPlace(t)) {
				t.addClass('currentFriendMoveDropTarget');
			}
			e.preventDefault();
		}
	
	}



	






	
	function friendShareDragleaveHandler(e) {
		var t, evt, src;
		src = e.target;
		t = $(this);
	
		if(src == this) {
			cleanDropTargetClasses();			
		}
	
	}
	
	function dragoverHandler(e) {
		var evt = e.event, t = $(this);
		dragHelper(e);
		if(canDropOnContent()) {
		     evt.dataTransfer.dropEffect = 'copy';
			e.preventDefault();
		    return false;

		} else if(o.getDraggedItemType(evt) == 'site') {
		// dropping a site
			if(t.hasClass('friend')) {
			    t.addClass('currentFriendShareDropTarget');
			}
			e.preventDefault();
			evt.dataTransfer.dropEffect = 'copy';
		} else if(canDropFriend(evt, this)) {
		// dropping a friend
			BBX.log('share dragover can drop');
			evt.dataTransfer.dropEffect = 'copy';
			if(canDropFriendInPlace(t)) {
				t.addClass('currentFriendMoveDropTarget');
			}
			e.preventDefault();
			
		} else {
			BBX.log('no dragover data');
		}

	
	
	}



	
	function friendShareDropHandler(e) {
		BBX.log('drop');
		var evt = e.event, t = $(this);
		if(o.getDraggedItemType(evt) == 'site') {
		// dropping a site, (for sharing)
			var a,friendname,siteid,data;
			a = t.cssSelect('a').first();
			friendname = BBX.utils.getHref(a).substr(1);

			shareSite(t, friendname);
			
			
				
			e.preventDefault();
		} else if(canDropFriend(evt, this)) {
		// dropping a friend
			var before, par, data = '', url = '', alreadyfriends = false;;
			par = t.parentNode;
			
			
			// see if the person being dragged is already a friend.  Return true if yes, false if no.
			function alreadyFriends() {
				var friendlist, friendname;
				BBX.log('checking if we are friends');
				BBX.log(' looking for ' + currentlyDragged.getAttribute('data-bbx-nickname'));
				friendlist = $(par).cssSelect('li.friend');
				friendname = currentlyDragged.getAttribute('data-bbx-nickname');
				
				if(friendlist.length > 0) {
					for(var i=0;i<friendlist.length;i++) {
					    if(friendlist[i].getAttribute('data-bbx-nickname') == friendname) {
						BBX.log('found a friend:');
						BBX.log(friendlist[i]);
						return true;
					    }
					}
					
				}				
				return false;
			}
			
			
			BBX.log('already friends?  ' + alreadyFriends());
			alreadyfriends = alreadyFriends();
			
			
			if(alreadyfriends || par.hasClass('customorder')) {
				before = t.getAttribute('data-bbx-nickname');
				par.insertBefore(currentlyDragged, t);
				
				if(alreadyfriends) {
					data += '&act=move';
					url = '/friend/move';
				} else {
					data += '&act=add';
					url = '/friend/add';
				}
			
			} else {
				before = 0;
			
				par.insertBefore(currentlyDragged, par.firstChild);
				
				data += '&act=add';
				url = '/friend/add';
			}
				
			
			data += '&f=' + encodeURIComponent(o.getData('X-BBX-friend'));
			data += '&before=' + encodeURIComponent(before);
			
			
			t.ajax({'url':url,'params':data, 'method':'POST','callback':friendMoveComplete});
		
			if(!alreadyfriends) {
				BBX.dialog.showmessagebox(null, BBX.langdata.getData('buddy request sent title'), BBX.langdata.getData('buddy request sent message'));
			}
			

//			BBX.magicbar.friendFilter(BBX.traverse.getAncestorByClass(t, 'friendlist'));
			
			BBX.magicbar.makeFriendRemoveable(currentlyDragged);
			
			e.preventDefault();
		} else {
			BBX.log('no drop data');
		
		}
	}
	
	function friendShareComplete(resp) {
	
		var title, text, r = eval(resp);

		if(this.nodeName.toLowerCase() == 'form') {
		    var dialog = BBX.traverse.getAncestorByClass(this, 'dialog');
		    dialog.close();
		}

		if(r.error == 0 && r.sent == 1) {
			title = decodeURIComponent(r.msg.title);
			text = decodeURIComponent(r.msg.text);
		} else {
			title = 'Error';
			text = 'There was a problem sending this site.';
		}
		
		BBX.dialog.showmessagebox(null, title, '<p>'+text+'</p>');
	
	}
	
	function friendMoveComplete(resp) {
	
		// nothing here at the moment.
	
	}







	// for BBX sharing & E-mail sharing.
	function shareSite(domobj, to, siteid, msg) {
	    var data;

	    if(siteid === undefined || siteid == null) {
		siteid = o.getData('X-BBX-site');
	    }

	    data = 's='+siteid+'&to='+to;
	    if(msg !== undefined && msg !== null && msg.length > 0) {
		data += '&msg=' + msg;
	    }
	    $(domobj).ajax({'url':'/apps/sendsite.php','params':data,'method':'POST','callback':friendShareComplete});
	}

	

	
	
	
	// We'll set this as BBX.dragdrop
	var o = {
		initSidebarFriend:function(elm) {
			elm = $(elm);
			elm.addEvent('dragover',dragoverHandler);
			elm.addEvent('dragenter', friendShareDragenterHandler);
			elm.addEvent('dragleave', friendShareDragleaveHandler);
			elm.addEvent('drop',friendShareDropHandler);
		
		},
		
		makeDraggable:function(elm) {
		
			elm = $(elm);
			elm.setAttribute('draggable','true');
			elm.addEvent('dragstart', dragStartHandler);
			elm.addEvent('drag',dragHandler);
			
			
			if (elm.dragDrop) {
				elm.addEvent('mousemove', dragStartHelper);
			}
			elm.addEvent('dragend',dragEndHandler);

		
		},
		
		setData:function(key, data) {
			dragData[key] = data;
		
		
		},
		
		
		getData:function(key) {
			if(dragData.hasOwnProperty(key)) {
				return dragData[key];
			}
			return undefined;		
		},

		/**
		 *
		 * Tells us what kind of item is being dragged right now.
		 * Currently the evt param is not being used due to irregularities
		 * in the HTML5 DnD Implementation.  o.getData is used instead.
		 *
		 * @param evt SyntheticEvent The event that has the drag data
		 *
		 * @return mixed The type of object we're dragging, (site or member at the moment) or false otherwise.
		 *
		 *
		 */

		getDraggedItemType:function(evt) {
		    if(o.getData('X-BBX-site') != undefined && o.getData('X-BBX-site') > 0) {
			return 'site';
		    }

		    if(o.getData('X-BBX-friend') != undefined && o.getData('X-BBX-friend').length > 0) {
			return 'friend';
		    }

		    return false;
		},


		friendShareComplete : friendShareComplete

		
	
	
	}
	
	
	BBX.namespace('dragdrop');
	BBX.dragdrop = o;


	// Initialize drag & drop on the grid.

	$('.item').each(function(elm,oth,coll) {
		
		o.makeDraggable(elm);

	});
	
	
	// do this on DOMReady
	DOMAssistant.DOMReady(function() {
		var lis = $("#content>ol>li");
		
		lis.each(function(elm,oth,coll) {
			prepareGridDrop(elm);
		});

		initShareServices();
	
	});
	


})();
