// ********************************************************************** //
// Visual XML Editor //
// This class represents an editor that allows you to modify XML visually //
// ********************************************************************** //
function visualXMLEditor(xmlString)
{
var _globalID = 0;
var _xml;
var _mainDiv = $("
", {"id":"veMainDiv"});
var _toolboxDiv = $("
", {"id":"veToolboxDiv"});
var _editorContainer = $("
", {"id":"veEditorContainer"});
var _editorDiv = $("
", {"id":"veEditorDiv"});
var _infoDiv = $("
", {"id":"veInfoDiv"});
var _rootElement;
var _selectedElement;
var _validDropSpot = false;
var _validDropType;
var _validDropElem;
var _origDDParent;
var _origDDPosition;
var _overList = new Array();
_overList.freeSpaces = new Array();
var _transactions = new Array();
var _childRestrictions =
{
gsf:
{
"choose-metadata":["gsf:metadata", "gsf:default"],
"metadata":[]
}
};
this.getXML = function()
{
return _xml;
}
this.undo = function()
{
if(_transactions.length > 0)
{
var t = _transactions.pop();
//Undo an added element
if(t.type == "addElem")
{
$(t.vElem.data("parentVEElement").getXMLNode()).remove();
t.vElem.remove();
resizeAll();
}
//Undo a removed or moved element
else if(t.type == "remMvElem")
{
var parent = t.vElemParent;
var pos = t.vElemPos;
var elem = t.vElem;
elem.detach();
if(pos == 0)
{
parent.prepend(elem);
$(parent.parent().data("parentVEElement").getXMLNode()).prepend(elem.data("parentVEElement").getXMLNode());
}
else if(pos == parent.children(".veElement").length)
{
$(parent.children(".veElement").eq(pos - 1).data("parentVEElement").getXMLNode()).after(elem.data("parentVEElement").getXMLNode());
parent.children(".veElement").eq(pos - 1).after(elem);
}
else
{
$(parent.children(".veElement").eq(pos).data("parentVEElement").getXMLNode()).before(elem.data("parentVEElement").getXMLNode());
parent.children(".veElement").eq(pos).before(elem);
}
resizeAll();
}
//Undo an added attribute
else if(t.type == "addAttr")
{
if(t.row)
{
t.row.remove();
}
}
//Undo a removed or edited attribute
else if(t.type == "editAttr")
{
t.elem.removeAttribute(t.newName);
t.elem.setAttribute(t.name, t.value);
if(t.row)
{
t.row.children("td").eq(0).text(t.name);
t.row.children("td").eq(1).text(t.value);
}
}
//Undo a removed or edited attribute
else if(t.type == "remAttr")
{
t.elem.setAttribute(t.name, t.value);
if(t.rowParent)
{
t.rowParent.append(t.row);
}
}
//Undo edited text
else if(t.type == "editText")
{
t.elem.nodeValue = t.value;
if(t.vElem)
{
t.vElem.text(t.value);
}
}
}
}
var checkRestricted = function(child, parent)
{
var pFullNodename = parent.tagName;
var cFullNodename = child.tagName;
var pNamespace;
var pNodeName;
if(pFullNodename.indexOf(":") == -1)
{
pNamespace = "no namespace";
pNodeName = pFullNodename;
}
else
{
pNamespace = pFullNodename.substring(0, pFullNodename.indexOf(":"));
pNodeName = pFullNodename.substring(pFullNodename.indexOf(":") + 1);
}
var namespaceList = _childRestrictions[pNamespace];
if(namespaceList)
{
var childList = namespaceList[pNodeName];
if(childList)
{
for(var i = 0; i < childList.length; i++)
{
if(childList[i] == cFullNodename)
{
return true;
}
}
return false;
}
}
return true;
}
var populateToolbar = function()
{
var elemList =
{
html:["a", "br", "div", "li", "link", "script", "span", "table", "td", "tr", "ul"],
xsl:
[
"apply-imports", "apply-templates", "attribute", "attribute-set", "call-template",
"choose", "copy", "copy-of", "decimal-format", "element",
"fallback", "for-each", "if", "import", "include",
"key", "message", "namespace-alias", "number", "otherwise",
"output", "param", "preserve-space", "processing-instruction", "sort",
"strip-space", "stylesheet", "template", "text", "transform",
"value-of", "variable", "when", "with-param"
],
gsf:
[
"cgi-param", "choose-metadata", "collectionText", "displayItem", "displayText",
"equivlinkgs3", "foreach-metadata", "icon", "if-metadata-exists", "image",
"interfaceText", "link", "meta-value", "metadata", "script",
"style", "switch", "template", "text", "variable"
]
};
var tabHolder = $("
");
_toolboxDiv.append(tabHolder);
for(var key in elemList)
{
var currentList = elemList[key];
var tab = $("
", {"class":"veEditCell"});
var link = $("edit");
link.click(function()
{
var nameCell = _row.children("td").eq(0);
var valueCell = _row.children("td").eq(1);
if(link.text() == "edit")
{
link.text("done");
var nameInput = $("");
nameInput.width(nameCell.width() - 5);
nameInput.val(_name);
var valueInput = $("");
valueInput.width(valueCell.width() - 5);
valueInput.val(_value);
nameCell.text("");
valueCell.text("");
nameCell.append(nameInput);
valueCell.append(valueInput);
}
else
{
link.text("edit");
var nameInput = nameCell.children("input");
var valueInput = valueCell.children("input");
var name = nameInput.val();
var value = valueInput.val();
nameCell.empty();
nameCell.text(name);
valueCell.empty();
valueCell.text(value);
if(nameCell.data("prevName") != "")
{
_xmlElem.removeAttribute(_name);
}
_xmlElem.setAttribute(name, value);
_transactions.push({type:"editAttr", elem:_xmlElem, row:_row, newName:name, name:_name, value:_value});
_name = name;
_value = value;
}
});
cell.append(link);
return cell;
}
var createDeleteCell = function()
{
var cell = $("
", {"class":"veDeleteCell"});
var link = $("delete");
link.click(function()
{
_transactions.push({type:"remAttr", elem:_xmlElem, row:_row, rowParent:_row.parent(), name:_name, value:_value});
_xmlElem.removeAttribute(_name);
_row.detach();
});
cell.append(link);
return cell;
}
this.getAsTableRow = function()
{
var tableRow = $("
");
var attributeName = createNameCell();
tableRow.append(attributeName);
var attributeValue = createValueCell();
tableRow.append(attributeValue);
var editCell = createEditCell()
tableRow.append(editCell);
var deleteCell = createDeleteCell();
tableRow.append(deleteCell);
_row = tableRow;
return tableRow;
}
}
// ********************************************************************************** //
// Visual Editor Element //
// This inner class represents a single xml element or text node in the visual editor //
// ********************************************************************************** //
var VEElement = function(xml)
{
var _div = $("
");
var _xmlNode = xml;
var _id = _globalID++;
_div.data("parentVEElement", this);
_div.data("expanded", "normal");
var makeDraggable = function()
{
_div.draggable(
{
"revert":"true",
"helper":function()
{
//Make sure the cursor is put in the centre of the div
var height = _div.children(".veTitleElement").height();
_div.draggable("option", "cursorAt", {top:(height / 2), left:(_div.width() / 2)});
var tempVEE = new VEElement(_xmlNode);
var tempDiv = tempVEE.getDiv();
tempDiv.css("border", "1px solid orangered");
tempDiv.css("background", "orange");
tempDiv.width(_div.width());
tempDiv.data("helper", true);
return tempDiv;
},
"cursor":"move",
"appendTo":_mainDiv,
"start":function(event, ui)
{
_origDDParent = _div.parent();
_origDDPosition = _div.index();
_div.siblings(".veElement").filter(function(){return !($(this).data("helper"))}).data("expanded", "normal");
_div.css("border", "1px solid orangered");
_div.data("prevBackground", _div.css("background"));
_div.css("background", "orange");
_div.css("float", "left");
_div.data("dragging", true);
if(_div.data("toolbar"))
{
var cloneElem = new VEElement(_xmlNode.cloneNode(true));
var cloneDiv = cloneElem.getDiv();
cloneDiv.css("float", "none");
cloneDiv.data("toolbar", true);
_div.before(cloneDiv);
}
_div.detach();
resizeAll();
},
"drag":function(event, ui)
{
var foundDefined = false;
for(var i = 0; i < _overList.length; i++)
{
if(!(_overList[i] === "undefined"))
{
foundDefined = true;
}
}
_validDropSpot = false;
if(foundDefined)
{
var overElement = getDeepestOverElement();
if(overElement && overElement.getXMLNode().nodeType != 3 && checkRestricted(_xmlNode, overElement.getXMLNode()))
{
_validDropSpot = true;
var overDiv = overElement.getDiv();
var overLeft = overDiv.offset().left;
var helperLeft = ui.helper.offset().left;
var helperMiddle = helperLeft + (ui.helper.width() / 2);
var overContainers = overDiv.children(".veContainerElement");
if(!overContainers.length)
{
overDiv.append($("
", {"class":"veContainerElement"}));
overContainers = overDiv.children(".veContainerElement");
}
var overChildren = overContainers.children(".veElement").filter(function(){return !($(this).data("helper")) && !(_div.data("parentVEElement").getID() == $(this).data("parentVEElement").getID())});
var overChildrenLength = overChildren.length + 1;
if(!overChildren.length)
{
_validDropElem = overDiv;
_validDropType = "into";
overContainers.append(_div);
}
else
{
var posPercent = (helperMiddle - overLeft) / overDiv.width();
if(posPercent < 0)
{
posPercent = 0;
}
else if(posPercent > 1)
{
posPercent = 1;
}
var pos = Math.floor(overChildrenLength * posPercent);
if(pos < overChildrenLength - 1)
{
_validDropElem = overChildren.eq(pos);
_validDropType = "before";
overChildren.eq(pos).before(_div);
}
else
{
_validDropElem = overChildren.eq(pos - 1);
//Necessary to fix a rare bug that causes pos to be off by one
if(!_validDropElem.length)
{
_validDropElem = overChildren.eq(pos - 2);
}
_validDropType = "after";
overChildren.eq(pos - 1).after(_div);
}
}
overChildren.data("expanded", "normal");
_div.data("expanded", "normal");
resizeAll();
}
}
},
"stop":function(event)
{
var transactionType = (_div.data("toolbar")) ? "addElem" : "remMvElem";
_div.data("dragging", false);
_div.data("toolbar", false);
_div.css("border", "1px dashed black");
_div.css("background", _div.data("prevBackground"));
//If the element was not dropped in a valid place then put it back
if(!_validDropSpot)
{
_div.detach();
if(_origDDPosition == 0)
{
_origDDParent.prepend(_div);
}
else if(_origDDPosition == _origDDParent.children(".veElement").length)
{
_origDDParent.children(".veElement").eq(_origDDPosition - 1).after(_div);
}
else
{
_origDDParent.children(".veElement").eq(_origDDPosition).before(_div);
}
resizeAll();
}
//Otherwise modify the XML
else
{
var xmlNode = _validDropElem.data("parentVEElement").getXMLNode();
if(_validDropType == "before")
{
$(xmlNode).before(_xmlNode);
}
else if (_validDropType == "after")
{
$(xmlNode).after(_xmlNode);
}
else if (_validDropType == "into")
{
$(xmlNode).append(_xmlNode);
}
_transactions.push({type:transactionType, vElemParent:_origDDParent, vElemPos:_origDDPosition, vElem:_div});
}
_overList = new Array();
_overList.freeSpaces = new Array();
}
});
_div.droppable(
{
"over":function(event, ui)
{
addToOverList($(this).data("parentVEElement"));
event.stopPropagation();
},
"out":function(event)
{
removeFromOverList($(this).data("parentVEElement"));
event.stopPropagation();
}
});
}
this.getDiv = function()
{
return _div;
}
this.getXMLNode = function()
{
return _xmlNode;
}
this.getID = function()
{
return _id;
}
var createTableHeader = function()
{
var tableHeader = $("