Index: main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts.js
===================================================================
--- main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts.js	(revision 25012)
+++ main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts.js	(revision 25012)
@@ -0,0 +1,695 @@
+var _transactions = new Array();
+var _collectionsToBuild = new Array();
+var _allContents = new Array();
+var _idCounter = 0;
+var _indexCounter = 0;
+var _deletedSections = new Array();
+var _undoOperations = new Array();
+var _baseURL;
+var _statusBar;
+
+function init()
+{
+	de.init();
+	//Get all of the links on the page
+	var allLinks = document.getElementsByTagName("a");
+	
+	//Work out which links are the actual document links
+	var docLinks = new Array();
+	for(var i = 0; i < allLinks.length; i++)
+	{
+		if(allLinks[i].getAttribute("class") && (hasClass(allLinks[i], "dbdoc")))
+		{
+			docLinks.push(allLinks[i]);
+		}
+	}
+	
+	if(docLinks.length == 0)
+	{
+		document.getElementById("gs_content").innerHTML = "No documents in the Document Basket";
+		return;
+	}
+	
+	//Create the top menu bar
+	var menuBar = createTopMenuBar();
+	
+	//Add the menu bar to the page
+	var mainContentDiv = document.getElementById("gs_content");
+	mainContentDiv.appendChild(menuBar);
+	
+	var dbDiv = document.createElement("DIV");
+	dbDiv.setAttribute("id", "dbDiv");
+	insertAfter(dbDiv, menuBar);
+	
+	var statusDiv = document.createElement("DIV");
+	statusDiv.setAttribute("class", "statusBar");
+	insertAfter(statusDiv, menuBar);	
+	_statusBar = new StatusBar(statusDiv);
+	
+	//Request the html for each link's page
+	for(var i = 0; i < docLinks.length; i++)
+	{
+		var callback = {
+			success: addDocumentStructureToPage,
+			failure: function(data){/*alert("FAILED");*/}
+		}
+		callback.currentLink = docLinks[i];
+		YAHOO.util.Connect.asyncRequest("GET", docLinks[i].getAttribute("href").concat('&dmd=true&excerptid=gs-document-text&hhf=[{"name":"Cache-Control", "value":"no-cache"}]'), callback);
+	}
+
+	_baseURL = gs.xsltParams.library_name;
+}
+
+function addDocumentStructureToPage(data)
+{
+	//Get the HTML
+	var page = data.responseText;
+	
+	//Add the HTML to the page inside an invisible div
+	var tempDiv = document.createElement("DIV");
+	tempDiv.innerHTML = page;
+	tempDiv.style.display = "none";
+	insertAfter(tempDiv, this.currentLink);
+	
+	//Get the collection that this document belongs to
+	var collection = document.getElementById("gs-document-text").getAttribute("collection");
+
+	//Get the Document Basket div
+	var dbDiv = document.getElementById("dbDiv");
+	
+	//Create the container list and add it to the Document Basket div
+	var containerUL = document.createElement("UL");
+	containerUL.setAttribute("class", "topLevel");
+	dbDiv.appendChild(containerUL);
+
+	//Get all of the headers in the page
+	var headers = getElementsByClassName("sectionTitle", tempDiv);
+
+	//Some necessary loop variables
+	var prevItem = null;
+	var prevDepth = 0;
+	var levelContainers = new Array();
+	levelContainers[0] = containerUL;
+	
+	//Loop through all of the headers
+	for(var i = 0; i < headers.length; i++)
+	{
+		var currentHeader = headers[i];
+		
+		//If the currentHeader is not a <td> element then we are not interested
+		if(currentHeader.nodeName.toLowerCase() != "td")
+		{
+			continue;
+		}
+
+		//Split the section ID on . to get its position in the document
+		var posArray = currentHeader.getAttribute("id").split(".");
+		
+		//Save the document ID
+		var docID = posArray[0].substring(6);
+		
+		//Save the depth of the section (top level is 0)
+		var depth = posArray.length - 1;
+		
+		//Turn the position array into a string
+		var position = ""; 
+		for(var j = 1; j < posArray.length; j++)
+		{
+			if(j != 1) 
+			{
+				position += ".";
+			}
+			position += posArray[j];
+		}
+		
+		//Save the section number
+		var secID = currentHeader.getAttribute("id").substring("6");
+		
+		//Get the text of the section
+		var currentText = document.getElementById("text" + secID);
+		var renderedDiv = createSectionTextDiv(currentText.innerHTML);
+
+		var newItem = document.createElement("LI");
+		new YAHOO.example.DDList(newItem);
+
+		var title = createSectionTitle(currentHeader.innerHTML);
+		newItem.sectionTitle = title;
+		newItem.appendChild(title);
+		newItem.setAttribute("class", depth == 0 ? "dragItem topLevel" : "dragItem");
+		newItem.textDiv = renderedDiv;
+		renderedDiv.parentItem = newItem;
+		
+		var metadataTable = document.getElementById("meta" + secID);
+		renderedDiv.insertBefore(metadataTable, renderedDiv.firstChild);
+		addFunctionalityToTable(metadataTable);
+		
+		if(depth > prevDepth)
+		{
+			var newContainer = document.createElement("UL");
+			new YAHOO.util.DDTarget(newContainer);
+			newContainer.setAttribute("class", "dragList");
+			
+			prevItem.childList = newContainer;
+			prevItem.menu.newSectionLink.style.display = "none";
+			newContainer.parentItem = prevItem;
+			levelContainers[depth - 1].appendChild(newContainer);
+			levelContainers[depth] = newContainer;
+		}
+		prevDepth = depth;
+
+		levelContainers[depth].appendChild(newItem);
+		levelContainers[depth].appendChild(renderedDiv);
+
+		createSectionMenu(newItem);
+		setMouseOverAndOutFunctions(newItem);
+
+		//Set various section properties
+		//newItem.collection = collectionName;
+		newItem.documentID = docID;
+		newItem.position = position;
+		newItem.nodeID = secID;
+		newItem.dbID = _idCounter++;
+		newItem.index = newItem.dbID;
+		newItem.collection = collection;
+		newItem.parentList = levelContainers[depth];
+
+		prevItem = newItem;
+
+		//Insert the section into the list of sections
+		_allContents.push(newItem);
+	}
+	
+	removeFromParent(this.currentLink);
+	updateFromTop();
+}
+
+function createSectionTextDiv(text)
+{
+	var renderedDiv = document.createElement("DIV");
+	renderedDiv.setAttribute("style", "display:none;");
+	
+	var textDiv = document.createElement("DIV");
+	if(text && text.length > 0)
+	{
+		textDiv.innerHTML = text;
+	}
+	else
+	{
+		textDiv.innerHTML = "&nbsp;";
+	}
+	textDiv.setAttribute("class", "renderedText editable");
+	
+	//This registering can cause a sizeable delay so we'll thread it (effectively) so the browser is not paused
+	setTimeout(function(){de.doc.registerEditSection(textDiv)}, 0);
+
+	renderedDiv.appendChild(textDiv);
+	textDiv.parentDiv = renderedDiv;
+	
+	return renderedDiv;
+}
+
+function createNewDocumentArea()
+{
+	//Create the necessary elements
+	var topLevelUL = document.createElement("UL");
+	var topLevelLI = document.createElement("LI");
+	var contentUL = document.createElement("UL");
+	
+	//Append the top-level list item to the top-level list
+	topLevelUL.appendChild(topLevelLI);
+	topLevelUL.setAttribute("class", "topLevel");
+	
+	//Set up the top-level item 
+	topLevelLI.setAttribute("class", "dragItem topLevel");
+	topLevelLI.childList = contentUL;
+	contentUL.parentItem = topLevelLI;
+	
+	//Add a textDiv to the top-level item
+	var textDiv = createSectionTextDiv(null);
+	topLevelLI.textDiv = textDiv;
+	topLevelUL.appendChild(textDiv);
+	
+	//Add a title to the top-level item
+	var title = createSectionTitle("UNTITLED DOCUMENT");
+	topLevelLI.appendChild(title);
+	topLevelLI.sectionTitle = title;
+	
+	createSectionMenu(topLevelLI);
+	setMouseOverAndOutFunctions(topLevelLI);
+	
+	//Set up the placeholder for the first section
+	contentUL.setAttribute("class", "dragList");
+	new YAHOO.util.DDTarget(contentUL);
+	
+	//Create a placeholder and add it to first section
+	var placeHolder = createPlaceholder(null, contentUL, false);
+	contentUL.appendChild(placeHolder);
+	
+	var dbDiv = document.getElementById("dbDiv");
+	
+	//Add elements to the page
+	if(dbDiv.firstChild)
+	{
+		dbDiv.insertBefore(topLevelUL, dbDiv.firstChild);
+	}
+	else
+	{
+		dbDiv.appendChild(topLevelUL);
+	}
+	insertAfter(contentUL, topLevelLI.textDiv);
+	
+	//Correct any issues
+	updateFromTop();
+}
+
+function createPlaceholder(parent, parentList, mouseEvents)
+{
+	//Create the place holder and assign its class
+	var placeHolder = document.createElement("LI");
+	placeHolder.setAttribute("class", "placeHolder");
+	
+	//If a parent was given then we can assign the collection and nodeID 
+	if(parent)
+	{
+		placeHolder.collection = parent.collection;
+		placeHolder.nodeID = parent.nodeID;
+	}
+	
+	//If this is to be a plain placeholder then we don't want it to react to mouse events
+	if(mouseEvents)
+	{
+		placeHolder.isEmptyList = true;
+		
+		//Create the delete section link
+		var deleteSectionLink = document.createElement("A");
+		deleteSectionLink.innerHTML = "delete section";
+		deleteSectionLink.setAttribute("href", "javascript:;");
+		deleteSectionLink.setAttribute("class", "menuLink");
+		deleteSectionLink.style.display = "none";
+		
+		//Set the onclick behaviour of the delete link
+		deleteSectionLink.onclick = function()
+		{
+			//Delete the place holder
+			removeFromParent(placeHolder);
+			
+			//If this is in a list then delete the list (as this will be the only thing in the list)
+			if(parentList)
+			{
+				var undo = new Array();
+			
+				undo.op = "mva";
+				undo.srcElem = parentList;
+				undo.refElem = parent;
+				undo.removeTransaction = false;
+				_undoOperations.push(undo);
+				
+				removeFromParent(parentList);
+			}
+			
+			//Enable the "add sub-section" menu option in the parent
+			if(parent)
+			{
+				parent.menu.newSectionLink.style.display = "inline";
+				parent.childList = null;
+			}
+		}
+		placeHolder.appendChild(deleteSectionLink);
+	
+		//Colour the list item and display the menu on mouse over
+		placeHolder.onmouseover = function(e)
+		{
+			deleteSectionLink.style.display = "inline";
+			this.style.background = "rgb(255, 200, 0)";
+		};
+		//Uncolour the list item and hide the menu on mouse out
+		placeHolder.onmouseout = function(e)
+		{
+			deleteSectionLink.style.display = "none";
+			this.style.background = "none";
+		};
+	}
+	
+	var dragItem = new YAHOO.example.DDList(placeHolder);
+	dragItem.addInvalidHandleClass("placeHolder");
+	return placeHolder;
+}
+
+function duplicateSection(section)
+{
+	var newLI = document.createElement("LI");
+	newLI.setAttribute("class", "dragItem");
+	new YAHOO.example.DDList(newLI);
+	
+	if(section.textDiv)
+	{
+		var textDiv = createSectionTextDiv(section.textDiv.innerHTML);
+		newLI.textDiv = textDiv;
+	}
+	newLI.collection = section.collectionName;
+	
+	if(section.sectionTitle)
+	{
+		var title = createSectionTitle(section.sectionTitle.innerHTML);
+		newLI.sectionTitle = title;
+		newLI.appendChild(title);
+	}
+	
+	createSectionMenu(newLI);
+	setMouseOverAndOutFunctions(newLI);
+	newLI.onmouseout();
+	
+	if(section.childList)
+	{
+		insertAfter(newLI, section.childList);
+	}
+	else if(section.textDiv)
+	{
+		insertAfter(newLI, section.textDiv);
+	}
+	else
+	{
+		insertAfter(newLI, section);
+	}
+
+	if(newLI.textDiv)
+	{
+		insertAfter(newLI.textDiv, newLI);
+	}
+	return newLI;
+}
+
+function duplicateSectionChildrenRecursive(duplicate, original)
+{
+	if(!original.childList)
+	{
+		return;
+	}
+	
+	var newUL = document.createElement("UL");
+	newUL.setAttribute("class", "dragList");
+	new YAHOO.util.DDTarget(newUL);
+	insertAfter(newUL, duplicate.textDiv);
+		
+	var children = new Array();
+	var current = original.childList.firstChild;
+	while(current != null)
+	{
+		children.push(current);
+		current = current.nextSibling;
+	}
+	
+	for(var i = 0; i < children.length; i++)
+	{
+		current = children[i];
+		if(current.nodeName.toLowerCase() == "li" && !hasClass(current, "placeHolder"))
+		{
+			var newSection = duplicateSection(current);
+			newUL.appendChild(newSection);
+			if(current.childList)
+			{
+				duplicateSectionChildrenRecursive(newSection, current);
+			}
+		}
+	}
+	
+	duplicate.childList = newUL;
+	newUL.parentItem = duplicate.childList;
+	
+	if(duplicate.menu)
+	{
+		duplicate.menu.newSectionLink.style.display = "none";
+	}
+}
+
+function deleteSection(section)
+{
+	var undo = new Array();
+	var prev = getPrevSiblingOfType(section, "li");
+	var next = getNextSiblingOfType(section, "li");
+	var parent = section.parentList;
+	if(prev)
+	{
+		undo.op = "mva";
+		undo.refElem = prev;
+	}
+	else if(next)
+	{
+		undo.op = "mvb";
+		undo.refElem = next;
+	}
+	else
+	{
+		undo.op = "mvi";
+		undo.refElem = parent;
+	}
+	undo.srcElem = section;
+	undo.removeTransaction = true;
+	_undoOperations.push(undo);
+
+	saveTransaction('{"operation":"delete", "collection":"' + section.collection + '", "oid":"' + section.nodeID + '"}');
+	addCollectionToBuild(section.collection);
+	
+	_deletedSections.push(section);
+	if(section.textDiv)
+	{
+		removeFromParent(section.textDiv);
+	}
+	if(section.childList)
+	{
+		removeFromParent(section.childList);
+	}
+	removeFromParent(section);
+	updateFromTop();
+}
+
+function createBlankSection(parent)
+{
+	if(parent.childList)
+	{
+		return;
+	}
+	
+	var newUL = document.createElement("UL");
+
+	newUL.setAttribute("class", "dragList emptyList");
+	new YAHOO.util.DDTarget(newUL);
+	
+	insertAfter(newUL, parent.textDiv);
+	parent.childList = newUL;
+	newUL.parentItem = parent;
+	
+	var menu = parent.menu;
+	menu.newSectionLink.style.display = "none";
+	
+	var undo = new Array();
+	undo.op = "del";
+	undo.srcElem = newUL;
+	undo.removeTransaction = false;
+	_undoOperations.push(undo);
+}
+
+function createSectionMenu(section)
+{
+	var menuBar = document.createElement("SPAN");
+	
+	//Separator
+	menuBar.appendChild(document.createTextNode(" "));
+	
+	//"Edit" link
+	var toggleLink = document.createElement("A");
+	toggleLink.innerHTML = "edit";
+	toggleLink.setAttribute("class", "menuLink");
+	toggleLink.setAttribute("href", "javascript:;");
+	toggleLink.onclick = function(){toggleTextDiv(section);};
+	menuBar.appendChild(toggleLink);
+	menuBar.editTextLink = toggleLink;
+	
+	//Separator
+	menuBar.appendChild(document.createTextNode(" "));
+		
+	var newSectionLink = document.createElement("A");
+	newSectionLink.innerHTML = "add&nbsp;sub-section";
+	newSectionLink.setAttribute("class", "menuLink");
+	newSectionLink.setAttribute("href", "javascript:;");
+	newSectionLink.onclick = function()
+	{
+		createBlankSection(section); 
+		updateFromTop();
+	};
+	menuBar.appendChild(newSectionLink);
+	menuBar.newSectionLink = newSectionLink;
+	
+	//"New Section" link
+	if(section.childList)
+	{
+		newSectionLink.style.display = "none";
+	}
+	
+	//Separator
+	menuBar.appendChild(document.createTextNode(" "));
+		
+	//"Duplicate" link
+	var duplicateLink = document.createElement("A");
+	duplicateLink.innerHTML = "duplicate";
+	duplicateLink.setAttribute("class", "menuLink");
+	duplicateLink.setAttribute("href", "javascript:;");
+	duplicateLink.onclick = function()
+	{
+		var newSection = duplicateSection(section);
+		if(section.childList)
+		{
+			duplicateSectionChildrenRecursive(newSection, section);
+		}
+		
+		var newNodeID = section.nodeID;
+		var lastDigit = parseInt(newNodeID.substring(newNodeID.lastIndexOf(".") + 1));
+		newNodeID = newNodeID.replace(/\.[^\.]*$/, "." + ++lastDigit);
+		
+		var undo = new Array();
+		undo.op = "del";
+		undo.srcElem = section;
+		undo.removeTransaction = true;
+		_undoOperations.push(undo);
+		
+		saveTransaction('{"operation":"duplicate", "subOperation":"insertBefore", "collection":"' + section.collection + '", "oid":"' + section.nodeID + '", "newCollection":"' + section.collection + '", "newOID":"' + newNodeID + '"}');
+		addCollectionToBuild(section.collection);
+
+		updateFromTop();
+	};
+	menuBar.appendChild(duplicateLink);
+	menuBar.duplicateLink = duplicateLink;
+	
+	//Separator
+	menuBar.appendChild(document.createTextNode(" "));
+	
+	//"Delete" link
+	var deleteLink = document.createElement("A");
+	deleteLink.innerHTML = "[X]";
+	deleteLink.setAttribute("class", "menuLink deleteLink");
+	deleteLink.setAttribute("href", "javascript:;");
+	deleteLink.onclick = function(){deleteSection(section)};
+	menuBar.appendChild(deleteLink);
+	menuBar.deleteLink = deleteLink;
+	
+	menuBar.style.display = "none";
+	section.appendChild(menuBar);
+	section.menu = menuBar;
+}
+
+function updateRecursive(parent, currentDocument, currentPosition, level)
+{
+	if(level == 0)
+	{
+		_indexCounter = 0;
+	}
+	
+	level++;
+	var current = parent.firstChild;
+	var posCount = 1;
+	var lastItem;
+	var liCount = 0;
+	while(current != null)
+	{
+		if(current.nodeName.toLowerCase() == "ul")
+		{
+			var pos = null;
+			if(level > 2)
+			{
+				if(!currentPosition)
+				{
+					pos = (posCount - 1);
+				}
+				else
+				{
+					pos = currentPosition + "." + (posCount - 1);
+				}
+			}
+			
+			updateRecursive(current, currentDocument, pos, level);
+		}
+		else if (current.nodeName.toLowerCase() == "li" && hasClass(current, "dragItem") && !hasClass(current, "placeHolder"))
+		{
+			if(currentDocument == null && current.nodeID)
+			{
+				currentDocument = current.nodeID;
+			}
+
+			var pos;
+			if(!currentPosition)
+			{
+				pos = posCount;
+			}
+			else
+			{
+				pos = currentPosition + "." + posCount;
+			}
+			
+			if(!hasClass(current, "topLevel"))
+			{
+				current.nodeID = currentDocument + "." + pos;
+				current.position = pos;
+				current.documentID = currentDocument;
+			}
+			posCount++;
+			
+			current.index = _indexCounter++;
+		}
+		else if (hasClass(current, "placeHolder") && !current.isEmptyList)
+		{
+			var pos;
+			if(!currentPosition)
+			{
+				pos = posCount - 1;
+			}
+			else
+			{
+				pos = currentPosition + "." + posCount - 1;
+			}
+			current.nodeID = currentDocument + "." + pos;
+		}
+		
+		if(current.nodeName.toLowerCase() == "li")
+		{
+			liCount++;
+			lastItem = current;
+		}
+		
+		current = current.nextSibling;
+	}
+
+	if(level > 2)
+	{
+		//If the last section a this level has a child list then insert a blank placeholder after it so we can insert sections after the list
+		if(lastItem && lastItem.childList)
+		{
+			var placeHolder = createPlaceholder(lastItem, parent, false);
+			parent.appendChild(placeHolder);
+		}
+
+		//If this list is empty or has 1 placeholder child
+		if(liCount == 0 || (liCount == 1 && hasClass(lastItem, "placeHolder")))
+		{
+			//Give it the emptyList css class (if it does not already have it)
+			if(!parent.getAttribute("class") || parent.getAttribute("class").search("emptyList") == -1)
+			{
+				var newClass = parent.getAttribute("class") ? parent.getAttribute("class") + " emptyList" : "emptyList";
+				parent.setAttribute("class", newClass);
+			}
+			
+			//If the list is empty then add a placeholder
+			if(liCount == 0)
+			{
+				var placeHolder = createPlaceholder(parent.previousSibling.previousSibling, parent, true); //Find a smarter way of doing this
+				parent.appendChild(placeHolder);
+			}
+		}
+		//Remove the empty list class if the list is no longer empty
+		else if(hasClass(parent, "emptyList"))
+		{
+			parent.setAttribute("class", parent.getAttribute("class").replace(/emptyList/g, ""));
+		}
+	}
+}
+
+YAHOO.util.Event.onDOMReady(init);
Index: main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts_dd.js
===================================================================
--- main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts_dd.js	(revision 25012)
+++ main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts_dd.js	(revision 25012)
@@ -0,0 +1,343 @@
+var Dom = YAHOO.util.Dom;
+var Event = YAHOO.util.Event;
+var DDM = YAHOO.util.DragDropMgr;
+//var tempPlaceholderList;
+//var tempPlaceholder;
+
+//////////////////////////////////////////////////////////////////////////////
+// custom drag and drop implementation
+//////////////////////////////////////////////////////////////////////////////
+
+YAHOO.example.DDList = function(id, sGroup, config) {
+
+	YAHOO.example.DDList.superclass.constructor.call(this, id, sGroup, config);
+
+	this.logger = this.logger || YAHOO;
+	var el = this.getDragEl();
+	Dom.setStyle(el, "opacity", 0.67); // The proxy is slightly transparent
+
+	this.goingUp = false;
+	this.lastY = 0;
+};
+
+YAHOO.extend(YAHOO.example.DDList, YAHOO.util.DDProxy, {
+
+	startDrag: function(x, y) {
+		this.logger.log(this.id + " startDrag");
+
+		/*
+		if(!tempPlaceholder)
+		{
+			tempPlaceholderList = document.createElement("UL");
+			tempPlaceholderList.setAttribute("class", "emptyList");
+			tempPlaceHolder = createPlaceholder(null, null, false);
+			tempPlaceholderList.appendChild(tempPlaceHolder);
+		}
+		*/
+		
+		// make the proxy look like the source element
+		var dragEl = this.getDragEl();
+		var clickEl = this.getEl();
+		
+		this.sourcePrevSibling = getPrevSiblingOfType(clickEl, "li");
+		this.sourceNextSibling = getNextSiblingOfType(clickEl, "li");
+		this.sourceParentList = clickEl.parentList;
+		
+		closeAllOpenContents();
+
+		Dom.setStyle(clickEl, "visibility", "hidden");
+		dragEl.innerHTML = clickEl.innerHTML;
+		
+		var child = clickEl.childList;
+		var contents = clickEl.textDiv;
+		if(child)
+		{
+			this.dragElChild = child;
+			Dom.setStyle(child, "display", "none");
+		}
+		else
+		{
+			this.dragElChild = null;
+		}
+		
+		if(contents)
+		{
+			this.contents = contents;
+		}
+		else
+		{
+			this.contents = null;
+		}
+
+		Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color"));
+		Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor"));
+		Dom.setStyle(dragEl, "border", "2px solid gray");
+	},
+
+	endDrag: function(e) {
+
+		var srcEl = this.getEl();
+		var proxy = this.getDragEl();
+
+		// Show the proxy element and animate it to the src element's location
+		Dom.setStyle(proxy, "visibility", "");
+
+		var a = new YAHOO.util.Motion( 
+			proxy, { 
+				points: { 
+					to: Dom.getXY(srcEl)
+				}
+			}, 
+			0.1,
+			YAHOO.util.Easing.easeOut 
+		);
+
+		var proxyElem = proxy;
+		var thisElem = this;
+		var last = this.lastMoved;
+
+		// Hide the proxy and show the source element when finished with the animation
+		a.onComplete.subscribe(function() 
+		{
+			//Make the helper invisible
+			Dom.setStyle(proxyElem.id, "visibility", "hidden");
+			//Make the actual item visible again
+			Dom.setStyle(srcEl.id, "visibility", "");
+			
+			//Show the child list if the section has one
+			if(srcEl.childList)
+			{
+				Dom.setStyle(srcEl.childList, "display", "block");
+				insertAfter(srcEl.childList, srcEl);
+			}
+
+			//Move the element's text div if it has one
+			if(srcEl.textDiv)
+			{
+				insertAfter(srcEl.textDiv, srcEl);
+			}
+
+			var reverse = false;
+			var newNodeID = null;
+	
+			//Make sure we have the ID of the last section moved
+			if(last.nodeID)
+			{
+				newNodeID = last.nodeID;
+				//We only want to modify the new node ID if the two sections are at the same level in the same document
+				if(!srcEl.nodeID || (newNodeID.split(".").length == srcEl.nodeID.split(".").length && srcEl.nodeID.split(".")[0] == newNodeID.split(".")[0]))
+				{
+					if(thisElem.goingUp && (last.index > srcEl.index))
+					{
+						var lastDigit = parseInt(newNodeID.substring(newNodeID.lastIndexOf(".") + 1));
+						if(lastDigit == 1){lastDigit = 2;} //Make sure we never get 0
+
+						newNodeID = newNodeID.replace(/\.[^\.]*$/, "." + --lastDigit);
+						reverse = true;
+					}
+					else if(!srcEl.nodeID || (!thisElem.goingUp && (last.index < srcEl.index)))
+					{
+						var lastDigit = parseInt(newNodeID.substring(newNodeID.lastIndexOf(".") + 1));
+						newNodeID = newNodeID.replace(/\.[^\.]*$/, "." + ++lastDigit);
+						reverse = true;
+					}
+				}
+			}
+
+			var operation;
+			if(thisElem.goingUp)
+			{
+				operation = reverse ? "insertAfter" : "insertBefore";
+			}
+			else
+			{
+				operation = reverse ? "insertBefore" : "insertAfter";
+			}
+
+			//If a new section has just been dragged in
+			if(srcEl.newSection)
+			{
+				srcEl.newSection = null;
+
+				//Create a new draggable section to replace the one we just moved
+				createDraggableNewSection(srcEl.parent);
+
+				//Add the normal things to the new section
+				var textDiv = createSectionTextDiv(null);
+				srcEl.textDiv = textDiv;
+				insertAfter(textDiv, srcEl);
+				textDiv.parentItem = srcEl;
+
+				createSectionMenu(srcEl);
+				setMouseOverAndOutFunctions(srcEl);
+				srcEl.setAttribute("class", srcEl.getAttribute("class").replace(/newSection/g, ""));
+				srcEl.dbID = _idCounter++;
+				srcEl.collection = last.collection;
+				_allContents.push(srcEl);
+
+				var undo = new Array();
+				undo.op = "del";
+				undo.srcElem = srcEl;
+				undo.removeTransaction = true;
+				_undoOperations.push(undo);
+
+				saveTransaction('{"operation":"create", "subOperation":"' + operation + '", "collection":"' + last.collection + '", "oid":"' + newNodeID + '"}');
+				addCollectionToBuild(last.collection);
+			}
+			else
+			{
+				var needsUndo = false;
+				if(hasClass(last, "placeHolder"))
+				{
+					//Removed the dashed border that surrounds an empty section
+					if(last.parentNode)
+					{
+						removeFromParent(last);
+					}
+					
+					if(last.isEmptyList)
+					{
+						saveTransaction('{"operation":"move", "subOperation":"append", "collection":"' + srcEl.collection + '", "oid":"' + srcEl.nodeID + '", "newCollection":"' + last.collection + '", "newOID":"' + last.nodeID + '"}');
+					}
+					else
+					{
+						saveTransaction('{"operation":"move", "subOperation":"insertAfter", "collection":"' + srcEl.collection + '", "oid":"' + srcEl.nodeID + '", "newCollection":"' + last.collection + '", "newOID":"' + last.nodeID + '"}');
+					}
+					addCollectionToBuild(srcEl.collection);
+					addCollectionToBuild(last.collection);
+					needsUndo = true;
+				}
+				else
+				{
+					if(srcEl.collection != last.collection || srcEl.nodeID != newNodeID)
+					{
+						saveTransaction('{"operation":"move", "subOperation":"' + operation + '", "collection":"' + srcEl.collection + '", "oid":"' + srcEl.nodeID + '", "newCollection":"' + last.collection + '", "newOID":"' + newNodeID + '"}');
+						addCollectionToBuild(srcEl.collection);
+						addCollectionToBuild(last.collection);
+						needsUndo = true;
+					}
+				}
+				
+				//Save the undo operation if necessary
+				if(needsUndo)
+				{
+					var undo = new Array();
+					if(thisElem.sourceNextSibling)
+					{
+						undo.op = "mvb";
+						undo.refElem = thisElem.sourceNextSibling;
+					}
+					else if(thisElem.sourcePrevSibling)
+					{
+						undo.op = "mva";
+						undo.refElem = thisElem.sourcePrevSibling;
+					}
+					else
+					{
+						undo.op = "mvi";
+						undo.refElem = thisElem.sourceParentList;
+					}
+
+					undo.srcElem = srcEl;
+					undo.removeTransaction = true;
+					_undoOperations.push(undo);
+				}
+			}
+
+			updateFromTop();
+		});
+		a.animate();
+
+		if(hasClass(srcEl.previousSibling, "placeHolder"))
+		{
+			removeFromParent(srcEl.previousSibling);
+			new YAHOO.example.DDList(srcEl.parentNode.previousSibling);
+		}
+		if(hasClass(srcEl, "topLevel"))
+		{
+			srcEl.setAttribute("class", srcEl.getAttribute("class").replace(/topLevel/g, ""));
+		}
+	},
+
+	onDragDrop: function(e, id) {
+
+		// If there is one drop interaction, the li was dropped either on the list,
+		// or it was dropped on the current location of the source element.
+		if (DDM.interactionInfo.drop.length === 1) {
+
+			// The position of the cursor at the time of the drop (YAHOO.util.Point)
+			var pt = DDM.interactionInfo.point; 
+
+			// The region occupied by the source element at the time of the drop
+			var region = DDM.interactionInfo.sourceRegion; 
+
+			// Check to see if we are over the source element's location.  We will
+			// append to the bottom of the list once we are sure it was a drop in
+			// the negative space (the area of the list without any list items)
+			if (!region.intersect(pt)) {
+				var destEl = Dom.get(id);
+				var destDD = DDM.getDDById(id);
+				if(hasClass(destEl, "topLevel") && hasClass(destEl, "dragList"))
+				{
+					destEl.appendChild(this.getEl());
+				}
+				destDD.isEmpty = false;
+				DDM.refreshCache();
+			}
+
+		}
+	},
+
+	onDrag: function(e) {
+
+		// Keep track of the direction of the drag for use during onDragOver
+		var y = Event.getPageY(e);
+
+		if (y < this.lastY) {
+			this.goingUp = true;
+		} else if (y > this.lastY) {
+			this.goingUp = false;
+		}
+
+		this.lastY = y;
+	},
+
+	onDragOver: function(e, id) {
+	
+		var srcEl = this.getEl();
+		var destEl = Dom.get(id);
+		
+		// We are only concerned with list items, we ignore the dragover
+		// notifications for the list.
+		if(destEl.nodeName.toLowerCase() == "li") {
+			if((hasClass(destEl, "dragItem") || hasClass(destEl, "placeHolder")) && !hasClass(destEl, "topLevel") && !hasClass(destEl, "newSection"))
+			{
+				var orig_p = srcEl.parentNode;
+				var p = destEl.parentNode;
+				
+				var child = destEl.childList;
+				var contents = destEl.textDiv;
+					
+				if (this.goingUp) {
+					p.insertBefore(srcEl, destEl);
+				} else {
+					p.insertBefore(srcEl, destEl.nextSibling);
+				}
+
+				//This is to stop items being inserted between a section and its children
+				if(child)
+				{
+					insertAfter(child, destEl);
+				}
+				if(contents)
+				{
+					insertAfter(contents, destEl);
+				}
+				
+				this.lastMoved = destEl;
+
+				DDM.refreshCache();
+			}
+		}
+	}
+});
Index: main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts_util.js
===================================================================
--- main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts_util.js	(revision 25012)
+++ main/trunk/greenstone3/web/interfaces/oran/js/documentmaker_scripts_util.js	(revision 25012)
@@ -0,0 +1,872 @@
+//Some "constants" to match the server constants
+var SUCCESS = 1;
+var ACCEPTED = 2;
+var ERROR = 3;
+var CONTINUING = 10;
+var COMPLETED = 11;
+var HALTED = 12;
+
+function getElementsByClassName(cl, parent) 
+{
+	var elemArray = [];
+	var classRegEx = new RegExp("\\b" + cl + "\\b");
+	var elems;
+	if(parent)
+	{	
+		elems = parent.getElementsByTagName("*");
+	}
+	else
+	{
+		elems = document.getElementsByTagName("*");
+	}
+	
+	for (var i = 0; i < elems.length; i++) 
+	{
+		var classeName = elems[i].className;
+		if (classRegEx.test(classeName)) elemArray.push(elems[i]);
+	}
+	
+	return elemArray;
+};
+
+function getNextSiblingOfType(elem, type)
+{
+	if(elem == null)
+	{
+		return null;
+	}
+	
+	var current = elem.nextSibling;
+	while(current != null)
+	{
+		if(current.nodeName.toLowerCase() == type)
+		{
+			return current;
+		}
+		
+		current = current.nextSibling;
+	}
+	return null;
+}
+
+function getPrevSiblingOfType(elem, type)
+{
+	if(elem == null)
+	{
+		return null;
+	}
+	
+	var current = elem.previousSibling;
+	while(current != null)
+	{
+		if(current.nodeName.toLowerCase() == type)
+		{
+			return current;
+		}
+		
+		current = current.previousSibling;
+	}
+	return null;
+}
+
+function saveTransaction(transaction)
+{
+	console.log(transaction);
+	_transactions.push(transaction);
+}
+
+function undo()
+{
+	if(_undoOperations.length == 0)
+	{
+		return;
+	}
+	
+	var undoOp = _undoOperations.pop();
+
+	//Create/Duplicate undo
+	if(undoOp.op == "del")
+	{
+		if(undoOp.srcElem.childList)
+		{
+			removeFromParent(undoOp.srcElem.childList);
+		}
+		if(undoOp.srcElem.parentItem)
+		{
+			undoOp.srcElem.parentItem.menu.newSectionLink.style.display = "inline";
+			undoOp.srcElem.parentItem.childList = null;
+		}
+		removeFromParent(undoOp.srcElem);
+	}
+	
+	if(undoOp.op == "delMeta")
+	{
+		if(undoOp.srcElem.childList)
+		{
+			removeFromParent(undoOp.srcElem.childList);
+		}
+		if(undoOp.srcElem.parentItem)
+		{
+			undoOp.srcElem.parentItem.menu.newSectionLink.style.display = "inline";
+			undoOp.srcElem.parentItem.childList = null;
+		}
+		de.doc.unregisterEditSection(undoOp.srcElem);
+		removeFromParent(undoOp.srcElem);
+	}
+	
+	//Move undo (mva is move after, mvb is move before, mvi is move into)
+	else if(undoOp.op == "mva" || undoOp.op == "mvb" || undoOp.op == "mvi")
+	{
+		if(undoOp.op == "mvb")
+		{
+			undoOp.refElem.parentNode.insertBefore(undoOp.srcElem, undoOp.refElem);
+		}
+		else if(undoOp.op == "mva")
+		{
+			insertAfter(undoOp.srcElem, undoOp.refElem);
+		}
+		else
+		{
+			undoOp.refElem.removeChild(undoOp.refElem.firstChild);
+			undoOp.refElem.appendChild(undoOp.srcElem);
+		}
+
+		if(undoOp.srcElem.textDiv)
+		{
+			insertAfter(undoOp.srcElem.textDiv, undoOp.srcElem);
+		}
+		if(undoOp.srcElem.childList)
+		{
+			insertAfter(undoOp.srcElem.childList, undoOp.srcElem.textDiv);
+		}
+
+		if(undoOp.srcElem.onmouseout)
+		{
+			//Uncolour the section if it coloured
+			undoOp.srcElem.onmouseout();
+		}
+		updateFromTop();
+	}
+
+	if(undoOp.removeTransaction)
+	{
+		_transactions.pop();
+	}
+}
+
+function addCollectionToBuild(collection)
+{
+	for(var i = 0; i < _collectionsToBuild.length; i++)
+	{
+		if(collection == _collectionsToBuild[i])
+		{
+			return;
+		}
+	}
+	_collectionsToBuild.push(collection);
+}
+
+function save()
+{
+	var changes = de.Changes.getChangedEditableSections();
+
+	for(var i = 0; i < changes.length; i++)
+	{
+		var changedElem = changes[i];
+		if(hasClass(changedElem, "metadataTableCellValue"))
+		{
+			//Save metadata
+		}
+		else if(hasClass(changedElem, "renderedText"))
+		{
+			//Save content
+			var section = changedElem.parentDiv.parentItem;
+			saveTransaction('{"operation":"setText", "text":"' + changedElem.innerHTML.replace(/"/g, "\\\"") + '", "collection":"' + section.collection + '", "oid":"' + section.nodeID + '"}');
+			addCollectionToBuild(section.collection);
+		}
+	}
+
+	var request = "[";
+	for(var i = 0; i < _transactions.length; i++)
+	{
+		request += _transactions[i];
+		if(i != _transactions.length - 1)
+		{
+			request +=",";
+		}
+	}
+	request += "]";
+	
+	var statusID;
+	var ajax = new gs.functions.ajaxRequest();
+	ajax.open("POST", _baseURL, true);
+	ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+	ajax.onreadystatechange = function()
+	{
+		if(ajax.readyState == 4 && ajax.status == 200)
+		{
+			var text = ajax.responseText;
+			var xml = validateXML(text);
+			
+			var errorElems;
+			if(!xml || checkForErrors(xml))
+			{
+				alert("There was an error saving, aborting");
+			
+				var saveButton = document.getElementById("saveButton");
+				saveButton.innerHTML = "Save changes";
+				saveButton.disabled = false;
+				
+				_statusBar.removeStatus(statusID);
+				return;
+			}
+
+			_statusBar.removeStatus(statusID);
+			buildCollections(_collectionsToBuild);
+		}
+	}
+	
+	var saveButton = document.getElementById("saveButton");
+	saveButton.innerHTML = "Saving...";
+	saveButton.disabled = true;
+	
+	statusID = _statusBar.addStatus("Modifying archive files...");
+	ajax.send("a=g&rt=r&s=DocumentExecuteTransaction&s1.transactions=" + request);
+}
+
+function buildCollections(collections)
+{
+	var saveButton = document.getElementById("saveButton");
+	if(!collections || collections.length == 0)
+	{
+		console.log("List of collections to build is empty");
+		saveButton.innerHTML = "Save changes";
+		saveButton.disabled = false;
+		return;
+	}
+
+	var counter = 0;
+	var statusID = 0;
+	var buildFunction = function()
+	{
+		var ajax = new gs.functions.ajaxRequest();
+		ajax.open("GET", _baseURL + "?a=g&rt=r&ro=1&s=BuildCollection&s1.collection=" + collections[counter]);
+		ajax.onreadystatechange = function()
+		{
+			if(ajax.readyState == 4 && ajax.status == 200)
+			{
+				var text = ajax.responseText;
+				var xml = validateXML(text);
+
+				if(!xml || checkForErrors(xml))
+				{
+					alert("Could not build collection -> " + collections[counter] + ", aborting");
+					
+					_statusBar.removeStatus(statusID);
+					saveButton.innerHTML = "Save changes";
+					saveButton.disabled = false;
+					
+					return;
+				}
+
+				var status = xml.getElementsByTagName("status")[0];
+				var pid = status.getAttribute("pid");
+
+				startCheckLoop(pid, "BuildCollection", statusID, function()
+				{
+					var localAjax = new gs.functions.ajaxRequest();
+					localAjax.open("GET", _baseURL + "?a=g&rt=r&ro=1&s=ActivateCollection&s1.collection=" + collections[counter], true);
+					localAjax.onreadystatechange = function()
+					{
+						if(localAjax.readyState == 4 && localAjax.status == 200)
+						{
+							var localText = localAjax.responseText;
+							var localXML = validateXML(localText);
+							
+							if(!xml || checkForErrors(xml))
+							{
+								alert("Could not activate collection -> " + collections[counter] + ", aborting");
+								
+								_statusBar.removeStatus(statusID);
+								saveButton.innerHTML = "Save changes";
+								saveButton.disabled = false;
+								
+								return;
+							}
+
+							var localStatus = localXML.getElementsByTagName("status")[0];
+							if(localStatus)
+							{
+								var localPID = localStatus.getAttribute("pid");
+								startCheckLoop(localPID, "ActivateCollection", statusID, function()
+								{
+									if(counter == collections.length - 1)
+									{
+										removeCollectionsFromBuildList(collections);
+									}
+									else
+									{
+										counter++;
+										buildFunction();
+									}
+
+									_transactions = new Array();
+
+									_statusBar.removeStatus(statusID);
+									saveButton.innerHTML = "Save changes";
+									saveButton.disabled = false;
+								});
+							}
+						}
+					}
+					_statusBar.changeStatus(statusID, "Activating collection " + collections[counter] + "...");
+					localAjax.send();
+				});
+			}
+		}
+		statusID = _statusBar.addStatus("Building collection " + collections[counter] + "...");
+		ajax.send();
+	}
+	buildFunction();
+}
+
+function startCheckLoop(pid, serverFunction, statusID, callbackFunction)
+{
+	var ajaxFunction = function()
+	{
+		var saveButton = document.getElementById("saveButton");
+		
+		var ajax = new gs.functions.ajaxRequest();
+		ajax.open("GET", _baseURL + "?a=g&rt=s&ro=1&s=" + serverFunction + "&s1.pid=" + pid, true);
+		ajax.onreadystatechange = function()
+		{
+			if(ajax.readyState == 4 && ajax.status == 200)
+			{
+				var text = ajax.responseText;
+				var xml = validateXML(text);
+				
+				if(!xml || checkForErrors(xml))
+				{
+					alert("Could not check status of " + serverFunction + ", there was an error in the XML, aborting");
+					
+					_statusBar.removeStatus(statusID);
+					saveButton.innerHTML = "Save changes";
+					saveButton.disabled = false;
+					
+					return;
+				}
+
+				var status = xml.getElementsByTagName("status")[0];
+				var code = status.getAttribute("code");
+
+				if (code == COMPLETED || code == SUCCESS)
+				{
+					callbackFunction();
+				}
+				else if (code == HALTED || code == ERROR)
+				{
+					alert("Could not check status of " + serverFunction + ", there was an error on the server, aborting");
+					
+					_statusBar.removeStatus(statusID);
+					saveButton.innerHTML = "Save changes";
+					saveButton.disabled = false;
+				}
+				else
+				{
+					setTimeout(ajaxFunction, 1000);
+				}
+			}
+		}
+		ajax.send();
+	}
+	ajaxFunction();
+}
+
+function removeCollectionsFromBuildList(collections)
+{
+	var tempArray = new Array();
+	for(var i = 0; i < _collectionsToBuild.length; i++)
+	{
+		var found = false;
+		for(var j = 0; j < collections.length; j++)
+		{
+			if(collections[j] == _collectionsToBuild[i])
+			{
+				found = true;
+				break;
+			}
+		}
+		
+		if(!found)
+		{
+			tempArray.push(_collectionsToBuild[i]);
+		}
+	}
+	_collectionsToBuild = tempArray;
+}
+
+function checkForErrors(xml)
+{
+	var errorElems = xml.getElementsByTagName("error");
+	
+	if(errorElems && errorElems.length > 0)
+	{
+		var errorString = "There was an error saving your changes: ";
+		for(var i = 0; i < errorElems.length; i++)
+		{
+			errorString += " " + errorElems.item(i).firstChild.nodeValue;
+		}
+		alert(errorString);
+		return true;
+	}
+	return false; //No errors
+}
+
+function validateXML(txt)
+{
+	// code for IE
+	if (window.ActiveXObject)
+	{
+		var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+		xmlDoc.async = "false";
+		xmlDoc.loadXML(document.all(txt).value);
+
+		if(xmlDoc.parseError.errorCode!=0)
+		{
+			txt = "Error Code: " + xmlDoc.parseError.errorCode + "\n";
+			txt = txt + "Error Reason: " + xmlDoc.parseError.reason;
+			txt = txt + "Error Line: " + xmlDoc.parseError.line;
+			console.log(txt);
+			return null;
+		}
+		
+		return xmlDoc;
+	}
+	// code for Mozilla, Firefox, Opera, etc.
+	else if (document.implementation.createDocument)
+	{
+		var parser = new DOMParser();
+		var xmlDoc = parser.parseFromString(txt,"text/xml");
+
+		if (xmlDoc.getElementsByTagName("parsererror").length > 0)
+		{
+			console.log("There was an error parsing the XML");
+			return null;
+		}
+		
+		return xmlDoc;
+	}
+	else
+	{
+		console.log('Your browser cannot handle XML validation');
+	}
+	return null;
+}
+
+function addFunctionalityToTable(table)
+{
+	var rows = table.getElementsByTagName("TR");
+	for(var i = 0; i < rows.length; i++)
+	{
+		var cells = rows[i].getElementsByTagName("TD");
+			
+		//This registering can cause a sizeable delay so we'll thread it (effectively) so the browser is not paused
+		setTimeout(function(){de.doc.registerEditSection(cells[1])}, 0);
+		
+		addRemoveLinkToRow(rows[i]);
+	}
+
+	var metaNameField = document.createElement("INPUT");
+	metaNameField.setAttribute("type", "text");
+	insertAfter(metaNameField, table);
+	
+	var addRowButton = document.createElement("BUTTON");
+	addRowButton.innerHTML = "Add new metadata";
+	addRowButton.onclick = function() 
+	{ 
+		var name = metaNameField.value;
+		if(!name || name == "")
+		{
+			console.log("No value given for new metadata name");
+			return;
+		}
+		
+		var newRow = document.createElement("TR");
+		var nameCell = document.createElement("TD");
+		var valueCell = document.createElement("TD");
+		nameCell.setAttribute("class", "metaTableCellName");
+		nameCell.innerHTML = name;
+		valueCell.setAttribute("class", "metaTableCell editable");
+		
+		newRow.appendChild(nameCell);
+		newRow.appendChild(valueCell);
+		addRemoveLinkToRow(newRow);
+		table.appendChild(newRow);
+		
+		var undo = new Array();
+		undo.op = "delMeta";
+		undo.srcElem = newRow;
+		undo.removeTransaction = false;
+		_undoOperations.push(undo);
+		
+		//Threading this function here probably isn't necessary like the other times it is called
+		de.doc.registerEditSection(valueCell);
+	};
+	insertAfter(addRowButton, metaNameField);
+}
+
+function addRemoveLinkToRow(row)
+{
+	var newCell = document.createElement("TD");
+	var removeLink = document.createElement("A");
+	removeLink.innerHTML = "remove";
+	removeLink.setAttribute("href", "javascript:;");
+	removeLink.onclick = function()
+	{
+		var undo = new Array();
+		var prev = getPrevSiblingOfType(row, "tr");
+		var next = getNextSiblingOfType(row, "tr");
+		var parent = row.parentNode;
+		if(prev)
+		{
+			undo.op = "mva";
+			undo.refElem = prev;
+		}
+		else if(next)
+		{
+			undo.op = "mvb";
+			undo.refElem = next;
+		}
+		else
+		{
+			undo.op = "mvi";
+			undo.refElem = parent;
+		}
+		undo.srcElem = row;
+		undo.removeTransaction = true;
+		_undoOperations.push(undo);
+
+		/*
+		saveTransaction('{"operation":"deleteMetadata", "collection":"' + section.collection + '", "oid":"' + section.nodeID + '"}');
+		addCollectionToBuild(section.collection);
+		*/
+
+		_deletedSections.push(row);
+		removeFromParent(row);
+	}
+	newCell.appendChild(removeLink);
+	newCell.setAttribute("class", "metaTableCell");
+	newCell.setAttribute("style", "font-size:0.6em; padding-left: 3px; padding-right: 3px;");
+	row.appendChild(newCell);
+}
+
+function createTopMenuBar()
+{
+	//Create the top menu bar
+	var headerTable = document.createElement("TABLE");
+	var tableBody = document.createElement("TBODY");
+	var row = document.createElement("TR");
+	var newDocCell = document.createElement("TD");
+	var newSecCell = document.createElement("TD");
+	var saveCell = document.createElement("TD");
+	var undoCell = document.createElement("TD");
+	newDocCell.setAttribute("class", "headerTableTD");
+	newSecCell.setAttribute("class", "headerTableTD");
+	undoCell.setAttribute("class", "headerTableTD");
+	saveCell.setAttribute("class", "headerTableTD");
+	
+	headerTable.appendChild(tableBody);
+	tableBody.appendChild(row);
+	row.appendChild(saveCell);
+	row.appendChild(undoCell);
+	row.appendChild(newDocCell);
+	row.appendChild(newSecCell);
+
+	//The "Save changes" button
+	var saveButton = document.createElement("BUTTON");
+	saveButton.innerHTML = "Save changes";
+	saveButton.setAttribute("onclick", "save();");
+	saveButton.setAttribute("id", "saveButton");
+	saveCell.appendChild(saveButton);
+	
+	//The "Undo" button
+	var undoButton = document.createElement("BUTTON");
+	undoButton.innerHTML = "Undo";
+	undoButton.setAttribute("onclick", "undo();");
+	undoCell.appendChild(undoButton);
+
+	//The "Create new document" button
+	var newDocButton = document.createElement("BUTTON");
+	newDocButton.innerHTML = "Create new document";
+	newDocButton.setAttribute("onclick", "createNewDocumentArea();");
+	newDocCell.appendChild(newDocButton);
+
+	//The "Insert new section" LI
+	var newSecLI = createDraggableNewSection(newSecCell);
+	
+	return headerTable;
+}
+
+function getMetadataFromNode(node, name)
+{
+	var currentNode = node.firstChild;
+	while(currentNode != null)
+	{
+		if(currentNode.nodeName == "metadataList")
+		{
+			currentNode = currentNode.firstChild;
+			break;
+		}
+		
+		currentNode = currentNode.nextSibling;
+	}
+	
+	while(currentNode != null)
+	{
+		if(currentNode.nodeName == "metadata" && currentNode.getAttribute("name") == name)
+		{
+			return currentNode.firstChild.nodeValue;
+		}
+		
+		currentNode = currentNode.nextSibling;
+	}
+	return "{UNTITLED}";
+}
+
+function storeMetadata(node, listItem)
+{
+	listItem.metadata = new Array();
+	
+	var currentNode = node.firstChild;
+	while(currentNode != null)
+	{
+		if(currentNode.nodeName == "metadataList")
+		{
+			currentNode = currentNode.firstChild;
+			break;
+		}
+		
+		currentNode = currentNode.nextSibling;
+	}
+	
+	while(currentNode != null)
+	{
+		if(currentNode.nodeName == "metadata")
+		{
+			listItem.metadata[currentNode.getAttribute("name")] = currentNode.firstChild.nodeValue;
+		}
+		
+		currentNode = currentNode.nextSibling;
+	}
+}
+
+function hasClass(elem, classVal)
+{
+	if(!elem || !elem.getAttribute("class"))
+	{
+		return false;
+	}
+
+	return (elem.getAttribute("class").search(classVal) != -1)
+}
+
+function getNodeContent(node)
+{
+	var currentNode = node.firstChild;
+	while(currentNode != null)
+	{
+		if(currentNode.nodeName == "nodeContent")
+		{
+			return currentNode.firstChild;
+		}
+		
+		currentNode = currentNode.nextSibling;
+	}
+	return null;
+}
+
+function containsDocumentNode(node)
+{
+	var currentNode = node.firstChild;
+	while(currentNode != null)
+	{
+		if(currentNode.nodeName == "documentNode")
+		{
+			return true;
+		}
+		
+		currentNode = currentNode.nextSibling;
+	}
+	return false;
+}
+
+function isExpanded(textDiv)
+{
+	if(!textDiv.style.display || textDiv.style.display == "block")
+	{
+		return true;
+	}
+	return false;
+}
+
+function toggleTextDiv(section)
+{
+	var textDiv = section.textDiv;
+	if(textDiv)
+	{
+		if(isExpanded(textDiv))
+		{
+			textDiv.style.display = "none";
+			section.menu.editTextLink.innerHTML = "edit";
+		}
+		else
+		{
+			textDiv.style.display = "block";
+			section.menu.editTextLink.innerHTML = "hide";
+		}
+	}
+}
+
+function updateFromTop()
+{
+	updateRecursive(document.getElementById("dbDiv"), null, null, 0);
+}
+
+function insertAfter(elem, refElem)
+{
+	if(refElem.nextSibling)
+	{
+		refElem.parentNode.insertBefore(elem, refElem.nextSibling);
+	}
+	else
+	{
+		refElem.parentNode.appendChild(elem);
+	}
+}
+
+function removeFromParent(elem)
+{
+	elem.parentNode.removeChild(elem);
+}
+
+function createSectionTitle(text)
+{
+	var textSpan = document.createElement("SPAN");
+	if(text)
+	{
+		textSpan.appendChild(document.createTextNode(" " + text + " "));
+	}
+	else
+	{
+		textSpan.appendChild(document.createTextNode(" [UNTITLED SECTION] "));
+	}
+	return textSpan;
+}
+
+function setMouseOverAndOutFunctions(section)
+{
+	//Colour the list item and display the menu on mouse over
+	section.onmouseover = function(e)
+	{
+		if(this.menu){this.menu.style.display = "inline";}
+		this.style.background = "rgb(255, 200, 0)";
+	};
+	//Uncolour the list item and hide the menu on mouse out
+	section.onmouseout = function(e)
+	{
+		if(this.menu){this.menu.style.display = "none";}
+		this.style.background = "none";
+	};
+}
+
+function createDraggableNewSection(parent)
+{
+	var newSecLI = document.createElement("LI");
+	var newSpan = document.createElement("SPAN");
+	newSpan.innerHTML = "Insert new section ";
+	
+	newSecLI.sectionTitle = newSpan;
+	newSecLI.appendChild(newSpan);
+	newSecLI.setAttribute("class", "dragItem newSection");
+	newSecLI.newSection = true;
+	newSecLI.parent = parent;
+	newSecLI.index = -1;
+	new YAHOO.example.DDList(newSecLI);
+	parent.appendChild(newSecLI);
+}
+
+function closeAllOpenContents()
+{
+	for(var i = 0; i < _allContents.length; i++)
+	{
+		if(isExpanded(_allContents[i].textDiv))
+		{
+			toggleTextDiv(_allContents[i]);
+		}
+	}
+	DDM.refreshCache();
+}
+
+//Status Bar class (initialised with new StatusBar(elem);)
+function StatusBar(mainElem)
+{
+	var _statusMap = new Array();
+	var _statusIDCounter = 0;
+	var _mainElem = mainElem;
+	var _activeMessages = 0;
+	
+	_mainElem.style.display = "none";
+	
+	this.addStatus = function(newStatus)
+	{
+		_mainElem.style.display = "block";
+		var newStatusDiv = document.createElement("DIV");
+		var newStatusSpan = document.createElement("SPAN");
+		
+		var workingImage = document.createElement("IMG"); 
+		workingImage.setAttribute("src", gs.imageURLs.loading);
+		workingImage.setAttribute("height", "16px");
+		workingImage.setAttribute("width", "16px");
+		newStatusDiv.appendChild(workingImage);
+		
+		newStatusDiv.appendChild(newStatusSpan);
+		newStatusSpan.innerHTML = " " + newStatus;
+		newStatusDiv.setAttribute("class", "statusMessage");		
+		newStatusDiv.span = newStatusSpan;
+		
+		_mainElem.appendChild(newStatusDiv);
+		_statusMap["status" + _statusIDCounter] = newStatusDiv;
+		_activeMessages++;
+		return _statusIDCounter++;
+	}
+	
+	this.changeStatus = function(id, newStatus)
+	{
+		if(_statusMap["status" + id])
+		{
+			_statusMap["status" + id].span.innerHTML = " " + newStatus;
+		}
+	}
+	
+	this.removeStatus = function(id)
+	{
+		if(_statusMap["status" + id])
+		{
+			removeFromParent(_statusMap["status" + id]);
+			
+			if(--_activeMessages == 0)
+			{
+				_mainElem.style.display = "none";
+			}
+		}
+	}
+}
+
+/*
+function toggleEdit(e)
+{
+	var mousePos = de.events.getXYInWindowFromEvent(e);
+	var cDesc = de.cursor.getCursorDescAtXY(mousePos.x, mousePos.y, de.events.getEventTarget(e));
+	de.cursor.setCursor(cDesc);
+}
+*/
