# Other Programming > AJAX >  Saving XML Root Attributes

## amiecutie

I've been working on a CMS built using AJAX and PHP. Pretty much it saves everything to XML files. Everything is working perfectly except getting the root node's attributes to save. These are never brought in to edit, I don't want these visible in the CMS. So what happens when I save, all of the root node attributes get removed. I've gone over this so much that I can't figure out what's causing this. So I thought another set of eyes my help.

The saving function is called saveXml() and is located at the bottom

----------


## amiecutie

```
var menuXmlData;
var pageXmlData;
var pageXmlRules;
var xmlFileToEdit;

jQuery(document).ready(function($)
{
	resetPage();
});

function resetPage()
{
	// get our xml file/data
	$.ajax({
		type:'GET',
		async: false,
		url:'../MenuSystem.xml'+'?bc='+Math.floor(Math.random()+9999999),
		success:function(xmlDoc){ menuXmlData = xmlDoc.documentElement; },
		dataType:'xml'
	});
	
	// if there's a GET var telling us which page to edit, edit that page
	// otherwise output a menu of pages to edit
	var editVar = getQueryVar('page');
	if (editVar == '' || editVar == null)
		fillInMenuOfPages();
	else
		startPageEditor(editVar);
}

function getQueryVar(nm)
{
	var results = RegExp('[?|&]'+nm+'=?(.*?)(&|$)').exec(window.location.search);
	return results==null ? null : decodeURIComponent(results[1]);
}

function fillInMenuOfPages()
{
	$('.top-description').text('Choose a page/section below to edit:');
	$('.save-btn').css('display','none');
	var pages = menuXmlData.getElementsByTagName('page'), i, ii = pages.length, nd, isLink, noClick;

	$('#main-editor').append('<div class="page-edit-link"><a href="edit-page-content.php?page=accordion">HOME BANNER</a></div>');

	for (i = 0; i < ii; i++)
	{
		nd = pages[i];
		isLink = nd.getAttribute('isLink');
		noClick = nd.getAttribute('noClick');
		if (isLink == 'true' || noClick == 'true')
			continue;
		
		$('#main-editor').append(
			$('<div class="page-edit-link"><a href="edit-page-content.php?page='+nd.getElementsByTagName('hash')[0].textContent.substr(1)+'">'+
				nd.getElementsByTagName('label')[0].textContent+
			'</a></div>')
		);
	}
	$('#main-editor').append($('<div style="clear:both"></div>'));
	$('.to-top-btn').css('visibility', 'hidden');
	
	$('.to-top-btn').css('visibility', 'visible');
}

var isNoRules = false;
function startPageEditor(editVar)
{
	editVar = '#'+editVar;
	var pages = menuXmlData.getElementsByTagName('page'), i, ii = pages.length, moduleFile;
	var siteUrl = document.URL.substr(0,document.URL.lastIndexOf('/admin')+1);

	//first page, accordion
	var pageHashHtml = '';
	var pageUrlHtml = '';
	//var xmlFileToEdit = 'xml/accordion.xml';
	// moduleFile = 'modules/accordion.swf';
	var noMenu = 'true';

	for (i = 0; i < ii; i++)
	{
		
		if (pages[i].getElementsByTagName('hash')[0].textContent == editVar)
		{
			//xmlFileToEdit = pages[i].getElementsByTagName('xml')[0].textContent;
			moduleFile = pages[i].getElementsByTagName('swf')[0].textContent;
			pageHashHtml = siteUrl+pages[i].getElementsByTagName('hash')[0].textContent;
			pageUrlHtml = siteUrl+pages[i].getElementsByTagName('path')[0].textContent;
			noMenu = 'false';

			if (pageHashHtml == "#fleet" || pageHashHtml == "#fuel" || pageHashHtml == "#service-area" || pageHashHtml == "#automotive" || pageHashHtml == "#home") {
				xmlFileToEdit = 'xml/'+pageHashHtml.replace("#", "")+'.xml';
			} else {
				xmlFileToEdit = pages[i].getElementsByTagName('xml')[0].textContent;
			}
		} else if (editVar == "#accordion") {
			xmlFileToEdit ='xml/accordion.xml';
			moduleFile = 'modules/accordion.swf';
			pageHashHtml = '';
			pageUrlHtml = '';
		}
	}
	
	if (xmlFileToEdit === undefined)
		{ $('.top-description').text("Invalid page specified to edit."); return; }
	
	$.ajax({
		type:'GET',
		async: false,
		url:'../'+xmlFileToEdit+'?bc='+Math.floor(Math.random()+9999999),
		success:function(xmlDoc){ pageXmlData = xmlDoc.documentElement; },
		dataType:'xml'
	});
	
	var slashIndex = moduleFile.lastIndexOf('/');
	var dotIndex = moduleFile.lastIndexOf('.');
	if (dotIndex < 0) dotIndex = moduleFile.length;
	
	// check to see if this page to edit is a SWF scroll modules that load other SWF modules
	if (pageHashHtml == "#fleet" || pageHashHtml == "#fuel" || pageHashHtml == "#service-area" || pageHashHtml == "#automotive" || pageHashHtml == "#home") {
		var rulesFile = 'scripts/xml-editor-rules-'+pageHashHtml.replace("#", "")+'.xml';
	} else {
		var rulesFile = 'scripts/xml-editor-rules-'+moduleFile.substring(slashIndex+1, dotIndex)+'.xml';
	}

	$.ajax({
		type:'GET',
		async: false,
		url:rulesFile+'?bc='+Math.floor(Math.random()+9999999),
		error:function(){ isNoRules = true; },
		success:function(xmlDoc){ pageXmlRules = xmlDoc.documentElement; },
		dataType:'xml'
	});
	
	if (!isNoRules)
	{
		var htmlData = convertXmlToHtml(pageXmlData, 1);
		$('#main-editor').html('<div id="drag-indic"></div>'+htmlData);
		addJavascriptActions();
		
		$(document).unbind('mouseup').bind('mouseup', dragEnd);
		$(document).unbind('mousemove').bind('mousemove', dragHappening);
		
		if (noMenu == 'true') {
			$('#page-info').html('<p class="page-loc">Editing content of page: <a href="../index.php" target="_blank">Home Banner'+rulesFile+'</a></p>');
		} else {
			$('#page-info').html('<p class="page-loc">Editing content of page: <a href="../'+pageUrlHtml+'" target="_blank">'+pageHashHtml+'</a> '+xmlFileToEdit+'</p>');
		}

	}
	else
	{
		var xmlText = '';
		$.ajax({
			type:'GET',
			async: false,
			url:'../'+xmlFileToEdit+'?bc='+Math.floor(Math.random()+9999999),
			success:function(xmlT){ xmlText = xmlT; },
			dataType:'text'
		});
		
		// adding iframe to acceptable tags for html editor
		WYMeditor.XhtmlLexer.prototype.addTokens = function() { this.addEntryPattern("</?iframe", 'Text', 'Text');this.addExitPattern(">", 'Text');this.addCommentTokens('Text');this.addScriptTokens('Text');this.addCssTokens('Text');this.addTagTokens('Text');};
		$('#main-editor').html('<textarea class="wysiwyg-editor">'+xmlText+'</textarea>');
		$('.wysiwyg-editor').wymeditor({
			toolsItems: [
				{'name': 'Bold', 'title':  'Strong', 'css':  'wym_tools_strong'},
				{'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'},
				{'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'},
				{'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
				{'name': 'InsertOrderedList', 'title': 'Ordered_List', 'css': 'wym_tools_ordered_list'},
				{'name': 'InsertUnorderedList', 'title': 'Unordered_List', 'css': 'wym_tools_unordered_list'},
				{'name': 'Paste', 'title': 'Paste_From_Word', 'css': 'wym_tools_paste'},
				{'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'},
				{'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'},
				{'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'}
			]
		});
		$('.wym_containers h2').text('Formatting');
		$('.wym_html_val').keydown(function(e){ // make tab work in html source view
			if (e.keyCode == 9)
				{var myValue = "\t";var startPos = this.selectionStart;var endPos = this.selectionEnd;var scrollTop = this.scrollTop;this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos,this.value.length);this.focus();this.selectionStart = startPos + myValue.length;this.selectionEnd = startPos + myValue.length;this.scrollTop = scrollTop;e.preventDefault();}
		});
		$('#main-editor').fadeOut(0).fadeIn(500);
		
		$('#page-info').html('<p class="page-loc">Editing HTML content of page: <a href="../'+pageUrlHtml+'" target="_blank">'+pageUrlHtml+'</a></p>');
		$('#page-info').append($('<p class="page-details">This is a custom swf or blank swf page, so please fill in HTML backup content for mobiles and search engines.</p>'));
	}
	
	$('.to-top-btn').css('visibility', 'visible');
}

function convertXmlToHtml(node, lvl)
{
	//if (isNaN(lvl)) lvl = 1; // now always sends the 1 for first call
	var returnVal = '';
	
	// jquery parsing
	var childNode, childNodeName, isText;
	$(node).children().each(function(){
		childNode = $(this);
		isText = childNode.children().length < 1;
		childNodeName = childNode[0].nodeName;
		
		returnVal += '<div class="node lvl-'+lvl+'"><div class="node-bg"></div><p class="node-label">'+
			childNodeName+
			'</p>'+
				(isText ? '' : '<a class="open-close-btn">open/close</a>') +
			'<span class="node-name">'+
			childNodeName+
			'</span><span class="node-attributes">'+
				getAttributesString(childNode[0]) +
			'</span><span class="node-text">'+
			(isText ? encodeURI(childNode.text()) : '')+
			'</span><span class="editor-rules">'+
			getRulesArray(childNode[0], true).join('')+
			'</span><div class="node-children">'+
				(isText ? '' : convertXmlToHtml(childNode,lvl+1)) +
			'</div></div>';
	});
	
	return returnVal;
}
// in the flash fast parser this is duplicated
function getAttributesString(node)
{
	var returnVal = '';
	var attributes = node.attributes;
	
	if (attributes==undefined || attributes.length==0)
		return '';
	
	var i = 0, ii = attributes.length, attr;
	for (i = 0; i < ii; i++)
	{
		attr = attributes[i];
		returnVal += '<span class="attribute">'+
			'<span class="attribute-name">'+attr.name+'</span>'+
			'<span class="attribute-value">'+attr.value+'</span>'+
		'</span>';
	}
	
	return returnVal;
}
function addJavascriptActions()
{
	$mainEditor = $('#main-editor');
	
	// by default, close all children nodes, start with just the root child nodes showing
	$mainEditor.find('.node-children').css('opacity',0).slideUp(1);
	$mainEditor.find('.open-close-btn').css('opacity', 0.6).bind('click',function(){
		if (!isSlidingNow) // only go if not already sliding something!!!!!
		{
			isSlidingNow = true;
			$(this).toggleClass('open');
			var isOpen = $(this).hasClass('open'), time = 500;
			if (isOpen)
				slideOpen($(this).parent().children('.node-children'));
			else
				slideClose($(this).parent().children('.node-children'));
		}
	}).bind('mouseover', function(){ $(this).css('opacity',1); }).bind('mouseout', function(){ $(this).css('opacity',0.6); });
	
	// open things with the rule to start out opened
	$mainEditor.find('action:contains(begin-open)').each(function(){
		var $par = $(this).parent().parent().parent();
		$par.children('.node-children').css('opacity',1).slideDown(1);
		$par.children('.open-close-btn').addClass('open');
	});
	
	// give custom labels to items with those defined in rules
	$mainEditor.find('rule label').each(function(){
		var $par = $(this).parent().parent().parent();
		$par.children('.node-label').text( $(this).text() );
	});
	
	// give editable input box to nodes with that in rules
	$mainEditor.find('action:contains(show-one-line-editor)').each(function(){
		var $par = $(this).parent().parent().parent();
		var spellcheck = $par.children('.editor-rules').children('rule').children('action:contains(use-spellcheck)').get(0)==undefined ? 'false' : 'true';
		var val = decodeURI($par.children('.node-text').text());
		$input = $('<input class="single-line-xml-editor" type="text" value="'+val+'" placeholder="blank" spellcheck="'+spellcheck+'" />');
		$par.children('.node-label').after($input);
		$input.bind('change', function(){
			$(this).parent().children('.node-text').text( encodeURI($(this).val()) );
			changesNotSaved = true;
		});
	});
	
	// make draggable the items that the rules say should be draggable
	$mainEditor.find('action:contains(allow-dragging)').parent().parent().parent().bind('mousedown', dragStart);
	
	// add in help buttons
	$mainEditor.find('help').each(function(){
		var $par = $(this).parent().parent().parent();
		$par.children('.node-children').before($('<div class="help-btn" style="top:-41px;margin-right:10px;"><p>'+$(this).text()+'</p></div>'));
	});
	
	// add attribute button to nodes needing ability to edit attributes
	$mainEditor.find('action:contains(allow-attribute-editing)').each(function(){
		var $par = $(this).parent().parent().parent();
		var $newElem = $('<p class="node-action-link"><a title="Edit this node\'s attributes." href="javascript:;">attributes</a></p>');
		$par.children('.node-children').before($newElem);
		$newElem.children('a').bind('click', function(){
			var $par = $(this).parent().parent();
			openAttributeEditor( $par.children('.node-attributes') );
		});
	});
	
	// add duplication button to nodes needing ability to duplicate
	$mainEditor.find('action:contains(allow-duplication)').each(function(){
		var $par = $(this).parent().parent().parent();
		var $newElem = $('<p class="node-action-link"><a title="Create and add a copy of this node." href="javascript:;">duplicate</a></p>');
		$par.children('.node-children').before($newElem);
		$newElem.children('a').bind('click', function(){
			var $fake = $('<div style="height:0px;"></div>');
			var $orig  = $(this).parent().parent();
			var $clone = $orig.clone(true,true);
			$orig.before($fake);
			$fake.animate({height:53}, {complete:function(){
				$fake.remove();
				$orig.before($clone);
				$clone.css({top:'50px',opacity:.1}).animate({top:0,opacity:1});
			}});
			changesNotSaved = true;
		});
	});
	
	// add deletion button to nodes needing ability to delete
	$mainEditor.find('action:contains(allow-deletion)').each(function(){
		var $par = $(this).parent().parent().parent();
		var $newElem = $('<p class="node-action-link"><a title="Delete this entire node." href="javascript:;">delete</a></p>');
		$par.children('.node-children').before($newElem);
		$newElem.children('a').bind('click', function(){
			var $orig  = $(this).parent().parent();
			var nodeName = $orig.children('.node-name').text();
			if ($orig.parent().children('.node').children('.node-name:contains('+nodeName+')').length <= 1)
				{ alert("You cannot delete the last node of a set, please edit it to your needs rather than delete it!"); return; }
			var $fake = $('<div style="height:'+($orig.height()+9)+'px;"></div>');
			$orig.fadeOut('normal', function(){
				$orig.before($fake);
				$orig.remove();
				$fake.animate({height:0}, {complete:function(){
					$fake.remove();
				}});
			});
			changesNotSaved = true;
		});
	});
	
	// add ability to select a file from the file manager
	$mainEditor.find('action:contains(allow-file-selection)').each(function(){
		var $par = $(this).parent().parent().parent();
		var $newElem = $('<p class="node-action-link"><a title="Upload or select a file from the file manager." href="javascript:;">upload/select new</a></p>');
		$par.children('.node-children').before($newElem);
		$newElem.children('a').bind('click', function(){
			var $par = $(this).parent().parent();
			var $textNode = $par.children('.node-text');
			openFileManager($textNode.text(), function(newFNm){
				$textNode.text(newFNm);
				$par.children('.single-line-xml-editor').val(newFNm);
			}, true);
		});
	});
	
	// add large text editor button to nodes needing ability to edit larger text content
	$mainEditor.find('action:contains(show-text-editor)').each(function(){
		var $par = $(this).parent().parent().parent();
		var $newElem = $('<p class="node-text-edit-link"><a title="Open the text editor to edit this content." href="javascript:;">edit</a></p>');
		$par.children('.node-children').before($newElem);
		$newElem.children('a').bind('click', function(){
			var $par  = $(this).parent().parent();
			var $textNode = $par.children('.node-text');
			openTextEditor($textNode);
		});
	});
}

function getRulesArray(n, asStr)
{
	// if no rules, give empty array
	if (pageXmlRules == undefined) return [];
	
	// figure out the appropriate path string for this node
	var path = n.nodeName, nodeP = n;
	while(true)
	{
		nodeP = nodeP.parentNode;
		
		if (String(nodeP.parentNode) == '[object Document]' // webkit check for if at root
			|| (nodeP.parentNode && nodeP.parentNode.nodeName == '#document') // ff check for if at root
			|| nodeP == undefined // generic check
		)
			{ path = 'root.'+path; break; }
		path = nodeP.nodeName + '.' + path;
	}
	
	// jquery way
	var rulesArr = [];
	$(pageXmlRules).children('rule[pathMatch="'+path+'"]').each(function(){
		rulesArr.push(asStr ? getXmlString($(this).get(0)) : $(this).get(0));
	});
	
	return rulesArr;
}

function convertHtmlToXml($par, lvl)
{
	if (isNaN(lvl)) lvl = 1;
	var returnVal = '';
	
	var $this, nodeName, txtContent, doCdata, tabStr, childContent;
	$par.children('.node').each(function(){
		$this = $(this);
		nodeName = $this.children('.node-name').text();
		tabStr = tabBy(lvl);
		txtContent = $.trim(decodeURI($this.children('.node-text').text()));
		doCdata = $this.children('.editor-rules').children('rule').children('action:contains(save-cdata)').length > 0;
		if (txtContent != '' && doCdata) txtContent = '<![CDATA[' + txtContent + ']]>';
		childContent = convertHtmlToXml($this.children('.node-children'),lvl+1);
		returnVal += '\n'+tabStr+'<'+nodeName+attributesToString($this)+'>'+
			txtContent +
			childContent +
		(txtContent != '' || $.trim(childContent) == '' ? '' : '\n'+tabStr)+'</'+nodeName+'>';
	});
	
	return returnVal;
}
function attributesToString($node)
{
	var retVal = ''
	var $attr = $node.children('.node-attributes');
	$attr.children('.attribute').each(function(){
		retVal += ' ' + $(this).children('.attribute-name').text() + '="' + $(this).children('.attribute-value').text().split('"').join('&quot;').split('&').join('&amp;') + '"';
	});
	return retVal;
}
function tabBy(n)
{
	var retVal = '';
	var i, ii = n;
	for (i=0; i<ii; i++)
		retVal += '\t';
	return retVal;
}

function saveXml()
{
	var xmlStr = '';
	if (!isNoRules)
	{
		var rootNodeName = pageXmlData.nodeName;
		xmlStr = '<?xml version="1.0" encoding="utf-8"?>\n<'+rootNodeName+'>'+convertHtmlToXml($('#main-editor'))+'\n</'+rootNodeName+'>';
		// xmlStr = vkbeautify.xml(xmlStr); // vkbeautify handles the cdata like crap, gonna have to make it pretty in the convertHtmlToXml function
	}
	else
	{
		var index = $('.wysiwyg-editor').data('wym_index');
		xmlStr = $.wymeditors(index).xhtml();
	}
	
	// clear the page while resaving
	$('#main-editor').html('<div class="page-waiting-busy"></div>');
	$('.to-top-btn').css('visibility', 'hidden');
	
	$.post('scripts/save-page-edit-xml.php', {data:xmlStr, fname:xmlFileToEdit}, function(status)
	{
		if (status == 'auth-failure')
			alert('Your log-in credentials have expired, please refresh the page and log-in again.');
		else if (status != 'success') 
			alert('Save failed. Please check that your file permissions are set to allow writing and that your host allows basic PHP read/write functions.\n\nThe following is a php error messages for debug purposes, ignore it:\n'+status);
		else
			setTimeout(function(){$('.save-btn').removeClass('working');$('.save-indic').fadeIn(150).delay(1200).fadeOut(300);}, 1000);
		
		resetPage();
		changesNotSaved = false;
	},
	'text');
}
```

----------


## amiecutie

To view the code better --> http://pastie.org/5055988

----------


## Priya456

```
[XmlRootAttribute("Customer", Namespace="", IsNullable=false)]
public class Customer
{
    private Bitmap picture;

    public Customer()
    {
    }

    [XmlAttributeAttribute(DataType="date")]
    public System.DateTime DateTimeValue;

    public int CustomerID;
    public string CustomerName;
    public int Age;

    [XmlIgnoreAttribute()]
    public bool CustomerPaid;

    [XmlIgnoreAttribute()]
    public Bitmap Picture
    {
        get { return picture; }
        set { picture = value; }
    }

 .
    [XmlElementAttribute("Picture")]
    public byte[] PictureByteArray
    {
        get 
        { 
            if (picture != null)
            {
                TypeConverter BitmapConverter = 
                     TypeDescriptor.GetConverter(picture.GetType());
                return (byte[]) 
                     BitmapConverter.ConvertTo(picture, typeof(byte[]));
            }
            else
                return null;
        }
        
        set 
        { 
            if (value != null)
                picture = new Bitmap(new MemoryStream(value)); 
            else
                picture = null; 
        }
    }

    [XmlArray ("Hobbies"), XmlArrayItem("Hobby", typeof(string))]
    public System.Collections.ArrayList Hobbies = 
           new System.Collections.ArrayList();

    [XmlArray ("EmailAddresses"), 
     XmlArrayItem("EmailAddress", typeof(EmailAddress))]
    public System.Collections.ArrayList EmailAddresses = 
           new System.Collections.ArrayList();
}
```

----------

