/*

 ADOBE CONFIDENTIAL
 ___________________

 Copyright 2012 Adobe Systems Incorporated
 All Rights Reserved.

 NOTICE:  All information contained herein is, and remains
 the property of Adobe Systems Incorporated and its suppliers,
 if any.  The intellectual and technical concepts contained
 herein are proprietary to Adobe Systems Incorporated and its
 suppliers and are protected by trade secret or copyright law.
 Dissemination of this information or reproduction of this material
 is strictly forbidden unless prior written permission is obtained
 from Adobe Systems Incorporated.
 
*/

if (typeof CssGrids == 'undefined') CssGrids = {}; // Create our namespace

CssGrids.LayoutDivManipulator = function(dw, dom, dwscripts, styleSheetManager) {

	var self = this; 
	
	self.publicFunctions = [ 
		'init',
		'isInited',
		'shutdown',
		'refreshUnhideMode',
		'onResize_OverlayWindow',
		'onDocumentVisibleViewsChanged',
		'onResourceLoadingCompleted_LiveView',
		'onMouseDown_Resizing',
		'onMouseMove_Resizing',
		'onMouseUp_Resizing',
		'onMouseDown_Shifting',
		'onMouseMove_Shifting',
		'onMouseUp_Shifting',
		'onMouseOver_OverlayDiv',
		'onMouseOut_OverlayDiv',
		'onMouseDown_OverlayDocument'
	];
	
	self.headlights = {
		UT_FLUID_GRID:                      'Fluid Grid',
		UT_FLUID_GRID_TOGGLE_START_ROW:     'Toggle Start Row',
		UT_FLUID_GRID_RESIZE_ELEMENT:       'Resize Element',
		UT_FLUID_GRID_SHIFT_ELEMENT:        'Shift Element',
		UT_FLUID_GRID_DUPLICATE_ELEMENT:    'Duplicate Element',
        UT_FLUID_GRID_DELETE_ELEMENT:       'Delete Element',
   		UT_FLUID_GRID_TOGGLE_LOCK:          'Toggle Lock',
		UT_FLUID_GRID_MOVE_UP_ELEMENT:      'Move Up Element',
		UT_FLUID_GRID_MOVE_DOWN_ELEMENT:    'Move Down Element',
		UT_FLUID_GRID_HIDE_ELEMENT:         'Hide Element',
		UT_FLUID_GRID_SHOW_ELEMENT:         'Show Element'
	}
	
	self.refs = { 	
		dw:						dw,
		dwscripts:				dwscripts, // Needed for logging.
		styleSheetManager: 		styleSheetManager,
		dwDom:					dom,
		overlayCtrl: 			overlayCtrl,
		window:					window,
		document:				document,
		resizeEdgeDiv: 			null,
		shiftEdgeDiv:			null,
		marginDiv:				null,
		startsRowDiv: 			null,
		draggingDiv: 			null,
		deleteDiv:				null,
		duplicateDiv:			null,
		moveUpDiv:				null,
		moveDownDiv:			null,
		hideDiv:				null,
        lockDiv:                null,
		showDiv:                null,
		overlayDivs:			[],
		cssRuleBeingModified: 	null,
		columnDivs:				[],
		internalStyleSheetDom: 	null,
		knobMiddle:				{}, // props: resize, shift
		dynamicTooltipDiv:		null,
		fluidElementRegExp: 	/(div|ul|ol|li|header|hgroup|nav|aside|article|section|footer|h1|h2|h3|h4|h5|h6|p|figure)/gi
	};
					
	self.data = {
		mouseStartX:								-1,
		mouseCurX:									-1,
		mouseStartY:								-1,
		scrollStartX:								-1,
		gridContainerWidthStart:					-1,
		selectedUnderlyingDivCurrentColSpan:		-1,
		selectedUnderlyingDivStartingColSpan:		-1,
		marginsOffsetLeft:							-1,
		startingSelectedOverlayDivOffsetWidth:		-1,
		startingSelectedUnderlyingDivOffsetLeft: 	-1,
		startingMarginDivWidth:						-1,
		selectedUnderlyingDivIndex:					-1,
		mouseOverDivIndex:							-1,
		lastColRight:								-1,
		gridColRects:								[],
        editsArray:                                 [],
        lockedArray:                                [],
		selectorWidthEdited:						[]
	};
		
	self.consts = {		
		selectedBorderWidth: 		2,
		draggingBorderWidth:		1,
		internalCssFileUrl:			self.refs.dw.getConfigurationPath() + '/Overlays/CssGrids/temp/internal.css',
		shouldDesignViewSnapFileUrl:self.refs.dw.getConfigurationPath() + '/Overlays/CssGrids/shouldDesignViewSnap.txt',
		tagName:					'*', 	// Used in 'getElementsByTagName()' to find layout divs.
		gridContainer:				'gridContainer', 	// The class name that signifies the master container for the layouts
		gridContainerBgColor:		'rgba(200,200,200,0.35)',
        pixelFixedPoint:            1,      // The deviation that we can have in widths.
		hudButtonWidth: 			26,
        hudButtonHeight:            22,
        lockButtonHeight:           16,
        lockButtonWidth:            16,
		moveUpDownButtonWidth:		20,
		startsNewRowButtonGapSize: 	3, 		// Distance between right edge of knob and this button.
		knobSize:					8,
		overlayDivBgColor:			'transparent',
		overlayDivBgColorHovered:	'rgba(172, 230, 238, 0.4)', // #ACE6EE
		hiddenDivBgImage:			'url(dw://Configuration/Overlays/CssGrids/images/Checks.png);',
		hudBgColor:					'#555',
		dynamicTooltipOffsetX:		15,
		dynamicTooltipOffsetY:		15
	};
    
    self.keyCodes = {
        ArrowLeftKey:				37,
        ArrowRightKey:				39,
        DeleteKey:					46,
        BackspaceKey:				8,
        altKey:                     18
    };
    
    self.timer = {
        typingTimer:                null,
        typingTimerInterval:        500
    };

    self.consts.overlayBorder = self.consts.draggingBorderWidth + 'px dotted #0083E8';	// light blue
	self.consts.selectedBorder = self.consts.selectedBorderWidth + 'px solid ' + self.consts.hudBgColor; 	// grey
	self.consts.hoverBorder = self.consts.selectedBorderWidth + 'px solid #ACE6EE';		// blue
	self.consts.misalignedDivBorder = self.consts.selectedBorderWidth + 'px solid ' + self.consts.hudBgColor; 	// same as selected border
	self.consts.draggingBorder = self.consts.draggingBorderWidth + 'px dashed #0083E8'; // light blue

	self.flags = {
		debug:										false, 
		showProtos:									false,
		isInitied:									false,
		isDragging:									false,
		isResizing:									false,
		isShifting:									false,
		isRefreshing:								false,
		wasDomShowingDivBoxModel:					false,
		logEvents:									false,
		showDebugDumpButton:						false,
		disabledForZoom:							false,
		shouldDesignViewSnap:						false,
        ignoreDOMChange:                            false,
        ignoreCssDOMChange:                         false
	};
	
	self.init = function() {
		if (!self.refs.styleSheetManager.beQuiet(true).loadGridProps()) {
			return;
		}
		self.refs.styleSheetManager.beQuiet(false);
		var userDom = self.refs.dwDom;
		var div = userDom.getElementById('div1');
		if(div)
		{
			var idSelector = self.getValidFluidGridIDSelector(div);
			if(!idSelector)
			{
				/* If first element doesn't have a valid ID selector this signifies that user has attached existing CSS as Fluid Grid CSS.
					We need to insert rule ourselves for default element */
				idSelector = self.getIDSelector(div);
				self.refs.styleSheetManager.insertRule(idSelector);	
			}
		}
		self.flags.shouldDesignViewSnap = self.calcShouldDesignViewSnap();
		self.createOverlayControls();
		self.refresh('gridAndRediscoverUnderlyingDivs', true);
		self.addEventListeners();
		if (self.flags.showDebugDumpButton) {
			self.showDiv(self.refs.document.getElementById('debugDumpDiv'));
			self.showDiv(self.refs.document.getElementById('dumpButton'));
			self.refs.document.getElementById('dumpButton').style.top = '200px';	
			self.refs.document.getElementById('debugDumpDiv').style.top = '400px';	
			self.refs.document.getElementById('debugDumpDiv').style.height = '300px';	
		}
		var isNewDoc = userDom.URL == '';
		if (isNewDoc) {
			// Format html to honor formatting prefs.			
			userDom.formatRange(0, userDom.documentElement.outerHTML.length);
			userDom.clearUndos();
			userDom.setModified(false);
			// Set the IP to after div1.
            self.selectDwDomNode(div);
            self.selectOverlayDiv(div, true);
		}		
		self.flags.isInitied = true;
		//load our user style sheets after we're inited
		userDom.browser.reloadDynamicUsersStyleSheet();
		self.updateInternalStyleSheetDom(self.getDefaultUserStyleSheetText(), true);
		self.flags.wasDomShowingDivBoxModel = self.refs.dwDom.getShowDivBoxModel();
		// disable box model tooltips when LDM is on.
		if( self.flags.wasDomShowingDivBoxModel )
			self.refs.dwDom.setShowDivBoxModel(false, false);
	}
	
	self.isInited = function() {
		return self.flags.isInitied;
	}

	self.calcShouldDesignViewSnap = function() {
		var str = DWfile.read(self.consts.shouldDesignViewSnapFileUrl);
		return str.toLowerCase().indexOf('yes') != -1;
	}
	
	self.shutdown = function() {
		self.flags.isInitied = false;
		self.removeEventListeners();
		self.updateInternalStyleSheetDom('', true);
		self.refs.dwDom.dettachInternalStyleSheet(self.consts.internalCssFileUrl);
		self.refs.dwDom.browser.reloadDynamicUsersStyleSheet();
		if( self.flags.wasDomShowingDivBoxModel )
			self.refs.dwDom.setShowDivBoxModel(true, false);
	}
	
	self.refreshUnhideMode = function() {		
		self.refresh('gridAndRediscoverUnderlyingDivs', false);
		self.selectdiv(null);
	}
	
	self.removeUnhideModeChanges = function() {		
		//hidden mode changes are only in Live view
		if( !self.isLiveView() )
			return;
			
		var bodyElement = self.getActiveDom().body;
		if(!bodyElement)
			return;
		
		//we need to make all the originally hidden divs to 
		//their original state
		var underlyingDivs = self.nodeListToArray(bodyElement.getElementsByTagName(self.consts.tagName));
		underlyingDivs.forEach(function(underlyingDiv){			
			if(underlyingDiv.getAttribute('data-isHidden') == 'true')
			{
				underlyingDiv.removeAttribute('data-isHidden');
				underlyingDiv.style.display = '';
			}
		});		
	}
	
	self.isDescendantOfHiddenElement = function(elem) {
		//hidden elements can occur only in live view
		if( !self.isLiveView() || !self.refs.dwDom.getIsInUnhideMode() )
			return false;
		
		if(!elem || !elem.parentNode)
			return false;
			
		var bodyElement = self.getActiveDom().body;
		if(!bodyElement)
			return false;
			
		var parent = elem.parentNode;
		while( parent && parent!=bodyElement )
		{
			if(parent.getAttribute('data-isHidden') == 'true')				
				return true; //we're a descendant of a hidden element
			parent = parent.parentNode;					
		}
		
		return false;
	}

	self.addEventListeners = function() {
		self.refs.window.addEventListener('resize', self.onResize_OverlayWindow); 
		self.refs.document.addEventListener('mousedown', self.onMouseDown_OverlayDocument);
		self.refs.dwDom.browser.addEventListener('ResourceLoadingCompleted', self.onResourceLoadingCompleted_LiveView);
		self.refs.dwDom.browser.addEventListener('DynamicUserStyleSheetRequested', self.onDynamicUserStyleSheetRequested_LiveView);
		self.refs.dwDom.addEventListener('DWDOMChanged', self.onDWDOMChanged_DesignView, false);
		self.refs.document.addEventListener('keydown', self.onKeyDown_OverlayDocument);
		self.refs.dwDom.documentElement.addEventListener('select', self.onSelectionChange_DesignView, false);
			
		var fluidStyleSheet = self.refs.styleSheetManager.getStyleSheetRec();
		if (fluidStyleSheet && fluidStyleSheet.type == 'attached') {
			var cssDom = self.refs.dw.getDocumentDOM(fluidStyleSheet.fileUrl);
			cssDom.addEventListener('CSSDOMChanged', self.onCssDOMChanged_DesignView, false);
		}
	}

	self.removeEventListeners = function() {
		self.refs.window.removeEventListener('resize', self.onResize_OverlayWindow); 
		self.refs.document.removeEventListener('mousedown', self.onMouseDown_OverlayDocument);
		self.refs.dwDom.browser.removeEventListener('ResourceLoadingCompleted', self.onResourceLoadingCompleted_LiveView);
		self.refs.dwDom.browser.removeEventListener('DynamicUserStyleSheetRequested', self.onDynamicUserStyleSheetRequested_LiveView);
		self.refs.dwDom.removeEventListener('DWDOMChanged', self.onDWDOMChanged_DesignView, false);
		self.refs.document.removeEventListener('keydown', self.onKeyDown_OverlayDocument);
		self.refs.dwDom.documentElement.removeEventListener('select', self.onSelectionChange_DesignView, false);
		
		var fluidStyleSheet = self.refs.styleSheetManager.getStyleSheetRec();
		if (fluidStyleSheet && fluidStyleSheet.type == 'attached') {
			var cssDom = self.refs.dw.getDocumentDOM(fluidStyleSheet.fileUrl);
			cssDom.removeEventListener('CSSDOMChanged', self.onCssDOMChanged_DesignView, false);
		}		
	}

	self.addDragEditingEventListeners = function( editType ) {
		if (self.flags.logEvents) {
			self.log('addDragEditingEventListeners');	
		}
		if( editType == 'resizing' ){
			self.refs.document.addEventListener('mousemove', self.onMouseMove_Resizing);
			self.refs.document.addEventListener('mouseup', self.onMouseUp_Resizing);
		}
		if( editType == 'shifting'){
			self.refs.document.addEventListener('mousemove', self.onMouseMove_Shifting);
			self.refs.document.addEventListener('mouseup', self.onMouseUp_Shifting);
		}
	}
	
	self.removeDragEditingEventListeners = function() {
		if (self.flags.logEvents) {
			self.log('removeDragEditingEventListeners');	
		}
		self.refs.document.removeEventListener('mousemove', self.onMouseMove_Resizing);
		self.refs.document.removeEventListener('mouseup', self.onMouseUp_Resizing);
		self.refs.document.removeEventListener('mousemove', self.onMouseMove_Shifting);
		self.refs.document.removeEventListener('mouseup', self.onMouseUp_Shifting);
		self.flags.isDragging = false;
		self.flags.isResizing = false;
		self.flags.isShifting = false;
		self.refs.cssRuleBeingModified = null;
	}
    
	self.onCssDOMChanged_DesignView = function() {
		if (self.flags.logEvents) {
			self.log('onCssDOMChanged_DesignView');
		}

  		if (!self.isDesignView() || self.flags.ignoreCssDOMChange) {
			return;
		}
		var noSelectionInfo = (self.data.selectedUnderlyingDivIndex == -1);
		self.refresh('gridAndRediscoverUnderlyingDivs', true);
        
		if (noSelectionInfo) {
            if (self.refs.dwDom.selectionIsExactlyOneTag()) {
                // try selecing an element
                var selectedObject = self.refs.dwDom.getSelectedNode();
                if (selectedObject)
                    self.selectOverlayDiv(selectedObject, true);
            }
            else
                self.selectDiv(null);
        }
	}
    
	self.onDWDOMChanged_DesignView = function() {
		if (self.flags.logEvents) {
			self.log('onDWDOMChanged_DesignView');
		}
		if (!self.isDesignView() || self.flags.ignoreDOMChange) {
			return;
		}
        var noSelectionInfo = (self.data.selectedUnderlyingDivIndex == -1);
		self.refresh('gridAndRediscoverUnderlyingDivs', true);
        
		if (noSelectionInfo) {
            if (self.refs.dwDom.selectionIsExactlyOneTag()) {
                // try selecing an element
                var selectedObject = self.refs.dwDom.getSelectedNode();
                if (selectedObject)
                    self.selectOverlayDiv(selectedObject, true);
            }
            else
                self.selectDiv(null);
        }
	}

	self.onResize_OverlayWindow = function() {
		if (self.flags.logEvents) {
			self.log('onResize_OverlayWindow');	
		}
		self.refresh('gridAndRediscoverUnderlyingDivs', false);
	}

	self.onResourceLoadingCompleted_LiveView = function(evt) {
		if (self.flags.logEvents) {
			self.log('onResourceLoadingCompleted_LiveView');
		}
		self.refresh('gridAndRediscoverUnderlyingDivs', true);
		if (self.isLiveView()) {
			var underlyingDiv = self.getSelectedUnderlyingDiv();
			if (underlyingDiv) {
				var liveCodeHiddenIdSib = parseInt(underlyingDiv.getAttribute('data-fluidgrid_id'));
				if(liveCodeHiddenIdSib > 0) {
					var underlyingDwDiv = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenIdSib));
					if (underlyingDwDiv) {
						self.selectDwDomNode(underlyingDwDiv);
	}	}	}	}	}
	
	self.onDynamicUserStyleSheetRequested_LiveView = function(evt) {
		if (self.flags.logEvents) {
			self.log('onDynamicUserStyleSheetRequested_LiveView');
		}
		evt.appendStringToUsersStyleSheet(self.getDefaultUserStyleSheetText());
	}
	
	self.getUnderlyingDivByIndex = function(index)
	{
		if(index >= 0 && index < self.refs.overlayDivs.length)
		{
			return self.refs.overlayDivs[index].underlyingDiv;
		}
		return null;
	}
	
	self.getNodeIfWholeTagSelected = function() {
		var dom = self.refs.dwDom;
		var curSelection = dom.getSelection();
		var curNode = dom.offsetsToNode(curSelection[0], curSelection[1]);
		if(curNode)
		{
			var nodeOffsets = dom.nodeToOffsets(curNode);
			if ( (nodeOffsets[0] == curSelection[0]) && (nodeOffsets[1] == curSelection[1]) )
				return curNode;
		}
		return null;
	}
	
	self.onSelectionChange_DesignView = function(evt) {
		if (self.flags.logEvents) {
			self.log('onSelectionChange_DesignView');	
		}
		if( self.isLiveView() )
			return;
		if( self.flags.isDragging )
			return;
		var dom = evt.target.ownerDocument;
		var SelectedNode = self.getNodeIfWholeTagSelected();
        if(SelectedNode)
        {
            self.selectOverlayDiv(SelectedNode, true);
            return;
        }
		else if( self.isEqual(self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex), evt.target)) {
            if (self.validFluidElement(evt.target))
            {
                var childNodes = self.childElementNodesToArray(evt.target.childNodes);
                if (childNodes.length == 0) { // empty element
                    return; // as it is already selected
                }
            }
            
			if(self.refs.dwscripts.selectionIsCursor(dom))
				self.selectDiv(null);//If it's an IP, then it's not this tag
			return; //already have it selected
		}
		else {
			var parent = evt.target.parentNode;
			while( parent )
			{
				if( self.isEqual(self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex), parent )) 
					return; //we're a child of the current selection, lets bail
				parent = parent.parentNode;
			}
			// Something else is selected so deselect this
			self.selectDiv(null);
			return;
		}
	}

	self.isEqual = function(fgElement1, fgElement2)
	{
		if(!fgElement1 && !fgElement2)
			return true;
		/* First check for IDs  to be equal*/
		if(fgElement1 && fgElement2)
		{
			var id1 = fgElement1.getAttribute('id');
			var id2 = fgElement2.getAttribute('id');
			if( (id1 || id2) && (id1 == id2) )
				return true;
			
			/* Check for DOM objects to be equal */
			else if( fgElement1 == fgElement2)
				return true;
			
			/* Finally check for Offsets */	
			else
			{
				if( self.isDesignView() )
				{
					offsets1 = self.refs.dwDom.nodeToOffsets(fgElement1);
					offsets2 = self.refs.dwDom.nodeToOffsets(fgElement2);
					if((offsets1[0] == offsets2[0]) && (offsets1[1] == offsets2[1]))
						return true;
				}
			}
		}	
		return false;
	}	
	
	self.onKeyDown_OverlayDocument = function(evt) {
        
		switch (evt.keyCode) {
			/* Left Arrow Key Down handler*/
			case self.keyCodes.ArrowLeftKey:
			if ( self.refs.dwDom.selectionIsExactlyOneTag() )
            {
				var expectedElementIndex = self.data.selectedUnderlyingDivIndex - 1;
				if(expectedElementIndex < 0)
					expectedElementIndex = self.refs.overlayDivs.length - 1;
				self.selectDiv(null);
				self.data.selectedUnderlyingDivIndex = expectedElementIndex;
				var div = self.getSelectedOverlayDiv();
				self.selectDiv(div);
				self.setUserNodeSelected(true);
				self.ensureSelectedDivStillVisible();
				evt.preventDefault();
				self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			}
                return;
			/* Right Arrow Key Down handler */
			case self.keyCodes.ArrowRightKey:
				if ( self.refs.dwDom.selectionIsExactlyOneTag() )
				{
					var expectedElementIndex = self.data.selectedUnderlyingDivIndex + 1;
					if( expectedElementIndex >= self.refs.overlayDivs.length )
						expectedElementIndex = 0;
					self.selectDiv(null);
					self.data.selectedUnderlyingDivIndex = expectedElementIndex;
					var div = self.getSelectedOverlayDiv();
					self.selectDiv(div);
					self.setUserNodeSelected(true);
					self.ensureSelectedDivStillVisible();
					evt.preventDefault();
					self.refs.overlayCtrl.preventRelayOfCurrentEvent();
				}
                return;
			/* Delete Key Down handler */
			case self.keyCodes.DeleteKey:
			/* Backspace Key Down handler */
			case self.keyCodes.BackspaceKey:
				
            	if (evt.ctrlKey && self.refs.dwDom.selectionIsExactlyOneTag())
            	{
					self.attemptDeletion();
					evt.preventDefault();
					self.refs.overlayCtrl.preventRelayOfCurrentEvent();
                    return; // let the normal DOM change event rebuild overlay divs
            	}
                break;
            default:
                break;
		}
        
        // we dont handle any modifier key events.
        // if a modifier is pressed we might need to recreate overlay divs immediately
        if (evt.shiftKey || evt.altKey || evt.ctrlKey || evt.metaKey || evt.keyCode == self.keyCodes.altKey)
            return;
        
        self.ignoreDOMChangeAndScheduleUpdate();
    }

	self.ignoreDOMChangeAndScheduleUpdate = function () {
        self.ignoreDOMChanges(true, true);
        if (self.timer.typingTimer)
        {
            window.clearTimeout(self.timer.typingTimer);
        }
        self.timer.typingTimer = window.setTimeout(self.handleDOMChange, self.timer.typingTimerInterval);
    }
    
    self.handleDOMChange = function () {
        self.ignoreDOMChanges(false, false);
        self.refresh('gridAndRediscoverUnderlyingDivs', false);
        if (self.refs.dwDom.selectionIsExactlyOneTag()) // select that element
        {
            var offsets = self.refs.dwDom.getSelection();
            var selectedObject = self.refs.dwDom.offsetsToNode(offsets[0], offsets[1]);
            if (selectedObject)
                self.selectOverlayDiv(selectedObject, true);
        }
    }
    
	self.isZooming = function() {
		return self.refs.dw.isZooming();
	}
	
	self.disableForZoom = function(shouldDisable) {
		if (shouldDisable) {
			self.selectDiv(null); // Hide controls.
			self.destroyOverlayDivs(); 
			self.flags.disabledForZoom = true;	
		} else { // Should enable.
			self.flags.disabledForZoom = false;
		}
	}

	self.createOverlayControls = function() {
		self.createDraggingDiv();
		self.createEdgeDiv('resize');
		self.createEdgeDiv('shift');
		self.createMarginDiv();
		self.createStartsRowDiv();		
		self.createDynamicTooltipDiv();
		self.createDuplicateDiv();
        self.createDeleteDiv();
		self.createHideDiv();
		self.createMoveUpDownDiv();
		self.createMoveUpDownDiv();
        self.createLockDiv();
		self.createShowDiv();
	}

	self.log = function(str) {
		self.refs.dwscripts.log(new Date().getTime() + ': ' + str);	
	}
	
	self.log = function(feature, usage) {
		self.refs.dw.logEvent(feature, usage);
	}

	self.refresh = function(inWhatToRefresh, resetStyleSheet) {
		var whatToRefresh = inWhatToRefresh; // Can be changed by zoom logic in this function.
		if (self.flags.isRefreshing || self.isCodeView() ) {
			return;
		}
		if (typeof resetStyleSheet == "undefined")
            resetStyleSheet = true;
        //reset the style sheet
        if (resetStyleSheet) {
            self.refs.styleSheetManager.reset();
        }
		if (self.isZooming()) {
			if (!self.flags.disabledForZoom) {
				self.disableForZoom(true);	
			}
		} else { // Not zooming.
			if (self.flags.disabledForZoom) {
				self.disableForZoom(false);	
				whatToRefresh = 'gridAndRediscoverUnderlyingDivs';
			}
		}
		self.flags.isRefreshing = true;
		// Update the grid.
		if (!self.flags.isDragging || whatToRefresh == "gridForBodyAdjustment") {
			// No need to recalc the grid col rects if we are dragging.
			// In fact, trying to yields peculiar results.
			self.data.gridColRects = self.getGridColRects();
		}
		if (self.data.gridColRects.length == 0) {
			self.flags.isRefreshing = false;
			return; // Can't refresh if we don't know the dimensions.
		}
		self.updateColumns();
		if (whatToRefresh == 'gridAndRediscoverUnderlyingDivs') {
			self.removeUnhideModeChanges();
			self.createOverlayDivs();
		}
		self.syncOverlayDivs();
		if (self.flags.showProtos) {
			self.proto_positionPi();
		}
		self.flags.isRefreshing = false;
	}

	self.isLiveView = function() {
		return self.refs.dwDom.getView() != 'code' && self.refs.dwDom.getDesignViewMode() == 'live';	
	}

	self.isDesignView = function() {
		return self.refs.dwDom.getView() != 'code' && self.refs.dwDom.getDesignViewMode() == 'editable';
	}

	self.isCodeView = function() {
		return self.refs.dwDom.getView() == 'code';
	}

	self.onMouseDown_Resizing = function(evt) {
		evt.preventDefault();
		self.refs.overlayCtrl.preventRelayOfCurrentEvent();
		self.flags.isDragging = true;
		self.flags.isResizing = true;

		self.data.mouseStartX = evt.x;
		self.data.mouseStartY = evt.y;
		self.data.scrollStartX = self.refs.window.scrollX;
		self.data.gridContainerWidthStart = self.getGridContainerWidth();
		if (self.isLiveView()) {
			self.refs.cssRuleBeingModified = self.findRuleForSelectedUnderlyingDiv_LiveView();
		} else { // is design view
			if (self.flags.shouldDesignViewSnap) {
				self.updateInternalStyleSheetDom(self.getDefaultUserStyleSheetText());
			}
		}
		var selectedUnderlyingDiv = self.getSelectedUnderlyingDiv();
		if (!selectedUnderlyingDiv) {
			return;	
		}
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if( !selectedOverlayDiv ) {
			return;
		}
		self.ensureSelectedDivResizable();
		self.data.selectedUnderlyingDivStartingColSpan = self.getColSpanFromOffsetWidth(selectedUnderlyingDiv.offsetWidth);
		self.data.selectedUnderlyingDivCurrentColSpan = self.data.selectedUnderlyingDivStartingColSpan;
		self.data.startingSelectedOverlayDivOffsetWidth = selectedOverlayDiv.offsetWidth;
		self.data.lastColRight = self.data.gridColRects[self.data.gridColRects.length - 1].right;
		self.addDragEditingEventListeners('resizing');
		self.positionDraggingDiv();		
		self.positionDynamicTooltipDiv();
		//make sure the whole div is selected when we start to edit it
		self.setUserNodeSelected(true);
	}
		
	self.onMouseMove_Resizing = function(evt) {
		if (self.flags.logEvents) {
			self.log('onMouseMove_Resizing');
		}
		self.refs.overlayCtrl.preventRelayOfCurrentEvent();
		var selectedDiv = self.getSelectedUnderlyingDiv();
		if(selectedDiv)
		{
			var selectedDivPosition = self.getPosition(selectedDiv);
			var parentDiv = selectedDiv.parentNode;
			var parentPosition = self.getPosition(parentDiv);
    		var parentWidth =  self.getCssPropValFromElem(parentDiv, 'width', 'int');
			var parentRightBoundary =  parentPosition.left + parentWidth;
			var newX;
			if(evt.x < self.data.mouseCurX)
				newX = Math.max(evt.x,  selectedDivPosition.left);
			else
				newX = Math.min(evt.x, parentRightBoundary);
			var diff = (newX - self.data.mouseStartX) + (self.refs.window.scrollX - self.data.scrollStartX);
			self.data.mouseCurX = newX;
		}
		var newDraggingDivWidth = self.data.startingSelectedOverlayDivOffsetWidth + diff;
		self.refs.draggingDiv.style.width = newDraggingDivWidth + 'px'; 		
		self.snapSelectedDivToNearestValidWidth(); // Dont Snap to the Grid live - as this changes the widths which the child would need to readjust.
		var selectedUnderlyingDiv = self.getSelectedUnderlyingDiv();
		if (!selectedUnderlyingDiv) {
			return;	
		}	
		self.positionDynamicTooltipDiv();
		if (self.getPosition(self.refs.draggingDiv).left != self.getPosition(selectedUnderlyingDiv).left &&
			self.data.gridContainerWidthStart != self.getGridContainerWidth())
		{
			//vdhawal: This case should never happen now as we are not snapping to grid live. But letting it be just in case.
			//The body has shifted while we're editing, we need to refresh
			self.refresh('gridForBodyAdjustment', true);
			self.positionDraggingDiv();
			self.data.gridContainerWidthStart = self.getGridContainerWidth();
		}
		if (self.getPosition(self.refs.draggingDiv).top != self.getPosition(selectedUnderlyingDiv).top) {
			// If the div being resized jumped up or down, we need to make sure the resizing div 
			// position is synced to the selected div.
			self.positionDraggingDiv();
		}
		self.ensureSelectedDivResizable();
		self.ensureSelectedDivStillVisible();
	}

	self.onMouseUp_Resizing = function(evt) {
		if (self.flags.logEvents) {
			self.log('onMouseUp_Resizing');
		}
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_RESIZE_ELEMENT);
		if( evt ) {
			evt.preventDefault();
			self.refs.overlayCtrl.preventRelayOfCurrentEvent();
		}
		self.removeDragEditingEventListeners();
		self.hideDiv(self.refs.draggingDiv);
		self.hideDiv(self.refs.dynamicTooltipDiv);
       
        self.ignoreDOMChanges(true, true); // we are changing design view as well with a dummy edit


		if (self.data.selectedUnderlyingDivCurrentColSpan != self.data.selectedUnderlyingDivStartingColSpan) {
			if (self.isDesignView()) {
				// initiate a persisting edit here which will require repagination so that nested repagination doesnt happen.
				var dwElement = self.getSelectedUnderlyingDwElement();
				self.forceDummyEdit(dwElement);
				// ending dummy edit here
			}

			var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
			var selector = self.getValidFluidGridSelector(selectedElement);
            
            var parentCols = self.getParentColsOfSelectedDiv();
            if (self.data.selectedUnderlyingDivCurrentColSpan > parentCols)
                self.data.selectedUnderlyingDivCurrentColSpan = parentCols;
            
			self.data.selectorWidthEdited.length = 0;
			self.data.selectorWidthEdited.push(selector);
			self.refs.styleSheetManager.setColSpan(self.getDeviceFromWindowWidth(), selector, self.data.selectedUnderlyingDivCurrentColSpan, self.getParentColsOfSelectedDiv());
            self.updateAllDescendents(self.getSelectedUnderlyingDiv(), self.data.selectedUnderlyingDivCurrentColSpan, false, false );
            
            if (self.data.editsArray.length > 0)
            {
                // apply the edits for the descendents
                self.applyEdits(self.data.editsArray);
                self.data.editsArray.length = 0;  // this will clear the array and call destructors of existing elements
            }
			self.data.selectorWidthEdited.length = 0;
			
            self.refs.styleSheetManager.updateCSSWithEdits();
		}
		if (self.flags.shouldDesignViewSnap) {
			self.updateInternalStyleSheetDom(self.getDefaultUserStyleSheetText());
		}
        
        self.ignoreDOMChanges(false, false);
	}

	self.getIDSelector = function (theElement) {
		if(!theElement)
			return null;
	    var id = theElement.getAttribute ? theElement.getAttribute('id') : null;
        if (id)
        {
            var selector = '#' + id;
			return selector;
		}
		return null;
	}
	
	self.getValidFluidGridSelector = function (theElement) {
		if(!theElement)
			return null;
		var selector = self.getValidFluidGridIDSelector(theElement);
		if(!selector)
			selector = self.getValidFluidGridClassSelector(theElement);
		return selector
	}
    
    self.getValidFluidGridIDSelector = function (theElement) {
		if(!theElement)
			return null;
        var selector = self.getIDSelector(theElement);
        if (selector && self.refs.styleSheetManager.isValidFluidSelector(selector)) {
			return selector;
		}
        return null;
    }
    
	self.getValidFluidGridClassSelector = function (theElement) {
		if(!theElement)
			return null;
		var classList = theElement.className.split(/\s+/);
		for(index in classList)
		{
			if(classList[index])
			{
				if (classList[index] != 'gridContainer')
				{
					var selector = '.'+classList[index];
					if (self.refs.styleSheetManager.isValidFluidSelector(selector))
						return selector;
				}
			}		
		}
		return null;
	}
	
    self.validFluidElement = function( theElement ) {
        if (!theElement)
            return false;
        
        if (typeof theElement == "undefined")
            return false;

        var selector = self.getValidFluidGridSelector(theElement);
		if(selector)
			return true;
		
        return false;
    }
    
    self.applyEdits = function (editsArray) {                
        var device = self.getDeviceFromWindowWidth();
        editsArray.forEach(function(editInfo){
		var element = editInfo.element;
            if (typeof editInfo.element != undefined && typeof editInfo.parentCols != undefined)
            {
                var selector = self.getValidFluidGridSelector(element);
                if (self.data.selectorWidthEdited.indexOf(selector) == -1)
                    self.data.selectorWidthEdited.push(selector);
                else
                    return;
                if (typeof editInfo.colSpan != undefined) {
                    if (editInfo.colSpan > editInfo.parentCols)
                        editInfo.colSpan = editInfo.parentCols; // the number of columns cannot be greater than the number of columns of parent
                    self.refs.styleSheetManager.setColSpan(device, selector, editInfo.colSpan, editInfo.parentCols);
                }
                if (typeof editInfo.colShifted != undefined) {
                    self.refs.styleSheetManager.setColShift(device, selector, editInfo.colShifted, editInfo.parentCols);
                }
            }
        });
    }
    
    self.updateAllDescendents = function (resizedElement, newParentCols, applyUpdates, forceUpdate) {
        if (applyUpdates) { // if updates are to be applied reset all previous info
            self.data.editsArray.length = 0; // this will clear the array and call destructors of existing elements
        }

        var childNodes = self.childElementNodesToArray(resizedElement.childNodes);
        if (childNodes.length && childNodes.length > 0)
        {
            childNodes.forEach(function(childNode){
                    if (self.validFluidElement(childNode)) // Do it for only fluid grid elements
                    {
                        if (!childNode || self.isElementLocked(childNode))
                            return;
                        var childWidth = self.getCssPropValFromElem(childNode, 'width', 'int');
                        var marginPx = self.getCssPropValFromElem(childNode, 'margin-left', 'int');
                        var colSpan = self.getColSpanFromOffsetWidth(childWidth); // calculate the existing number of columns. TODO: can this information be stored to optimise performance if required ??
                        var colShifted = self.getColShiftFromMarginLeftPx(marginPx); // calculate the existing number of shifted columns. TODO: can this information be stored to optimise performance if required ??
                        var updateDescendents = false;
                        var recordedChanges = new Object();
                        recordedChanges.element = childNode;
                        if (newParentCols < colSpan)
                        {
                               // if parent col span has reduced, reduce the colspan to match it
                               // This will result in change in size of the element in pixels => will need to update the children as well.
                               colSpan = newParentCols;
                               updateDescendents = true;
                        }
                        recordedChanges.colSpan = colSpan;
                        recordedChanges.colShifted = colShifted;
                        recordedChanges.parentCols = newParentCols
                        self.data.editsArray.push(recordedChanges);
                        if ( updateDescendents || forceUpdate)
                        {
                               self.updateAllDescendents(childNode, colSpan, false, forceUpdate); // just append to the edit information
                        }
                    }
            });
        }
        
        if (applyUpdates) {
            if (self.data.editsArray.length > 0)
            {
                self.applyEdits(self.data.editsArray);
                // All the edits have only been saved. Update the Style Sheet Manager and the Style Sheet with the new information.
                self.refs.styleSheetManager.updateCSSWithEdits();
                self.data.editsArray.length = 0;  // this will clear the array and call destructors of existing elements
            }
        }
    }
		
	self.onMouseDown_Shifting = function(evt) {
		if (self.flags.logEvents) {
			self.log('onMouseDown_Shifting');
		}
		evt.preventDefault();
		self.refs.overlayCtrl.preventRelayOfCurrentEvent();
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		self.flags.isDragging = true;
		self.flags.isShifting = true;
		self.data.mouseStartX = evt.x;
		self.data.mouseCurX = evt.x;
		self.data.mouseStartY = evt.y;
		self.data.scrollStartX = self.refs.window.scrollX;
		self.data.gridContainerWidthStart = self.getGridContainerWidth();
		if (self.isLiveView()) {
			self.refs.cssRuleBeingModified = self.findRuleForSelectedUnderlyingDiv_LiveView();
		} else { // Is Design View.
			if (self.flags.shouldDesignViewSnap) {
				self.updateInternalStyleSheetDom(self.getDefaultUserStyleSheetText());
			}
		}
		var selectedUnderlyingDiv = self.getSelectedUnderlyingDiv();
		if (!selectedUnderlyingDiv) {
			return;	
		}
		var marginLeft = self.getCssPropValFromElem(selectedElement, 'margin-left', 'int');	
		self.data.startingMarginDivWidth = marginLeft;		
		self.data.marginsOffsetLeft = self.getPosition(selectedUnderlyingDiv).left - marginLeft;
		self.data.selectedDivStartingColShift = self.getColShiftFromMarginLeftPx(marginLeft);
		self.data.selectedDivCurrentColShift = self.data.selectedDivStartingColShift;
		self.data.startingSelectedUnderlyingDivOffsetLeft = self.getPosition(selectedUnderlyingDiv).left;
		self.addDragEditingEventListeners('shifting');
		self.positionDraggingDiv();
		self.positionDynamicTooltipDiv();
		//make sure the whole div is selected when we start to edit it
		self.setUserNodeSelected(true);
	}

	self.onMouseMove_Shifting = function(evt) {
		if (self.flags.logEvents) {
			self.log('onMouseMove_Shifting');
		}
		self.refs.overlayCtrl.preventRelayOfCurrentEvent();
		var selectedDiv = self.getSelectedUnderlyingDiv();
		if(selectedDiv)
		{
			var parentDiv = selectedDiv.parentNode;
			var parentPosition = self.getPosition(parentDiv);
    		var parentWidth =  self.getCssPropValFromElem(parentDiv, 'width', 'int');
			var parentRightBoundary =  parentPosition.left + parentWidth;
			var newX;
			var diff;
			if(evt.x < self.data.mouseCurX)
				newX = Math.max(evt.x,  parentPosition.left);
			else
				newX = Math.min(evt.x, parentRightBoundary - parseInt(self.refs.draggingDiv.style.width));
		
			self.data.mouseCurX = newX;
			diff = (newX - self.data.mouseStartX) + (self.refs.window.scrollX - self.data.scrollStartX);
		}
		var newDraggingDivLeft = self.data.startingSelectedUnderlyingDivOffsetLeft + diff;
		self.refs.draggingDiv.style.left = newDraggingDivLeft + 'px';
		self.positionDynamicTooltipDiv();
		self.snapSelectedDivToNearestValidLeft();
		self.ensureSelectedDivStillVisible();
	}
	
	self.onMouseUp_Shifting = function(evt) {
		if (self.flags.logEvents) {
			self.log('onMouseUp_Shifting');
		}
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_SHIFT_ELEMENT);
		evt.preventDefault();
		self.refs.overlayCtrl.preventRelayOfCurrentEvent();
		self.removeDragEditingEventListeners();
		self.hideDiv(self.refs.draggingDiv);
		self.hideDiv(self.refs.dynamicTooltipDiv);
        
        self.ignoreDOMChanges(true, true); // we are changing design view as well with a dummy edit hence ignore both for the time being.

		if (self.data.selectedDivCurrentColShift != self.data.selectedDivStartingColShift) {

			if (self.isDesignView()) {
				// initiate a persisting edit here which will require repagination so that nested repagination doesnt happen.
				var dwElement = self.getSelectedUnderlyingDwElement();
				self.forceDummyEdit(dwElement);
				// ending dummy edit here
			}
			
			var selector = self.getValidFluidGridSelector(selectedElement);
			self.refs.styleSheetManager.setColShift(
				self.getDeviceFromWindowWidth(),
				selector,
				self.data.selectedDivCurrentColShift,
                self.getParentColsOfSelectedDiv()
			);
			
			self.refs.styleSheetManager.updateCSSWithEdits();
			
			// Select the node again right now to prevent the selection from jumping around in code
			// view on the mac.			
			self.setUserNodeSelected(true);					
		}
		if (self.flags.shouldDesignViewSnap) {
			self.updateInternalStyleSheetDom(self.getDefaultUserStyleSheetText());
		}
        
        self.ignoreDOMChanges(false, false);
	}

	self.onMouseOver_OverlayDiv = function(evt) {
		if (self.flags.isDragging) {
			return;				
		}
		var oldMouseOverDiv = self.getMouseOverOverlayDiv();
		self.data.mouseOverDivIndex = self.getUnderlyingDivIndex(evt.target.underlyingDiv);
		self.updateOverlayDivBorder(oldMouseOverDiv);
		if( oldMouseOverDiv != evt.target ) {
			self.updateOverlayDivBorder(evt.target);
			if (oldMouseOverDiv) {
				self.setBackgroundForDiv(oldMouseOverDiv);				
			}
		}
		if( !self.isUnderlyingDivHidden(evt.target) )
			evt.target.style.backgroundColor = self.consts.overlayDivBgColorHovered;				
	}

	self.onMouseOut_OverlayDiv = function(evt) {
		if (self.flags.isDragging) {
			return;
		}
		var oldMouseOverDiv = self.getMouseOverOverlayDiv();
		self.data.mouseOverDivIndex = -1;
		self.updateOverlayDivBorder(oldMouseOverDiv);
		self.updateOverlayDivBorder(evt.target);
		if(evt.target)
			self.setBackgroundForDiv(evt.target);
	}
    
	self.getUnderlyingDivIndex = function(element)
	{
		for (var i = 0; i < self.refs.overlayDivs.length; i++) {
			var overlayDiv = self.refs.overlayDivs[i];
			if (overlayDiv.underlyingDiv == element) {
				return i;
			}
		}	
		return -1;	
	}
	
	self.onMouseDown_OverlayDocument = function(evt) {
		if (evt.target == self.refs.resizeEdgeDiv || evt.target == self.refs.shiftEdgeDiv) {
			evt.preventDefault();
			self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			return;
		}
		if (evt.target == self.refs.startsRowDiv) {
			evt.preventDefault();
			self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			/* If the fluid grid operation buttons overlap with the margin of the previous elment, selection issues are observed. 
			Fluid Grid operation buttons are placed on top boundary of the selected element extending upwards in some cases . 
			Once the click happens we finish inserting proper HTML/CSS and providing selections, thereafter mouseMove event gets
			handled	by the DW. Since place of the click was the operation buttons which overlap the margin of the previous element
			DW tries to give selection to that region while handling the mouse event. And this operation messes up with the selection
			that we set after the operation. 
			Need to call <code>setIgnoreMouseMoveForFGOp</code> API to notify DW to skip the immediate mouseMove event.*/
			self.refs.dw.setIgnoreMouseMoveForFGOp();
			self.toggleStartsRow();
			return;
		}
        if (evt.target == self.refs.deleteDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.attemptDeletion();
            return;
        }        
        if (evt.target == self.refs.lockDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.toggleFixedFluid();
            return;
        }
        if (evt.target == self.refs.duplicateDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.attemptNestedDuplication();
            return;
        }
		if (evt.target == self.refs.moveUpDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.swapWithPrevious();
            return;
        }
        if (evt.target == self.refs.moveDownDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.swapWithNext();
            return;
        }
		if (evt.target == self.refs.hideDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.hideSelectedDiv();
            return;
        }
		if (evt.target == self.refs.showDiv) {
            evt.preventDefault();
            self.refs.overlayCtrl.preventRelayOfCurrentEvent();
			self.refs.dw.setIgnoreMouseMoveForFGOp();
            self.showSelectedDiv();
            return;
        }
		if (!evt.target.underlyingDiv) {
			// If it doesn't have a underlyingDiv property, it isn't an overlay div.
			self.selectDiv(null);
		} else if (evt.target != self.getSelectedOverlayDiv()) {
			self.selectDiv(evt.target);
			// Select user node here instead of in selectDiv() because having it there caused
			// the LDM to jump around when live view reloaded after an edit.
			self.setUserNodeSelected();
		}
	}
	
	self.updateOverlayDivBorder = function( overlayDiv ) {
		if( !overlayDiv ) {
			return;
		}
		var newBorder = self.consts.overlayBorder;
		if (overlayDiv == self.getSelectedOverlayDiv()) { 
			newBorder = self.consts.selectedBorder;
		}
		else if( overlayDiv == self.getMouseOverOverlayDiv() ) {
			newBorder = self.consts.hoverBorder;
		}
		else if(self.isDivMisaligned(overlayDiv)) {
			newBorder = self.consts.misalignedDivBorder;
		}
		self.setOverlayDivBorder(overlayDiv, newBorder);
	}

	self.positionControls = function() {
		self.positionEdge('resize');
		self.positionEdge('shift');
		self.positionMarginDiv();
        self.positionMoveUpDownDiv();
        self.positionRemainingOverlayControls(); // hide, starts row, duplicate, delete and lock
	}
  
    self.getSelectedUnderlyingElems = function(selElem, includeLockedElement, includeHiddenElement, onlySupportedFluidElements) {
		if(typeof includeLockedElement == 'undefined')
			includeLockedElement = true;
		if(typeof includeHiddenElement == 'undefined')
			includeHiddenElement = false;
        if(typeof onlySupportedFluidElements == 'undefined')
			onlySupportedFluidElements = false;
        
		var divs = self.nodeListToArray(selElem.getElementsByTagName(self.consts.tagName));
		var outDivs = [];
		var validWidths = self.getValidLayoutDivWidths();
		divs.forEach(function(div){	
			if (!div) {
				return;
			}

            if (onlySupportedFluidElements)
            {
                if (!div.tagName.match(self.refs.fluidElementRegExp))
                     return;
            }
			
			if ( !self.validFluidElement(div) && !self.isElementLocked(div) ) {
				return;
			}
			
			//we should not include elements inside a hidden element
			if( self.isDescendantOfHiddenElement(div) ) {
				return;
			}
			
			if (self.getCssPropValFromElem(div, 'display') == 'none')
			{
				if(includeHiddenElement)
					outDivs.push(div);
				//if in unhide mode, show the hidden divs too
				//and mark them with an attribute
				if(self.refs.dwDom.getIsInUnhideMode() && self.isLiveView())
				{
					//filter elements with inline display property
					if( div.style.display && div.style.display != '')
						return;

					//filter elements hidden due to non-FG selectors
					var validFGSelectorApplied = false;
					var selector = self.getValidFluidGridSelector(div);
					var curDevice = self.getDeviceFromWindowWidth();
					if ( selector && selector.charAt(0) == '.') 
					{
						//if it is class based, then there should be specific 'hide_' class applied
						var liveCodeHiddenId = div.getAttribute('data-fluidgrid_id');
						var dwElement = null;
						if(liveCodeHiddenId)
							dwElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenId));
					
						if (dwElement) 
						{							
							var classesApplied = dwElement.getAttribute('class');							
							if (classesApplied)
							{								
								var hideClass = 'hide_' + curDevice;
								var index = classesApplied.indexOf(hideClass);
								//if we find this class, then this was hidden by us
								if(index != -1)
									validFGSelectorApplied = true;
							}
						}
					}
					else 
					{
						//if it is ID based, make sure that the display property is none in the selector.
						var displayVal = self.refs.styleSheetManager.getCSSPropertyInSelector(curDevice, selector, 'display');
						if(displayVal == 'none')
							validFGSelectorApplied = true;
					}
					
					if(validFGSelectorApplied == false)
						return;
				
					//display the hidden element
					div.style.display = 'block';
					//check if its a broken FG element
					var divwidth = self.getCssPropValFromElem(div, 'width', 'int');
					if( validWidths.some(self.getWidthMatchingFxn(divwidth)) )
					{
						div.setAttribute('data-isHidden', 'true');
						if(!includeHiddenElement)
							outDivs.push(div);
					}
					else
						div.style.display = '';
				}
				return;		
			}

            var width = self.getCssPropValFromElem(div, 'width', 'int');

			if( (validWidths.some(self.getWidthMatchingFxn(width))) || (includeLockedElement && self.isElementLocked(div) ) ){
				outDivs.push(div);
			}
		});
		
		return outDivs;	
	}
	
	self.getUnderlyingDivs = function() {
		if (self.flags.disabledForZoom) {
			return [];
		}
		
		return self.getSelectedUnderlyingElems(self.getActiveDom().body, true, false, true);
	}
	
    self.getWidthMatchingFxn = function ( widthToFind ) {
        return function(widthToTest) {
            var _self = self;
            return _self.widthMatch(widthToTest, widthToFind);
        }
    }
	
	self.getActiveDom = function() {
		return self.isLiveView() ? self.refs.dwDom.browser.getWindow().document : self.refs.dwDom;
	}
    
    self.childElementNodesToArray = function (nodeList) {
        var list = [];
		for (var i = 0; i < nodeList.length; i++) {
            if (nodeList[i].nodeType == 1)
                list.push(nodeList[i]); // push in only valid HTML Elements
		}
		return list;	  
    }

	self.nodeListToArray = function(nodeList) {
		var list = [];
		for (var i = 0; i < nodeList.length; i++) {
			list.push(nodeList[i]);
		}
		return list;	
	}
	
	self.getValidLayoutDivWidths = function() {
		var colRects = self.data.gridColRects;
		if (colRects.length == 0) {
			return [];
		}
		var firstColLeft = colRects[0].left;
		var widths = [];
		colRects.forEach(function(colRect) {
			var width = colRect.right - firstColLeft;
			widths.push(width);
		});
		return widths;
	}
		
	self.getValidMarginLefts = function(startsNewRow) {
		var colRects = self.data.gridColRects;
		if (colRects.length == 0) {
			return [];
		}
		var colWidth = colRects[0].right - colRects[0].left;
		var gutterWidth = colRects[1].left - colRects[0].right;
		var validWidths = self.getValidLayoutDivWidths();
		var validMarginLefts = validWidths.map(function(validWidth){
				var marginLeft = validWidth - colWidth; 
				if (!startsNewRow) {			
					marginLeft += gutterWidth;
				}
				return marginLeft;
		});
		return validMarginLefts;			
	}

	self.selectDiv = function(overlayDiv) {
		// !! call selectDiv when you know there is a valid underlyingDiv as self.isDivMisaligned() is called which accesses that.!!
		var isSameDiv = (overlayDiv && overlayDiv == self.getSelectedOverlayDiv());
		if( !isSameDiv ) {
			//If we're changing selection, ensure we aren't still listening to mouse moves
			//incase the mouseup is somehow not balanced
			self.removeDragEditingEventListeners();
		}
		if (self.data.selectedUnderlyingDivIndex >= 0) {	
			// Unselect the currently selected div.
			var selectedOverlayDiv = self.getSelectedOverlayDiv();
			if (selectedOverlayDiv) {
				if (self.isDivMisaligned(selectedOverlayDiv)) {
					var newBorder = self.consts.misalignedDivBorder;
				} else {
					var newBorder = self.consts.overlayBorder;
				}
				self.setOverlayDivBorder(selectedOverlayDiv, newBorder);
				self.setBackgroundForDiv(selectedOverlayDiv);
			}
			// We used to remove the translated attr 'hiliteChildrenOnSelect' here, but that
			// would make it so a user could not pop up the table column context menu, so
			// we don't remove it anymore.		
			self.data.selectedUnderlyingDivIndex = -1;
		}
		if (overlayDiv)	{
			self.data.selectedUnderlyingDivIndex = self.getUnderlyingDivIndex(overlayDiv.underlyingDiv);
			self.setOverlayDivBorder(overlayDiv, self.consts.selectedBorder);
			self.setBackgroundForDiv(overlayDiv);
			if (self.isDesignView()) {
				//tell dw to not hilite the children when we select this div
				var underlyingDiv = overlayDiv.underlyingDiv;
				if( underlyingDiv ) {
					underlyingDiv.setTranslatedAttribute('hiliteChildrenOnSelect', 'false');
				}
			}
		}

		self.ensureSelectedDivResizable();
		self.positionControls();
		if (self.flags.showProtos) {
			self.proto_showPi(overlayDiv != null);
		}
	}
		
	self.setOverlayDivBorder = function(div, border) {
		var oldBorderWidth = parseInt(div.style.borderWidth);
		if (isNaN(oldBorderWidth)) {
			oldBorderWidth = 0;
		}
		self.setDivBorder(div, border)
		var newBorderWidth = parseInt(div.style.borderWidth);
		if (isNaN(newBorderWidth)) {
			newBorderWidth = 0;
		}
		if (newBorderWidth > oldBorderWidth) {
			self.deflateDiv(div, newBorderWidth - oldBorderWidth);
		} else if (oldBorderWidth > newBorderWidth) {
			self.inflateDiv(div, oldBorderWidth - newBorderWidth);
		}
	}

	self.setDivBorder = function(div, border) {
		div.style.border = border;	
	}
		
	self.createOverlayDivs = function() {
		if (self.refs.overlayDivs.length > 0) {
			self.destroyOverlayDivs();
		}
		var underlyingDivs = self.getUnderlyingDivs();

		underlyingDivs.forEach(function(underlyingDiv){			
			var div = self.createOverlayDiv(underlyingDiv);
			self.refs.overlayDivs.push(div);
			self.refs.document.body.appendChild(div);
		});
	}
    
    self.widthMatch = function(lWidth, rWidth) {
        if( lWidth == rWidth )
            return true;

        var lInt = parseInt(lWidth);
        var rInt = parseInt(rWidth);
        var diff = 0;

        if (lInt > rInt)
            diff = lInt - rInt;
        else
            diff = rInt - lInt;

        if (diff <= self.consts.pixelFixedPoint)
            return true;

        return false;
	}
    
	self.destroyOverlayDivs = function() {
		self.refs.overlayDivs.forEach(function(div){
			self.refs.document.body.removeChild(div);
		});
		self.refs.overlayDivs = [];
	}
	
	self.createOverlayDiv = function(underlyingDiv) {
		var overlayDiv = self.refs.document.createElement('div');
		overlayDiv.underlyingDiv = underlyingDiv;
		if (self.isElementLocked(underlyingDiv))
			overlayDiv.setAttribute('class', 'overlayDivLocked');
		else
			overlayDiv.setAttribute('class', 'overlayDiv');
		overlayDiv.addEventListener('mouseover', self.onMouseOver_OverlayDiv);
		overlayDiv.addEventListener('mouseout', self.onMouseOut_OverlayDiv);
		return overlayDiv;
	}

	self.positionEdge = function(whichEdge) {
		// Position edge div.
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		var edgeDiv = whichEdge == 'resize' ? self.refs.resizeEdgeDiv : self.refs.shiftEdgeDiv;
		if ( !selectedElement || (self.isSelectedElementLocked()) || selectedElement.getAttribute('data-isHidden') == 'true' ) {
			if( self.flags.isDragging ) {
				self.refs.dwscripts.debugAlert("Attemping to hide the edges while dragging");
			}
			else {
				self.hideDiv(edgeDiv);
				self.hideDiv(self.refs.knobMiddle[whichEdge]);
			}
			return;
		}		

		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!selectedOverlayDiv) {
			return;	
		}
		var edgeDivStyle = edgeDiv.style;		
		var selectedOverlayDivStyle = selectedOverlayDiv.style;
		// Left differs based on which edge we are positioning.					
		if (whichEdge == 'resize') {
			edgeDivStyle.left = parseInt(selectedOverlayDivStyle.left) + self.consts.selectedBorderWidth + 			
						parseInt(selectedOverlayDivStyle.width) + (self.consts.selectedBorderWidth / 2) - (self.consts.knobSize / 2) + 'px';						
		} else {
			edgeDivStyle.left = parseInt(selectedOverlayDivStyle.left) + (self.consts.selectedBorderWidth / 2) - (self.consts.knobSize / 2) + 'px';						
		}									
		// Top and height are the same whether we are positioning the resize or shift edge.
		edgeDivStyle.top = parseInt(selectedOverlayDivStyle.top) - (self.consts.selectedBorderWidth / 2) + 'px';
		edgeDivStyle.height = parseInt(selectedOverlayDivStyle.height) + (self.consts.selectedBorderWidth * 3) + 'px';
		// Position knob divs.
		self.refs.knobMiddle[whichEdge].style.top = parseInt(selectedOverlayDivStyle.top) + (parseInt(selectedOverlayDivStyle.height) / 2) + 
								self.consts.selectedBorderWidth - (self.consts.knobSize / 2) + 'px';
		self.refs.knobMiddle[whichEdge].style.left = edgeDivStyle.left;

        var tooltip = whichEdge == 'resize' ? self.refs.dw.loadString('overlay/fluidGridLayout/resize/tooltip') : self.refs.dw.loadString('overlay/fluidGridLayout/shift/tooltip'); //load tooltip

		tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + self.getValidFluidGridSelector(selectedElement)); //format tooltip with tagname and ID
        edgeDiv.setAttribute('title', tooltip);
        
		self.showDiv(edgeDiv);
		self.showDiv(self.refs.knobMiddle[whichEdge]);
	}
		
	self.isLeftCloseEnough = function(colNum, left1, left2) {
		var marginOfError = colNum * 2; //Worst case error grows left to right at 1px per gutter + 1 px per col
		return Math.abs(left1 - left2) <= marginOfError;
	}

	self.isWidthCloseEnough = function(width1, width2) {
		var marginOfError = 5;
		return Math.abs(width1 - width2) <= marginOfError;
	}

	self.isDivMisaligned = function(div) {
		var isFirstDivInRow = false;
		for (var i = 0; i < self.refs.overlayDivs.length; i++) {
			if (self.refs.overlayDivs[i] == div) {
				var underlyingDiv = div.underlyingDiv;
				var firstChild = false;
				if (underlyingDiv)
				{
					var parentElement = underlyingDiv.parentNode;
					if (parentElement) {
						var childArray = self.childElementNodesToArray(parentElement.childNodes);
						if (childArray.length > 0 && self.isEqual(childArray[0], underlyingDiv)) {
							firstChild = true;
						}
					}
				}
				if (i == 0 || firstChild || self.getPosition(div).top != self.getPosition(self.refs.overlayDivs[i - 1]).top) {
					isFirstDivInRow = true;
					break;	
				}
			}
		}
		if (!isFirstDivInRow) {
			return false;	
		}
		var left = self.getPosition(div).left;
		var rects = self.data.gridColRects;
		for (var i = 0; i < rects.length; i++) {
			var validLeft = rects[i].left;
			var colNum = i + 1;
			if (self.isLeftCloseEnough(colNum, validLeft, left)) {
				return false;
			}
			else if( validLeft > left ) {
				return true; //none of the other divs to the right will match either
			}
		}
		return true;
	}
		
	self.positionDraggingDiv = function() {			
		if (self.data.selectedUnderlyingDivIndex < 0) {
			self.hideDiv(self.refs.draggingDiv);
			self.hideDiv(self.refs.dynamicTooltipDiv);
			return;
		}
		// Inflate div to make client rect size of actual.
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!selectedOverlayDiv) {
			return;	
		}
		self.inflateDiv(selectedOverlayDiv, self.consts.selectedBorderWidth);
		var draggingDivStyle = self.refs.draggingDiv.style;
		var selectedOverlayDivStyle = selectedOverlayDiv.style;
		draggingDivStyle.left = selectedOverlayDivStyle.left;
		draggingDivStyle.top = selectedOverlayDivStyle.top;
		draggingDivStyle.width = selectedOverlayDivStyle.width;
		draggingDivStyle.height = selectedOverlayDivStyle.height;
		// Now deflate them based on respective border widths.
		self.deflateDiv(self.refs.draggingDiv, self.consts.draggingBorderWidth);
		self.deflateDiv(selectedOverlayDiv, self.consts.selectedBorderWidth);
		self.showDiv(self.refs.draggingDiv);
	}
	
	self.positionDynamicTooltipDiv = function() {
		//  This method should be called after the dragging div has been updated.
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!self.flags.isDragging || !selectedOverlayDiv) {
			self.hideDiv(self.refs.dynamicTooltipDiv);
			return;	
		}
		var widthLabel = self.refs.dw.loadString('overlay/fluidGridLayout/width/label');
		var colLabel = self.refs.dw.loadString('overlay/fluidGridLayout/column/label');
		var marginLeftLabel = self.refs.dw.loadString('overlay/fluidGridLayout/marginLeft/label');					
		var left = parseInt(self.refs.draggingDiv.style.left) + self.consts.dynamicTooltipOffsetX;			
		if (self.flags.isResizing) {			
			var tentativeWidth = parseInt(self.refs.draggingDiv.style.width);
			left += tentativeWidth;
			var colSpan = self.getColSpanFromOffsetWidth(tentativeWidth);
			var width = self.refs.styleSheetManager.calcCssWidthFromColSpanUsingParentColCount(
				self.getDeviceFromWindowWidth(), 
				colSpan,
                self.getParentColsOfSelectedDiv()
			);
			self.refs.dynamicTooltipDiv.innerHTML = '<nobr>' + widthLabel + ' ' + width + '<br>' + colLabel + ' ' + colSpan + '</nobr>';
		} else { // isShifting
			var tentativeWidth = self.data.startingMarginDivWidth + self.data.mouseCurX - self.data.mouseStartX;
			var colShift = self.getColShiftFromMarginLeftPx(tentativeWidth);
			var marginLeft = self.refs.styleSheetManager.calcCssMarginLeftUsingParentColCount(
				self.getDeviceFromWindowWidth(),
				colShift,
				self.startsNewRow(selectedOverlayDiv.underlyingDiv),
                self.getParentColsOfSelectedDiv()
			);
			self.refs.dynamicTooltipDiv.innerHTML = '<nobr>' + marginLeftLabel + ' ' + marginLeft + '<br>' + colLabel + ' ' +  colShift + '</nobr>';
		}
		tipOffsetWidth = self.refs.dynamicTooltipDiv.offsetWidth ? self.refs.dynamicTooltipDiv.offsetWidth : 150; // offsetWidth is zero first time around.
		var isOutOfViewRight = (left + tipOffsetWidth) > self.refs.window.screen.availWidth + self.refs.document.body.scrollLeft;
		if (isOutOfViewRight) {
			left = self.refs.window.screen.availWidth - tipOffsetWidth + self.refs.document.body.scrollLeft;	
		}
		var top = self.data.mouseStartY + self.consts.dynamicTooltipOffsetY + self.refs.document.body.scrollTop;
		var isOutOfViewBottom = (top + self.refs.dynamicTooltipDiv.offsetHeight) > self.refs.window.screen.availHeight + self.refs.document.body.scrollTop;
		if (isOutOfViewBottom) {
			top = self.refs.window.screen.availHeight - self.refs.dynamicTooltipDiv.offsetHeight + self.refs.document.body.scrollTop;	
		}
		self.refs.dynamicTooltipDiv.style.left = left + 'px';
		self.refs.dynamicTooltipDiv.style.top = top + 'px';
		self.showDiv(self.refs.dynamicTooltipDiv);		
	}	

	self.getSelectedUnderlyingDiv = function() {
		return self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
	}
	
	self.getSelectedOverlayDiv = function() {
		if (self.data.selectedUnderlyingDivIndex < 0) {
			return null;	
		}
		return self.refs.overlayDivs[self.data.selectedUnderlyingDivIndex];
	}
	
	self.getMouseOverOverlayDiv = function() {
		if (self.data.mouseOverDivIndex < 0) {
			return null;	
		}
		return self.refs.overlayDivs[self.data.mouseOverDivIndex];
	}
	
	self.getOverlayDiv = function(div) {
		for (var i = 0; i < self.refs.overlayDivs.length; i++) {
			var overlayDiv = self.refs.overlayDivs[i];
			if (overlayDiv.underlyingDiv == div) {
				return overlayDiv;
			}
		}
		return null;
	}
	
	self.getBackgroundStringForDiv = function(div) {
		if( self.isUnderlyingDivHidden(div) )
			return 'background-image:' + self.consts.hiddenDivBgImage + ';';
		else
			return 'background-color:' + self.consts.overlayDivBgColor + ';';
	}
	
	self.setBackgroundForDiv = function(div) {
		if(!div)
			return;
		
		if( self.isUnderlyingDivHidden(div) )
			div.style.backgroundImage = self.consts.hiddenDivBgImage;
		else
			div.style.backgroundColor = self.consts.overlayDivBgColor;
	}
	
	self.isUnderlyingDivHidden = function(div) {
		//hidden elements can occur only in live view
		if( !self.isLiveView() || !self.refs.dwDom.getIsInUnhideMode() )
			return false;
		
		for (var i = 0; i < self.refs.overlayDivs.length; i++) {
			var overlayDiv = self.refs.overlayDivs[i];
			if (overlayDiv == div) {
				if (overlayDiv.underlyingDiv && overlayDiv.underlyingDiv.getAttribute('data-isHidden') == 'true')
					return true;
				else
					return false;				
			}
		}
		return false;
	}
	
	self.createEdgeDiv = function(whichEdge) {
		// Edge div
		var edgeDiv = self.refs.document.createElement('div');
		edgeDiv.style.position = 'absolute';
		edgeDiv.style.zIndex = 10001; // z-index one higher than the knobs.
		edgeDiv.style.width = self.consts.knobSize + 'px';
		edgeDiv.style.cursor = 'e-resize';
		edgeDiv.style.webkitUserSelect = "none";
		edgeDiv.addEventListener('mousedown', whichEdge == 'resize' ? self.onMouseDown_Resizing : self.onMouseDown_Shifting);
		self.refs[whichEdge == 'resize' ? 'resizeEdgeDiv' : 'shiftEdgeDiv'] = edgeDiv;
		self.hideDiv(edgeDiv);
		self.refs.document.body.appendChild(edgeDiv);
		// Knob divs (overlaid by edge div)
		self.refs.knobMiddle[whichEdge] = self.createKnobDiv();
	}
	
	self.createKnobDiv = function() {
		var knobDiv = self.refs.document.createElement('div');
		knobDiv.style.position = 'absolute';
		knobDiv.style.zIndex = 10000;
		knobDiv.style.width = self.consts.knobSize + 'px';
		knobDiv.style.height = self.consts.knobSize + 'px';
		knobDiv.style.backgroundImage = "url(dw://Configuration/Overlays/CssGrids/images/knob.png)";
		knobDiv.style.webkitUserSelect = "none";
		self.hideDiv(knobDiv);
		self.refs.document.body.appendChild(knobDiv);
		return knobDiv;		
	}

	self.createDraggingDiv = function() {
		var draggingDiv = self.refs.document.createElement('div');		
		draggingDiv.style.position = 'absolute';			
		draggingDiv.style.border =  self.consts.draggingBorder;
		self.hideDiv(draggingDiv);			
		self.refs.document.body.appendChild(draggingDiv);
		self.refs.draggingDiv = draggingDiv;
	}		

	self.createDynamicTooltipDiv = function() {
		var div = self.refs.document.createElement('div');		
		div.style.position = 'absolute';
		div.style.backgroundColor = 'white';
		div.style.borderRadius = '10px';
		div.style.padding = '10px';
		div.style.border =  '1px solid #ccc';
		div.style.fontFamily = 'Arial';
		div.style.fontSize = '10pt';
		div.style.zIndex = 20000;
		self.hideDiv(div);			
		self.refs.document.body.appendChild(div);
		self.refs.dynamicTooltipDiv = div;
	}

	self.snapSelectedDivToNearestValidWidth = function() {
		var newColSpan = self.getColSpanFromOffsetWidth(self.refs.draggingDiv.offsetWidth);
		self.data.selectedUnderlyingDivCurrentColSpan = newColSpan;
	}
				
	self.getColSpanFromOffsetWidth = function(offsetWidth) {
		// Called for snapping to nearest valid col span and for converting valid width to valid col span.
		var smallestDiff = 100000;
		var nearestWidthIndex = -1;
		var validWidths = self.getValidLayoutDivWidths();
		validWidths.forEach(function(validWidth, i){			
			var diff = Math.abs(validWidth - offsetWidth);
			if (diff < smallestDiff) {
				nearestWidthIndex = i;
				smallestDiff = diff;	
			}
		});
		return nearestWidthIndex + 1;
	}
		
	self.getGridPropsRec = function() {
		return self.refs.styleSheetManager.getGridPropsRec(self.getDeviceFromWindowWidth());
	}
    
    self.getParentColsOfSelectedDiv = function () {
        var selectedDiv = self.getSelectedUnderlyingDiv();
        if (selectedDiv)
        {
            var parentElement = selectedDiv.parentNode;
            if ( parentElement )
            {
                return self.getColSpanFromOffsetWidth(parentElement.offsetWidth);
            }
        }
        return -1;
    }

	self.getGridColRects = function() {
		var gridContainerStyle = self.getGridContainerStyle();
		if (!gridContainerStyle)		
			return [];
		var gridPropsRec = self.getGridPropsRec();
		var rects = [];			
		var curX = 0;		
		for (var i = 0; i < gridPropsRec.numCols; i++) {
			var rect = {};
			rect.left = curX;
			curX += gridPropsRec.colWidth;
			rect.right = curX;
			curX += gridPropsRec.gutterWidth;
			rects.push(rect);
		}		
		var stretchFactor = gridContainerStyle.width/gridPropsRec.allColsWidth;		
		var activeViewScale = self.getActiveViewScale();
		var bodyLeft = gridContainerStyle.paddingLeft + gridContainerStyle.borderLeftWidth + gridContainerStyle.marginLeft;
		rects = rects.map(function(rect){
			return {	
				left:  (Math.floor(rect.left * stretchFactor) / activeViewScale) + Math.floor(bodyLeft / activeViewScale), 
				right: (Math.floor(rect.right * stretchFactor) / activeViewScale) + Math.floor(bodyLeft / activeViewScale)
			};
		});	
		return rects;
	}			
	
	self.getActiveViewScale = function() {
		return self.isLiveView() ? self.refs.dw.activeViewScale : 1;
	}

	self.getGridContainerStyle = function() {
		var finalStyle = null;
		if (self.isLiveView()) {
			try {
				var win = self.refs.dwDom.browser.getWindow(); 
				//if the document is loading, wait for it
				if( win.document.body.childElementCount == 0 )
					return null;
				var gridContainerDiv = win.document.getElementsByClassName(self.consts.gridContainer);
				if (gridContainerDiv.length > 0) {
					gridContainerDiv = gridContainerDiv[0];
				} else {
					gridContainerDiv = win.document.body;
				}
				var gridContainerStyle = win.getComputedStyle(gridContainerDiv);
			} catch(e) {
				return null;
			}
			if (!gridContainerStyle) {
				return null; 
			}
			finalStyle = {
					width: parseInt(gridContainerStyle.width),
					paddingLeft: parseInt(gridContainerStyle.paddingLeft),
					borderLeftWidth: parseInt(gridContainerStyle.borderLeftWidth), 
					marginLeft: parseInt(gridContainerStyle.marginLeft)};
		} else { // is design view
			var gridContainerDiv = self.refs.dwDom.getElementsByClassName(self.consts.gridContainer); 
			if (gridContainerDiv.length > 0) {
				gridContainerDiv = gridContainerDiv[0];
			} else {
				gridContainerDiv = self.refs.dwDom.body;
			}
			finalStyle = {
				width: parseInt(gridContainerDiv.getComputedStyleProp('width')),
				paddingLeft: parseInt(gridContainerDiv.getComputedStyleProp('padding-left')),
				borderLeftWidth: parseInt(gridContainerDiv.getComputedStyleProp('border-left-width')), 
				marginLeft: parseInt(gridContainerDiv.getComputedStyleProp('margin-left'))};
				
			var maxWidth = parseInt(gridContainerDiv.getComputedStyleProp('max-width'));
			var minWidth = parseInt(gridContainerDiv.getComputedStyleProp('min-width'));
			if( finalStyle.width > maxWidth ) { 
				finalStyle.width = maxWidth;
			}
			if( finalStyle.width < minWidth ) {
				finalStyle.width = minWidth;
			}
		}
		
		if( !finalStyle )
			return null;
		if( isNaN(finalStyle.width) || (finalStyle.width <= 0) )
			return null;
		if( isNaN(finalStyle.paddingLeft))
			finalStyle.paddingLeft = 0;
		if( isNaN(finalStyle.borderLeftWidth))
			finalStyle.borderLeftWidth = 0;
		if( isNaN(finalStyle.marginLeft))
			finalStyle.marginLeft = 0;
		return finalStyle;
	}
	
	self.getGridContainerWidth = function() {
		var gridContainerStyle = self.getGridContainerStyle();
		return (gridContainerStyle ? gridContainerStyle.width : -1);
	}
	
	self.syncOverlayDivs = function() {
		self.syncSelectedDiv();
		var overlayDivs = self.refs.overlayDivs;
		for (var i = 0; i < overlayDivs.length; i++) {
			var overlayDiv = overlayDivs[i];
			if (i == self.data.selectedUnderlyingDivIndex) {
				continue; // It was synced first.
			}
			var underlyingDiv = overlayDiv.underlyingDiv;
			if( !underlyingDiv ) {
				return;
			}
			var top = 'top:' + self.getPosition(underlyingDiv).top + 'px;';
			var left = 'left:' + self.getPosition(underlyingDiv).left + 'px;';
			var width = parseInt(underlyingDiv.offsetWidth);
			var height = parseInt(underlyingDiv.offsetHeight);
			width = width - ( 2 * self.consts.draggingBorderWidth ); // deflate the width
			height = height - ( 2 * self.consts.draggingBorderWidth ); // deflate the height
			var offsetWidth = 'width:' + width + 'px;';
			var offsetHeight = 'height:' + height + 'px;';
			var background = self.getBackgroundStringForDiv(overlayDiv);
			var border = "border:" + self.consts.overlayBorder + ';';
			overlayDiv.style.cssText = top + left + offsetWidth + offsetHeight + background + border ;

			if (self.isDivMisaligned(overlayDiv)) {
				self.setOverlayDivBorder(overlayDiv, self.consts.misalignedDivBorder);
			} else if (self.getUnderlyingDivByIndex(self.data.mouseOverDivIndex) == overlayDiv.underlyingDiv) {
				self.setOverlayDivBorder(overlayDiv, self.consts.hoverBorder);
			}
		}
		
		if (self.data.selectedUnderlyingDivIndex >= 0 && self.data.selectedUnderlyingDivIndex < overlayDivs.length) {
			self.positionControls(); // position controls on the selected div
		}
		
		if (self.flags.debug) {
			self.debug_showWidths();
			self.debug_showColRects();
		}
	}
	
	self.getUnderlyingDiv = function (underlyingDivId) {
		// Returns a div that is in the DW dom or Live View dom depending on which view is showing.
		return self.getActiveDom().getElementById(underlyingDivId);	
	}

	self.syncSelectedDiv = function() {
		if (self.data.selectedUnderlyingDivIndex < 0) {
			return;
		}
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!selectedOverlayDiv) {
			self.selectDiv(null);
			return;	
		}
		var underlyingDiv = self.getSelectedUnderlyingDiv();
		if (!underlyingDiv) {
			self.selectDiv(null);
			return;
		}
		var left = 'left:' + self.getPosition(underlyingDiv).left + 'px;';
		var top = 'top:' + self.getPosition(underlyingDiv).top + 'px;';
		var width = parseInt(underlyingDiv.offsetWidth);
		width = width - ( 2 * self.consts.selectedBorderWidth ); // deflate the width
		var height = parseInt(underlyingDiv.offsetHeight);
		height = height - ( 2 * self.consts.selectedBorderWidth ); // deflate the height
		var offsetWidth = 'width:' + width + 'px;';
		var offsetHeight = 'height:' + height + 'px;';
		var background = self.getBackgroundStringForDiv(selectedOverlayDiv);
		var border = "border:" + self.consts.selectedBorder + ';';
		selectedOverlayDiv.style.cssText = top + left + offsetWidth + offsetHeight + background + border;
		
		var dragDiv = self.refs.draggingDiv;
		// Let the draggingDiv be whatever width the user indicates with the mouse.
		dragDiv.style.height = underlyingDiv.offsetHeight - (self.consts.draggingBorderWidth * 2) + 'px';
		self.ensureSelectedDivStillVisible();
		self.ensureSelectedDivResizable();
		
		if (self.isDesignView()) {
			//tell dw to not hilite the children when we select this div
			if( underlyingDiv ) {
				underlyingDiv.setTranslatedAttribute('hiliteChildrenOnSelect', 'false');
			}
		}
	}
	
	self.selectOverlayDiv = function(underlyingElement, checkOffsets) {
		if(typeof checkOffsets == 'undefined')
			checkOffsets = false;

		if (!underlyingElement) {
			return;
		}

		for (var i = 0; i < self.refs.overlayDivs.length; i++) {
			var div = self.refs.overlayDivs[i];
			if(checkOffsets)
			{
				if (self.isEqual(div.underlyingDiv, underlyingElement)) {
					self.selectDiv(div);
					return;
				}
			}
			else
			{
				if (div.underlyingDiv == underlyingElement) {
					self.selectDiv(div);
					return;
				}			
			}
		}
	}

	self.getDeviceFromWindowWidth = function() {
		var viewWidth = self.refs.dwDom.getViewActualSize()[0];
		viewWidth = Math.floor(viewWidth * self.getActiveViewScale());
		return self.refs.styleSheetManager.getDeviceFromWindowWidth(viewWidth);
	}
		
	self.showDiv = function(div) {
		div.style.display = '';
	}
	
	self.hideDiv = function(div) {
		div.style.display = 'none';
	}
		
	self.inflateDiv = function(div, amt) {
		div.style.width = parseInt(div.style.width) + (amt * 2) + 'px';
		div.style.height = parseInt(div.style.height) + (amt * 2) + 'px';
	}

	self.deflateDiv = function(div, amt) {
		div.style.width = parseInt(div.style.width) - (amt * 2) + 'px';
		div.style.height = parseInt(div.style.height) - (amt * 2) + 'px';
	}	
		
	self.snapSelectedDivToNearestValidLeft = function() {			
		var marginLeftPx = self.getPosition(self.refs.draggingDiv).left - self.data.marginsOffsetLeft;	
		var newColShift = self.getColShiftFromMarginLeftPx(marginLeftPx);
		self.data.selectedDivCurrentColShift = newColShift;
	}
		
	self.startsNewRow = function(underlyingDiv) {
		return self.getCssPropValFromElem(underlyingDiv, 'clear') == 'both';
	}

	self.getColShiftFromMarginLeftPx = function(marginLeftPx) {		
		var smallestDiff = 100000;
		var nearestMarginLeftIndex = -1;
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!selectedOverlayDiv) {
			return;	
		}
		var validMarginLefts = self.getValidMarginLefts(self.startsNewRow(selectedOverlayDiv.underlyingDiv));
		validMarginLefts.forEach(function(validMarginLeft, i){				
			var diff = Math.abs(validMarginLeft - marginLeftPx);
			if (diff < smallestDiff) {
				nearestMarginLeftIndex = i;
				smallestDiff = diff;	
			}
		});
		return nearestMarginLeftIndex; 
	}
    
    self.createHUDDiv = function () {
        var newHUDDiv = self.refs.document.createElement('div');
		newHUDDiv.style.position = 'absolute';
		newHUDDiv.style.zIndex = 1000;
		newHUDDiv.style.cursor = 'pointer';
		newHUDDiv.style.width = self.consts.hudButtonWidth + 'px';
		newHUDDiv.style.height = self.consts.hudButtonHeight + 'px';
		newHUDDiv.style.backgroundColor = self.consts.hudBgColor;
		self.hideDiv(newHUDDiv);
		self.refs.document.body.appendChild(newHUDDiv);
        return newHUDDiv;
    }
    
    self.createStartsRowDiv = function() {
		self.refs.startsRowDiv =  self.createHUDDiv();
	}
    
	self.createHideDiv = function () {
        self.refs.hideDiv = self.createHUDDiv();
        self.refs.hideDiv.setAttribute('class', 'hideDiv centerImage');
	}
	
	self.createShowDiv = function () {
        self.refs.showDiv = self.createHUDDiv();
        self.refs.showDiv.setAttribute('class', 'showDiv centerImage');
	}
    
    self.createLockDiv = function () {
        var newHUDDiv = self.refs.document.createElement('div');
		newHUDDiv.style.position = 'absolute';
		newHUDDiv.style.zIndex = 10002; // z-index one higher than the resize Div;
		newHUDDiv.style.cursor = 'pointer';
		newHUDDiv.style.width = self.consts.lockButtonWidth + 'px';
		newHUDDiv.style.height = self.consts.lockButtonHeight + 'px';
		newHUDDiv.style.backgroundColor = self.consts.hudBgColor;
		self.hideDiv(newHUDDiv);
		self.refs.document.body.appendChild(newHUDDiv);
        self.refs.lockDiv = newHUDDiv;
	}
    
    self.createDuplicateDiv = function () {
        self.refs.duplicateDiv = self.createHUDDiv();
        self.refs.duplicateDiv.setAttribute('class', 'duplicateDiv centerImage');
	}
    
    self.createDeleteDiv = function () {
        self.refs.deleteDiv = self.createHUDDiv();
        self.refs.deleteDiv.setAttribute('class', 'deleteDiv centerImage');
	}
	
	self.createMoveUpDownDiv = function () { 
		var moveUpDiv = self.refs.document.createElement('div');
        var moveDownDiv = self.refs.document.createElement('div');
		moveUpDiv.style.position = moveDownDiv.style.position = 'absolute';
        moveUpDiv.style.zIndex = moveDownDiv.style.zIndex = 1000;
        moveUpDiv.style.cursor = moveDownDiv.style.cursor = 'pointer';
        moveUpDiv.style.width = moveDownDiv.style.width = '20px';
        moveUpDiv.style.height = moveDownDiv.style.height = '12px';
        moveUpDiv.style.backgroundColor = moveDownDiv.style.backgroundColor = self.consts.hudBgColor;
		self.refs.moveDownDiv = moveDownDiv;
        self.refs.moveUpDiv = moveUpDiv;
        self.refs.moveUpDiv.setAttribute('class', 'moveUp centerImage');
        self.refs.moveDownDiv.setAttribute('class', 'moveDown centerImage');
		self.hideDiv(moveDownDiv);
        self.hideDiv(moveUpDiv);
		self.refs.document.body.appendChild(moveDownDiv);
        self.refs.document.body.appendChild(moveUpDiv);
	}
    
	self.calcMoveUpButtonPos = function (selectedOverlayDiv) {
		var selectedOverlayDivStyle = selectedOverlayDiv.style;	
		var left = ( parseInt(selectedOverlayDivStyle.left) + parseInt(selectedOverlayDivStyle.width)/2 ) - self.consts.moveUpDownButtonWidth / 2 ;
		var top = parseInt(selectedOverlayDivStyle.top) - parseInt(self.refs.moveUpDiv.style.height);
		var isFirstOverlayDiv = selectedOverlayDiv == self.refs.overlayDivs[0];
		if (isFirstOverlayDiv || top < 0) {
			top += parseInt(self.refs.moveUpDiv.style.height) + self.consts.startsNewRowButtonGapSize + parseInt(selectedOverlayDivStyle.height);
			left -= self.consts.moveUpDownButtonWidth / 2;
		}
		return {top: top, left: left};
	}
	
	self.calcMoveDownButtonPos = function (selectedOverlayDiv) {
		var selectedOverlayDivStyle = selectedOverlayDiv.style;
		var divPos = self.calcMoveUpButtonPos (selectedOverlayDiv);
		var isFirstOverlayDiv = selectedOverlayDiv == self.refs.overlayDivs[0];
		if (isFirstOverlayDiv || divPos.top > parseInt(selectedOverlayDivStyle.top)) { // the move up button is already below the div
			divPos.left += self.consts.moveUpDownButtonWidth;
		}
		else {
			var expectedTop = divPos.top + parseInt(self.refs.moveUpDiv.style.height) + self.consts.startsNewRowButtonGapSize + parseInt(selectedOverlayDivStyle.height);
			var expectedBottom = expectedTop + parseInt(self.refs.moveDownDiv.style.height)

			var isOutOfViewBottom =  expectedBottom >= self.refs.window.screen.availHeight + self.refs.document.body.scrollTop;
			if(isOutOfViewBottom)
			{
				/* If moveUP icon exists then we will need to accomodate both MoveUP and moveDown icons. */
				if(self.refs.moveUpDiv.style.display != 'none')
				{	
					/* If swap-up icon's expected position is going out of view we need to place it beside the swap-down icon.*/
					var moveUpDivLeft = parseInt(self.refs.moveUpDiv.style.left) - self.consts.moveUpDownButtonWidth/2 + 'px';
					self.refs.moveUpDiv.style.left = moveUpDivLeft;
					divPos.left += self.consts.moveUpDownButtonWidth/2;
				}
				/* else moveDown icon will simply take the palce of MoveUP icon. */	
			}
			else
			{
				divPos.top = expectedTop;
			}	
		}
		
		return divPos;
	}
	
	self.positionMoveUpDownDiv = function () {
		var selectedFGElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		if ( (!selectedFGElement) || (self.isSelectedElementLocked()) || selectedFGElement.getAttribute('data-isHidden') == 'true' ) {
            self.hideDiv(self.refs.moveUpDiv);
            self.hideDiv(self.refs.moveDownDiv);
            return;
        }
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
        if (!selectedOverlayDiv) {
            return;
        }
		var tooltip = "";
		
		var prevSibling = self.getPreviousVisibleSibling(selectedFGElement, false);
		if (prevSibling != null)
		{
			tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/moveup/tooltip'); //load tooltip
			var prevSiblingInfo = prevSibling.tagName;
			var previousSiblingSelector = self.getValidFluidGridSelector(prevSibling);
			if (previousSiblingSelector)
				prevSiblingInfo += previousSiblingSelector;
   			tooltip = self.refs.dwscripts.sprintf(tooltip, selectedFGElement.tagName + self.getValidFluidGridSelector(selectedFGElement), prevSiblingInfo); //format tooltip with tagname and ID of selected and previous element
			self.refs.moveUpDiv.setAttribute('title', tooltip);
			var moveUpDivPos = self.calcMoveUpButtonPos(selectedOverlayDiv);
			self.refs.moveUpDiv.style.left = moveUpDivPos.left + 'px';
			self.refs.moveUpDiv.style.top = moveUpDivPos.top + 'px';
			self.showDiv(self.refs.moveUpDiv);
		}
		else
		{
		    self.hideDiv(self.refs.moveUpDiv);
		}
		
		var nextSibling = self.getNextVisibleSibling(selectedFGElement, false);
		if (nextSibling != null)
		{
			tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/movedown/tooltip'); //load tooltip
            var nextSiblingInfo = nextSibling.tagName;
			var nextSiblingSelector = self.getValidFluidGridSelector(nextSibling);
			if (nextSiblingSelector)
				nextSiblingInfo += nextSiblingSelector;
			tooltip = self.refs.dwscripts.sprintf(tooltip, selectedFGElement.tagName + self.getValidFluidGridSelector(selectedFGElement), nextSiblingInfo); //format tooltip with tagname and ID of selected and next element
			self.refs.moveDownDiv.setAttribute('title', tooltip);
			var moveDownDivPos = self.calcMoveDownButtonPos(selectedOverlayDiv);
			self.refs.moveDownDiv.style.left = moveDownDivPos.left + 'px';
			self.refs.moveDownDiv.style.top = moveDownDivPos.top + 'px';
			self.showDiv(self.refs.moveDownDiv);
		}
		else
		{
		    self.hideDiv(self.refs.moveDownDiv);
		}
	}
    
    self.positionRemainingOverlayControls = function () {
        // This method will position the following icons :- Hide, Start Row Toggle, Duplicate and Delete.
        // The above buttons will positioned togerher.
        // Subsequently the Lock Icon will also be placed
        
        if (self.data.selectedUnderlyingDivIndex < 0) {
			self.hideDiv(self.refs.startsRowDiv);
            self.hideDiv(self.refs.duplicateDiv);
            self.hideDiv(self.refs.deleteDiv);
            self.hideDiv(self.refs.hideDiv);
            self.hideDiv(self.refs.lockDiv);
			self.hideDiv(self.refs.showDiv);
			return;
		}
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!selectedOverlayDiv) { 
			return;
		}

		//if it is a hidden div, show only 'Show' button
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
        if( selectedElement && selectedElement.getAttribute('data-isHidden') == 'true' )
		{
			self.hideDiv(self.refs.startsRowDiv);
            self.hideDiv(self.refs.duplicateDiv);
            self.hideDiv(self.refs.deleteDiv);
            self.hideDiv(self.refs.hideDiv);
            self.hideDiv(self.refs.lockDiv);
			
			var pos = self.calcButtonGroupPos(selectedOverlayDiv, 1);
            self.positionShowDiv({top:pos.top, left:pos.left} );
			return;
		}
		
		self.hideDiv(self.refs.showDiv);		
        if ( self.isSelectedElementLocked() ) {
            self.hideDiv(self.refs.startsRowDiv);
            self.hideDiv(self.refs.duplicateDiv);
            self.hideDiv(self.refs.deleteDiv);
            self.hideDiv(self.refs.hideDiv);
        }
        else {
            var positionStartsRowDiv = self.positionMisalignedDivIcon(selectedOverlayDiv); // this will position the starts row icon if div is misaligned
            
            if (typeof positionStartsRowDiv == "undefined")
                positionStartsRowDiv = false; // something is wrong. try other controls
            
            var buttonGroupSize = 3; // Hide, Duplicate and Delete
            if (positionStartsRowDiv)
                buttonGroupSize = 4; // Position the Starts Row Div as well
            
            var pos = self.calcButtonGroupPos(selectedOverlayDiv, buttonGroupSize);
            // position the entire button group
            self.positionHideDiv({top:pos.top, left:pos.left} );
            var offsetmul = 1;
            if (positionStartsRowDiv){
                self.positionStartsRowDiv({top:pos.top, left:pos.left + offsetmul * self.consts.hudButtonWidth});
                offsetmul++ ;
            }
            self.positionDuplicateDiv({top:pos.top, left:pos.left + offsetmul * self.consts.hudButtonWidth});
            offsetmul++ ;
            self.positionDeleteDiv({top:pos.top, left:pos.left + offsetmul * self.consts.hudButtonWidth});
        }
        
        // now position the lock icon
        var lockPos = self.calcLockButtonPos(selectedOverlayDiv, pos, buttonGroupSize);
        self.positionLockDiv(lockPos);
    }

    self.positionDeleteDiv = function (pos) {
        if (self.data.selectedUnderlyingDivIndex < 0) {
            self.hideDiv(self.refs.deleteDiv);
            return;
        }
        var selectedElement =  self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
		var selector = self.getValidFluidGridSelector(selectedElement);
		var tooltip = "";
		if(selector)
		{
			if(selector.charAt(0) == '#')
				tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/deleteHTMLAndCSS/tooltip'); //load tooltip
			else if(selector.charAt(0) == '.')
				tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/deleteHTML/tooltip'); //load tooltip
		}
		tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + selector); //format tooltip with tagname and ID
        self.refs.deleteDiv.setAttribute('title', tooltip);
        self.refs.deleteDiv.style.left = pos.left + 'px';
        self.refs.deleteDiv.style.top = pos.top + 'px';
        self.showDiv(self.refs.deleteDiv);
    }
    
    self.positionDuplicateDiv = function (pos) {
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div       
		if (!selectedElement) {
            self.hideDiv(self.refs.duplicateDiv);
            return;
        }
        
		var tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/duplicate/tooltip');  //load tooltip
		tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + self.getValidFluidGridSelector(selectedElement)); //format tooltip with tagname and ID
        self.refs.duplicateDiv.setAttribute('title', tooltip);
        self.refs.duplicateDiv.style.left = pos.left + 'px';
        self.refs.duplicateDiv.style.top = pos.top + 'px';
        self.showDiv(self.refs.duplicateDiv);
    }
    
    self.positionMisalignedDivIcon = function (selectedOverlayDiv) {
        if (self.data.selectedUnderlyingDivIndex < 0) {
			self.hideDiv(self.refs.startsRowDiv);
			return;
		}
        
        var isFirstOverlayDiv = selectedOverlayDiv == self.refs.overlayDivs[0];
		if (isFirstOverlayDiv && !self.isDivMisaligned(selectedOverlayDiv)) {
			// Don't show 'move up a row' button if this is the very first div and it's not misaligned.
			self.hideDiv(self.refs.startsRowDiv);
			return false; // need not be positioned
		}

        if (!self.startsNewRow(selectedOverlayDiv.underlyingDiv)) {
			if (self.isDivMisaligned(selectedOverlayDiv)) {
				self.refs.startsRowDiv.setAttribute('class', 'moveLeft centerImage');
                self.refs.startsRowDiv.style.backgroundColor = 'transparent';
				var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);  // get the underlying div
				var selector = self.getValidFluidGridSelector(selectedElement);
				var tooltip = '';
				if (selector.charAt(0) == '.') {
					var curDevice = self.getDeviceFromWindowWidth();
					var zeroMarginClass = '.zeroMargin_' + curDevice;
					tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/zeroMargin/tooltip');  //load tooltip
					tooltip = self.refs.dwscripts.sprintf(tooltip, zeroMarginClass);
				} else {
					tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/clickToAlign/tooltip');  //load tooltip
					tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + self.getValidFluidGridSelector(selectedElement)); //format tooltip with tagname and ID
				}
				self.refs.startsRowDiv.setAttribute('title', tooltip);
				var pos = self.calcButtonPos('top', 'left', selectedOverlayDiv);
                self.refs.startsRowDiv.style.left = pos.left + 'px';
                self.refs.startsRowDiv.style.top = pos.top + 'px';
                self.showDiv(self.refs.startsRowDiv);
                return false; // ask not to position again
            }
        }
        return true; // position the start row div
    }
    
	self.positionStartsRowDiv = function (pos) {
		if (self.data.selectedUnderlyingDivIndex < 0) {
			self.hideDiv(self.refs.startsRowDiv);
			return;
		}
        
		if (self.startsNewRow(self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex))) {
			self.refs.startsRowDiv.setAttribute('class', 'moveUpRow centerImage');
			self.refs.startsRowDiv.setAttribute('title', 'overlay/fluidGridLayout/moveUpRow/tooltip');
		} 
        else { // Does not start new row
            self.refs.startsRowDiv.setAttribute('class', 'startNewRow centerImage');
            self.refs.startsRowDiv.setAttribute('title', 'overlay/fluidGridLayout/startNewRow/tooltip');
        }
        
        self.refs.startsRowDiv.style.backgroundColor = self.consts.hudBgColor;
        self.refs.startsRowDiv.style.left = pos.left + 'px';
        self.refs.startsRowDiv.style.top = pos.top + 'px';
        self.showDiv(self.refs.startsRowDiv);
	}
    
    self.positionHideDiv = function (pos) {
        if (self.data.selectedUnderlyingDivIndex < 0) {
            self.hideDiv(self.refs.hideDiv);
            return;
        }
        
		var tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/hide/tooltip'); //load tooltip
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
		tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + self.getValidFluidGridSelector(selectedElement)); //format tooltip with tagname and ID
        self.refs.hideDiv.setAttribute('title', tooltip);
        self.refs.hideDiv.style.left = pos.left + 'px';
        self.refs.hideDiv.style.top = pos.top + 'px';
        self.showDiv(self.refs.hideDiv);
	}
	
	self.positionShowDiv = function (pos) {
        if (self.data.selectedUnderlyingDivIndex < 0) {
            self.hideDiv(self.refs.showDiv);
            return;
        }
        
		var tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/show/tooltip'); //load tooltip
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
		tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + self.getValidFluidGridSelector(selectedElement)); //format tooltip with tagname and ID
        self.refs.showDiv.setAttribute('title', tooltip);
        self.refs.showDiv.style.left = pos.left + 'px';
        self.refs.showDiv.style.top = pos.top + 'px';
        self.showDiv(self.refs.showDiv);
	}
    
    self.doRectsCollide = function (rect1, rect2) {
        // rect should be x,y,width,height. x,y should be top left coordinates
        // validate that no corner of rect1 lies in rect2
        if (self.pointContainedInRect({x: rect1.x, y: rect1.y}, rect2))
            return true;
        if (self.pointContainedInRect({x: rect1.x + rect1.width, y: rect1.y}, rect2))
            return true;
        if (self.pointContainedInRect({x: rect1.x, y: rect1.y + rect1.height}, rect2))
            return true;
        if (self.pointContainedInRect({x: rect1.x + rect1.width, y: rect1.y + rect1.height}, rect2))
            return true;
		if (self.pointContainedInRect({x: rect2.x, y: rect2.y}, rect1))
            return true;
        if (self.pointContainedInRect({x: rect2.x + rect2.width, y: rect2.y}, rect1))
            return true;
        if (self.pointContainedInRect({x: rect2.x, y: rect2.y + rect2.height}, rect1))
            return true;
        if (self.pointContainedInRect({x: rect2.x + rect2.width, y: rect2.y + rect2.height}, rect1))
            return true;
        
        return false
    }
    
    self.pointContainedInRect = function (point, rect) {
        if (point.x >= rect.x && point.x <= rect.x + rect.width) {
            if (point.y >= rect.y && point.y <= rect.y + rect.height) {
                return true;
            }
        }
        
        return false;
    }
    
    self.calcLockedElementLockPos = function (selectedOverlayDiv) {
        var selectedOverlayDivStyle = selectedOverlayDiv.style;	
        var top = parseInt(selectedOverlayDivStyle.top) + parseInt(selectedOverlayDivStyle.height) - self.consts.lockButtonHeight;
        var left = parseInt(selectedOverlayDivStyle.left) + parseInt(selectedOverlayDivStyle.width) - self.consts.lockButtonWidth / 2;
        
        if ( (left + self.consts.hudButtonWidth) > (self.refs.window.screen.availWidth + self.refs.document.body.scrollLeft) ) 
        {
            var newPos = self.calcButtonPos('bottom', 'right', selectedOverlayDiv);
            top = newPos.top;
            left = newPos.left + (self.consts.hudButtonWidth - self.consts.lockButtonWidth);
            if (top + self.consts.hudButtonHeight > (self.refs.window.screen.availHeight + self.refs.document.body.scrollTop)) { // goes beyond the window. switch to the top
                top = parseInt(selectedOverlayDivStyle.top) - self.consts.startsNewRowButtonGapSize;
            }
        }
        return { top: top, left: left};
    }
    
    self.calcLockButtonPos = function (selectedOverlayDiv, buttonGroupPos, buttonGroupSize) {
        
        if (self.isSelectedElementLocked())
        {
            return self.calcLockedElementLockPos(selectedOverlayDiv);
        }
        
        var selectedOverlayDivStyle = selectedOverlayDiv.style;	
        var top = parseInt(selectedOverlayDivStyle.top) + parseInt(selectedOverlayDivStyle.height) - self.consts.lockButtonHeight;
        var left = parseInt(selectedOverlayDivStyle.left) + parseInt(selectedOverlayDivStyle.width) - self.consts.lockButtonWidth / 2;
        var knobDivStyle = self.refs.knobMiddle['resize'].style;
        if (top < parseInt(knobDivStyle.top) + parseInt(knobDivStyle.height)) // collided with the the knob; push outside
		{
			// shift beyond the knob icon
            left = parseInt(knobDivStyle.left) + parseInt(knobDivStyle.width) + self.consts.startsNewRowButtonGapSize;
            top = parseInt(knobDivStyle.top) + parseInt(knobDivStyle.height) / 2 - self.consts.lockButtonHeight / 2;
        }
        if (top < 0)
            top = 0; // fix the top
        
        if ( (left + self.consts.lockButtonWidth) > (self.refs.window.screen.availWidth + self.refs.document.body.scrollLeft) ) { 
            // went beyond the right side of the screen; position below the selected div
            var newPos = self.calcButtonPos('bottom', 'right', selectedOverlayDiv);
            var lockAtBottom = true;
            top = newPos.top;
            left = newPos.left + (self.consts.hudButtonWidth - self.consts.lockButtonWidth);
            if (top + self.consts.lockButtonHeight > (self.refs.window.screen.availHeight + self.refs.document.body.scrollTop)) { // goes beyond the window. switch to the top
                top = parseInt(selectedOverlayDivStyle.top) - self.consts.lockButtonHeight - self.consts.startsNewRowButtonGapSize;
                lockAtBottom = false;
            }
        }
        
        var rectOfLock = {x: left, y: top, width: self.consts.lockButtonWidth, height: self.consts.lockButtonHeight};
        var rectOfButtonGroup = {x: buttonGroupPos.left, y: buttonGroupPos.top, width: buttonGroupSize * self.consts.hudButtonWidth, height: self.consts.hudButtonHeight};
        
        var moveDownDivStyle = self.refs.moveDownDiv.style; // get the move down button styles
        var rectOfMoveDownButton = {x: parseInt(moveDownDivStyle.left), y: parseInt(moveDownDivStyle.left), width: parseInt(moveDownDivStyle.width), height: parseInt(moveDownDivStyle.height)};
        var moveUpDivStyle = self.refs.moveUpDiv.style;     // get the move up button styles
        var rectOfMoveUpButton = {x: parseInt(moveUpDivStyle.left), y: parseInt(moveUpDivStyle.left), width: parseInt(moveUpDivStyle.width), height: parseInt(moveUpDivStyle.height)};

        if (self.doRectsCollide(rectOfLock, rectOfButtonGroup) 
			|| ( (moveDownDivStyle.display != 'none') && (self.doRectsCollide(rectOfLock, rectOfMoveDownButton)) )
			|| ( (moveUpDivStyle.display != 'none') && (self.doRectsCollide(rectOfLock, rectOfMoveUpButton)) )
			)
        {
            // rects collide => the button group is at the bottom of the selected div.
            if (lockAtBottom) {
                // just place the lock below the group.
                top = buttonGroupPos.top + self.consts.hudButtonHeight + self.consts.startsNewRowButtonGapSize;
            } else {
                // just place the lock top of the group
                top = buttonGroupPos.top - self.consts.hudButtonHeight - self.consts.startsNewRowButtonGapSize;
            }
        }
		return {top: top, left: left};
    }
    
	self.calcButtonPos = function(whereVert, whereHorz, selectedOverlayDiv) {
		var selectedOverlayDivStyle = selectedOverlayDiv.style;		
		if (whereHorz == 'left') {
			var left = parseInt(selectedOverlayDivStyle.left) - self.consts.hudButtonWidth + self.consts.startsNewRowButtonGapSize;
		} else if (whereHorz == 'right') {
			var left = parseInt(selectedOverlayDivStyle.left) + parseInt(selectedOverlayDivStyle.width) - self.consts.hudButtonWidth + self.consts.selectedBorderWidth;
		}
		if (whereVert == 'top') {
			var top = parseInt(selectedOverlayDivStyle.top) + (self.consts.selectedBorderWidth / 2) - (self.consts.knobSize / 2);
		} else if (whereVert == 'bottom') {
			var top = parseInt(selectedOverlayDivStyle.top) + parseInt(selectedOverlayDivStyle.height) + self.consts.startsNewRowButtonGapSize + 3 * self.consts.selectedBorderWidth / 2; // not sure why
		}
		return {top: top, left: left};
	}
    
    self.calcButtonGroupPos = function (selectedOverlayDiv, buttonGroupSize) {
        if (typeof buttonGroupSize == "undefined") // cant calculate valid position without proper button info.
            return {top: 0, left: 0};            // return default value
        
        var widthOfGroup = buttonGroupSize * self.consts.hudButtonWidth;    
        // determine the basic position which should work most cases
        var selectedOverlayDivStyle = selectedOverlayDiv.style;	
		var left = parseInt(selectedOverlayDivStyle.left) + parseInt(selectedOverlayDivStyle.width) - widthOfGroup + self.consts.selectedBorderWidth; // align to the right of the selected div
		var top = parseInt(selectedOverlayDivStyle.top) - self.consts.hudButtonHeight - self.consts.startsNewRowButtonGapSize; // should generally come on top of the selected overlay element
        var buttonGroupOnTop = true;
		var isFirstOverlayDiv = selectedOverlayDiv == self.refs.overlayDivs[0];
		if (isFirstOverlayDiv || top < 0) { // hits top or has gone beyond the top
			var expectedBottom = top + self.consts.hudButtonHeight + 2 * self.consts.startsNewRowButtonGapSize + parseInt(selectedOverlayDivStyle.height); // if no space above it will come below the selected overlay element
			if(expectedBottom < self.refs.window.screen.availHeight + self.refs.document.body.scrollTop)
				top = expectedBottom;
			else
				top += self.consts.hudButtonHeight + 2 * self.consts.startsNewRowButtonGapSize;
            buttonGroupOnTop = false;
		}
        
        // determine the move up down button group pos which runs a chance of overlap
        var moveDownDivStyle = self.refs.moveDownDiv.style; // get the move down button styles
        var moveUpDivStyle = self.refs.moveUpDiv.style;     // get the move up button styles
        var moveGroupPos = {left: 0, right: 0};
        var moveButtonPosValid = true;
        if (moveDownDivStyle.display != 'none') { // this will determine the right pos of the move group
            moveGroupPos.right = parseInt(moveDownDivStyle.left) + parseInt(moveDownDivStyle.width);
            if (moveUpDivStyle.display != 'none')
                moveGroupPos.left = parseInt(moveUpDivStyle.left);
            else
                moveGroupPos.left = parseInt(moveDownDivStyle.left);
        }
        else if (moveUpDivStyle.display != 'none') {
            moveGroupPos.right = parseInt(moveUpDivStyle.left) + parseInt(moveUpDivStyle.width);
            moveGroupPos.left = parseInt(moveUpDivStyle.left);
        }
        else // both the move up and move down buttons are hidden
        {
            moveButtonPosValid = false;
        }
        
        if (moveButtonPosValid) { // align according to the left right values here. period.
            if (left < moveGroupPos.right) { // it collided; or went over the left side of window. both taken care of
                left = moveGroupPos.right + self.consts.startsNewRowButtonGapSize; // shift to the right
            }
            if ( (left + widthOfGroup) > self.refs.window.screen.availWidth + self.refs.document.body.scrollLeft ) { // the right position goes over the window
                left = moveGroupPos.left - widthOfGroup - self.consts.startsNewRowButtonGapSize; // shift to the left of the move button group
            }
            // at this point it cannot be left < 0
        }
        else { // neither move button is being shown
            // its aligned to the right of the selected div; hence cant go over the right edge
            // check if it goes over the left edge
            if (left < 0)
                left = parseInt(selectedOverlayDivStyle.left)
        }
        
        return {top: top, left: left};
    }
	
    self.isElementLocked = function(element) { 
		var device = self.getDeviceFromWindowWidth();
		return	self.isElementLockedForDevice(element, device);	
    }

    self.isSelectedElementLocked = function () {
        if (self.data.selectedUnderlyingDivIndex < 0) {
            return false;
        }
		var element = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
        return  self.isElementLocked(element);
    }

	self.isElementLockedForDevice = function(element, device) {
		var selector = self.getValidFluidGridSelector(element);
		return self.refs.styleSheetManager.getPositionAttributeOfElement(device, selector) == 'absolute';
	}
	
    self.positionLockDiv = function (lockDivPos) {
        if ( self.data.selectedUnderlyingDivIndex < 0) {
            self.hideDiv(self.refs.lockDiv);
            return;
        }
        
        if (self.isSelectedElementLocked())
            var cssClass = 'locked'
        else
            var cssClass = 'unlocked'

        var tooltip = self.refs.dw.loadString('overlay/fluidGridLayout/' + cssClass + '/tooltip'); //load tooltip
        self.refs.lockDiv.setAttribute('class', cssClass + ' centerImage');
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
		tooltip = self.refs.dwscripts.sprintf(tooltip, selectedElement.tagName + self.getValidFluidGridSelector(selectedElement)); //format tooltip with tagname and ID
        self.refs.lockDiv.setAttribute('title', tooltip);
        
        self.refs.lockDiv.style.left = lockDivPos.left + 'px';
        self.refs.lockDiv.style.top = lockDivPos.top + 'px';
        self.showDiv(self.refs.lockDiv);
    }
    
	self.hideSelectedDiv = function () {
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_HIDE_ELEMENT);
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		var dwElement = self.getSelectedUnderlyingDwElement();
	    if (!selectedElement) {
            self.hideDiv(self.refs.hideDiv);
            return;
        }
        self.ignoreDOMChanges(true, true); // we are changing design view as well with a dummy edit hence ignore both for the time being.
		
		if(!self.refs.dwDom.getIsInUnhideMode())
			self.selectDiv(null);
		var selector = self.getValidFluidGridSelector(selectedElement);
		var curDevice = self.getDeviceFromWindowWidth();
		var changeDisplayProp = true;
		if (selector.charAt(0) == '.') {
			// since it a class selector apply the hide class
			changeDisplayProp = false;
			if (dwElement) {
				var classesApplied = dwElement.getAttribute('class');
				var hideClass = 'hide_' + curDevice;
				if (classesApplied)
					classesApplied = classesApplied + ' ' + hideClass;
				else
					classesApplied = hideClass;
				dwElement.setAttribute('class', classesApplied);
			}
		} else if (self.isDesignView()) {
			// initiate a persisting edit here which will require repagination so that nested repagination doesnt happen.
			self.forceDummyEdit(dwElement);
			// ending dummy edit here
		}
		
		self.refs.styleSheetManager.hideElement(curDevice, selector, changeDisplayProp);

        self.ignoreDOMChanges(false, false);
	}
	
	self.showSelectedDiv = function () {
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_SHOW_ELEMENT);
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		var dwElement = self.getSelectedUnderlyingDwElement();
	    if (!selectedElement) {
            self.hideDiv(self.refs.showDiv);
            return;
        }
        self.ignoreDOMChanges(true, true);
		
		var selector = self.getValidFluidGridSelector(selectedElement);
		var curDevice = self.getDeviceFromWindowWidth();
		if (selector.charAt(0) == '.') {
			// since it a class selector remove the hide class			
			if (dwElement) {
				var classesApplied = dwElement.getAttribute('class');
				var hideClass = 'hide_' + curDevice;
				if (classesApplied)
				{
					var index = classesApplied.indexOf(hideClass);
					if(index != -1)
					{
						classesApplied = classesApplied.replace(hideClass, '');
						classesApplied = classesApplied.replace('  ', ' ');
						classesApplied = classesApplied.trim();
					}
				}
				if(classesApplied.length > 0)
					dwElement.setAttribute('class', classesApplied);
				else
					dwElement.removeAttribute('class');
			}
		}
		else		
			self.refs.styleSheetManager.showElement(curDevice, selector);

		//we do not need to remove the unhide mode changes here
		//since it will be removed when the CSS change takes place
		//in the document and grids get rebuilt
        self.ignoreDOMChanges(false, false);
	}
	
	self.getSelectedUnderlyingDwElement = function() {

		if (self.data.selectedUnderlyingDivIndex < 0)
			return null;
			
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		if (self.isDesignView())
			return selectedElement;
		else if (self.isLiveView()){
			var liveCodeHiddenId = selectedElement.getAttribute('data-fluidgrid_id');
			var dwElement = null;
			if(liveCodeHiddenId)
				dwElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenId));
			return dwElement;
		}
		else
			return null;
	}
	
	self.swapWithNext = function () {
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_MOVE_DOWN_ELEMENT);
		var fgElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
	    if (!fgElement) {
            self.hideDiv(self.refs.moveUpDiv);
            self.hideDiv(self.refs.moveDownDiv);
            return;
        }
        self.ignoreDOMChanges(true, true);
		var nextSibling = self.getNextVisibleSibling(fgElement, false);
		if( self.isLiveView() )
		{
			var liveCodeHiddenIdCur = fgElement.getAttribute('data-fluidgrid_id');
			if(liveCodeHiddenIdCur)
				fgElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenIdCur));	
		}
		
		if(nextSibling != null)
		{
			var nextElement = nextSibling;
			if( self.isLiveView() )
			{
				var liveCodeHiddenIdSib = nextElement.getAttribute('data-fluidgrid_id');
				if(liveCodeHiddenIdSib)
					nextElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenIdSib));		
			}			
			if (nextElement != null)
			{   
			    var addOne = 0;
                if (self.validFluidElement(nextSibling) || self.isElementLocked(nextSibling))
                    addOne = 1;
				var indexToSelect = self.data.selectedUnderlyingDivIndex + self.getSelectedUnderlyingElems(nextSibling, true, false, true).length + addOne;
				self.selectDiv(null); // remove any Selection before removing outerHTML
				var storedHTML = fgElement.outerHTML;
				var isListElement = false;
				var lowerTagName = fgElement.tagName.toLowerCase();
				if(lowerTagName == 'li' || lowerTagName == 'ul' || lowerTagName == 'ol')
				 isListElement = true;
				self.selectDwDomNode(fgElement);
				self.refs.dwDom.deleteSelection();
				self.selectDwDomNode(nextElement);
				if(isListElement)
					self.insertHtmlForListElements(nextElement, storedHTML);
				else
					self.refs.dwDom.insertHTML(storedHTML, false, false);
				self.data.selectedUnderlyingDivIndex = indexToSelect;
			}
		}
        self.ignoreDOMChanges(false, false);
	}
	
	self.swapWithPrevious = function () {
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_MOVE_UP_ELEMENT);
		var fgElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
	    if (!fgElement) {
            self.hideDiv(self.refs.moveUpDiv);
            self.hideDiv(self.refs.moveDownDiv);
            return;
        }
        self.ignoreDOMChanges(true, true);
		var prevSibling = self.getPreviousVisibleSibling(fgElement, false);
		if( self.isLiveView() )
		{
			var liveCodeHiddenIdCur = fgElement.getAttribute('data-fluidgrid_id');
			if(liveCodeHiddenIdCur)
				fgElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenIdCur));		
		}
		if(prevSibling != null )
		{
			var prevElement = prevSibling;
			if( self.isLiveView() )
			{
				var liveCodeHiddenIdSib = prevElement.getAttribute('data-fluidgrid_id');
				if(liveCodeHiddenIdSib)
					prevElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenIdSib));		
			}
			
			if (prevElement != null)
			{ 
				var subOne = 0;
				if (self.validFluidElement(prevSibling) || self.isElementLocked(prevSibling))
					subOne = 1;
				var indexToSelect = self.data.selectedUnderlyingDivIndex - self.getSelectedUnderlyingElems(prevSibling, true, false, true).length - subOne;
				self.selectDiv(null); // remove any Selection before removing the outerHTML
				var storedHTML = prevElement.outerHTML;
				var lowerTagName = prevElement.tagName.toLowerCase();
				var isListElement = false;
				if(lowerTagName == 'li' || lowerTagName == 'ul' || lowerTagName == 'ol')
					isListElement = true;
				self.selectDwDomNode(prevElement);
				self.refs.dwDom.deleteSelection();
				self.selectDwDomNode(fgElement);				
				if(isListElement)
					self.insertHtmlForListElements(fgElement, storedHTML);
				else
					self.refs.dwDom.insertHTML( storedHTML, false, false);
				self.selectDwDomNode(fgElement);
				self.data.selectedUnderlyingDivIndex = indexToSelect;
			}
		}
        self.ignoreDOMChanges(false, false);
	}

    self.getPreviousSibling = function(fgElement, onlyVisible, includeLockedElement)
	{
        if (typeof onlyVisible == "undefined")
            onlyVisible = false;
        if(typeof includeLockedElement == 'undefined')
			includeLockedElement = true;
		while(fgElement != null)
		{
			fgElement = fgElement.previousSibling;
			if(fgElement != null && (fgElement.nodeType == 1))
			{
                if (!onlyVisible)
                    return fgElement;
				else if(onlyVisible && self.getCssPropValFromElem(fgElement, 'display') != 'none')
				{
					if(includeLockedElement || (!includeLockedElement && !self.isElementLocked(fgElement)))
						return fgElement;
				}	
			}		
		}
		return null;
	}
	
	self.getNextSibling = function(fgElement, onlyVisible, includeLockedElement)
	{
        if (typeof onlyVisible == "undefined")
            onlyVisible = false;
        if(typeof includeLockedElement == 'undefined')
			includeLockedElement = true;
		while(fgElement != null)
		{
			fgElement = fgElement.nextSibling;
			if(fgElement != null && (fgElement.nodeType == 1))
			{
                if (!onlyVisible)
                    return fgElement;
				else if(onlyVisible && self.getCssPropValFromElem(fgElement, 'display') != 'none')
				{
					if(includeLockedElement || (!includeLockedElement && !self.isElementLocked(fgElement)))
						return fgElement;
				}	
			}		
		}
		return null;
	}
    
	self.getPreviousVisibleSibling = function(fgElement, includeLockedElement)
	{
		return self.getPreviousSibling(fgElement, true, includeLockedElement);
	}
	
	self.getNextVisibleSibling = function(fgElement, includeLockedElement)
	{
		return self.getNextSibling(fgElement, true, includeLockedElement);
	}

 
    self.getLockedColShiftValue = function (selector, device) {
        var colShift = 0;
        self.data.lockedArray.forEach(function(lockedInfo){
                if (lockedInfo.selector == selector && lockedInfo.device == device)
                {
                    colShift = lockedInfo.marginLeft;
                }
            });
        return colShift;
    }

    self.toggleFixedFluid = function () {
        self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_TOGGLE_LOCK);
        var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
        if (!selectedElement) {
            self.hideDiv(self.refs.lockDiv);
            return;
        }

        self.ignoreDOMChanges(true, false); // css is going to change. Listen to CSS Changes
				
        if (self.isDesignView())
        {
			self.refs.dwDom.forceRepaginateForCSSChange(true);
        }

		var parentElement = selectedElement.parentNode;
		var parentPosition = self.getPosition(parentElement);
		var parentSelector = self.getValidFluidGridSelector(parentElement);
        var divPosition = self.getPosition(selectedElement);
        var width = self.getCssPropValFromElem(selectedElement, "width", "int");
        var marginLeftPx = self.getCssPropValFromElem(selectedElement, "margin-left", "int");
        var top = divPosition.top - parentPosition.top;
        var left = divPosition.left - parentPosition.left;
		var selector = self.getValidFluidGridSelector(selectedElement);
        var device = self.getDeviceFromWindowWidth();
	
        if (self.isSelectedElementLocked()) {
            //convert it to fluid
			var siblingArray = self.childElementNodesToArray(parentElement.childNodes);
			var noAbsoluteSibling = true;
			for (var i = 0 ; i < siblingArray.length; i++) {
				if (self.isEqual(selectedElement, siblingArray[i]))
					continue;

				var absValue = self.getCssPropValFromElem(siblingArray[i], 'position', 'string');
				if (typeof absValue != 'undefined' && absValue) {
					if (absValue.toLowerCase() == 'absolute') {
						var siblingSelector = self.getValidFluidGridClassSelector(siblingArray[i]);
						if(selector.charAt(0) == '.' && selector == siblingSelector)
							continue;

						noAbsoluteSibling = false;
						break;
					}
				}
			}
			
			var childArray = self.childElementNodesToArray(selectedElement.childNodes);
			var noAbsoluteChild = true;
			for (var i = 0 ; i < childArray.length; i++) {
				var absValue = self.getCssPropValFromElem(childArray[i], 'position', 'string');
				if (typeof absValue != 'undefined' && absValue) {
					if (absValue.toLowerCase() == 'absolute') {
						noAbsoluteChild = false;
						break;
					}
				}
			}
			
      		var newColSpan = self.getColSpanFromOffsetWidth(width);
            var newColShift = self.getLockedColShiftValue(selector, device); // default back to zero shifting. There is no way to memorize the earlier shifting value after the file has been closed.
			self.data.selectorWidthEdited.length = 0;
			self.data.selectorWidthEdited.push(selector);
			self.refs.styleSheetManager.makeElementFluid(device, selector, noAbsoluteChild);
            self.refs.styleSheetManager.setColSpan(device, selector, newColSpan, self.getParentColsOfSelectedDiv());
            self.refs.styleSheetManager.setColShift(device, selector, newColShift, self.getParentColsOfSelectedDiv());
			if (parentSelector != selector && noAbsoluteSibling) {
				self.refs.styleSheetManager.editCSSProperty(device, parentSelector, 'position', 'static');
			}
            self.updateAllDescendents(selectedElement, newColSpan, false, true ); // update all the fluid elements contained with the new width
            
            if (self.data.editsArray.length > 0)
            {
                // apply the edits for the descendents
                self.applyEdits(self.data.editsArray);
                self.data.editsArray.length = 0;  // this will clear the array and call destructors of existing elements
            }
            self.data.selectorWidthEdited.length = 0;
			
			/* Now since the element is not fixed anymore, remove its entry from lockedArray. */
			self.data.lockedArray.forEach(function(lockedInfo, index){
				if (lockedInfo.selector == selector && lockedInfo.device == device)
				{
					self.data.lockedArray.splice(index,1); 
				}
			});
			
        } else {
            // convert it to fixed
            var oldColShift = self.getColShiftFromMarginLeftPx(marginLeftPx);
            self.data.lockedArray.push({selector: selector, device: device, marginLeft: oldColShift}); // store the margin-left for this session
            self.refs.styleSheetManager.makeElementFixed(device, selector, parentSelector);
            self.refs.styleSheetManager.editCSSProperty(device, selector, 'width', width + 'px');
            self.refs.styleSheetManager.editCSSProperty(device, selector, 'top', top + 'px');
            self.refs.styleSheetManager.editCSSProperty(device, selector, 'left', left + 'px');
			if (self.isDesignView())
			{
				self.refs.dw.showInfoBarMessage('fluidGridLayout/absoluteElement/infobar', 'warning', 'Fluid Grid Overlay');
			}
        }
         // All the edits have only been saved. Update the Style Sheet Manager and the Style Sheet with the new information.
        self.refs.styleSheetManager.updateCSSWithEdits();
		
		if (self.isDesignView())
        {
			self.refs.dwDom.forceRepaginateForCSSChange(false);
        }
		
        self.ignoreDOMChanges(false, false);
    }

	self.removeEntriesFromLockedArray = function(div){
		var devices = self.refs.styleSheetManager.getAvailableDevices();
		devices.forEach(function(device){
		if(self.isElementLockedForDevice(div,device))
			{
				self.data.lockedArray.forEach(function(lockedInfo,index){
					if (lockedInfo.selector == self.getValidFluidGridSelector(div) && lockedInfo.device == device)
					{
						self.data.lockedArray.splice(index,1); 
					}
				});
			}
		});
	}

	self.attemptDeletion = function () {
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_DELETE_ELEMENT);
		var div = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
        if (!div) {
            self.hideDiv(self.refs.deleteDiv);
            self.hideDiv(self.refs.duplicateDiv);
            return;
        }
        
        self.ignoreDOMChanges(true, true);

		self.data.editsArray.length = 0;
			
        if (div) {
			var userDomElement;			
         
            // figure out which css selectors to delete 
            var idSelector = self.getValidFluidGridIDSelector(div);
            if(idSelector)
                self.data.editsArray.push(idSelector);
            self.removeEntriesFromLockedArray(div);
            self.getNestedSelectorsForCSSDeletion(div);
			
			var indexToSelect = self.data.selectedUnderlyingDivIndex ;
			if (indexToSelect == self.refs.overlayDivs.length - 1) {
				indexToSelect -= 1;
			}
			
            self.selectDiv(null); // remove any Selection
			
			// always delete the HTML first so as to initiate an EditOp in the HTML Doc Root.
			if( self.isLiveView() )
			{
				var liveCodeHiddenId = div.getAttribute('data-fluidgrid_id');
				if(liveCodeHiddenId)
					userDomElement = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenId));
				if(userDomElement) {
					self.selectDwDomNode(userDomElement);
					self.refs.dwDom.deleteSelection();
				}
			}
			else {
				self.selectDwDomNode(div);
				self.refs.dwDom.deleteSelection();
			}
			
			//delete the CSS
            if (self.data.editsArray.length > 0)
            {
				self.refs.styleSheetManager.deleteRules(self.data.editsArray);
				self.data.editsArray.length = 0;  // this will clear the array and call destructors of existing elements
            }

			self.data.selectedUnderlyingDivIndex = indexToSelect;
        }

        self.ignoreDOMChanges(false, false);
    }
	
	self.getNestedSelectorsForCSSDeletion = function(parentElement)
	{
		var childNodes = self.childElementNodesToArray(parentElement.childNodes);		
		if (childNodes && childNodes.length && childNodes.length > 0)
		{
			childNodes.forEach(function(childNode){
				if(self.validFluidElement(childNode) || self.isElementLocked(childNode))
				{
					var validIDSelector = self.getValidFluidGridIDSelector(childNode);
					if(validIDSelector)
						self.data.editsArray.push(validIDSelector);
					self.removeEntriesFromLockedArray(childNode);
				}
		
				self.getNestedSelectorsForCSSDeletion(childNode);
			});
		}	
	}
	
	self.getHtmlToInsert = function(tagName, elementID) {
		var content = self.refs.dw.loadString('Objects/layout/' + tagName.toLowerCase() + '/defaultFluidContent');
        content = self.refs.dwscripts.sprintf(content, elementID);
		return content;
	}
	
	self.getIdToInsert = function(tagName) {
		var baseElementID = 'Layout' + tagName;
		return self.refs.dwscripts.getUniqueId(baseElementID, true);
	}
	
	self.attemptNestedDuplication = function() 
	{
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_DUPLICATE_ELEMENT);
		var fgElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
		if (!fgElement) {
            self.hideDiv(self.refs.duplicateDiv);
            return;
        }
        
        self.ignoreDOMChanges(true, true);

		var sourceTargetElemMap  = new Object(); //Maps source elements to their duplicated counterparts.

		var fgElementParentLeft  = fgElement.parentNode.offsetLeft;
		var fgElementParentWidth = fgElement.parentNode.offsetWidth;
		
		var clearCssValue = self.getCssPropValFromElem(fgElement, "clear", "string");
		
		var heightOfFgElementDuplicated = fgElement.offsetHeight;
		var widthOfFgElementDuplicated  = fgElement.offsetWidth;
		var leftOfFgElementDuplicated   = fgElement.offsetLeft ;
		/* Calculate the colspan left between current element's right boundary and its parent right boundary. */
		var remainingRightColSpan = self.getColSpanFromOffsetWidth(fgElementParentLeft + fgElementParentWidth - leftOfFgElementDuplicated - widthOfFgElementDuplicated );
		var sourceWidthColSpan    = self.getColSpanFromOffsetWidth(fgElement.offsetWidth);
		
		var indexToSelect = self.data.selectedUnderlyingDivIndex + self.getSelectedUnderlyingElems(fgElement, true, false, true).length + 1;
		
		/* Update all the IDs with new valid values in the outerHTML code for duplicated elements and insert in DOM */
		// Always update the HTML first so as to initiate an EditOP in the HTML Doc Root
		/* In case we do domSource edit in <code>setOuterHtmlWithDupIDs</code> element passed gets corrupted.
		 * Store the offsets to retrieve the updated element later. */
		if (self.isDesignView()) {
			var offsets = self.refs.dwDom.nodeToOffsets(fgElement);		
			self.setOuterHtmlWithDupIDs(sourceTargetElemMap, fgElement);
			fgElement = self.refs.dwDom.offsetsToNode(offsets[0], offsets[1]);
		}
		else
			self.setOuterHtmlWithDupIDs(sourceTargetElemMap, fgElement);
		
        var selectedUnderlyingDivs = new Array();
		selectedUnderlyingDivs.push(fgElement);
		var underlyingDivs = self.getSelectedUnderlyingElems(fgElement, true, true);
		underlyingDivs.forEach(function(underlyingDiv){	
			selectedUnderlyingDivs.push(underlyingDiv);
		});
		
		self.data.editsArray.length = 0; 
		var devices = self.refs.styleSheetManager.getAvailableDevices();
		
		//selectedUnderlyingDivs array contains all the child fluid grid elements of the element to be duplicated.
		selectedUnderlyingDivs.forEach(function(fluidElem){
			var sourceElement = fluidElem;
			var dupElement =  self.refs.dwDom.getElementById(sourceTargetElemMap[fluidElem.getAttribute('id')]);
			/* Duplicate the CSS */
			var sourceIDSelector = self.getValidFluidGridIDSelector(sourceElement);
			if(sourceIDSelector && sourceTargetElemMap[fluidElem.getAttribute('id')])
			{
				var duplicateRuleObject = new Object();
				duplicateRuleObject.sourceSelector = sourceIDSelector;
				duplicateRuleObject.dupSelector = self.getIDSelector(dupElement);	
				self.data.editsArray.push(duplicateRuleObject);
			}

			devices.forEach(function(device){
				if(self.isElementLockedForDevice(sourceElement,device))
				{
					/* If the element to be duplicated was locked then make the duplicated element as locked. */ 
					/* Duplicate the CSS rules from source locked element and make it fixed element.*/
					
					/* Since by duplication we have created a locked element we need to push its relative column shift value to lockedArray.
					   This information will be used to place the element relative to its parent once we will unlock it. */	
					var sourceHasIDSelector = false;
					var selector;
					var sourceIdSelector = self.getValidFluidGridIDSelector(sourceElement);
					var sourceClassSelector;
					if(sourceIdSelector)
					{
						sourceHasIDSelector = true;
						selector = sourceIdSelector;
					}	
					else
					{
						sourceHasIDSelector = false;
						sourceClassSelector = self.getValidFluidGridClassSelector(sourceElement);
						selector = sourceClassSelector;
					}	
					var sourceColShift = self.getLockedColShiftValue(selector, device); //ColumnShift value will be same for both source and duplicate elements.

					if(sourceHasIDSelector)
						selector  =	self.getIDSelector(dupElement);
					else
						selector = sourceClassSelector;
						
					self.data.lockedArray.push({selector: selector, device: device, marginLeft: sourceColShift}); // store the margin-left for this session	

				}	
			});
		});

		// Update the CSS
		if (self.data.editsArray.length > 0)
			self.refs.styleSheetManager.duplicateRules(self.data.editsArray);

		self.data.editsArray.length = 0; 
		self.data.selectedUnderlyingDivIndex = indexToSelect;
        self.ignoreDOMChanges(false, false);
	}
	
	self.insertHtmlForListElements = function(fgElement, html)
	{
		self.forceDummyEdit(fgElement);
		var dom = self.refs.dwDom;
		var domSource = dom.source;
		if (self.refs.dw.getFocus(false) != 'textView') {
			domSource.updateSelectionFromDOM();
		}			
        var selOffsets = domSource.getSelection();
		domSource.insert(selOffsets[1], html);
		domSource.setSelection(selOffsets[1], selOffsets[1] + html.length); // Select the inserted code
		domSource.syncCodeToDOM();
        if (self.isLiveView()) {
            dom.browser.refreshPage();
        }
	}
	
	self.setOuterHtmlWithDupIDs = function(sourceTargetElemMap,fgElement)
	{	
		var elementID;
		var outerHTML = '';
		var elem = null;
		
		if( self.isLiveView() )
		{
			var attr = fgElement.getAttribute('data-fluidgrid_id');
			if(attr)
			{
				elem = self.refs.dw.getDwdomElementByFgID(parseInt(attr));
				outerHTML = elem.outerHTML;
			}
		}
		else
			outerHTML = fgElement.outerHTML;

		var locallyUsedIDs = new Array(); //Array that will contain all the elements which are not inserted in the DOM yet.
		var idsHavingNameAttribute = new Array();
		var regex          = new RegExp(/[ ]+id[ ]*=[ ]*"([^"]*?)"/gi); //extract the IDs of the elements.
		var idMatches      = outerHTML.match(regex);

		for (id in idMatches) {
			regex       = new RegExp(/"([^"]*?)"/gi);  // Regular Expression to extract the ID value.
			var nodeKey = idMatches[id];
			var idValue = nodeKey.match(regex); //ID value with quotes.
			nodeKey     = idValue[0].replace(/['"]/g,''); //Remove the quotes.

			var curElement         = self.refs.dwDom.getElementById(nodeKey);
			var curElementType     = curElement.getAttribute('type');
			var curElementNameAttr = curElement.getAttribute('name');

			var tagName            = curElement.tagName.toLowerCase();
			var basename;
			
			if(curElementType != undefined)
			{
				/* If the element has a associated type then its ID will be as per the type. */
				baseName     = curElementType;
				var possName = self.refs.dwscripts.getUniqueId(baseName, false);
			}	
			else
			{
				/* If the element has NO associated type then its ID will be as per the TagName. */
				baseName     = tagName;
				var possName = self.refs.dwscripts.getUniqueId(baseName, true);
			}
			
			var tagCounter = possName.replace(baseName,'');
			possName       = baseName + tagCounter;	
			//Make sure that newly returned ID doesn't collide with the elements not yet inserted in the DOM.
			/* We rely on DW to return us the unique value. Adding an extra defensive check for the presence of the ID in the DOM to
				ensure the ID generated is unique. */
			while((self.refs.dwscripts.findInArray(locallyUsedIDs,possName) != -1) || self.refs.dwDom.getElementById(possName))
			{
				tagCounter++;
				possName = baseName+tagCounter;	
			}
			elementID =  possName;
			sourceTargetElemMap[nodeKey] = elementID;
			locallyUsedIDs.push(elementID);
			if(curElementNameAttr != undefined)
				idsHavingNameAttribute.push(elementID);
			
			outerHTML = outerHTML.replace(idMatches[id]," id=\""+elementID+"\""); // Update the IDs of the target elements.
		}
		locallyUsedIDs = [];
		/* Place the duplicate element set after the source element set.*/

		if( self.isLiveView() )
		{
			var attr = fgElement.getAttribute('data-fluidgrid_id');
			if(attr)
			{
				fgElement = self.refs.dw.getDwdomElementByFgID(parseInt(attr));
			}	
		}

		self.selectDiv(null); // hide current selection before changing the outerHTML

		self.selectDwDomNode(fgElement);
		// Place selection appropriately and insert the HTML
		var lowerTagName = fgElement.tagName.toLowerCase();
		if(lowerTagName == 'li' || lowerTagName == 'ul' || lowerTagName == 'ol')
			self.insertHtmlForListElements(fgElement, outerHTML);
		else
			self.refs.dwDom.insertHTML(outerHTML, false, false);
		/* If fgElement is needed any further we need to get the latest one as it may have got corrupted in case we do explicit dom source edit. */
		for (index in idsHavingNameAttribute) {
			var ele = self.refs.dwDom.getElementById(idsHavingNameAttribute[index]); // get the dw dom element
			if(ele)
				ele.setAttribute('name',idsHavingNameAttribute[index]);
		}
	
		idsHavingNameAttribute = [];
	}

	
	self.forceDummyEdit = function(dwDomElement) {
		self.selectDwDomNode(dwDomElement);
		self.refs.dwDom.insertHTML("<div id='dw_fluidgrid_dummy_id'></div>", false, false, true);
		self.refs.dwDom.deleteSelection(true);
		self.selectDwDomNode(dwDomElement);
	}
											   
	self.toggleStartsRow = function() {
		self.log(self.headlights.UT_FLUID_GRID, self.headlights.UT_FLUID_GRID_TOGGLE_START_ROW);
		var element = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex); // get the underlying div
		if (!element) {
			self.hideDiv(self.refs.startsRowDiv);
			return;
		}
		self.ignoreDOMChanges(true, true); // we are changing design view as well with a dummy edit hence ignore both for the time being.
		var dwElement = self.getSelectedUnderlyingDwElement();
		var selector = self.getValidFluidGridSelector(element);
		var curDevice = self.getDeviceFromWindowWidth();
		var changeMarginProp = true;
		var clearVal = self.getCssPropValFromElem(element, "clear", "string");
		var startNewRow = clearVal != 'both';
		var overlayDiv = self.getSelectedOverlayDiv();
		var htmlEdited = false;
		if (selector.charAt(0) == '.' && self.isDivMisaligned(overlayDiv) && startNewRow) {
			// only do this when the "align to grid is being shown"
			// since it a class selector apply the hide class
			changeMarginProp = false;
			if (dwElement) {
				var classesApplied = dwElement.getAttribute('class');
				var zeroMarginClass = 'zeroMargin_' + curDevice;
				if (classesApplied)
				{
					if(classesApplied.indexOf(zeroMarginClass) == -1) {
						// same class is already not applied.
						classesApplied = classesApplied + ' ' + zeroMarginClass;
						dwElement.setAttribute('class', classesApplied);
						htmlEdited = true;
					}
				}
				else {
					classesApplied = zeroMarginClass;
					dwElement.setAttribute('class', classesApplied);
					htmlEdited = true;
				}
			}
		}
		if (!htmlEdited && self.isDesignView()) {
			// initiate a persisting edit here which will require repagination so that nested repagination doesnt happen.
			self.forceDummyEdit(dwElement);
			// ending dummy edit here
		}
		
		self.refs.styleSheetManager.toggleStartsNewRow(curDevice, selector, startNewRow, self.getParentColsOfSelectedDiv(), changeMarginProp);
		self.ensureSelectedDivStillVisible();
		self.ignoreDOMChanges(false, false);
	}
		
	self.createMarginDiv = function() {
		var marginDiv = self.refs.document.createElement('div');			
		marginDiv.style.position = 'absolute';			
		marginDiv.style.backgroundColor = self.consts.hudBgColor;
		marginDiv.style.opacity = '.6';
		self.refs.marginDiv = marginDiv;
		self.hideDiv(marginDiv);
		self.refs.document.body.appendChild(marginDiv);				
	}

	self.positionMarginDiv = function() {
		var marginDiv = self.refs.marginDiv;
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		if ( !selectedElement || (self.isSelectedElementLocked()) || selectedElement.getAttribute('data-isHidden') == 'true' ) {
			self.hideDiv(marginDiv);
			return;
		}		
		var marginLeft = self.getCssPropValFromElem(selectedElement, "margin-left", "int");
		if (marginLeft == 0) {
			self.hideDiv(marginDiv);
			return;				
		}
		var selectedUnderlyingDiv = self.getSelectedUnderlyingDiv();
		if (!selectedUnderlyingDiv) {
			return;	
		}
		marginDiv.style.top = self.getPosition(selectedUnderlyingDiv).top + 'px';
		marginDiv.style.height = selectedUnderlyingDiv.offsetHeight + 'px';
		marginDiv.style.width = marginLeft + 'px';
		marginDiv.style.left = self.getPosition(selectedUnderlyingDiv).left - marginLeft + 'px';
		self.showDiv(marginDiv);
	}
	
	self.getCssPropValFromElem = function(elemNode, prop, expectedReturnType) {
		var returnType = 'string';
		if (expectedReturnType == 'int') {
			returnType = 'int';
		}
		var strVal = '';
		if (self.isLiveView()) {
			var computedStyle = self.refs.dwDom.browser.getWindow().getComputedStyle(elemNode);
			if (computedStyle) {
				strVal = computedStyle.getPropertyValue(prop);
			}
		} else {
				strVal = elemNode.getComputedStyleProp(prop);
			}
		if (returnType == 'string') {
			return strVal;			
		}
		var intVal = parseInt(strVal);
		if (isNaN(intVal)) {
			intVal = 0;		
		}
		intVal = Math.floor(intVal / self.getActiveViewScale());
		return intVal;						
	}	

	self.findRuleForDiv_LiveView = function(div) {
		var liveViewWin = self.refs.dwDom.browser.getWindow();
		var rules = liveViewWin.getMatchedCSSRules(div, '');
		if (!rules) {
			return null;	
		}
		// The last matching rule that has a width should be
		// the one we want.
		for (var i = rules.length - 1; i >= 0; i--) {
			var rule = rules[i];
			if (rule.style.width) {
				return rule;
			}
		}
		return null;		
	}
	
	self.findRuleForSelectedUnderlyingDiv_LiveView = function() {		
		var liveViewWin = self.refs.dwDom.browser.getWindow();
		var div = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		return self.findRuleForDiv_LiveView(div);
	}	

	self.createColumnDivs = function() {	
		var maxNumCols = self.refs.styleSheetManager.getMaxNumColsAllDevices();		
		for (var i = 0; i < maxNumCols; i++) {
			var div = self.refs.document.createElement('div');
			div.setAttribute('class', 'gridColumn');
			self.refs.document.body.appendChild(div);
			self.refs.columnDivs.push(div);				
		}			
	}
		
	self.updateColumns = function() {
		if (self.refs.columnDivs.length == 0) {
			self.createColumnDivs();
		}
		var numVisibleCols = self.data.gridColRects.length;
		for (var i = 0; i < self.refs.columnDivs.length; i++) {
			var div = self.refs.columnDivs[i];
			if (i < numVisibleCols && !self.flags.disabledForZoom) {
				var rect = self.data.gridColRects[i];
				var left = 'left:' + rect.left + 'px;';
				var width = 'width:' + (rect.right - rect.left) + 'px;';
				var display = 'display:block;';
				div.style.cssText = left + width + display;
			} else {
				div.style.display = 'none';
			}
		}
	}
	
	//If the div was visible when we started, ensure it's still visible as we edit
	self.ensureSelectedDivStillVisible = function() {
		var divToScroll;
		if (self.isDesignView()) {
			if( self.data.selectedUnderlyingDivIndex< 0 )
				return;
			divToScroll = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		}
		else {
			divToScroll = self.getSelectedUnderlyingDiv();
		}
		
		if (!divToScroll) {
			return;	
		}
		
		var clientRect = divToScroll.getBoundingClientRect();
		var windowHeight = divToScroll.ownerDocument.defaultView.innerHeight;
		
		if(clientRect.top < 0 && clientRect.bottom < 0 )
		{
			//The div is scrolled above, move the bottom into view
			divToScroll.scrollIntoView(false);
		}
		else if(clientRect.top > windowHeight && clientRect.bottom > windowHeight)
		{
			//The div is scrolled below, move top into view
			divToScroll.scrollIntoView(true);
		}
	}
	
	//Makes sure the right edge resizes are always visible
	self.ensureSelectedDivResizable = function() {
		var selectedOverlayDiv = self.getSelectedOverlayDiv();
		if (!selectedOverlayDiv) {
			return;	
		}
		//verify that the right edge is alway accessible and not wider than what a user can scroll to
		var minWidthNeeded = self.refs.document.documentElement.scrollWidth - (self.consts.selectedBorderWidth*2 + self.consts.knobSize/2);
		var unscrollableSize = (parseInt(selectedOverlayDiv.style.left) + parseInt(selectedOverlayDiv.style.width)) - minWidthNeeded;
		if (unscrollableSize > 0) {
			selectedOverlayDiv.style.width = (parseInt(selectedOverlayDiv.style.width) - unscrollableSize) + "px";
		}
	}

    self.selectDwDomNode = function (elementToSelect) {
        if (elementToSelect) {
			var userDom = self.refs.dwDom;
            var delayShowingCaretUntilEditsFinishes = self.isDesignView() && userDom.isDesignViewPaginationPending();
            if( elementToSelect && elementToSelect != userDom.getSelectedNode() )
                userDom.setSelectedNode(elementToSelect, false, false, true, delayShowingCaretUntilEditsFinishes);
        }
    }
                                               
	self.setUserNodeSelected = function(setEvenInDesignView) {
		if( self.isDesignView() && !setEvenInDesignView )
			return;

		var userNode = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);		
		if( !userNode )
			return;
		
		var liveCodeHiddenId = userNode.getAttribute('data-fluidgrid_id');
		if(liveCodeHiddenId)
			userNode = self.refs.dw.getDwdomElementByFgID(parseInt(liveCodeHiddenId));

		if( !userNode )
			return;
			
		self.selectDwDomNode(userNode);
	}
                                               
    self.ignoreDOMChanges = function (ignoreDesignViewDOM, ignoreCSSDom) {
		if(typeof ignoreDesignViewDOM == 'undefined')
			ignoreDesignViewDOM = false;

		if(typeof ignoreCSSDom == 'undefined')
			ignoreCSSDom = false;
		
		self.flags.ignoreDOMChange = ignoreDesignViewDOM;
		self.flags.ignoreCssDOMChange = ignoreCSSDom;

    }
	
	//This is the default text applied to the user style sheet, for now it adds a slight background to the conatiner divs so you can see them
	self.getDefaultUserStyleSheetText = function() {
		if( !self.isInited() ) {
			return "";
		}
		return "." + self.consts.gridContainer + " { background-color: " + self.consts.gridContainerBgColor + "; }\n";
	}
	
	self.updateInternalStyleSheetDom = function(textToWrite, evenIfNotDesignView) {
		//this only applies to design view
		if( !self.isDesignView() && !evenIfNotDesignView ) {
			return;
		}
		//future optimization, internally the DOM that's open only by a script (not an open user doc)
		//will get unloaded between user events like each mouse move.In order to persist the DOM 
		//and our changes we have to constantly open, write, and save it. Some addtional
		//internal work needs to get done to allow this to persist until we're done with it and
		//only get it once per instance
		self.refs.internalStyleSheetDom = self.refs.dw.getDocumentDOM(self.consts.internalCssFileUrl);
		if( self.refs.internalStyleSheetDom ) {
			if( self.refs.internalStyleSheetDom.documentElement.outerHTML != textToWrite)
			{
				self.refs.internalStyleSheetDom.documentElement.outerHTML = textToWrite;
				self.refs.internalStyleSheetDom.saveFile();
				self.refs.dwDom.attachInternalStyleSheet(self.consts.internalCssFileUrl);
			}
		}
		self.syncOverlayDivs();
	}
	
	self.getPosition = function( ele ) {
		var result = { top : 0, left : 0 };
		
		while( ele ) {
			result.top += ele.offsetTop;
			result.left += ele.offsetLeft;
			ele = ele.offsetParent;
		}
		
		return result;
	}
	
	//////////// Prototype code. /////////////////////////////////////////////////////////////////////////////////	
	
	self.proto_positionPi = function() {
		var div = self.refs.document.getElementById('pi');
		var style = div.style;
		style.top = screen.height - div.offsetHeight - 20 + 'px';
		style.left = screen.width / 2 - div.offsetWidth / 2 + 'px';
	}
		
	self.proto_showPi = function(show) {
		var piDiv = self.refs.document.getElementById('pi');
		var selectedElement = self.getUnderlyingDivByIndex(self.data.selectedUnderlyingDivIndex);
		if (show) {
			piDiv.style.display = '';
			var selectedUnderlyingDiv = self.getSelectedUnderlyingDiv();
			if (!selectedUnderlyingDiv) {
				return;	
			}
			self.refs.document.getElementById('startsNewRowCheckbox').checked = self.startsNewRow(selectedElement);
			self.refs.document.getElementById('colShiftInput').value = 
				self.getColShiftFromMarginLeftPx(self.getCssPropValFromElem(selectedElement, 'margin-left', 'int'));
			self.refs.document.getElementById('colSpanInput').value = 
				self.getColSpanFromOffsetWidth(selectedUnderlyingDiv.offsetWidth);
			self.proto_positionPi();
		} else {
			piDiv.style.display='none';
		}
	}
		
	//////////// Useful debugging functions.  //////////////////////////////////////////////////////
	
	self.debug_showWidths = function() {
		self.refs.overlayDivs.forEach(function(overlayDiv){
			overlayDiv.style.backgroundColor = 'white';
			overlayDiv.innerHTML = 'offsetLeft: _offsetLeft; offsetWidth: _offsetWidth;'
				.replace(/_offsetLeft/, self.getPosition(overlayDiv).left)
				.replace(/_offsetWidth/, overlayDiv.offsetWidth);
		});	
	}
		
	self.debug_showColRects = function() {
		var rects = self.data.gridColRects;
		var debugStr = '';
		rects.forEach(function(rect, i){
			debugStr += '   ' + (i + 1) + ': left:' + rect.left + '; right: ' + rect.right + '\r\n'
		});
		if (!self.refs.rects) {
			self.refs.rects = [];
			for (var i = 0; i < 24; i++) {
				var div = self.refs.document.createElement('div');
				div.style.position = 'absolute';
				div.style.top = '10px';
				div.style.height = '10px';
				div.style.backgroundColor = 'yellow';
				self.refs.document.body.appendChild(div);
				self.refs.rects.push(div);
			}
		}
		self.refs.rects.forEach(function(div, i){
			if (i < rects.length) {
				div.style.display = '';
				div.style.left = rects[i].left + 'px';
				div.style.width = rects[i].right - rects[i].left + 'px';
				div.innerHTML = 'left: _left; width: _width; screen.height: _screenHeight' 
					.replace(/_left/, rects[i].left)
					.replace(/_width/, rects[i].right - rects[i].left)
					.replace(/_screenHeight/, self.refs.dwDom.browser.getWindow().screen.height);
			} else {
				div.style.display = 'none';
			}
		});			
	}		
	
	self.debug_dump_info = function() {
		// General useful function for help during debugging.  Set self.flags.showDebugButton to true
		// to show a button in the top left of the overlay that calls this function when clicked.
		alert(self.debug_getDumpStr());
	}
	
	self.dump =  function(info)
	{
		var debugDiv = self.refs.document.getElementById('debugDumpDiv');
		debugDiv.innerHTML += info + '<br/>';
	}
	
	self.debug_getDumpStr = function() {
		var lines = [];
		if (1) {
			lines.push('self.refs.dw.activeViewScale: ' + self.refs.dw.activeViewScale);
			lines.push('self.getActiveViewScale(): ' + self.getActiveViewScale());
			lines.push('self.refs.dw.getViewSize(): ' + self.refs.dw.getViewSize());		
			lines.push('self.refs.dw.getViewDesiredSize()[0]: ' + self.refs.dwDom.getViewDesiredSize()[0]);
			if (self.isDesignView()) {
				lines.push('design body width: ' + self.refs.dwDom.body.getComputedStyleProp('width'));
				lines.push('design body padding-left: ' + self.refs.dwDom.body.getComputedStyleProp('padding-left'));
				lines.push('design body margin-left: ' + self.refs.dwDom.body.getComputedStyleProp('margin-left'));
				lines.push('design body border-left-width: ' + self.refs.dwDom.body.getComputedStyleProp('border-left-width'));
			} else {
				lines.push('live view body width: ' + self.refs.dwDom.browser.getWindow().getComputedStyle(self.refs.dwDom.browser.getWindow().document.body).width);
				lines.push('live view body padding-left: ' + self.refs.dwDom.browser.getWindow().getComputedStyle(self.refs.dwDom.browser.getWindow().document.body).paddingLeft);
				lines.push('live view body margin-left: ' + self.refs.dwDom.browser.getWindow().getComputedStyle(self.refs.dwDom.browser.getWindow().document.body).marginLeft);
				lines.push('live view body border-left-width: ' + self.refs.dwDom.browser.getWindow().getComputedStyle(self.refs.dwDom.browser.getWindow().document.body).borderLeftWidth);
			}		
			lines.push('self.getGridColRects(): ' + self.debug_dumpGridColRects());		
			lines.push('self.getUnderlyingDivs().length ' + self.getUnderlyingDivs().length);
		}
		if (1) {
			//lines.push('getValidLayoutWidthPcts(): ' + self.getValidLayoutWidthPcts()); doesnt make sense with nested layouts
			lines.push('self.getDeviceFromWindowWidth(): ' + self.getDeviceFromWindowWidth());
			lines.push('self.refs.dwDom.getViewActualSize()[0]: ' + self.refs.dwDom.getViewActualSize()[0]);
			lines.push('self.refs.window.screen.availWidth: ' + self.refs.window.screen.availWidth);
		}
		return lines.join('\n');		
	}

	self.debug_dumpGridColRects = function() {		
		var str = '';
		self.data.gridColRects.forEach(function(rect){
			if (str) {
				str += ', ';
			}
			str += '{left: ' + rect.left + ', right: ' + rect.right + '}';
		});
		return str;	
	}
}
