/*

 ADOBE CONFIDENTIAL
 ___________________

 Copyright 2011 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.StyleSheetManager = function(inDw, inDom, inDwscripts, inDWfile, inStyleSheet) {		

	var self = this; 
	
	self.publicFunctions = [
		'beQuiet',
		'calcMarginOfError',
		'getGutterCssWidth',
		'getDeviceFromWindowWidth',
		'setColSpan',
		'setColShift',
		'toggleStartsNewRow',
		'insertRule',
		'loadGridProps',
		'isFluidGridDoc',
		'getGridPropsRec',
		'getMaxNumColsAllDevices',
        'duplicateRule',
        'calcCssWidthFromColSpanUsingParentColCount',
        'calcCssMarginLeftUsingParentColCount',
        'updateCSSWithEdits',
        'makeElementFixed',
        'makeElementFluid',
		'getAvailableDevices',
		'getPositionOfElementRelToDevice',
		'getPositionAttributeOfElement',
        'isValidFluidSelector',
        'reset',
		'deleteRules',
		'duplicateRules',
        'editCSSProperty',
		'getStyleSheetRec',
		'hideElement',
		'showElement',
		'getCSSPropertyInSelector'
	];

	self.refs =	{		
		dw:	 			inDw,
		dom:			inDom,
		dwscripts: 		inDwscripts,
		DWfile: 		inDWfile,
		styleSheet: 	inStyleSheet,
		styleSheetRec: 	null,
		sheet: 			null,
		subSheet: 		null,
		rule:		 	null
	};
		
	self.data = {
        type: '',
		device: 		'',
		colWidth:		-1,
		gutterWidth:	-1,
		allColsWidth: { 	
			desktop:	-1,
			tablet: 	-1,
			mobile:		-1
		},	
		maxNumCols: { 
			desktop:	-1, 
			tablet:		-1, 
			mobile:		-1 
		}
	};
			
	self.consts = {
		devices: ['mobile', 'tablet', 'desktop'],
		msgs: {		
			couldNotFindStyleSheet:	'command/insertFluidGridLayoutDiv/CouldNotFindStyleSheet/errMsg',
			couldNotCssText:		'command/insertFluidGridLayoutDiv/CouldNotCssText/errMsg',
			couldNotFindMatchingMediaQueryStyleSheet: 'command/insertFluidGridLayoutDiv/CouldNotFindMatchingSheet/errMsg'
		}		
	};
					
	self.flags = {
		shouldBeQuiet: 										false,
		couldNotFindStyleSheet: 							false,
		couldNotCssText: 									false,
		couldNotFindMatchingMediaQueryStyleSheet_mobile: 	false,
		couldNotFindMatchingMediaQueryStyleSheet_tablet:	false,
		couldNotFindMatchingMediaQueryStyleSheet_desktop:	false
	};
		
	self.isFluidGridDoc = function() {
		return self.getStyleSheetRec() != null;
	}
	
	self.getMaxNumColsAllDevices = function() {
		var maxNumCols  = self.data.maxNumCols['desktop'];
		maxNumCols = Math.max(maxNumCols, self.data.maxNumCols['tablet']);
		maxNumCols = Math.max(maxNumCols, self.data.maxNumCols['mobile']);
		return maxNumCols;
	}
	
	self.getAvailableDevices = function() {
		return self.consts.devices;
	}
	
	self.getGridPropsRec = function(device) {
		return {
			allColsWidth:	self.data.allColsWidth[device],
			numCols:		self.data.maxNumCols[device],
			colWidth:		self.data.colWidth,
			gutterWidth:	self.data.gutterWidth
		};
	}
	
	self.loadGridProps = function() {
		if (!self.isFluidGridDoc()) {
			self.reportError(self.consts.msgs.couldNotFindStyleSheet, true);	
			return false;
		}
		var sheetTxt = self.getStyleSheetRec().cssText;
		self.data.maxNumCols =	{
			desktop:	self.getGridProp(sheetTxt, 'dw-num-cols-desktop:'), 
			tablet:		self.getGridProp(sheetTxt, 'dw-num-cols-tablet:'), 
			mobile: 	self.getGridProp(sheetTxt, 'dw-num-cols-mobile:') 
		};
		self.data.colWidth = 100;
		self.data.gutterWidth = self.getGridProp(sheetTxt, 'dw-gutter-percentage:');
		self.data.allColsWidth = {
			desktop:	self.data.maxNumCols['desktop'] * self.data.colWidth + (self.data.maxNumCols['desktop'] - 1) * self.data.gutterWidth,
			tablet: 	self.data.maxNumCols['tablet'] * self.data.colWidth + (self.data.maxNumCols['tablet'] - 1) * self.data.gutterWidth,
			mobile:		self.data.maxNumCols['mobile'] * self.data.colWidth + (self.data.maxNumCols['mobile'] - 1) * self.data.gutterWidth
		};
		return (
			self.data.maxNumCols['desktop'] != -1
			&&
			self.data.maxNumCols['tablet'] != -1
			&&
			self.data.maxNumCols['mobile'] != -1
			&&
			self.data.gutterWidth != -1
		);
	}
	
	self.getData = function () {
	    return self.data;
	}

	self.getGridProp = function(sheetTxt, propNameAndColon) {
		var pos = sheetTxt.indexOf(propNameAndColon);
		if (pos == -1) {
			return -1;
		}
		pos += propNameAndColon.length;	
		var val = parseInt(sheetTxt.substr(pos, 25));
		if (isNaN(val) || val <= 0) {
			return -1;
		}
		return val;
	}
		
	self.beQuiet = function(bool) {
		if (typeof bool == 'undefined') {
			self.shouldBeQuiet = true;
		} else {
			self.shouldBeQuiet = bool;
		}
		return self;
	}

	self.getDeviceFromWindowWidth = function(screenWidth) {
		if (screenWidth < 481) {
			return 'mobile';
		}
		if (screenWidth < 769) {
			return 'tablet';
		}
		return 'desktop';
	}

	self.loadSubSheet = function(device) {
		// todo: this function assumes all the less framework subsheets are in one css file or one embedded style sheet.
		if (!self.refs.styleSheetRec) {
			self.refs.styleSheetRec = self.getStyleSheetRec();
			if (!self.refs.styleSheetRec) {
				self.reportError(self.consts.msgs.couldNotFindStyleSheet, false);	
				return false;
			}
			else {
				self.flags.couldNotFindStyleSheet = false;
			}
		}	
		self.data.device = device;
		if (!self.refs.sheet) {
			self.refs.sheet = self.refs.styleSheet.loadCss(self.refs.styleSheetRec.cssText);
			if (self.refs.sheet.errorStr) {
				self.reportError(self.consts.msgs.couldNotCssText, false);	
				return false;
			}
			else {
				self.flags.couldNotCssText = false;
			}
		}
		self.refs.subSheet = self.findCorrectSubSheet(device);
		if (!self.refs.subSheet) {
			self.reportErrorForDevice(self.consts.msgs.couldNotFindMatchingMediaQueryStyleSheet, false, device);
			return false;
		}
		else {
			if (device == 'mobile')
				self.flags.couldNotFindMatchingMediaQueryStyleSheet_mobile = false;
			if (device == 'tablet')
				self.flags.couldNotFindMatchingMediaQueryStyleSheet_tablet = false;
			if (device == 'desktop')
				self.flags.couldNotFindMatchingMediaQueryStyleSheet_desktop = false;
		}
		
		self.removeInfoBarMessageIfRequired();
		
		return true;
	}

	self.removeInfoBarMessageIfRequired = function () {
		if ( !self.flags.couldNotFindStyleSheet
				&& !self.flags.couldNotCssText
				&& !self.flags.couldNotFindMatchingMediaQueryStyleSheet_tablet
				&& !self.flags.couldNotFindMatchingMediaQueryStyleSheet_mobile
				&& !self.flags.couldNotFindMatchingMediaQueryStyleSheet_desktop
			)
			{
				self.refs.dw.removeInfoBarMessageGroup("warning", 'Fluid Grid');
			}
	}
	
	self.loadRule = function(device, selector) {
		if (!self.loadSubSheet(device)) {
			return false; // loadSubSheet informs the user of errors.
		}
		self.refs.rule = self.refs.sheet.getRule(self.refs.subSheet, selector);
		return self.refs.rule != null;
	}

	self.setProperty = function(rule, inProperty, inValue) {
		return self.setProperties(rule, [ { property: inProperty, value: inValue } ] );
	}

	self.setProperties = function(rule, declarations) {
		// declarations is an array of objects: [{property, value}]
		for (var i = 0; i < declarations.length; i++) {
			self.refs.sheet.setProperty(rule, declarations[i].property, declarations[i].value);
		}
		return true;
	}
    
    self.editProperties = function(rule, declarations) {
		// declarations is an array of objects: [{property, value}]
        // It will be callers responsibility to update the stylesheet manager with the new css that contains edits by calling updateCSSWithEdits
		for (var i = 0; i < declarations.length; i++) {
			self.refs.sheet.setProperty(rule, declarations[i].property, declarations[i].value, 'saveEditRecOnly');
		}
		return true;
	}

	self.reportErrorForDevice = function (str, showAlert, device) {
		if (typeof showAlert == 'undefined' || showAlert == null)
			showAlert = true
		if (!self.shouldBeQuiet) {
			if (showAlert) {
				self.alert(str);
			}
			else {
				if (str == self.consts.msgs.couldNotFindMatchingMediaQueryStyleSheet) {
					if (device == 'mobile' && !self.flags.couldNotFindMatchingMediaQueryStyleSheet_mobile) {
						self.flags.couldNotFindMatchingMediaQueryStyleSheet_mobile = true;
						self.refs.dw.showInfoBarMessage(str, "warning", "Fluid Grid");
					}
					else if (device == 'tablet' && !self.flags.couldNotFindMatchingMediaQueryStyleSheet_tablet) {
						self.flags.couldNotFindMatchingMediaQueryStyleSheet_tablet = true;
						self.refs.dw.showInfoBarMessage(str, "warning", "Fluid Grid");
					}
					else if (device == 'desktop' && !self.flags.couldNotFindMatchingMediaQueryStyleSheet_desktop) {
						self.flags.couldNotFindMatchingMediaQueryStyleSheet_desktop = true;
						self.refs.dw.showInfoBarMessage(str, "warning", "Fluid Grid");
					}
				}
			}
		}
	}
	
	self.reportError = function(str, showAlert) {
		if (typeof showAlert == 'undefined' || showAlert == null)
			showAlert = true
		if (!self.shouldBeQuiet) {
			if (showAlert) {
				self.alert(str);
			}
			else {
				if ( str == self.consts.msgs.couldNotFindStyleSheet && !self.flags.couldNotFindStyleSheet) {
					self.flags.couldNotFindStyleSheet = true;
					self.refs.dw.showInfoBarMessage(str, "warning", "Fluid Grid");
				} 
				else if (str == self.consts.msgs.couldNotCssText && !self.flags.couldNotCssText) {
					self.flags.couldNotCssText = true;
					self.refs.dw.showInfoBarMessage(str, "warning", "Fluid Grid");
				}
				else if (str == self.consts.msgs.couldNotFindMatchingMediaQueryStyleSheet && !self.flags.couldNotFindMatchingMediaQueryStyleSheet) {
					self.flags.couldNotFindMatchingMediaQueryStyleSheet = true;
					self.refs.dw.showInfoBarMessage(str, "warning", "Fluid Grid");
				}
			}
		}
	}

	self.alert = function (str) {
		var msg = self.refs.dw.loadString(str);
		alert(msg);
	}

	self.getStyleSheetRec = function(forceReload) {
		if (!forceReload && self.refs.styleSheetRec) {
			return self.refs.styleSheetRec;
		}		
		var styleSheetRec = self.getEmbeddedStyleSheetRec();
		if (styleSheetRec) {
		    self.refs.styleSheetRec = { type: 'embedded', styleElem: styleSheetRec.styleElem, cssText: styleSheetRec.cssText };
		    self.data.type = 'embedded';
		} else {
			styleSheetRec = self.getAttachedStyleSheetRec();
			if (styleSheetRec) {
			    self.refs.styleSheetRec = { type: 'attached', fileUrl: styleSheetRec.fileUrl, cssText: styleSheetRec.cssText };
			    self.data.type = 'attached';
			}
		}			
		return self.refs.styleSheetRec; // Could be null.
	}

	self.getEmbeddedStyleSheetRec = function() {
		var styleElems = self.refs.dom.getElementsByTagName('style');
		for (var i = 0; i < styleElems.length; i++) {
			var cssText = styleElems[i].innerHTML;
			if (self.isLessFrameworkStyleSheet(cssText)) {
				return { styleElem: styleElems[i], cssText: cssText };
			}
		}
		return null;
	}

	self.getAttachedStyleSheetRec = function() {
		var depFiles = self.refs.dom.getDependentFiles();
		for (var i = 0; i < depFiles.length; i++) {	
			var fileUrl = depFiles[i];
			try { 
				if (!fileUrl.isOfFileType('css')) {
					continue;
				}	
			} catch (e) {
				// fileUrl might not have any properties for some reason.
				continue;	
			}
			var cssText = '';		
			// todo
			//if (!makeStyleFileWritable(fileUrl))	
			//{
			//	window.close();
			//	return;	
			//}
			try {
				var dom = self.refs.dw.getDocumentDOM(fileUrl);
			} catch(e) {
				dom = null;
			}
			if (dom) {		
				cssText = dom.documentElement.outerHTML;
			}
			else {
				continue;
			}
			if (self.isLessFrameworkStyleSheet(cssText)) {
				return { fileUrl: fileUrl, cssText: cssText };
			}
		}		
		return null;
	}

	self.isLessFrameworkStyleSheet = function(cssText) {
		return typeof cssText == 'string' && cssText.indexOf('dw-num-cols-desktop') != -1; 
	}

	self.updateCss = function(stringRefObj) {
		if (!self.refs.styleSheetRec || !self.refs.sheet || !self.refs.subSheet) {
			return;
		}
		var cssText = stringRefObj ? stringRefObj.theString : self.refs.sheet.toString();
		if (self.refs.styleSheetRec.type == 'embedded') {
			self.refs.styleSheetRec.styleElem.innerHTML = cssText;
		} else if (self.refs.styleSheetRec.type == 'attached') {
			var fileUrl = self.refs.styleSheetRec.fileUrl;
			var relatedDocsEnabled = self.refs.dw.getPreferenceString("General Preferences", "Show Related Docs", "TRUE") == "TRUE";
			var dom = self.refs.dw.getDocumentDOM(fileUrl);
			if (dom) {
				if (!self.refs.dwscripts.fileIsCurrentlyOpen(fileUrl) && !relatedDocsEnabled) {
					var htmlFileDom = self.refs.dw.getDocumentDOM();
					dom = self.refs.dw.openDocument(fileUrl);
					self.refs.dw.setActiveWindow(htmlFileDom, true, true);
				}
				dom.documentElement.outerHTML = cssText;
			}
		}
	}

	self.findCorrectSubSheet = function(device) {
		var map = { 'mobile': 0, 'tablet': 1, 'desktop': 2 };
		return self.refs.sheet.getSubSheets()[map[device]];
	}	
	
	self.reset = function() {
		self.refs.styleSheetRec = null;
		self.refs.sheet = null;
		self.refs.subSheet = null;
	}

    self.duplicateRules = function(editsArray) {
		//Force a reload of styles from dom istead of using our cached self.refs.rule. somtimes our cache will not have styles newly added bug#3805518
		self.getStyleSheetRec(true);
		self.deleteDefaultHideAndMarginRules();
		for (var i = 0; i < self.consts.devices.length; i++) {
            var device = self.consts.devices[i];
			for( var editsArrayIndex = 0; editsArrayIndex < editsArray.length; ++editsArrayIndex) {
					if (!self.loadRule(device, editsArray[editsArrayIndex].sourceSelector)) {
						return;
					}
					var declarations = self.refs.rule.text;
					declarations = declarations.replace(/\s*.*\s*{/,'').replace(/\s*}\s*/,'').replace(/\s*/,'');				
					var css = 'ID { DECLARATIONS }'
					.replace(/ID/, 				editsArray[editsArrayIndex].dupSelector)
					.replace(/DECLARATIONS/, 	declarations);
					self.refs.sheet.appendRule(self.refs.subSheet, css, 'saveEditRecOnly');
			}	
		}
		// ensure the css has the new rule inserted. If we append the margin, hide rule without prepping the css sometimes the order goes wrong.
		self.refs.styleSheet.loadCss( self.refs.sheet.getTextAfterApplyingEditsAndClearEdits());
		self.appendDefaultHideAndMarginRules();
		
		if (editsArray.length > 0)
			self.updateCSSWithEdits();
	}

	self.deleteRules = function (editsArray) {
	    self.reset(); // Make sure we have the latest css.
        for (var i = 0; i < self.consts.devices.length; i++) {
			var device = self.consts.devices[i];			
			if (!self.loadSubSheet(device)) {
				return;
			}
            for( var editsArrayIndex = 0; editsArrayIndex < editsArray.length; ++editsArrayIndex) {
                self.refs.sheet.deleteRuleWithSelector(self.refs.subSheet, editsArray[editsArrayIndex], 'saveEditRecOnly');
			}	
		}
        self.updateCSSWithEdits();
	}
	
	self.insertRule = function(selector) {

		self.deleteDefaultHideAndMarginRules();
		
		for (var i = 0; i < self.consts.devices.length; i++) {
			var device = self.consts.devices[i];			
			if (!self.loadSubSheet(device)) {
				return;
			}			
			var ruleText = self.getBlankCssToInsert(device, selector);
			self.refs.sheet.appendRule(self.refs.subSheet, ruleText, 'saveEditRecOnly');
		}

		// ensure the css has the new rule inserted. If we append the margin, hide rule without prepping the css sometimes the order goes wrong.
		self.refs.sheet = self.refs.styleSheet.loadCss( self.refs.sheet.getTextAfterApplyingEditsAndClearEdits());

		// append the hide and margin rules now.
		self.appendDefaultHideAndMarginRules();

		var stringRefObj = {theString: self.refs.sheet.getTextAfterApplyingEditsAndClearEdits()};
		
		self.updateCss(stringRefObj);
	}
	
	self.deleteDefaultHideAndMarginRules = function() {
		for (var i = 0; i < self.consts.devices.length; i++) {
			var device = self.consts.devices[i];			
			if (!self.loadSubSheet(device)) {
				return;
			}
			self.refs.sheet.deleteRuleWithSelector(self.refs.subSheet, '.zeroMargin_' + device, 'saveEditRecOnly');
			self.refs.sheet.deleteRuleWithSelector(self.refs.subSheet, '.hide_' + device, 'saveEditRecOnly');
		}
	}

	self.appendDefaultHideAndMarginRules = function() {
		for (var i = 0; i < self.consts.devices.length; i++) {
			var device = self.consts.devices[i];			
			if (!self.loadSubSheet(device)) {
				return;
			}
			
			var zeroMarginRule = ' SELECTOR { DECLARATIONS }'
									.replace(/SELECTOR/, '.zeroMargin_' + device)
									.replace(/DECLARATIONS/, 'margin-left: 0;');
			var hideRule = ' SELECTOR { DECLARATIONS }'
							.replace(/SELECTOR/, '.hide_' + device)
							.replace(/DECLARATIONS/, 'display: none;');
									
			self.refs.sheet.appendRule(self.refs.subSheet, zeroMarginRule, 'saveEditRecOnly');
			self.refs.sheet.appendRule(self.refs.subSheet, hideRule, 'saveEditRecOnly');
		}
	}
	
	self.getBlankCssToInsert = function(device, selector) {
		var declarations = '';

		var css = 'ID { DECLARATIONS }'
			.replace(/ID/, 				selector)
			.replace(/DECLARATIONS/, 	declarations);
		return css;
	}
    
    self.calcCssWidthFromColSpanUsingParentColCount = function(device, colSpan, parentCols) {
         if (parentCols != -1 && parentCols < colSpan) 
             colSpan = parentCols; // max width of the child can be 100% of its parent
        
         var offsetWidth = colSpan * self.data.colWidth + (colSpan - 1) * self.data.gutterWidth;
         if (parentCols != -1)
             var parentWidth = parentCols * self.data.colWidth + (parentCols - 1) * self.data.gutterWidth;
         else
             var parentWidth = self.data.allColsWidth[device];
         return self.percentFormat(offsetWidth / parentWidth) + '%';
	}
    
    self.calcCssMarginLeftUsingParentColCount = function (device, colShift, startsNewRow, parentCols) {
         var gutterSpan = colShift;
         if (!startsNewRow) {
             gutterSpan++;
         }
         var marginLeft = colShift * self.data.colWidth + gutterSpan * self.data.gutterWidth;
         if (parentCols != -1)
             var parentWidth = parentCols * self.data.colWidth + (parentCols - 1) * self.data.gutterWidth;
         else
             var parentWidth = self.data.allColsWidth[device];            
         marginLeft = self.percentFormat(marginLeft / parentWidth);
         return marginLeft + (marginLeft == 0 ? '' : '%');
    }
	self.getGutterCssWidth = function(device, gutterPosition) {
		var padding = self.data.gutterWidth;
		if( gutterPosition == "outer" )
			 padding = padding * 0.5;
		padding = self.percentFormat(padding / self.data.allColsWidth[device]);
		return padding + (padding == 0 ? '' : '%');
	}
	
	self.percentFormat = function(num) {
		return Math.floor(num * 1000000)/10000;  // Floor after four decimal places.
	}
					
	self.calcMarginOfError = function(device) {
		var effectiveColCount = Math.max(0, self.data.maxNumCols[device]);
		return effectiveColCount * 2;
	}			
		
	self.setColSpan = function(curDevice, selector, colSpan, parentCols) {
		self.reset(); // Make sure we have the latest css.
		if (!self.loadRule(curDevice, selector)) {
			return;
		}
        // It will be callers responsibility to update the stylesheet manager with the new css that contains edits by calling updateCSSWithEdits
        self.editUserCssProperty(
            self.refs.rule,
            'width',
			self.calcCssWidthFromColSpanUsingParentColCount(curDevice, colSpan, parentCols)
            );

		for (var i = 0; i < self.consts.devices.length; i++) {
            var device = self.consts.devices[i];
			if (device != curDevice) {
				if (self.loadRule(device, selector)) {
					var curWidthVal = self.refs.sheet.getProperty(self.refs.rule, 'width');
					if ( !curWidthVal && curWidthVal == '') {
						// add this property otherwise it will be overridden by cascade
						self.refs.sheet.setProperty(self.refs.rule, 'width', '100%', 'saveEditRecOnly');
		}	}	}	}
	}
		
	self.setColShift = function(curDevice, selector, colShift, parentCols) {
		self.reset(); // Make sure we have the latest css.
		if (!self.loadRule(curDevice, selector)) {
			return;
		}
		var startsNewRow = self.startsNewRow(self.refs.rule);
		// It will be callers responsibility to update the stylesheet manager with the new css that contains edits by calling updateCSSWithEdits
		self.editUserCssProperty(
			self.refs.rule,
			'margin-left', 
			self.calcCssMarginLeftUsingParentColCount(curDevice, colShift, startsNewRow, parentCols)
		);
		
		for (var i = 0; i < self.consts.devices.length; i++) {
            var device = self.consts.devices[i];
			if (device != curDevice) {
				if (self.loadRule(device, selector)) {
					var curMarginLeftVal = self.refs.sheet.getProperty(self.refs.rule, 'margin-left');
					if ( !curMarginLeftVal || curMarginLeftVal == '') {
						// add this property otherwise it will be overridden by cascade
						self.refs.sheet.setProperty(self.refs.rule, 'margin-left', '0', 'saveEditRecOnly');
		}	}	}	}
	}
    
    self.makeElementFixed = function (device, selector, parentSelector) {
	    self.reset(); // Make sure we have the latest css.
        for (var i = 0; i < self.consts.devices.length; i++) {
            var curDevice = self.consts.devices[i];
            if (device != curDevice)
            {
                if (self.loadRule(curDevice, selector)) {
                    var curPositionValue =  self.refs.sheet.getProperty(self.refs.rule, 'position');
					if ( !curPositionValue || curPositionValue == '')
						self.editCSSProperty(curDevice, selector, 'position', 'static'); // make it static
					var curHeightValue =  self.refs.sheet.getProperty(self.refs.rule, 'height');
					if ( !curHeightValue || curHeightValue == '')
						self.editCSSProperty(curDevice, selector, 'height', 'auto'); // make it height auto
					var curWidthValue =  self.refs.sheet.getProperty(self.refs.rule, 'width');
					if ( !curWidthValue || curWidthValue == '')
						self.editCSSProperty(curDevice, selector, 'width', '100%'); // add default width in case its not there.
                }
				if (selector != parentSelector) {
					if (self.loadRule(curDevice, parentSelector)) {
						var curPositionValue =  self.refs.sheet.getProperty(self.refs.rule, 'position');
						if ( !curPositionValue || curPositionValue == '')
							self.editCSSProperty(curDevice, parentSelector, 'position', 'static'); // make the parent static
					}
				}
            }
        }

        self.editCSSProperty(device, selector, 'margin-left', 'auto'); // delete the margin-left property
        self.editCSSProperty(device, selector, 'float', ''); // delete the float property
        self.editCSSProperty(device, selector, 'position', 'absolute'); // make position absolute
		self.editCSSProperty(device, selector, 'height', 'auto');
		if (selector != parentSelector) {
			self.editCSSProperty(device, parentSelector, 'position', 'relative'); // make the parent relative
		}
    }
    
    self.makeElementFluid = function (device, selector, noAbsoluteChild) {
        self.editCSSProperty(device, selector, 'top', ''); // delete the top property
        self.editCSSProperty(device, selector, 'left', ''); // delete the left property
		if (noAbsoluteChild)
			self.editCSSProperty(device, selector, 'position', 'static'); // make it back to static
		else
			self.editCSSProperty(device, selector, 'position', 'relative'); // make it relative
        self.editCSSProperty(device, selector, 'height', 'auto'); // make it auto
    }
    
    self.isValidFluidSelector = function ( selector ) {
        // Call this function when you are sure we have the latest css.
        var ValidFluidElement = true;
        for (var i = 0; i < self.consts.devices.length; i++) {
            var device = self.consts.devices[i];
            if (!self.loadRule(device, selector)) {
                ValidFluidElement = false;
                break;
            }
        }
        return ValidFluidElement;
    }
    
	self.getPositionOfElementRelToDevice = function(device, selector) {
		var result = { top : '0px', left : '0px' };
		self.reset(); // Make sure we have the latest css.
        if (!self.loadRule(device, selector)) {
           return;
        }
		result.left = self.refs.sheet.getProperty(self.refs.rule, 'left');
		result.top = self.refs.sheet.getProperty(self.refs.rule, 'top');
		return result;
	}
		
	self.getPositionAttributeOfElement = function(device, selector) {
        // it is the responsibility of the caller to ensure that css is most recent
		if (!self.loadRule(device, selector)) {
           return;
        }
		return self.refs.sheet.getProperty(self.refs.rule, 'position');
	}

    self.editCSSProperty = function (device, selector, cssProperty, newValue) {
        // Caller need to make sure we have the latest css.
        // This method can be called multiple times without actually affecting the css
		if (!self.loadRule(device, selector)) {
			return;
		}
        self.editUserCssProperty(
                                 self.refs.rule,
                                 cssProperty, 	
                                 newValue
                                );
        // caller needs to call updateCSSWithEdits() to udpate the css himself.
    }

    self.hideElement = function (curDevice, selector, changeDisplayProp) {
        self.reset(); // Make sure we have the latest css.

		if (changeDisplayProp)
		{
			if (!self.loadRule(curDevice, selector)) {
				return;
			}

			self.editUserCssProperty(
									self.refs.rule,
									'display',
									'none'
									);
		}

		for (var i = 0; i < self.consts.devices.length; i++) {
            var device = self.consts.devices[i];
			if (device != curDevice) {
				if (self.loadRule(device, selector)) {
					var curDisplayVal = self.refs.sheet.getProperty(self.refs.rule, 'display');
					if ( !curDisplayVal || curDisplayVal== '') {
						// add this property otherwise it will be overridden by cascade
						self.refs.sheet.setProperty(self.refs.rule, 'display', 'block', 'saveEditRecOnly');
		}	}	}	}

		self.updateCSSWithEdits();
    }
	
	self.showElement = function (curDevice, selector) {
        self.reset(); // Make sure we have the latest css.

		if (!self.loadRule(curDevice, selector)) {
			return;
		}

		self.editUserCssProperty(
								self.refs.rule,
								'display',
								'block'
								);
		self.updateCSSWithEdits();
    }
	
	self.getCSSPropertyInSelector = function (curDevice, selector, propertyName)
	{
		if (!self.loadRule(curDevice, selector)) {
			return '';
		}
		
		return self.refs.sheet.getProperty(self.refs.rule, propertyName);
	}

	self.toggleStartsNewRow = function(curDevice, selector, startNewRow, parentCols, changeMarginProp) {
		self.reset(); // Make sure we have the latest css.
		if (!self.loadRule(curDevice, selector)) {
			return;
		}
		var colShift = self.getColShift(self.refs.rule, !startNewRow, parentCols);
		if (colShift == -1) {
			return;
		}
		// Toggle values.
		if (changeMarginProp)
		{
			var clearValue = startNewRow ? 'both' : 'none';
			var marginLeftValue = self.calcCssMarginLeftUsingParentColCount(curDevice, colShift, startNewRow, parentCols);
			self.editUserCssProperties(
				self.refs.rule,
				[	{ property: 'margin-left', 	value: marginLeftValue },
					{ property: 'clear', 		value: clearValue }
				]
			);
		}
		
		for (var i = 0; i < self.consts.devices.length; i++) {
            var device = self.consts.devices[i];
			if (device != curDevice) {
				if (self.loadRule(device, selector)) {
					var curClearVal = self.refs.sheet.getProperty(self.refs.rule, 'clear');
					if (!curClearVal || curClearVal == '') {
						// add this property otherwise it will be overridden by cascade
						self.refs.sheet.setProperty(self.refs.rule, 'clear', 'both', 'saveEditRecOnly');
					}
					var curMarginLeftVal = self.refs.sheet.getProperty(self.refs.rule, 'margin-left');
					if ( !curMarginLeftVal || curMarginLeftVal == '') {
						// add this property otherwise it will be overridden by cascade
						self.refs.sheet.setProperty(self.refs.rule, 'margin-left', '0', 'saveEditRecOnly');
		}	}	}	}
		
		self.updateCSSWithEdits();
	}
	
	self.startsNewRow = function(rule) {
		var clearVal = self.refs.sheet.getProperty(rule, 'clear');
		if (!clearVal || clearVal == '')
			return true;
		else 
			return clearVal == 'both';
	}
		
	self.getColShift = function(rule, startsNewRow, parentCols) {
		var marginLeft = self.refs.sheet.getProperty(rule, 'margin-left');
		if (!marginLeft)
			return 0;
		for (var i = 0; i < self.data.maxNumCols[self.data.device]; i++) {
			var testMarginLeft = self.calcCssMarginLeftUsingParentColCount(self.data.device, i, startsNewRow, parentCols);
			if (testMarginLeft == marginLeft) {
				return i;
			}
		}
		return -1;
	}

    self.editUserCssProperties = function(rule, declarations) {
		// declarations is an array of objects: [{property, value}]
        // It will be callers responsibility to update the stylesheet manager with the new css that contains edits by calling updateCSSWithEdit
        self.editProperties(rule, declarations);
	}
    
	self.editUserCssProperty = function(rule, inProperty, inValue) {
        // It will be callers responsibility to update the stylesheet manager with the new css that contains edits by calling updateCSSWithEdits
        self.editUserCssProperties(rule, [ { property: inProperty, value: inValue } ]);      
    }
    
    self.updateCSSWithEdits = function () {
		if (!self.refs.sheet)
		{
			return;
		}
        var stringRefObj = {theString: self.refs.sheet.getTextAfterApplyingEditsAndClearEdits()};
		self.updateCss(stringRefObj);
		self.reset();
    }
}

