﻿//=====================================================================================
//                                      xml_layout.js
//                                      =============
// holds all variables and methods needed for the specific layout xml manipulations.
// each page can use more then one layout xml thus will use different layout objects.
// NOTE: must be included after browsercheck.js & layer.js
// NOTE: if isSaveAll option is enabled file must be included within the body tags
//=====================================================================================

// global variables
XMLLayout.lookUpTable = new Array() // holds all layout objects by name
XMLLayout.Actions = new Array()     // holds data about last actions for undoing and redoing
XMLLayout.pLastAction = -1			// pointer to last entry in the last actions array
XMLLayout.editedObj = null          // layout being edited
XMLLayout.undoCounter = 0           // number of possible undos
XMLLayout.redoCounter = 0           // number of possible redos
XMLLayout.MAXUNDO = 2               // maximum number of allowable undos
XMLLayout.isSaveAll = true          // save all xml objects option
XMLLayout.editmode = 0
XMLLayout.DEBUG = false
XMLLayout.DEBUGwhich = 19

// write save all form's container
if (XMLLayout.isSaveAll) {
    var html = '<div id="XMLLayoutSaveAllDiv" style="position:absolute;visibility:hidden;"></div>'
    document.write(html)
    XMLLayout.saveAllDiv = new Layer('XMLLayoutSaveAllDiv')
    XMLLayout.saveAllDiv.hide()
	XMLLayout.onsave = null
}

// the layout object constructor
// =============================
function XMLLayout(
    xslFilePath     // path is relative to the file using the xml
    ,xmlFilePath    // path is relative to the file using the xml
    ,adminmode      // optional - if 1 user is admin
    ,hasPermissions // optional - if true user has permissions to edit layout xml
    )
{
    // properties
    this.name = null    // object's name
    this.xslObj = null  // holde the stylesheet
    this.xmlObj = null  // holds the xml dom
    this.sectionId = -1 // a unique id for the layout used to check admin user permissions
    this.container = null     // div object that holds the layout's html
    this.xslFile = xslFilePath
    this.xmlFile = xmlFilePath
    this.adminmode = (adminmode == 1)
    this.hasPermissions = (hasPermissions == true)
    this.editable = false               // boolean indicating whether xml is editable
    this.editDetails = null         // holds data of current action
    this.lastActions = new Array()  // holds data needed for undoing and redoing
    this.pLastAction = -1           // pointer to the last entry in the last actions array
    this.active = false // indicates whether element been initialized
    // events
    this.onset = null
    this.onsetedit = null
    this.onundo = null
    this.onredo = null
    this.onsave = null
}

// layout object methods
// =====================

// initialize layout properties
function XMLLayoutInit(
    editable    // optional - indicates whether xml is to be set as editable on init
    ,container  // optional - id of the div to write layout's html to. if null container is document
	,inline		// optional - boolean indicating whether html written in automatically. true by default
    )
{
    if (!this.adminmode) // user is not admin
        return
    
    // initialize xml & xsl objects
    this.xslObj = XMLLayout.loadXML(this.xslFile)
    this.xmlObj = XMLLayout.loadXML(this.xmlFile)
    
    // set editable tag
    this.editable = (editable == true)
    this.xmlObj.documentElement.selectSingleNode('PARAMETERS/EDITABLE').text = (this.editable)? '1': '0'
    
    // set sectionId and object's name
    this.sectionId = this.xmlObj.documentElement.selectSingleNode('PARAMETERS/SECTIONID').text
    if (this.sectionId < 0) {
        alert('Error: XML section id is invalid')
        return
    }
    this.name = 'layout' + this.sectionId
	// verify name is unique
	if (XMLLayout.lookUpTable[this.name]) {
        alert('Error: XML section id already exists')
        return
	}
	
    // initialize the container object
    this.container = (!container)? document: new Layer(container)
    var html
    html = '<div id="' + this.name + 'HtmlDiv" style="position:relative"></div>' // div to write transformed xml to
    html += '<div id="' + this.name + 'FormDiv" style="position:absolute;visibility:hidden;"></div>' // div to write xml form to
    this.container.write(html)
    this.container = new Layer(this.name + 'HtmlDiv',container)
    
    var formdiv = new Layer(this.name + 'FormDiv',container)
	formdiv.hide()
    html = '<form name="' + this.name + 'Form" action="xmlSave.asp" method="post">' + 
           '<input type="hidden" name="editmode" value="' + (this.editable? 1: 0) + '">' + 
           '<input type="hidden" name="layouts" value="' + this.name + '">' + 
           '<input type="hidden" name="' + this.name + '_file" value="' + this.xmlFile + '">' + 
           '<textarea name="' + this.name + '_xml" cols="1" rows="1">' + this.xmlObj.documentElement.xml + '</textarea>' + 
           '</form>'
    formdiv.write(html)
    this.xmlForm = formdiv.doc.forms[this.name + 'Form']
    
    // initlaize editDetails variables
    this.editDetails = new Object()
    this.editDetails.elmId = -1            // the selected element's unique id
    this.editDetails.editNode = null    // the node to be edited
    this.editDetails.replaceNode = false// indicates whether node itself is to be replaced
    this.editDetails.generator = null   // if node itself is to be replaced then generator is url of the xml generator page
    this.editDetails.lastData = null    // the selected element's node's last data for undo
    this.editDetails.newData = null     // the selected element's node's new data for redo and undo replace
    this.editDetails.position = 0       // needed for add action
    this.editor = null      // editor window object
    this.active = true      // flag object as active
	
    // save object to look up table
    XMLLayout.lookUpTable[this.name] = this
    XMLLayout.lookUpTable.length++

    // write layout transformed xml
    if (inline != false && inline != 0)
		this.write()
}
XMLLayout.prototype.init = XMLLayoutInit

// set xml editable mode
function XMLLayoutSetEditable(bool)
{
    if (!this.active)
        return
    if (!this.adminmode) // user is not admin
        return
    if (this.editable == bool) // nothing to do
        return
    this.editable = bool
    this.xmlObj.documentElement.getElementsByTagName('PARAMETERS/EDITABLE')(0).text = (this.editable)? '1': '0'
    this.write()
    if (this.onsetedit) // execute event handler
        eval(this.onsetedit)
}
XMLLayout.prototype.setEditable = XMLLayoutSetEditable

// set any node's text and rewrite it - regardless if xml is editable or not 
function XMLLayoutSimpleSetNode(
    nodetag // must specify a single node
    ,val
    )
{
    if (!this.active)
        return
    if (!nodetag) {
        alert('Error: node tag must be specified')
        return
    }
    var node
    if (!(node = this.xmlObj.documentElement.selectSingleNode(nodetag))) {
        alert('Error: XML node ' + nodetag + ' not found')
        return
    }
    node.text = val
    
    this.write()
}
XMLLayout.prototype.simpleSetNode = XMLLayoutSimpleSetNode

// replace any node and rewrite xml 
function XMLLayoutSimpleReplaceNode(
    nodetag // must specify a single node
    ,newNode
    )
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (!this.hasPermissions) { // user has no permissions to edit
        alert('אין לך הרשאות לערוך אזור תוכן זה')
        return
    }
    if (!nodetag) {
        alert('Error: node tag must be specified')
        return
    }
    var node
    if (!(node = this.xmlObj.documentElement.selectSingleNode(nodetag))) {
        alert('Error: XML node ' + nodetag + ' not found')
        return
    }
	var parent = node.parentNode
    parent.replaceChild(newNode,node)
    
    this.write()
}
XMLLayout.prototype.simpleReplaceNode = XMLLayoutSimpleReplaceNode

// write layout's html
function XMLLayoutWrite()
{
    if (XMLLayout.DEBUG && this.sectionId == XMLLayout.DEBUGwhich){
        pop = window.open('','debug')
        pop.document.open()
        pop.document.write(this.xmlObj.documentElement.xml)
        pop.document.close()
    }
    if (!this.active)
        return
    var html = this.xmlObj.transformNode(this.xslObj)
    this.container.write(html)
}
XMLLayout.prototype.write = XMLLayoutWrite

// set selected element properties when element is to be edited or repleced
function XMLLayoutEditElement(
    elmId           // the selected node's element anccestor's id - if null nodetag must specify the path to the edited node
    ,nodetag        // required if elmId is null - the selected node's tag name - if ommited element's node is edited
    ,replaceNode    // optional - indicates whether node itself is to be replaced - false by default
    ,editortype     // optional - an attribute corresponding with an editot tag in the xml
                    // if not specified then editor assumed to be specified in selected node's attributes
    )
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (!this.hasPermissions) { // user has no permissions to edit
        alert('אין לך הרשאות לערוך אזור תוכן זה')
        return
    }
    
    // validate the selected node and editing tools
    if (elmId == null && nodetag == null) {
        alert('Error: XML either element\'s id or selected node must be specified')
        return
    }
    
    if (elmId != null) {
        // element's id can be either a non-negative integer or a non-empty string
        // note that !isNaN('') == true and isNaN(parseInt('')) == true
        if (!isNaN(elmId) && (isNaN(parseInt(elmId)) || elmId < 0)) {
            alert('Error: XML element\'s invalid id ' + elmId)
            return
        }
        var elm
        if (!(elm = this.xmlObj.documentElement.getElementsByTagName('ELEMENT[@ID=' + elmId + ']'))) {
            alert('Error: XML element ' + elmId + ' not found')
            return
        }
        else if (!(elm = elm.item(0))) {
            alert('Error: XML element ' + elmId + ' not found')
            return
        }
    }
    else
        elm = this.xmlObj.documentElement
    
    var node
    if (nodetag) { // one of element's child being edited
        if (!(node = elm.getElementsByTagName(nodetag))) {
            alert('Error: XML node ' + nodetag + ' not found in element ' + elmId)
            return
        }
        if (!(node = node.item(0))) {
            alert('Error: XML node ' + nodetag + ' not found in element ' + elmId)
            return
        }
    }
    else { // element node's being edited
        node = elm
        nodetag = 'ELEMENT ' + elmId
    }
    
    var editor = null
    if (editortype == null)
        editortype = node.getAttribute('EDITOR')
	
	if (editortype) {
	    // node's editor type can be either an integer or a non-empty string
	    // note that !isNaN('') == true and isNaN(parseInt('')) == true
	    if (!isNaN(editortype) && isNaN(parseInt(editortype))) {
	        alert('Error: XML node ' + nodetag + ', in element ' + elmId + ', cannot be edited')
	        return
	    }
	    else if (isNaN(editortype)) // editor type is a string
	        editortype = '"' + editortype + '"'
	    
	    if (!(editor = this.xmlObj.documentElement.getElementsByTagName('PARAMETERS/EDITOR[@TYPE=' + editortype + ']')) || !(editor = editor.item(0))) {
	        alert('Error: XML node ' + nodetag + '\'s editor not found')
	        return
	    }
	}
    
    // editor is a prompt
    var prmpt, data
    if (editor && (prmpt = editor.selectSingleNode('PROMPT'))) {
        data = prompt(prmpt.text,node.firstChild.text)
        if (data) {
            // set variables
            this.editDetails.elmId = elmId
            this.editDetails.editNode = node
            this.editDetails.lastData = node.text
            this.editDetails.replaceNode = false
            this.editDetails.generator = null
            this.setNode(data)
            // save this object in a global variable for the editor window
            XMLLayout.editedObj = this
        }
        return
    }
    
    // set variables
    replaceNode = (replaceNode == true)
    var lastData, editNode, generator = null
    //alert(node.text)
    if (replaceNode) { // edited node is to be replaced
        if (editor && (generator = editor.selectSingleNode('GENERATOR')) && !generator.text) {
            alert('Error: XML node ' + nodetag + '\'s generator not found')
            return
        }
        lastData = node
        editNode = node.parentNode  // edited node is the node's (to be replaced) parent
        if (generator) generator = generator.text // the url of the page that generates new node's xml
    }
    else { // edited node's text is to be edited
        editNode = node
        lastData = editNode.text
    }
    
    // save current edit details
    this.editDetails.elmId = elmId
    this.editDetails.editNode = editNode
    this.editDetails.lastData = lastData
    this.editDetails.refNode = null
    this.editDetails.replaceNode = replaceNode
    this.editDetails.generator = generator
    
    // save this object in a global variable for the editor window
    XMLLayout.editedObj = this
	
    // open editor
    if (editor && this.openEditor(editor,nodetag) != 0)
        return 
}
XMLLayout.prototype.editElement = XMLLayoutEditElement

// set editDetails object's properties to enable adding new node in the selected node's
// parent's collection.
// works similar to the editElement() method
function XMLLayoutAddElement(
    elmId           // the selected node's element anccestor's id - if null nodetag must specify the path to the edited node
    ,refNodetag     // required if elmId is null - the selected node's tag name - if ommited element's node is edited
    ,position       // optional - an integer indicating where node is to be placed
                    // if not specified then position assumed as 0 i.e in begining
    ,editortype     // optional - an attribute corresponding with an editot tag in the xml
                    // if not specified then editor assumed to be specified in selected node's attributes
    )
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (!this.hasPermissions) { // user has no permissions to edit
        alert('אין לך הרשאות לערוך אזור תוכן זה')
        return
    }
    
    // validate arguments
    if (elmId == null && refNodetag == null) {
        alert('Error: either element\'s id or selected node must be specified')
        return
    }
    if (isNaN(position) || position < 0) {
        alert('Error: XMLLayoutAddElement invalid argument - position')
        return
    }
    
    if (elmId != null) {
        // element's id can be either a non-negative integer or a non-empty string
        // note that !isNaN('') == true and isNaN(parseInt('')) == true
        if (!isNaN(elmId) && (isNaN(parseInt(elmId)) || elmId < 0)) {
            alert('Error: XML element\'s invalid id ' + elmId)
            return
        }
        var elm
        if (!(elm = this.xmlObj.documentElement.getElementsByTagName('ELEMENT[@ID=' + elmId + ']'))) {
            alert('Error: XML element ' + elmId + ' not found')
            return
        }
        else if (!(elm = elm.item(0))) {
            alert('Error: XML element ' + elmId + ' not found')
            return
        }
    }
    else
        elm = this.xmlObj.documentElement
    
    var refNode
    if (refNodetag) { // one of element's child being edited
        if (!(refNode = elm.getElementsByTagName(refNodetag))) {
            alert('Error: XML node ' + refNodetag + ' not found in element ' + elmId)
            return
        }
        if (!(refNode = refNode.item(0))) {
            alert('Error: XML node ' + refNodetag + ' not found in element ' + elmId)
            return
        }
    }
    else { // element node's being edited
        refNode = elm
        refNodetag = 'ELEMENT ' + elmId
    }
    
    if (editortype == null) {
        if ((editortype = refNode.getAttribute('ADDEDITOR')) == null) {
            alert('Error: XML cannot add node')
            return
        }
    }
    if (isNaN(editortype)) // editortype is a string
        editortype = '"' + editortype + '"'
    
    var editor
    if (!(editor = this.xmlObj.documentElement.getElementsByTagName('PARAMETERS/EDITOR[@TYPE=' + editortype + ']')) || !(editor = editor.item(0))) {
        alert('Error: XML node ' + refNodetag + '\'s editor not found')
        return
    }
    
    var parent = refNode.parentNode
    
    // get maximum of elements allowed in the collection
    var maxadd
    if (!(maxadd = parent.selectSingleNode('PARAMETERS/MAXADD'))) {
        alert('Error: XML node ' + refNodetag + '\'s MAXADD not found')
        return
    }
    if (isNaN(maxadd = parseInt(maxadd.text))) {
        alert('Error: XML node ' + refNodetag + '\'s MAXADD is invalid')
        return
    }
    
    // get the number of elements in the collection
    var nelmsNode, nelms
    if (!(nelmsNode = parent.selectSingleNode('PARAMETERS/NELEMENTS'))) {
        alert('Error: XML node ' + refNodetag + '\'s NELEMENTS not found')
        return
    }
    if (isNaN(nelms = parseInt(nelmsNode.text))) {
        alert('Error: XML node ' + refNodetag + '\'s NELEMENTS is invalid')
        return
    }
    
    // check whether it is allowed to add a new node
    if (nelms >= maxadd) {
        alert('!לא ניתן להוסיף אלמנטים נוספים באזור זה')
        return
    }
    
    // get generator url
    var generator = null
    if ((generator = editor.selectSingleNode('GENERATOR')) && !generator.text) {
        alert('Error: XML node ' + refNodetag + '\'s generator not found')
        return
    }
    
    // save current edit details
    this.editDetails.elmId = elmId
    this.editDetails.editNode = parent // edited node is the node's (to be replaced) parent
    this.editDetails.lastData = null
    this.editDetails.refNode = refNode
    this.editDetails.replaceNode = true
    this.editDetails.generator = generator
    this.editDetails.position = !isNaN(position)? position: 0
    
    // save this object in a global variable for the editor window
    XMLLayout.editedObj = this
    
    // open editor
    if (this.openEditor(editor,refNodetag) != 0)
        return
}
XMLLayout.prototype.addElement = XMLLayoutAddElement

// set selected element properties when element is to be deleted
function XMLLayoutDeleteElement(
    elmId           // the selected node's element anccestor's id - if null nodetag must specify the path to the edited node
    ,nodetag        // required if elmId is null - the selected node's tag name - if ommited element's node is edited
    )
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (!this.hasPermissions) { // user has no permissions to edit
        alert('אין לך הרשאות לערוך אזור תוכן זה')
        return
    }
    
    // validate the selected node and editing tools
    if (elmId == null && nodetag == null) {
        alert('Error: XML either element\'s id or selected node must be specified')
        return
    }
    
    if (elmId != null) {
        // element's id can be either a non-negative integer or a non-empty string
        // note that !isNaN('') == true and isNaN(parseInt('')) == true
        if (!isNaN(elmId) && (isNaN(parseInt(elmId)) || elmId < 0)) {
            alert('Error: XML element\'s invalid id ' + elmId)
            return
        }
        var elm
        if (!(elm = this.xmlObj.documentElement.getElementsByTagName('ELEMENT[@ID=' + elmId + ']'))) {
            alert('Error: XML element ' + elmId + ' not found')
            return
        }
        else if (!(elm = elm.item(0))) {
            alert('Error: XML element ' + elmId + ' not found')
            return
        }
    }
    else
        elm = this.xmlObj.documentElement
    
    var node
    if (nodetag) { // one of element's child being edited
        if (!(node = elm.getElementsByTagName(nodetag))) {
            alert('Error: XML node ' + nodetag + ' not found in element ' + elmId)
            return
        }
        if (!(node = node.item(0))) {
            alert('Error: XML node ' + nodetag + ' not found in element ' + elmId)
            return
        }
    }
    else { // element node's being edited
        node = elm
        nodetag = 'ELEMENT ' + elmId
    }
    
    var parent = node.parentNode
	
    // get the number of elements in the collection
    var nelmsNode, nelms
    if (!(nelmsNode = parent.selectSingleNode('PARAMETERS/NELEMENTS'))) {
        alert('Error: XML node ' + nodetag + '\'s NELEMENTS not found')
        return
    }
    if (isNaN(nelms = parseInt(nelmsNode.text))) {
        alert('Error: XML node ' + nodetag + '\'s NELEMENTS is invalid')
        return
    }
    
    // check whether it is allowed to add a new node
    if (nelms <= 0) {
        alert('!לא ניתן למחוק אלמנטים נוספים באזור זה')
        return
    }
    
    // save current edit details
    this.editDetails.elmId = elmId
    this.editDetails.editNode = parent // edited node is the node's (to be replaced) parent
    this.editDetails.lastData = node
    this.editDetails.refNode = null
    this.editDetails.replaceNode = true
    this.editDetails.generator = null
    this.editDetails.position = 0 //this.getPosition(node)
    
    // save this object in a global variable for the editor window
    XMLLayout.editedObj = this
	
	// delete node
	this.deleteNode()
}
XMLLayout.prototype.deleteElement = XMLLayoutDeleteElement

// open editor window
function XMLLayoutOpenEditor(editorNode,nodetag)
{
    if (!this.active)
        return -1
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return -1
    if (!editorNode)
        return -1
        
    if(this.editDetails.editNode.nodeName == 'LAYOUT' && this.editDetails.editNode.attributes.getNamedItem("LAYUOTID"))
		var layoutid = this.editDetails.editNode.attributes.getNamedItem("LAYUOTID").value

    var url, dimm, pos
    if (!(url = editorNode.selectSingleNode('URL'))) {
        alert('Error: XML node' + nodetag + '\'s editor not found')
        return -1
    }
    url = url.text
    if(layoutid)url += (url.indexOf('?') == -1? '?': '&') + "layoutid="+layoutid
    //url += (url.indexOf('?') == -1? '?': '&') + 'xml=' + this.xmlFile + '&elmId=' + elmId + '&nodetag=' + nodetag
	
    if (dimm = editorNode.selectSingleNode('DIMM'))
        dimm = dimm.text
    if (pos = editorNode.selectSingleNode('POS'))
        pos = pos.text
    
    var w, h, params = ''
    // set pop up width & height
    if (!dimm) {
        w = 400
        h = 200
        params += 'width=' + w + ',height=' + h
    }
    else {
        params += dimm
        w = parseInt(dimm.split('width=')[1])
        h = parseInt(dimm.split('height=')[1])
    }
    // set pop up position
    if (!pos) {
        var winleft = screen.width/2 - w/2
	    var wintop = screen.height/2 - h/2 - 30
        params +=  ',top=' + wintop + ',left=' + winleft
    }
    else
        params += pos
    
    params += ',scrollbars=no,menubar=no,resizable=yes,toolbar=no,location=no,status=yes'
    
    var win 
    if (!(win = window.open(url,'editor',params))) {
        alert('Error: XML node ' + nodetag + '\'s editor could not be opened')
        return -1
    }
    
    win.focus()
    return 0
}
XMLLayout.prototype.openEditor = XMLLayoutOpenEditor

// set selected node. if replaceNode property is true
// data is used as a parameter to an xml generator that returns a new node
function XMLLayoutSetNode(
    newData // data text or an xml node
    ,position // optional - set the edited nodes position in its parent collection
    )
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (this.editDetails.elmId < 0) // no node been selected
        return
    this.editDetails.elmId = -1
    
    var editedNode = this.editDetails.editNode
    
    // edit and redisplay xml
    if (this.editDetails.replaceNode) { // replace node
        var oldNode = this.editDetails.lastData, newNode
        
       var isBig = oldNode.getAttribute("TYPE")
       if( isBig == null ){
        isBig = 'SMALL' }
               
        // get xml element
        if (this.editDetails.generator) { // generate xml node where new data is an item's id
			//alert(this.editDetails.generator + (this.editDetails.generator.indexOf('?') == -1? '?': '&') + 'id=' + newData + '&size=' + isBig)
            var xmlObj = XMLLayout.loadXML(this.editDetails.generator + (this.editDetails.generator.indexOf('?') == -1? '?': '&') + 'id=' + newData + '&size=' + isBig)
			newNode = xmlObj.documentElement
            if (!newNode) {
				alert('Error: XML Generator returned an ilegal value') 
				return
			}			
            // set new node's attributes to have same values as the old node
            for (var i = 0; i < oldNode.attributes.length; i++) {
                newNode.setAttribute(oldNode.attributes(i).baseName,oldNode.attributes(i).text)
            }
            newData = newNode
        }
        else if (newData.xml) // new data is an xml node
            newNode = newData
        else // error
            return
      //  alert( 'old node : \n\r' + oldNode.xml + '\n\r' + 'new node : \n\r' + newNode.xml+ '\n\r' + 'newData : \n\r' + newData.xml);
        editedNode.replaceChild(newNode,oldNode)
    }
    else // replace node's text
        editedNode.text = newData
    
    // set node's position
    var curPosition = null
    var newPosition = null
    if (!isNaN(position)) {
        curPosition = this.getPosition(newNode)
        newPosition = position
        if (this.setPosition(newNode,position) != 0)
            return
    }    
    // insert new action
    this.insertAction(editedNode,this.editDetails.lastData,newData,'edit',this.editDetails.replaceNode,curPosition,newPosition)
    
	this.write()
    if (XMLLayout.undoCounter < XMLLayout.MAXUNDO)
        XMLLayout.undoCounter++
    XMLLayout.redoCounter = 0
    if (this.onset) // execute event handler
        eval(this.onset)
}
XMLLayout.prototype.setNode = XMLLayoutSetNode

// add a new node relatively to the selected node and of same structure
// the editDetails.replaceNode property must be set to true
function XMLLayoutAddNode(
    newData // new xml node to be added or item id to generate the new node
    ,position // optional - an integer indicating where node is to be placed
            // if not specified then taken from the editDetails properties
    )
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (this.editDetails.elmId < 0) // no node been selected
        return
    if (!this.editDetails.replaceNode) // not in replace node mode
        return
    this.editDetails.elmId = -1
    
    // get new node
    var refNode = this.editDetails.refNode
    var newNode
    if (this.editDetails.generator) { // generate xml node where new data is an item's id
        var xmlObj = XMLLayout.loadXML(this.editDetails.generator + (this.editDetails.generator.indexOf('?') == -1? '?': '&') + 'id=' + newData)
        newNode = xmlObj.documentElement
        
        // set new node's attributes to have same values as the old node
        for (var i = 0; i < oldNode.attributes.length; i++) {
            newNode.setAttribute(refNode.attributes(i).baseName,oldNode.attributes(i).text)
        }
        newData = newNode
    }
    else if (newData.xml) // new data is an xml node
        newNode = newData
    else // error
        return
        
    var parent = this.editDetails.editNode
    if (position == null) position = this.editDetails.position
        
    // add the new node
    if (this.doAdd(newNode,parent,position) != 0)
        return
    
    // insert new action
    this.insertAction(parent,null,newNode,'add',true,position,position)
    
	this.write()
    if (XMLLayout.undoCounter < XMLLayout.MAXUNDO)
        XMLLayout.undoCounter++
    XMLLayout.redoCounter = 0
    if (this.onset) // execute event handler
        eval(this.onset)
}
XMLLayout.prototype.addNode = XMLLayoutAddNode

// delete the selected node
// the editDetails.replaceNode property must be set to true
function XMLLayoutDeleteNode()
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user not admin or layout not editable
        return
    if (this.editDetails.elmId < 0) // no node been selected
        return
    if (!this.editDetails.replaceNode) // not in replace node mode
        return
    this.editDetails.elmId = -1
    
    var node = this.editDetails.lastData
    
    var curPosition = this.getPosition(node)
    // delete the node
    if (this.doDelete(node) != 0)
        return
    
    // insert new action
    this.insertAction(this.editDetails.editNode,node,null,'delete',true,curPosition,curPosition)
    
	this.write()
    if (XMLLayout.undoCounter < XMLLayout.MAXUNDO)
        XMLLayout.undoCounter++
    XMLLayout.redoCounter = 0
    if (this.onset) // execute event handler
        eval(this.onset)
}
XMLLayout.prototype.deleteNode = XMLLayoutDeleteNode

// undo last editing
function XMLLayoutUndo()
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user has no permissions to edit
        return
    if (XMLLayout.undoCounter == 0) // nothing to undo
        return
    // get last action
    var lastAction = this.lastActions[this.pLastAction % XMLLayout.MAXUNDO]
    this.pLastAction--
    
    // edit and redisplay xml
    if (lastAction.replaceNode) { // replace node
        if (lastAction.type == 'add') // a new node been added in last action
            this.undoAdd(lastAction.newData)
        else if (lastAction.type == 'delete') // a node been deleted
            this.undoDelete(lastAction.lastData,lastAction.editNode,lastAction.lastPosition)
        else // a node been replaced in last action
            lastAction.editNode.replaceChild(lastAction.lastData,lastAction.newData)
        if (lastAction.lastPosition != lastAction.newPosition)
            this.setPosition(lastAction.lastData,lastAction.lastPosition)
    }
    else { // replace node's text
        lastAction.editNode.text = lastAction.lastData
        if (lastAction.lastPosition != lastAction.newPosition)
            this.setPosition(lastAction.editNode,lastAction.lastPosition)
    }
    
	this.write()
    XMLLayout.undoCounter--
    if (XMLLayout.redoCounter < XMLLayout.MAXUNDO)
        XMLLayout.redoCounter++
    if (this.onundo) // execute event handler
        eval(this.onundo)
}
XMLLayout.prototype.undo = XMLLayoutUndo

// redo last editing
function XMLLayoutRedo()
{
    if (!this.active)
        return
    if (!this.adminmode || !this.editable) // user has no permissions to edit
        return
    if (XMLLayout.redoCounter == 0) // nothing to redo
        return
    // get last action
    this.pLastAction++
    var lastAction = this.lastActions[this.pLastAction % XMLLayout.MAXUNDO]
    
    // edit and redisplay xml
    if (lastAction.replaceNode) { // replace node
        if (lastAction.type == 'add') // a new node been added in last action
            this.doAdd(lastAction.newData,lastAction.editNode,lastAction.newPosition,true)
        else if (lastAction.type == 'delete') // a node been deleted
            this.doDelete(lastAction.lastData)
        else // a node been replaced in last action
            lastAction.editNode.replaceChild(lastAction.newData,lastAction.lastData)
        if (lastAction.lastPosition != lastAction.newPosition)
            this.setPosition(lastAction.newData,lastAction.newPosition)
    }
    else { // replace node's text
        lastAction.editNode.text = lastAction.newData
        if (lastAction.lastPosition != lastAction.newPosition)
            this.setPosition(lastAction.editNode,lastAction.newPosition)
    }
	this.write()
    if (XMLLayout.undoCounter < XMLLayout.MAXUNDO)
        XMLLayout.undoCounter++
    XMLLayout.redoCounter--
    if (this.onredo) // execute event handler
        eval(this.onredo)
}
XMLLayout.prototype.redo = XMLLayoutRedo

// add new node to parent collection in specified position
function XMLLayoutDoAdd(
    newNode     // new node to be added
    ,parent     // the parent to which node is added to
    ,position   // the position to be placed in the parent's collection
    ,hasId      // optional - boolean indicating whether new node is to be given new id
                // false by default. true in case node is to be readded or undo deleted
    )// equals the number of elements in the collection
{
    hasId = (hasId == true)
    var nodetag = newNode.tagName
	
    // get maximum of elements allowed in the collection
    var maxadd
    if (!(maxadd = parent.selectSingleNode('PARAMETERS/MAXADD'))) {
        alert('Error: XML node ' + nodetag + '\'s MAXADD not found')
        return -1
    }
    if (isNaN(maxadd = parseInt(maxadd.text))) {
        alert('Error: XML node ' + nodetag + '\'s MAXADD is invalid')
        return -1
    }
    
    // get number of elements in the parent's collection
    var nelmsNode, nelms
    if (!(nelmsNode = parent.selectSingleNode('PARAMETERS/NELEMENTS'))) {
        alert('Error: XML node ' + nodetag + '\'s NELEMENTS not found')
        return -2
    }
    if (isNaN(nelms = parseInt(nelmsNode.text))) {
        alert('Error: XML node ' + nodetag + '\'s NELEMENTS is invalid')
        return -2
    }
    // check whether it is allowed to add a new node
    if (nelms >= maxadd) {
        alert('!לא ניתן להוסיף אלמנטים נוספים באזור זה')
        return -3
    }
       
    nelms++ // increment number of elements
    nelmsNode.text = nelms // set number of elements node's text
    
    if (!hasId) {
        // get maximum id of nodes in the collection - for new id
        var maxidNode, maxid
        if (!(maxidNode = parent.selectSingleNode('PARAMETERS/MAXID'))) {
            alert('Error: XML node ' + nodetag + '\'s MAXID not found')
            return -2
        }
        if (isNaN(maxid = parseInt(maxidNode.text))) {
            alert('Error: XML node ' + nodetag + '\'s MAXADD is invalid')
            return -2
        }
    
        maxid++ // increment max id
        
        //remember the layout id
        if(newNode.baseName == 'LAYOUT')
			newNode.setAttribute('LAYUOTID',newNode.attributes.getNamedItem('ID').value)   		  
        
        newNode.setAttribute('ID',maxid) // set id attribute
        maxidNode.text = maxid // set maxid node's text        
    }
    
    // add node
    parent.appendChild(newNode)    
    return this.setPosition(newNode,position)
}
XMLLayout.prototype.doAdd = XMLLayoutDoAdd

// undo add new node
function XMLLayoutUndoAdd(newNode)
{
    var parent = newNode.parentNode
    var nodetag = newNode.tagName
    
    // get number of elements in the parent's collection
    var nelmsNode, nelms
    if (!(nelmsNode = parent.selectSingleNode('PARAMETERS/NELEMENTS'))) {
        alert('Error: XML node ' + nodetag + '\'s NELEMENTS not found')
        return -1
    }
    if (isNaN(nelms = parseInt(nelmsNode.text))) {
        alert('Error: XML node ' + nodetag + '\'s NELEMENTS is invalid')
        return -1
    }
    
    nelms-- // decrement number of elements
    nelmsNode.text = nelms // set number of elements node's text
    parent.removeChild(newNode)
    return 0
}
XMLLayout.prototype.undoAdd = XMLLayoutUndoAdd

// delete node
function XMLLayoutDoDelete(node)
{
    return this.undoAdd(node)
}
XMLLayout.prototype.doDelete = XMLLayoutDoDelete

// undo delete node
function XMLLayoutUndoDelete(node,parent,position)
{
    return this.doAdd(node,parent,position,true)
}
XMLLayout.prototype.undoDelete = XMLLayoutUndoDelete

// set node's position
function XMLLayoutSetPosition(node,position)
{
    if (isNaN(position) || position < 0) {
        alert('Error: XMLLayoutSetPosition invalid argument - position')
        return -1
    }
    
    var parent, elements
    parent = node.parentNode
    parent.removeChild(node)
    elements = parent.getElementsByTagName(node.baseName + '[@DISPLAY="YES"]') // get displayed nodes in parent's collection
    
    if (position >= elements.length) // assume appending to parents collection
        parent.appendChild(node)
    else
        parent.insertBefore(node,elements.item(position))
    return 0
}
XMLLayout.prototype.setPosition = XMLLayoutSetPosition

// return node's position
function XMLLayoutGetPosition(node)
{
    var parent, elements
    parent = node.parentNode
    elements = parent.getElementsByTagName(node.baseName + '[@DISPLAY="YES"]') // get displayed nodes in parent's collection
    for (i = 0; i < elements.length; i++) {
        if (elements.item(i) == node)
            break
    }
    return i
}
XMLLayout.prototype.getPosition = XMLLayoutGetPosition

// return node's text/xml
function XMLLayoutNodeText(nodetag)
{
	if (!nodetag) return
	var node
	if (!(node = this.xmlObj.documentElement.selectSingleNode(nodetag)))
		return null
	return node.text
}
XMLLayout.prototype.getNodeText = XMLLayoutNodeText

// save xml
function XMLLayoutSave(
	newName // optional - new file name. if specified file is saved with new name
	)
{
    if (!this.active)
        return
    if (!this.adminmode) // user not admin
        return
    if (!this.hasPermissions) { // user has no permissions to edit
        alert('אין לך הרשאות לערוך אזור תוכן זה')
        return
    }
	
	if (newName) { // save xml with new name
		var oldName = XMLLayout.parseFileName(this.xmlFile)
		var newPath = this.xmlFile.replace(oldName,newName)
		this.xmlFile = newPath
    	this.xmlForm.elements[this.name + '_file'].value = newPath
		this.xmlForm.action += (this.xmlForm.action.indexOf('?') == -1? '?': '&') + 'action=saveAs'
	}
	
	// reset following xml fields before saving
	this.simpleSetNode('PARAMETERS/EDITABLE',0)
	
    // submit the save form
    this.xmlForm.editmode.value = (this.editable)? 1: 0
    this.xmlForm.elements[this.name + '_xml'].value = this.xmlObj.documentElement.xml
	if (window.opener) // window is a popup
		this.xmlForm.action += (this.xmlForm.action.indexOf('?') == -1? '?': '&') + 'popup=1'
    if (this.onsave) // execute event handler
        eval(this.onsave)
    this.xmlForm.submit()
}
XMLLayout.prototype.save = XMLLayoutSave

// save xml on window unload
function XMLLayoutSaveOnExit(
	newName // optional - new file name. if specified file is saved with new name
	)
{
    if (!this.active)
        return
    if (!this.adminmode) // user not admin
        return
    if (!this.hasPermissions) { // user has no permissions to edit
        alert('אין לך הרשאות לערוך אזור תוכן זה')
        return
    }
	
	if (newName) { // save xml with new name
		var oldName = XMLLayout.parseFileName(this.xmlFile)
		var newPath = this.xmlFile.replace(oldName,newName)
		this.xmlFile = newPath
    	this.xmlForm.elements[this.name + '_file'].value = newPath
		this.xmlForm.action += (this.xmlForm.action.indexOf('?') == -1? '?': '&') + 'action=saveAs'
	}
	
	// reset following xml fields before saving
	this.simpleSetNode('PARAMETERS/EDITABLE',0)
	
    // set form fields
    this.xmlForm.editmode.value = (this.editable)? 1: 0
    this.xmlForm.elements[this.name + '_xml'].value = this.xmlObj.documentElement.xml
	this.xmlForm.action += (this.xmlForm.action.indexOf('?') == -1? '?': '&') + 'popup=1'
	
	// write form into popup window
	this.xmlForm.target = 'formTarget'
    
	var winleft = screen.width/2 - 200/2
	var wintop = screen.height/2 - 100/2 - 30
	window.open(null,'formTarget','width=200,height=100,top=' + wintop + ',left=' + winleft + ',scrollbars=no,menubar=no,resizable=yes,toolbar=no,location=no,status=yes')
	self.focus()
	
    // submit form
    if (this.onsave) // execute event handler
        eval(this.onsave)
	this.xmlForm.submit()
}
XMLLayout.prototype.saveOnExit = XMLLayoutSaveOnExit

// insert a new action
function XMLLayoutInsertAction(
    editNode        // the node being edited
    ,lastData       // the last data for undo
    ,newData        // the new data for redo
    ,type           // action's type - 'edit', 'add' or 'delete'
    ,replaceNode    // a boolean indicating whether node is to be replaced. if true generator is required
    ,lastPosition   // node's last position
    ,newPosition    // node's new position
    )
{
    var newObj = new Object()
    newObj.editNode = editNode
    newObj.lastData = lastData
    newObj.newData = newData
    newObj.type = type
    newObj.replaceNode = replaceNode
    newObj.lastPosition = lastPosition
    newObj.newPosition = newPosition
    this.lastActions[++this.pLastAction % XMLLayout.MAXUNDO] = newObj
    XMLLayout.Actions[++XMLLayout.pLastAction % XMLLayout.MAXUNDO] = this.name
}
XMLLayout.prototype.insertAction = XMLLayoutInsertAction

// global functions
// ================

// set node
function XMLLayoutGlobalSetNode(newData)
{
    XMLLayout.editedObj.setNode(newData)
}
XMLLayout.setNode = XMLLayoutGlobalSetNode

// set all layouts edit mode
function XMLLayoutSetEditableAll(bool)
{
    for (i in XMLLayout.lookUpTable)
        XMLLayout.lookUpTable[i].setEditable(bool)
    XMLLayout.editmode = (bool == true)? 1: 0
}
XMLLayout.setEditableAll = XMLLayoutSetEditableAll

// undo last editing
function XMLLayoutGlobalUndo()
{
	if (XMLLayout.undoCounter == 0)
		return
	var editedObjName = XMLLayout.Actions[XMLLayout.pLastAction % XMLLayout.MAXUNDO]
	var editedObj = XMLLayout.lookUpTable[editedObjName]
	XMLLayout.pLastAction--
	editedObj.undo()
}
XMLLayout.undo = XMLLayoutGlobalUndo

// redo last editing
function XMLLayoutGlobalRedo()
{
	if (XMLLayout.redoCounter == 0)
		return
	XMLLayout.pLastAction++
	var editedObjName = XMLLayout.Actions[XMLLayout.pLastAction % XMLLayout.MAXUNDO]
	var editedObj = XMLLayout.lookUpTable[editedObjName]
	editedObj.redo()
}
XMLLayout.redo = XMLLayoutGlobalRedo

// this function writes a 'SaveAllForm' form into a hidden layer, containing all layout's xmls
// and extra data. the form is then submitted
function XMLLayoutSaveAll()
{
    var html = '<form name="SaveAllForm" action="xmlSave.asp" method="post">\n' + 
           '<input type="hidden" name="editmode" value="' + this.editmode + '">\n' + 
           '<input type="hidden" name="layouts" value="">\n'
    var layout, layoutObj, layouts = ''
    for (layout in this.lookUpTable) {
        layoutObj = this.lookUpTable[layout]
		// save only objects that were changed
		if (layoutObj.pLastAction > -1) {
        	layouts += layout + ','
	        html += '<input type="hidden" name="' + layout + '_file" value="' + layoutObj.xmlFile + '">\n'
			html += '<textarea name="' + layout + '_xml" cols="1" rows="1">\n' + layoutObj.xmlObj.documentElement.xml.replace(/&/g,'&amp;') + '\n</textarea>\n'
			// reset following xml fields before saving
			layoutObj.simpleSetNode('PARAMETERS/EDITABLE',0)
		}
    }
    html += '</form>'
    // write form
    this.saveAllDiv.write(html)
    // submit form
	this.xmlForm = this.saveAllDiv.doc.forms['SaveAllForm']
    layouts = layouts.substring(0,layouts.length-1) // cut the last comma
    this.xmlForm.layouts.value = layouts
	if (window.opener) // window is a popup
		this.xmlForm.action += '?popup=1'
    if (this.onsave) // execute event handler
        eval(this.onsave)
    this.xmlForm.submit()
}
XMLLayout.saveAll = XMLLayoutSaveAll

// this function is called on page unload, where saveAll almost always fails to
// submit the 'SaveAllForm' from. this function writes the form into a pop up 
// rather then into a hidden layer
function XMLLayoutSaveAllOnExit()
{
    var html = '' + 
		'<form name="SaveAllForm" action="xmlSave.asp?popup=1" method="post">\n' + 
		'<input type="hidden" name="editmode" value="' + this.editmode + '">\n' + 
		'<input type="hidden" name="layouts" value="">\n'
    var layout, layoutObj, layouts = ''
    for (layout in this.lookUpTable) {
        layoutObj = this.lookUpTable[layout]
		// save only objects that were changed
		if (layoutObj.pLastAction > -1) {
        	layouts += layout + ','
	        html += '<input type="hidden" name="' + layout + '_file" value="' + layoutObj.xmlFile + '">\n'
	        html += '<textarea name="' + layout + '_xml" cols="1" rows="1">\n' + layoutObj.xmlObj.documentElement.xml.replace(/&/g,'&amp;') + '\n</textarea>\n'
			// reset following xml fields before saving
			layoutObj.simpleSetNode('PARAMETERS/EDITABLE',0)
		}
    }
    html += '</form>'
    
	// write form
	var winleft = screen.width/2 - 200/2
	var wintop = screen.height/2 - 100/2 - 30
	var submitter = window.open(null,null,'width=200,height=100,top=' + wintop + ',left=' + winleft + ',scrollbars=no,menubar=no,resizable=yes,toolbar=no,location=no,status=yes')
	self.focus()
	submitter.document.open()
	submitter.document.write(html)
	submitter.document.close()
	
    // submit form
	this.xmlForm =  submitter.document.forms['SaveAllForm']
    layouts = layouts.substring(0,layouts.length-1) // cut the last comma
    this.xmlForm.layouts.value = layouts
    if (this.onsave) // execute event handler
        eval(this.onsave)
	this.xmlForm.submit()
}
XMLLayout.saveAllOnExit = XMLLayoutSaveAllOnExit

// loading xml/xsl. returns an xml/xsl object
function XMLLayoutLoadXML(xmlFilePath)
{	
    var err
    var xmlObj = new ActiveXObject('Microsoft.XMLDOM')
    if (xmlObj == null) {
        alert('Error: Unable to create server resources.')
        return
    }
    xmlObj.async = false
    
    xmlObj.load(xmlFilePath)
            
    err = xmlObj.parseError        
    
    if (err.errorCode != 0) { // error on load redirect to error page		
		if (!XMLLayout.DEBUG)
			location = '/site/error.asp?code=' + err.errorCode + '&reason=' + err.reason
		else
			alert('Error loading XML: ' + err.reason)
	}	
    return xmlObj
}
XMLLayout.loadXML = XMLLayoutLoadXML

function XMLLayoutLoadXMLString(xmlString)
{
    var err
    var xmlObj = new ActiveXObject('Microsoft.XMLDOM')
    if (xmlObj == null) {
        alert('Error: Unable to create server resources.')
        return
    }
    xmlObj.async = false
    xmlObj.loadXML(xmlString)
    err = xmlObj.parseError
    if (err.errorCode != 0) { // error on load redirect to error page
		if (!XMLLayout.DEBUG)
			location = 'error.asp?code=' + err.errorCode + '&reason=' + err.reason
		else
			alert('Error loading XML: ' + err.reason)
	}
    return xmlObj
}
XMLLayout.loadXMLString = XMLLayoutLoadXMLString

// get selected node
function XMLLayoutGetSelectedNode()
{
	var editedObj = this.editedObj
    if (editedObj.editDetails.replaceNode) {
		if (editedObj.editDetails.refNode)
	        return editedObj.editDetails.refNode
	    return editedObj.editDetails.lastData
	}
	return editedObj.editDetails.editNode
}
XMLLayout.getSelectedNode = XMLLayoutGetSelectedNode

// get layout by section id
function XMLLayoutGetLayout(id)
{
	return XMLLayout.lookUpTable['layout' + id]
}
XMLLayout.getLayout = XMLLayoutGetLayout

// parse file name from file's path
function XMLLayoutParseFileName(filePath)
{
    var p = Math.max(filePath.lastIndexOf('/'),filePath.lastIndexOf('\\'))
    return filePath.substring(p + 1,filePath.length)
}
XMLLayout.parseFileName = XMLLayoutParseFileName
