﻿///////// LOOK BELOW !!!!!!!!!!!!!!!

var columnsAreMovable = false;
/* var columnContentIsHidable = true; */
var smoothSectionExpandContract = true;

var sectionsAreMovable = false;
var nSpace = 'Menzies.Web.Services';
var removeLIPlaceHolderWhenInMiddle = false;


///////// LOOK ABOVE !!!!!!!!!!!!!!!

var wsContentUrl = '/services/content.asmx';
var wsFilesUrl = '/services/files.asmx';

var columnDivs = new Array();
var sectionDivs = new Array();
var templateDivs = new Array();

var columns = new Array();
var sections = new Array();

// Each item should have some sort of ID, or a means to identify it.
// Perhaps a hidden field?
// When something gets moved, we will need to record this movement on the server.

// This is for the page editor. Need another function to setup DnD on the ShowNodes page
setupDnD = function() {
    // as well as setting us up the sections that can be moved, need to set us up the templates
    
    var allDivs = document.getElementsByTagName('div');
    //alert ('allDivs.length ' + allDivs.length);
    //elMatchesClassName
    
    for (var c = 0; c < allDivs.length; c++) {
        var div = allDivs[c];
        //if (elBeginsWithClassName(div, 'section-content')) {
        if (elMatchesIdBeginning(div, 'section')) {
            //alert ('elBeginsWithClassName section-content');
        //if (elMatchesClassName(div, 'section-content')) {
            sectionDivs.push(div);
        } else {
            //if (elBeginsWithClassName(div, 'column-content')) {
            if (elMatchesIdBeginning(div, 'column')) {
            //if (elMatchesClassName(div, 'column-content')) {
                columnDivs.push(div);
            } else {
                //if (elMatchesClassName(div, 'add-template-item')) {
                //if (elBeginsWithClassName(div, 'add-template-item')) {
                if (elMatchesIdBeginning(div, 'template')) {
                    templateDivs.push(div);
                }
            }
        }
    }
    
    for (var c = 0; c < columnDivs.length; c++) {
        var activeColumn = new Column(columnDivs[c]);
        columns.push(activeColumn);
    }
    for (var c = 0; c < sectionDivs.length; c++) {
        var activeSection = new Section(sectionDivs[c]);
        sections.push(activeSection);
    }
    for (var c = 0; c < templateDivs.length; c++) {
        var activeTemplate = new Template(templateDivs[c]);
        sections.push(templateDivs);
    }
    // then we apply a control to each of them that activates them.
    // activate the TemplateSelector.
    
    var divTemplateSelector = $('add-template');
    var templateSelector = new TemplateSelector(divTemplateSelector);
}

var sectionBeingDragged;

// maybe it is not a section being dragged, but a template.

Template = function(div) {
    this.div = div;
    this.init();
}
var p = Template.prototype;
p.init = function() {
    // need to apply a different drag-and drop behaviour.
    this.div.ctrl = this;
    this.div.onmousedown = function() { this.ctrl.div_onmousedown(); return false; }
    
    this.div.onmousemove = function() {
        return false;
    }
    
    this.div.onmousedown = function(e) {
        // start the drag (or note that something is being dragged).
        //  no need to change anything yet in the GUI.
        
        //sectionBeingDragged = this;
        
        // only begin the drag if the left button has been pressed.
        if(!e) var e=window.event;
        //alert (e.button);
        
        // In IE, left and right pressed together is 3.
        if (e.button != 2 && e.button != 3) {
            //alert ('beginDrag');
            beginTemplateDrag(this.ctrl);
            document.oncontextmenu=new Function("return false");
        }
        return false;
    }
    
    this.div.onmouseup = function(e) {
        // stop the drag
        if(!e) var e=window.event;
        if (e.button != 2) {
            endTemplateDrag(sectionBeingDragged);
            document.oncontextmenu=null;
        }
    }
    
    this.div.onmouseover = function() {
        // show the 
        this.ctrl.div_onmouseover();
    }
    this.div.onmouseout = function() {
        this.ctrl.div_onmouseout();
    }
    this.div.onmousemove = function() {
        this.ctrl.div_onmousemove();
    }
    
}
p.getTemplateId = function() {
    return getAfterLastUnderscore(this.div.id);
}

p.div_onmousemove = function() {
    var templatePreviewElId = 'template_img_' + this.getTemplateId();
    //alert ('templatePreviewElId ' + templatePreviewElId);
    var templatePreviewEl = $(templatePreviewElId);
    //templatePreviewEl.style.display = 'block';
    templatePreviewEl.style.left = (posX + 20) + 'px';
    templatePreviewEl.style.top = (posY + 20) + 'px';
}

p.mouseBeenOver = function() {
    this.showPreview();
}

var controlOver;
controlBeenOver = function() {
    controlOver.mouseBeenOver();
}


var currentTemplatePreviewEl;

p.showPreview = function() {
    var templatePreviewElId = 'template_img_' + this.getTemplateId();
    //alert ('templatePreviewElId ' + templatePreviewElId);
    
    if (currentTemplatePreviewEl != templatePreviewEl) {
        currentTemplatePreviewEl.style.display = 'none';
    }
    
    var templatePreviewEl = $(templatePreviewElId);
    templatePreviewEl.style.display = 'block';
    templatePreviewEl.style.left = (posX + 20) + 'px';
    templatePreviewEl.style.top = (posY + 20) + 'px';
    currentTemplatePreviewEl = templatePreviewEl;
    
}

p.div_onmouseover = function() {
    // get the id of the template from the id of the template element.
    controlOver = this;
    this.timeoutOver = setTimeout(controlBeenOver, 400);
    
    
}
p.div_onmouseout = function() {
    clearTimeout(this.timeoutOver);
    
    var templatePreviewElId = 'template_img_' + this.getTemplateId();
    //alert ('templatePreviewElId ' + templatePreviewElId);
    $(templatePreviewElId).style.display = 'none';
}

p.getPos = function() {
    return findPos(this.div)
}
p.type = 'template';

// Columns need to be draggable for re-arrangement.

var dragOriginColumn;
var columnInside;

Column = function(div) {
    this.div = div;
    this.init();
}
var p = Column.prototype;
p.init = function() {
    this.div.ctrl = this;
    // get reference to the titlebar div , id 'column-content-toolbar'
    
    var found = false;
    var c = 0;
    
    // Rather than searching for the childnode, do a recursive search.
    this.divTitleBar = getElementByCssClass(this.div, 'column-content-toolbar');
    
    this.div.onmouseover = function() {
        //alert ('set columnInside');
        columnInside = this.ctrl;
    }
    
    if (columnsAreMovable) {
        
        this.divTitleBar.onmousedown = function(e) {
            // start the drag (or note that something is being dragged).
            //  no need to change anything yet in the GUI.
            //sectionBeingDragged = this;
            // only begin the drag if the left button has been pressed.
            if(!e) var e=window.event;            
            // In IE, left and right pressed together is 3.
            if (e.button != 2 && e.button != 3) {
                var control = this.parentNode.ctrl;
                if (!control) control = this.parentNode.parentNode.ctrl;
                //alert ('this.parentNode.ctrl ' + this.parentNode.ctrl);
                //alert ('this.parentNode.parentNode.ctrl ' + this.parentNode.parentNode.ctrl);
                beginColumnDrag(control);
                document.oncontextmenu=new Function("return false");
            }
        }
        
        this.divTitleBar.onmouseup = function(e) {
            // stop the drag
            if(!e) var e=window.event;
            if (e.button != 2) {
                endColumnDrag(sectionBeingDragged);
                document.oncontextmenu=null;
            }
        }
    } else {
        if (this.divTitleBar) this.divTitleBar.style.cursor = 'default';
    }
}
p.getPos = function() {
    return findPos(this.div)
}
p.cachePosition = function() {
    this.pos = findPos(this.div);
    //alert ('this.pos[0] ' + this.pos[0]);
    //alert ('this.pos[1] ' + this.pos[1]);
}
p.cacheSections = function() {
    // go through this, and record which sections it contains.
    this.sections = new Array();
    for (var c = 0; c < this.div.childNodes.length; c++) {
        var div = this.div.childNodes[c];
        if (div.ctrl) {
            var section = div.ctrl;
            section.cachePosition();
            this.sections.push(section);
        }
    }
}
p.isWithinHorizontalBounds = function(x) {
    if (x < this.pos[0]) return false;
    if (x > this.pos[0] + this.div.offsetWidth) return false;
    return true;
}
p.isWithinVerticalBounds = function(y) {
    if (y < this.pos[1]) return false;
    if (y > this.pos[1] + this.div.offsetHeight) return false;
    return true;
}
p.isWithinBounds = function(x, y) {
    return (this.isWithinHorizontalBounds(x) && this.isWithinVerticalBounds(y));
}

p.isAboveHalfwayDown = function(x, y, excludeSectionBeingDragged) {
    // the locations of stuff will be cached.
    if (excludeSectionBeingDragged && this == sectionBeingDragged) return false;
    // must be above and to the left.
    var isAbove = (y < this.pos[1] + (this.div.offsetHeight / 2));
    var isLeftOf = (x < this.pos[0] + (this.div.offsetWidth / 2));
    
    return (isAbove && isLeftOf);
}

p.getSectionBelowPointer = function(y) {
    var c = 0;
    var found = false;
    var section;
    // need to also look out for the placeholder.
    while (!found && c < this.sections.length) {
        section = this.sections[c];
        found = this.sections[c].isAboveHalfwayDown(y);
        c++;
    }
    
    if (found) return section;
}
p.type = 'column';

Section = function(div) {
    this.div = div;
    
    this.init();
}
var p = Section.prototype;
p.init = function() {
    // don't get the first child node - there may be some white space.
    // get the first child node with the class of 'section-content-toolbar'
    //alert ('this.div.id ' + this.div.id);
    this.isExpanded = true;
    this.div.ctrl = this;
    
    var found = false;
    var c = 0;
    //while (c < this.div.childNodes.length &! found) {
    //alert('this.div ' + this.div);
    
    
    this.divTitleBar = getElementByCssClass(this.div, 'section-content-toolbar');
    this.divInner = getElementByCssClass(this.div, 'section-content-inner');
    // needs to have a content div that gets shrunk / made full size through toggle.
    // make a reference to section-content-inner
    // also, if someone right-clicks while dragging, need to not show the context menu.
    //alert('this.divTitleBar ' + this.divTitleBar);
    //if (this.divTitleBar) {
    this.divTitleBar.onmousedown = function(e) {
        // start the drag (or note that something is being dragged).
        //  no need to change anything yet in the GUI.
        //sectionBeingDragged = this;
        // only begin the drag if the left button has been pressed.
        if(!e) var e=window.event;
        // In IE, left and right pressed together is 3.
        if (e.button != 2 && e.button != 3) {
            var control = this.parentNode.ctrl;
            if (!control) control = this.parentNode.parentNode.ctrl;
            beginDrag(control);
            document.oncontextmenu=new Function("return false");
        }
    }
    
    this.divTitleBar.onmouseup = function(e) {
        // stop the drag
        if(!e) var e=window.event;
        if (e.button != 2) {
            endDrag(sectionBeingDragged);
            document.oncontextmenu=null;
        }
    }
    //}
}

p.toggle = function() {
    //section-content-inner
    // call a smooth expand/contract function.
    // perhaps this is not possible to do on a given div if it does not have a size set.
    if (!this.isBeingResized) {
        if (this.isExpanded) {
            this.isBeingResized = true;
            collapseDiv(this.divInner);
        } else {
            this.isBeingResized = true;
            expandDiv(this.divInner);
        }
    }
}
p.collapseComplete = function() {
    this.isExpanded = false;
    this.isBeingResized = false;
}
p.expandComplete = function() {
    this.isExpanded = true;
    this.isBeingResized = false;
}

p.getPos = function() {
    return findPos(this.div)
}
p.cachePosition = function() {
    this.pos = findPos(this.div);
}
p.isAboveHalfwayDown = function(y) {
    return (y < this.pos[1] + (this.div.offsetHeight / 2));
    
}
p.type = 'section';

var dragCorrectionX;
var dragCorrectionY;
var dPlaceHolder;

var liPlaceHolder;
var liPlaceHolderPos;
var liPlaceHolderWidth;
var liPlaceHolderHeight;

isWithinLiPlaceHolder = function(x, y) {
    if (!liPlaceHolder) return false;
    if (!liPlaceHolder.parentNode) return false;
    if (!liPlaceHolderPos) cacheLIPlaceHolderPosition();
    
    if (y < liPlaceHolderPos[1]) return false;
    if (y > liPlaceHolderPos[1] + liPlaceHolderHeight) return false;
    return true;
}

beginTemplateDrag = function(template) {
    document.body.style.cursor = 'move';
    // need to correct for the scroll position.
    
    // get the scroll position of the 'add-template-body'.
    var addTemplateBody = document.getElementById('add-template-body');
    //alert ('addTemplateBody.scrollTop ' + addTemplateBody.scrollTop);
    
    var sectionPos = template.getPos();
    
    dragOffsetX = posX - sectionPos[0];
    dragOffsetY = posY - sectionPos[1];
    
    var w = template.div.offsetWidth;
    var h = template.div.offsetHeight;
    
    sectionBeingDragged = template;
    
    var sectionPos2 = findPos(template.div);
    //alert ('sectionPos[0] ' + sectionPos[0] + ', ' + 'sectionPos[1] ' + sectionPos[1]);
    
    var diffX = sectionPos2[0] - sectionPos[0];
    var diffY = sectionPos2[1] - sectionPos[1];
    
    dragCorrectionX = -1 * (diffX);
    dragCorrectionY = -1 * (diffY) - addTemplateBody.scrollTop;
    
    var dragClone = nD();
    dragClone.innerHTML = template.div.innerHTML;
    // copy the style of the template being dragged
    var divClassName = getElClassName(template.div);
    //alert('divClassName ' + divClassName);
    setClass(dragClone, divClassName);
    
    
    template.dragClone = dragClone;
    
    dPlaceHolder = nD();
    //dPlaceHolder.style.width = toPx(w - 2);
    
    dPlaceHolder.style.height = toPx(h - 2);
    //dPlaceHolder.style.margin = toPx(5);
    dPlaceHolder.style.border = '1px dashed black';
    dPlaceHolder.isPlaceHolder = true;
    setClass(dPlaceHolder, 'dPlaceHolder');
    
    // in FF the height
    
    dragClone.style.position = 'absolute';
    dragClone.onmousemove = doDrag;
    dragClone.onmouseup = function(e) {
        // stop the drag
        if(!e) var e=window.event;
        if (e.button != 2) {
            if (sectionBeingDragged) endTemplateDrag(sectionBeingDragged);
            document.oncontextmenu = null;
        }
    }
    document.body.onmouseup = function(e) {
        // stop the drag
        if(!e) var e=window.event;
        if (e.button != 2) {
            if (sectionBeingDragged) endTemplateDrag(sectionBeingDragged);
            document.oncontextmenu=null;
        }
        document.body.onmouseup = null;
    }
    document.body.appendChild(dragClone);
    
    for (var c = 0; c < columns.length; c++) {
        columns[c].cachePosition();
        columns[c].cacheSections();
    }
    return false;
}

getElIndex = function(el) {
    var parent = el.parentNode;
    var found = false;
    var i = 0;
    var cnl = parent.childNodes.length;
    while (!found && i < cnl) {
        if (parent.childNodes[i] == el) found = true;
        if (!found) i++;
    }
    if (found) return i;
    return -1;
}

endLiDrag = function(section) {
    // remove the element being dragged from the DOM.
    
    var el = section.el;
    sectionBeingDragged = null;
    
    //removeNode(el);
    //alert('el ' + el);
    
    // swap the placeholder with the section's elements.
    
    // but before making the replacement in the DOM, probably best to tell the server what replacement
    // is being made.
    
    // find out the: string nodeIDStr, string parentIDStr, string languageIDStr, string newOrderStr
    
    //nodeIDStr = el.id;
    
    var pos1 = el.id.lastIndexOf('_');
    var nodeIDStr = el.id.substring(pos1 + 1);
    
    //alert('nodeIDStr ' + nodeIDStr);
    
    // the parent id string will be above, the great grandparent.
    
    var parentLi = liPlaceHolder.parentNode.parentNode.parentNode;
    var parentLiId = parentLi.id;
    
    // ...languageID_nodeID of where it is being dropped.
    // get the parentNodeID.
    
    pos1 = parentLiId.lastIndexOf('_');
    var parentNodeID = parentLiId.substring(pos1 + 1);
    //alert('parentNodeID ' + parentNodeID);
    
    var pos2 = parentLiId.lastIndexOf('_', pos1);
    var parentLanguageID = parentLiId.substring(pos2 + 0, pos1 - 1);
    //alert('parentLanguageID ' + parentLanguageID);
    
    //alert ('pos1 ' + pos1);
    //alert ('pos2 ' + pos2);
    //var parentLanguageID = '1';
    
    // need to find the new order.
    
    //alert('parentLiId ' + parentLiId);
    
    var idx = getElIndex(liPlaceHolder);
    
    //liPlaceHolder.parentNode.replaceChild(el, liPlaceHolder);
    
    //alert('idx ' + idx);
    // need to find the location that it has been dragged to:
    //  its position within the heirachy.
    
    var cb_param = new Object();
    cb_param.el = el;
    
    // extract the li that is being dragged from the div that contains it.
    registerLiDrag(nodeIDStr, parentNodeID, parentLanguageID, idx, cb_param);
}

registerLiDrag = function(nodeIDStr, parentIDStr, parentLanguageID, newOrderStr, cb_param) {
    // call the MoveNode webmethod.
    
    var tUrl = getAbsoluteRootUrl() + wsContentUrl;
    var sr = new SoapRequest(tUrl, 'MoveNode', nSpace);
    sr.addNewParam('nodeIDStr', nodeIDStr);
    sr.addNewParam('parentIDStr', parentIDStr);
    sr.addNewParam('languageIDStr', parentLanguageID);
    sr.addNewParam('newOrderStr', newOrderStr);
    //var cbRegister
    sr.send(cb_registerLiDrag, error_registerLiDrag, cb_param);
    // what about passing callback parameters through?
}

cb_registerLiDrag = function(rx, cb) {
    //alert('cb_registerLiDrag');
    
    //alert('rx.xml ' + rx.xml);
    
    liPlaceHolder.parentNode.replaceChild(cb.el, liPlaceHolder);
    
}

error_registerLiDrag = function() {
    alert('error_registerLiDrag');
    
}

endTemplateDrag = function(section) {
    //section.div.style.position = 'static';
    //dPlaceHolder.parentNode.replaceChild(sectionBeingDragged.div, dPlaceHolder);
    if (section) {
        var div = section.dragClone;
        document.body.style.cursor = 'default';
        
        if (!dPlaceHolder || !dPlaceHolder.parentNode) {
            //dPlaceHolder.parentNode.removeChild(dPlaceHolder);
            div.parentNode.removeChild(div);
            sectionBeingDragged = null;
        } else {
            var oColumn = dPlaceHolder.parentNode.ctrl;
            // the position finder will work a bit differently.
            
            if (oColumn) {
                
                var d = 0;
                var foundPos = -1;
                for (var c = 0; c < oColumn.div.childNodes.length; c++) {
                    var n = oColumn.div.childNodes[c];
                    if (n == dPlaceHolder) {
                        foundPos = d;
                    }
                    if (n.ctrl) d++;
                }
                
                if (foundPos > -1) {
                    // get the id of the template that has been added.
                    var templateID = intAfterUnderscore(section.div.id);
                    var columnID = intAfterUnderscore(oColumn.div.id);
                    registerAddSection(columnID, templateID, foundPos, cb_registerAddSection, cb_registerAddSection);
                }
            }
            div.parentNode.removeChild(div);
            sectionBeingDragged = null;
        }
    }
}

intAfterUnderscore = function(input) {
    var pos1 = input.lastIndexOf('_') + 1;
    var res = parseInt(input.substring(pos1));
    return res;
}

toInt = function(val) {
    return parseInt(val);
}

beginColumnDrag = function(column) {
    sectionBeingDragged = column;
    
    document.body.style.cursor = 'move';
    var columnPos = column.getPos();
    dragOffsetX = posX - columnPos[0];
    dragOffsetY = posY - columnPos[1];
    var w = column.div.offsetWidth;
    var h = column.div.offsetHeight;
    
    var paddingLeft = 0;
    var paddingTop = 0;
    var paddingRight = 0;
    var paddingBottom = 0;
    
    if (column.div.currentStyle){
        //alert ("section.div.currentStyle['paddingLeft'] " + section.div.currentStyle['paddingLeft']);
        paddingLeft = toInt(column.div.currentStyle['paddingLeft']);
        paddingTop = toInt(column.div.currentStyle['paddingTop']);
        paddingRight = toInt(column.div.currentStyle['paddingRight']);
        paddingBottom = toInt(column.div.currentStyle['paddingBottom']);
    }
    
    if (window.getComputedStyle) {
        //alert ("window.getComputedStyle(section.div, '').getPropertyValue('padding-left') " + window.getComputedStyle(section.div, '').getPropertyValue('padding-left'));
        paddingLeft = toInt(window.getComputedStyle(column.div, '').getPropertyValue('padding-left'));
        paddingTop = toInt(window.getComputedStyle(column.div, '').getPropertyValue('padding-top'));
        paddingRight = toInt(window.getComputedStyle(column.div, '').getPropertyValue('padding-right'));
        paddingBottom = toInt(window.getComputedStyle(column.div, '').getPropertyValue('padding-bottom'));
    }
    
    column.div.style.left = toPx(columnPos[0]);
    column.div.style.top = toPx(columnPos[1]);
    column.div.style.width = toPx(w - 2 - paddingLeft - paddingRight);
    column.div.style.height = toPx(h - 2 - paddingTop - paddingBottom);
    
    var columnPos2 = findPos(column.div);
    
    var diffX = columnPos2[0] - columnPos[0];
    var diffY = columnPos2[1] - columnPos[1];
    
    dragCorrectionX = -1 * (diffX);
    dragCorrectionY = -1 * (diffY);
    
    // get the border left width;
    column.div.style.left = toPx(columnPos[0] + dragCorrectionX);
    column.div.style.top = toPx(columnPos[1] + dragCorrectionY);
    
    // Also on beginning the drag, need to get the position of various elements (cache them).
    
    // This will enable a placeholder to be put where the element is going to go.
    
    dPlaceHolder = nD();
    dPlaceHolder.style.width = toPx(w - 2);
    dPlaceHolder.style.height = toPx(h - 2);
    //dPlaceHolder.style.margin = toPx(5);
    dPlaceHolder.style.border = '1px dashed black';
    
    // get the computed float values.
    
    var cssFloat = getFloat(column.div);
    setFloat(dPlaceHolder, cssFloat);
    
    dPlaceHolder.isPlaceHolder = true;
    // in FF the height
    column.div.parentNode.insertBefore(dPlaceHolder, column.div);
    column.div.style.position = 'absolute';
    column.div.style.left = toPx(columnPos[0] + dragCorrectionX);
    column.div.style.top = toPx(columnPos[1] + dragCorrectionY);
    document.body.appendChild(column.div);
    // make it so that the column is dragged around by the user.
    for (var c = 0; c < columns.length; c++) {
        columns[c].cachePosition();
    }
}

beginDrag = function(section) {
    document.body.style.cursor = 'move';
    // for each of the columns, note its position within the document.
    // do we cache the position of each of the sections?
    // this way, if any item would go above the section, we can spot this.
    
    // get the offset of the mouse position within whatever is being dragged.
    // get the top and left of the item.
    
    // also want it so that when dragging something, moving the mouse to the top or bottom portion
    // of the window causes the window to scroll.
    dragOriginColumn = columnInside;
    
    var sectionPos = section.getPos();
    
    dragOffsetX = posX - sectionPos[0];
    dragOffsetY = posY - sectionPos[1];
    
    var w = section.div.offsetWidth;
    var h = section.div.offsetHeight;
    
    sectionBeingDragged = section;
    
    section.div.style.position = 'absolute';
    
    var paddingLeft = 0;
    var paddingTop = 0;
    var paddingRight = 0;
    var paddingBottom = 0;
    
    if (section.div.currentStyle){
        paddingLeft = toInt(section.div.currentStyle['paddingLeft']);
        paddingTop = toInt(section.div.currentStyle['paddingTop']);
        paddingRight = toInt(section.div.currentStyle['paddingRight']);
        paddingBottom = toInt(section.div.currentStyle['paddingBottom']);
    }
    
    if (window.getComputedStyle) {
        paddingLeft = toInt(window.getComputedStyle(section.div, '').getPropertyValue('padding-left'));
        paddingTop = toInt(window.getComputedStyle(section.div, '').getPropertyValue('padding-top'));
        paddingRight = toInt(window.getComputedStyle(section.div, '').getPropertyValue('padding-right'));
        paddingBottom = toInt(window.getComputedStyle(section.div, '').getPropertyValue('padding-bottom'));
    }
    
    section.div.style.left = toPx(sectionPos[0]);
    section.div.style.top = toPx(sectionPos[1]);
    section.div.style.width = toPx(w - 2 - paddingLeft - paddingRight);
    section.div.style.height = toPx(h - 2 - paddingTop - paddingBottom);
    
    // a correction may need to be made, because IE messes around the position.
    // find the offset between what we get and what we ask for.
    var sectionPos2 = findPos(section.div);
    
    var diffX = sectionPos2[0] - sectionPos[0];
    var diffY = sectionPos2[1] - sectionPos[1];
    
    dragCorrectionX = -1 * (diffX);
    dragCorrectionY = -1 * (diffY);
    
    section.div.style.left = toPx(sectionPos[0] + dragCorrectionX);
    section.div.style.top = toPx(sectionPos[1] + dragCorrectionY);
    // Also on beginning the drag, need to get the position of various elements (cache them).
    
    // This will enable a placeholder to be put where the element is going to go.
    
    //alert ('h ' + h);
    
    // is there a section padder?
    var divSectionPadder = getElementByCssClass(section.div, 'section-padder');
    var divSectionPadderHeight = 0;
    if (divSectionPadder) {
        
        if (divSectionPadder.currentStyle){
            divSectionPadderHeight = toInt(divSectionPadder.currentStyle['height']);
        }
        if (window.getComputedStyle) {
            divSectionPadderHeight = toInt(window.getComputedStyle(divSectionPadder, '').getPropertyValue('height'));
        }
        //alert('divSectionPadderHeight ' + divSectionPadderHeight);
    }
    
    
    dPlaceHolder = nD();
    
    // the placeholder needs 2 divs inside it: 1 for the outlined box, 1 for extra padding below.
    
    var dPlaceHolderBox = nD();
    dPlaceHolder.appendChild(dPlaceHolderBox);
    var dPlaceHolderPadding = nD();
    dPlaceHolder.appendChild(dPlaceHolderPadding);
    
    dPlaceHolder.style.width = toPx(w - 2);
    dPlaceHolderBox.style.height = toPx(h - 2 - divSectionPadderHeight);
    //dPlaceHolderBox.style.margin = toPx(5);
    dPlaceHolderBox.style.border = '1px dashed black';
    
    dPlaceHolderPadding.style.height = toPx(divSectionPadderHeight);
    
    //dPlaceHolder.style.marginBottom = divSectionPadderHeight + 'px';
    dPlaceHolder.isPlaceHolder = true;
    // in FF the height
    
    section.div.parentNode.insertBefore(dPlaceHolder, section.div);
    
    document.body.appendChild(section.div);
    
    for (var c = 0; c < columns.length; c++) {
        columns[c].cachePosition();
        columns[c].cacheSections();
    }
    
}

var containmentInfo;

// perhaps something about what is beign dragged - is it a section or template?
// perhaps allow this to be done with a maximum recursion depth of 2 (starting at 1).

var previousListItem;

doDrag = function() {
    // also want it so that when dragging something, moving the mouse to the top or bottom portion
    // of the window causes the window to scroll.
    
    if (sectionBeingDragged != null) {
        
        // how far is the mouse from the top or bottom of the viewable area?
        
        var div = sectionBeingDragged.div;
        if (sectionBeingDragged.dragClone) {
            div = sectionBeingDragged.dragClone;
        }
        
        divX = toPx(posX - dragOffsetX + dragCorrectionX);
        divY = toPx(posY - dragOffsetY + dragCorrectionY);
        
        div.style.left = divX;
        div.style.top = divY;
        
        if (sectionBeingDragged.type == 'draggableListItemNode' || sectionBeingDragged.type == 'droppableListItemNode') {
            // for further efficiency could index them according to if they are contained within
            // various positions.
            // don't do anything if the mouse is within the (vertical bounds of) the liPlaceHolder
            // no need to do the loop either.
            // the position of the liPlaceHolder should have been cached.
            if (isWithinLiPlaceHolder(posX, posY)) {
                //alert('isWithinLiPlaceHolder');
                setStatusHTML('isWithinLiPlaceHolder');
                
            } else {
                
                // May be better if only one draggable / droppable is found.
                // we don't want to rearrange the GUI, then re-cache the locations of items too often
                
                var foundCount = 0;
                var foundlins = new Array();
                
                var found = false;
                var c = 0;
                
                //setStatusHTML('droppableListItemNodes.length ' + droppableListItemNodes.length);
                
                //for (var c = 0; c < droppableListItemNodes.length; c++) {
                while (found == false && c < droppableListItemNodes.length) {
                    var dlin = droppableListItemNodes[c];
    //                var containment = dlin.getContainmentInformation(posX, posY);
    //                
    //                if (containment.offsetTop <= 2) {
    //                    dlin.el.style.backgroundColor = '#EFABCD';
    //                }
    //                if (containment.offsetBottom <= 3) {
    //                    dlin.el.style.backgroundColor = '#ABEFCD';
    //                }
                    containmentInfo = dlin.getSpanLabelContainmentInformation(posX, posY);
                    if (containmentInfo != false) containmentInfo.droppableListItemNode = dlin;
                    
    //                if (containmentInfo.offsetTop <= 3) {
    //                    dlin.spanLabel.style.backgroundColor = '#EFABCD';
    //                }
    //                if (containmentInfo.offsetBottom <= 3) {
    //                    dlin.spanLabel.style.backgroundColor = '#ABEFCD';
    //                }
                    // then need to show the outline of where it is going to be placed
                    // do we want indicateContainment repeated lots of times?
                    
                    //dlin.indicateContainment(containmentInfo);
                    
                    // then if the liPlaceholder is a sibling of the sectionBeingDragged's el, remove it
                    // from the document.
                    
                    //alert ('sectionBeingDragged.el.parentNode)
                    if (containmentInfo.offsetBottom) {
                        //dlin.el.style.backgroundColor = '#ABCDEF';
                        found = dlin;
                        // keep on looking.
                        foundCount = foundCount + 1;
                        foundlins.push(dlin);
                        dlin.indicateContainment(containmentInfo);
                    }
                    c++;
                }
                
                c = 0;
                while (found == false && c < draggableListItemNodes.length) {
                    var dlin = draggableListItemNodes[c];
                    containmentInfo = dlin.getSpanLabelContainmentInformation(posX, posY);
                    if (containmentInfo != false) containmentInfo.draggableListItemNode = dlin;
                    
                    if (containmentInfo.offsetBottom) {
                        //dlin.el.style.backgroundColor = '#ABCDEF';
                        found = dlin;
                        // keep on looking.
                        foundCount = foundCount + 1;
                        foundlins.push(dlin);
                        dlin.indicateContainment(containmentInfo);
                    }
                    c++;
                }
                
                // why is it not finding some droppable nodes if the mouse is moved over it?
                
                if (found) {
                    //setStatusHTML(found.el.innerHTML);
                    
                    // start the timer 
                    
                    // if the found item is different to the one that the mouse was over...
                    if (linInMiddleOf && found != linInMiddleOf) {
                        //alert('found != linInMiddleOf');
                        linInMiddleOf.onmiddleout();
                    }
                    
                } else {
                    if (linInMiddleOf) {
                        //alert('found != linInMiddleOf');
                        //setStatusHTML('in middle');
                        linInMiddleOf.onmiddleout();
                    }
                }
                
                if (sectionBeingDragged.el.parentNode) {
                    //alert ('sectionBeingDragged.el.parentNode');
                    var previousSibling = getPreviousSibling(sectionBeingDragged.el);
                    if (previousSibling) {
                        sectionBeingDragged.el.parentNode.removeChild(previousSibling);
                        cacheLIDragDropPositions();
                    }
                    var nextSibling = getNextSibling(sectionBeingDragged.el);
                    if (nextSibling) {
                        sectionBeingDragged.el.parentNode.removeChild(nextSibling);
                        cacheLIDragDropPositions();
                    }
                    
                }
            }
//            if (foundCount > -1) {
//                //alert('foundCount ' + foundCount);
//                
//                // only one should be found.
//                
//                var str = '&nbsp;';
//                for (var c = 0; c < foundlins.length; c++) {
//                    if (c > 0) str += ', ';
//                    str += foundlins[c].spanLabel.innerHTML;
//                }
//                
//                $('spnStatus').innerHTML = str;
//            }
            
            // need to find out if it inside any DroppableListItemNode
            
            // need to also find it it is in the top or bottom 2 pixels of it.
        }
        
        if (sectionBeingDragged.type == 'column') {
            var divColumnHolder = document.getElementById('column-holder');
            // when dragging the column, we need to find the first column that the cursor is before.
            // It counts as 'before' if the mouse is above the half-way-down point.
            
            var found = false;
            var c = 0
            var column;
            
            // can not go through the columns as held in the array - need to go through the
            // column divs in the DOM.
            
            while (!found && c < divColumnHolder.childNodes.length) {
                var dColumn = divColumnHolder.childNodes[c];
                column = dColumn.ctrl;
                if (column && column.isAboveHalfwayDown(posX, posY, true)) {
                    found = true;
                }
                c++;
            }
            if (found) {
                column.div.parentNode.insertBefore(dPlaceHolder, column.div);
                
            } else {
                // this can be problematic if things are not found when they should be.
                // not before any columns - move the placeholder past the last column.
                var cH = document.getElementById('column-holder');
                cH.appendChild(dPlaceHolder);
            }
            for (var c = 0; c < columns.length; c++) {
                columns[c].cachePosition();
                //columns[c].cacheSections();
            }
        }
        
        if (sectionBeingDragged.type == 'section' || sectionBeingDragged.type == 'template') {
            var found = false;
            var c = 0
            var column;
            while (!found && c < columns.length) {
                column = columns[c];
                if (column.isWithinBounds(posX, posY)) {
                    columnInside = column;
                    found = true;
                }
                c++;
            }
            if (found) {
                // if it finds a section, then there will not be this empty-section.
                
                if (columnDivWhichHasHadEmptySectionRemoved) {
                    if (!divContainsSectionOrEmptySection(columnDivWhichHasHadEmptySectionRemoved)) {
                        if (!removedEmptySection) {
                            removedEmptySection = nD();
                            setClass (removedEmptySection, 'empty-section');
                            //columnDiv.appendChild(divEmptySection);
                        }
                        //alert('append');
                        columnDivWhichHasHadEmptySectionRemoved.appendChild(removedEmptySection);
                        columnDivWhichHasHadEmptySectionRemoved = null;
                        removedEmptySection = null;
                    }
                }
                
                // need to make sure the section that is being added has the right width.
                
                // get the offset width of the column.
                
                var section = column.getSectionBelowPointer(posY);
                if (section) {
                    var columnWidth = section.div.parentNode.offsetWidth;
                    dPlaceHolder.style.width = (columnWidth - 2) + 'px';
                    
                    section.div.parentNode.insertBefore(dPlaceHolder, section.div);
                    // maybe should have a different function on removal of a section to
                    // ensure it does contain an empty section if necessary.
                    //ensureColumndivContainsSectionOrEmptySectionOrEmptySection(column.div);
                    
                    var emptySection = getChildNodeByCssClass(column.div, 'empty-section');
                    
                    if (emptySection) {
                        if (columnDivWhichHasHadEmptySectionRemoved) {
                            // find out if it contains 
                            
                            if (!divContainsSectionOrEmptySection(columnDivWhichHasHadEmptySectionRemoved)) {
                                
                                if (!removedEmptySection) {
                                    removedEmptySection = nD();
                                    setClass (removedEmptySection, 'empty-section');
                                    //columnDiv.appendChild(divEmptySection);
                                }
                                //alert('append');
                                columnDivWhichHasHadEmptySectionRemoved.appendChild(removedEmptySection);
                            }
                        }
                        
                        removedEmptySection = emptySection;
                        removeNode(emptySection);
                    }
                    // but then be sure to put it back later.
                    columnDivWhichHasHadEmptySectionRemoved = column.div;
                } else {
                    // if there is an empty-section within the column, remove it.
                    var emptySection = getChildNodeByCssClass(column.div, 'empty-section');
                    if (emptySection) {
                        if (columnDivWhichHasHadEmptySectionRemoved) {
                            
                            if (!divContainsSectionOrEmptySection(columnDivWhichHasHadEmptySectionRemoved)) {
                                if (!removedEmptySection) {
                                    removedEmptySection = nD();
                                    setClass (removedEmptySection, 'empty-section');
                                    //columnDiv.appendChild(divEmptySection);
                                }
                                
                                // only append the empty section if it does not already contain one.
                                
                                //alert('append');
                                columnDivWhichHasHadEmptySectionRemoved.appendChild(removedEmptySection);
                            }
                        }
                        removedEmptySection = emptySection;
                        removeNode(emptySection);
                    }
                    // but then be sure to put it back later.
                    columnDivWhichHasHadEmptySectionRemoved = column.div;
                    //ensureColumndivContainsSectionOrEmptySectionOrEmptySection(column.div);
                    
                    var columnWidth = column.div.offsetWidth;
                    dPlaceHolder.style.width = (columnWidth - 2) + 'px';
                    
                    
                    column.div.appendChild(dPlaceHolder);
                }
                for (var c = 0; c < columns.length; c++) {
                    columns[c].cachePosition();
                    columns[c].cacheSections();
                }
            } else {
                if (dPlaceHolder && dPlaceHolder.parentNode) {
                    //alert('dPlaceHolder ' + dPlaceHolder);
                    removeNode(dPlaceHolder);
                }
                if (columnDivWhichHasHadEmptySectionRemoved) {
                    if (!divContainsSectionOrEmptySection(columnDivWhichHasHadEmptySectionRemoved)) {
                        if (!removedEmptySection) {
                            removedEmptySection = nD();
                            setClass (removedEmptySection, 'empty-section');
                            //columnDiv.appendChild(divEmptySection);
                        }
                        //alert('append');
                        columnDivWhichHasHadEmptySectionRemoved.appendChild(removedEmptySection);
                        columnDivWhichHasHadEmptySectionRemoved = null;
                        removedEmptySection = null;
                    }
                }
                //ensureColumndivContainsSectionOrEmptySectionOrEmptySection(column.div);
            }
        }
        // then find the first div it is above the half-way mark on.
        // want to do this efficiently. Use cached values to save using DOM to get the values.
        clearSelection();
    }
}

var columnDivWhichHasHadEmptySectionRemoved;
var removedEmptySection;

showPlaceholder = function() {
    
}

endColumnDrag = function(column) {
    // maybe will just use endDrag.
    //alert ('endColumnDrag');
    column.div.style.position = 'static';
    // or set back to original height;
    column.div.style.height = '';
    dPlaceHolder.parentNode.replaceChild(sectionBeingDragged.div, dPlaceHolder);
    document.body.style.cursor = 'default';
    sectionBeingDragged = null;
}

endDrag = function(section) {
    section.div.style.position = 'static';
    section.div.style.width = '';
    section.div.style.height = '';
    dPlaceHolder.parentNode.replaceChild(sectionBeingDragged.div, dPlaceHolder);
    document.body.style.cursor = 'default';
    
    //var oColumn = sectionBeingDragged.div.parentNode.ctrl;
    
    //columnInside
    
    //var oColumn = dragOriginColumn;
    var oColumn = columnInside;
    
    // we need the column that it has been moved into, that's where it looks for the foundPos.
    
    
    //alert ('dragOriginColumn is oColumn ' + (dragOriginColumn == oColumn));
    //alert ('dragOriginColumn ' + dragOriginColumn);
    
    
    // adjust this so that while being dragged, there is a global reference to the
    //  the parent node's control.
    // or the column.
    // we want to get this, if poss, without looking at parent node's controls
    // could do this by noticing when the mouse is moved within a column.
    
    
    // Undefined in IE.
    //alert ('oColumn ' + oColumn);
    
    // this is where there is the problem.
    
    // alert('oColumn.div.childNodes.length ' + oColumn.div.childNodes.length);
    
    var d = 0;
    var foundPos = -1;
    for (var c = 0; c < oColumn.div.childNodes.length; c++) {
        var n = oColumn.div.childNodes[c];
        if (n == sectionBeingDragged.div) {
            foundPos = d;
        }
        // increment d if we have just gone through a div of the correct type.
        if (n.ctrl) d++;
    }
    
    
    //alert('foundPos ' + foundPos);
    if (foundPos > -1) {
        registerMove(sectionBeingDragged.div.id, oColumn.div.id, foundPos, cb_registerMove, cb_error_registerMove);
    }
    sectionBeingDragged = null;
}

simulatedGetElementStringContentByTagName = function(xmlNode, elementName) {
    var zSer = new zXMLSerializer();
    var xmlText = zSer.serializeToString(xmlNode);
    //alert ('xmlText ' + xmlText);
    var search1 = '<' + elementName + '>';
    //alert ('search1 ' + search1);
    var pos1 = xmlText.indexOf(search1) + search1.length;
    //alert ('xmlText ' + xmlText);
    var search2 = '</' + elementName + '>';
    //alert ('search2 ' + search2);
    var pos2 = xmlText.indexOf(search2);
    //alert ('xmlText ' + xmlText);
    var res = xmlText.substring(pos1, pos2);
    return res;
}

simulatedRemoveFromCData = function(xmlNodeText) {

    var search1 = '<![CDATA[';
    //alert ('search1 ' + search1);
    var pos1 = xmlNodeText.indexOf(search1) + search1.length;
    //alert('pos1')
    var search2 = ']]>';
    var pos2 = xmlNodeText.indexOf(search2) + search2.length;
    //alert('pos2')
    var res = xmlNodeText.substring(pos1, pos2);
    //var pos1 = xmlNodeText.indexOf(search1) + search1.length;
    
    return res;
    
}

// also need a function to get the content from the server.

getSectionContent = function(sectionID, callback, errorCallback) {
    var tUrl = getAbsoluteRootUrl() + wsContentUrl;
    var sr = new SoapRequest(tUrl, 'GetSectionItem', nSpace);
    sr.addNewParam('sectionID', sectionID);
    sr.send(callback, errorCallback);
}
cb_getSectionContent = function(xml) {
    //alert ('cb_getSectionContent xml.text ' + xml.text);
    
    // this will need to do an HTML replacement.
    
    //var nRes = xml.getElementsByTagName('GetSectionItemResult')[0].childNodes[0];
    //var s = nRes.nodeValue;
    var d = nD();
    var s = simulatedRemoveFromCData(simulatedGetElementStringContentByTagName(xml, 'GetSectionItemResult'));
    
    d.innerHTML = s;
    
    var d1 = d.childNodes[0];
    document.body.appendChild(d);
    // swap it with the placeholder.
    
    //alert('d1.nodeType ' + d1.nodeType);
    //alert('d1.nodeType ' + d1.nodeType);
    
    //document.body.appendChild(d);
    dPlaceHolder.parentNode.replaceChild(d1, dPlaceHolder);
    //dPlaceHolder.parentNode.removeChild(dPlaceHolder);
    
    // then need to activate the item that was put in, (also re-make the array of sections);
    
    removeNode(d);
    
    var s = new Section(d1);
    sections.push(s);
    
    // then add it to the list of columns's sections.
    var col = d1.parentNode.ctrl;
    col.sections.push(s);
    
}
error_getSectionContent = function(xml) {
    alert ('error_getSectionContent xml.text ' + xml.text);
}


registerAddSection = function(columnID, templateID, order, callback, errorCallback) {
    var tUrl = getAbsoluteRootUrl() + wsContentUrl;
    //alert ('tUrl ' + tUrl);
    var sr = new SoapRequest(tUrl, 'AddSection', nSpace);
    // get the bits after the '_' for the section, column ids.
    sr.addNewParam('columnIDStr', columnID);
    sr.addNewParam('templateIDStr', templateID);
    sr.addNewParam('orderStr', order);
    sr.send(callback, errorCallback);
}

cb_registerAddSection = function(xml) {
    //alert('xml ' + xml);
    //alert('xml.xml ' + xml.xml);
    
    //var zSer = new zXMLSerializer();
    //var xmlText = zSer.serializeToString(xml);
    //alert('xmlText ' + xmlText);
    
    //var n = xml.getElementsByTagName('AddSectionResult')[0];
    //alert ('n ' + n);
    //alert ('n.nodeValue ' + n.nodeValue);
    //var cn = n.childNodes[0];
    //alert ('cn ' + cn);
    //alert ('cn.nodeValue ' + cn.nodeValue);
    //var sectionID = parseInt(cn.nodeValue);
    // then we download that section's html.
    //alert ('sectionID ' + sectionID);
    
    var sectionID = simulatedGetElementStringContentByTagName(xml, 'AddSectionResult');
    //alert ('sectionID ' + sectionID);
    
    getSectionContent(sectionID, cb_getSectionContent, error_getSectionContent);
    
}
error_registerAddSection = function(xml) {
    alert('error_registerAddSection');
}

registerMove = function(sectionID, columnID, order, callback, errorCallback) {
    //url, methodName, xmlNamespace
    // get the base url that is being used right now.
    var tUrl = getAbsoluteRootUrl() + wsContentUrl;
    var sr = new SoapRequest(tUrl, 'MoveSection', nSpace);
    
    var pos1 = sectionID.lastIndexOf('_');
    sectionID = sectionID.substring(pos1 + 1);
    
    pos1 = columnID.lastIndexOf('_');
    columnID = columnID.substring(pos1 + 1);
    
    sr.addNewParam('sectionIDStr', sectionID);
    sr.addNewParam('columnIDStr', columnID);
    sr.addNewParam('newOrderStr', order);
    //p.send = function(/* Callback */ callback, errorCallback, callbackParam) {
    
    sr.send(callback, errorCallback);
}

cb_registerMove = function(xml) {
    //alert ('cb_registerMove');
}
cb_error_registerMove = function(xml, status) {
    alert ('cb_error_registerMove status: ' + status);
    alert ('cb_error_registerMove xml: ' + xml.text);
}

registerDelete = function(sectionID, callback, errorCallback) {
    var tUrl = getAbsoluteRootUrl() + wsContentUrl;
    var sr = new SoapRequest(tUrl, 'DeleteSection', nSpace);
    var pos1 = sectionID.lastIndexOf('_');
    sectionID = sectionID.substring(pos1 + 1);
    sr.addNewParam('sectionIDStr', sectionID);
    sr.send(callback, errorCallback, sectionID);
}

divContainsEmptySection = function(div) {
    var foundEmptySection;
    for (var c = 0; c < div.childNodes.length; c++) {
        var d = div.childNodes[c];
        var className = getElClassName(d)
        if (className == 'empty-section') {
            foundEmptySection = d;
        }
    }
    if (foundEmptySection) return true;
    return false;
}
divContainsSectionOrEmptySection = function(div) {
    var foundSection;
    for (var c = 0; c < div.childNodes.length; c++) {
        var d = div.childNodes[c];
        var className = getElClassName(d)
        if (className == 'section-content') {
            foundSection = d;
        }
        if (className == 'empty-section') {
            foundSection = d;
        }
    }
    if (foundSection) return true;
    return false;
}

ensureColumndivContainsSectionOrEmptySectionOrEmptySection = function(columnDiv) {
    var sectionDivs = new Array();
    var foundEmptySection;
    var foundDPlaceholder;
    for (var c = 0; c < columnDiv.childNodes.length; c++) {
        var d = columnDiv.childNodes[c];
        var className = getElClassName(d)
        if (className == 'section-content') {
            sectionDivs.push(d);
        }
        if (className == 'empty-section') {
            foundEmptySection = d;
        }
        if (className == 'dPlaceHolder') {
            foundDPlaceholder = d;
        }
    }
    
    // also make sure it does not already contain an empty-section
    
    if (sectionDivs.length == 0 &! foundEmptySection &! foundDPlaceholder) {
        var divEmptySection = nD();
        setClass (divEmptySection, 'empty-section');
        columnDiv.appendChild(divEmptySection);
    }
    
    if (foundEmptySection && foundDPlaceholder) {
        removeNode(foundEmptySection);
        if (columnDivWhichHasHadEmptySectionRemoved && columnDivWhichHasHadEmptySectionRemoved != columnDiv) {
            ensureColumndivContainsSectionOrEmptySectionOrEmptySection(columnDivWhichHasHadEmptySectionRemoved);
        }
        columnDivWhichHasHadEmptySectionRemoved = columnDiv;
    }
    
    // if it contains the placeholder:
    
//    alert('sectionDivs.length ' + sectionDivs.length);
//    
//    if (sectionDivs.length > 1 && foundEmptySection) {
//        //alert('removing empty');
//        removeNode(foundEmptySection);
//    }
    
}

cb_registerDelete = function(xml, sectionID) {
    var divSection = document.getElementById('section_' + sectionID);
    var sectionParent = divSection.parentNode;
    sectionParent.removeChild(divSection);
    ensureColumndivContainsSectionOrEmptySectionOrEmptySection(sectionParent);
}
cb_error_registerDelete = function(xml, status) {
    alert ('cb_error_registerDelete status: ' + status);
    alert ('cb_error_registerDelete xml: ' + xml.text);
}

removeSection = function(element) {
    // the element is an <a> tag. the section to remove is identified by the last bit of its id tag.
    
    // id = "remove_section_18"
    registerDelete(element.id, cb_registerDelete, cb_error_registerDelete)
}

toggleSection = function(element) {
    var divSection = element.parentNode.parentNode;
    if (divSection.ctrl) divSection.ctrl.toggle();
}

TemplateSelector = function(div) {
    this.div = div;
    this.init();
}
var p = TemplateSelector.prototype;
p.init = function() {
    this.div.ctrl = this;
    // find the button?
    this.isExpanded = true;
    // maybe find the toggle button - make it so that this changes the src and alt image when pressed.
    // need to register the inner div.
    this.divInner = getChildNodeByCssClass(this.div, 'add-template-body');
    
}
p.toggle = function() {
    //section-content-inner
    // call a smooth expand/contract function.
    // perhaps this is not possible to do on a given div if it does not have a size set.
    if (!this.isBeingResized) {
        if (this.isExpanded) {
            //this.divInner.style.display = 'none';
            this.isBeingResized = true;
            collapseDiv(this.divInner);
        } else {
            //this.divInner.style.display = 'block';
            this.isBeingResized = true;
            expandDiv(this.divInner);
        }
    }
    //this.isExpanded = !this.isExpanded;
}
p.collapseComplete = function() {
    //alert ('collapseComplete');
    this.isExpanded = false;
    this.isBeingResized = false;
}
p.expandComplete = function() {
    this.isExpanded = true;
    this.isBeingResized = false;
}

toggleTemplateSelector = function(divToggleButton) {
    var templateSelector = divToggleButton.parentNode.ctrl;
    
    templateSelector.toggle();
}

// perhaps this could be part of draggable and droppable node classes.

setupShowNodes = function() {
    // expand + contract on spans with class expander_node_title
    var expanderNodeTitles = getElementsByTagNameAndCssClass('span', 'expander_node_title');
    
    for (var c = 0; c < expanderNodeTitles.length; c++) {
        activateExpanderNodeByTitleSpan(expanderNodeTitles[c]);
    }
    
    // make the li draggable nodes draggable.
    
}

activateExpanderNodeByTitleSpan = function(expanderNodeTitleSpan) {
    var expanderNode = expanderNodeTitleSpan.parentNode;
    //alert ('expanderNode.tagName ' + expanderNode.tagName);
    var expanderNodeContent = getChildNodeByCssClass(expanderNode, 'expander_node_content');
    //if (expanderNodeContent) alert(expanderNodeContent.innerHTML);
    // also need the image that is used as a toggle
    
    var found = false;
    var c = 0;
    while (c < expanderNodeTitleSpan.childNodes.length && !found) {
        var node = expanderNodeTitleSpan.childNodes[c];
        //alert ('node.tagName ' + node.tagName);
        
        if (node.tagName) {
            //alert ('node.tagName.toLowerCase() ' + node.tagName.toLowerCase());
        }
        
        if (node.tagName && node.tagName.toLowerCase() == 'img') {
            found = node;
        }
        c++;
    }
    //alert('found ' + found);
    if (found) {
        // set it up so that a click on the image causes the expander_node_content to expand / contract.
        found.onclick = function() {
            //expanderNodeContent.style.display = 'block';
            // want to toggle if it is shown or not.
            if (!(expanderNodeContent.isResizing || expanderNodeContent.isExpanded)) {
                expanderNodeContent.isResizing = true;
                if (!expanderNodeContent.expandComplete) expanderNodeContent.expandComplete = function() {
                    //alert ('expandComplete');
                    expanderNodeContent.isResizing = false;
                    expanderNodeContent.isExpanded = true;
                }
                expandDiv(expanderNodeContent);
                return false;
            }
            //alert ('(!expanderNodeContent.isResizing) && (expanderNodeContent.isExpanded)' + (!expanderNodeContent.isResizing) && (!expanderNodeContent.isExpanded));
            
            if ((!expanderNodeContent.isResizing) && (expanderNodeContent.isExpanded)) {
                // then contract it.
                expanderNodeContent.isResizing = true;
                if (!expanderNodeContent.collapseComplete) expanderNodeContent.collapseComplete = function() {
                    //alert ('expandComplete');
                    
                    expanderNodeContent.isResizing = false;
                    expanderNodeContent.isExpanded = false;
                }
                collapseDiv(expanderNodeContent);
                return false;
            }
        }
        //found.onmousedown = function() {return false;};
    }
    // now 
}

activateDragDropListItems = function() {
    activateDraggableListItems();
    activateDroppableListItems();
}

activateDraggableListItems = function() {
    var lis = document.getElementsByTagName('li');
    for (var c = 0; c < lis.length; c++) {
        var li = lis[c];
        if (getElClassName(li) == 'draggable_node') {
            var dn = new DraggableListItemNode(li);
        }
    }
}

activateDroppableListItems = function() {
    var lis = document.getElementsByTagName('li');
    for (var c = 0; c < lis.length; c++) {
        var li = lis[c];
        if (getElClassName(li) == 'droppable_node') {
            var dn = new DroppableListItemNode(li);
        }
    }
}

setStatusHTML = function(value) {
    var spnStatus = $('spnStatus');
    if(spnStatus) spnStatus.innerHTML = value;
}

beginListItemDrag = function(listItem) {
    //sectionBeingDragged = listItem;
    document.body.style.cursor = 'move';
    
    // put the placeholder in the DOM where the listItem el was (or before it because the
    // li will be removed).
    
    
    
    // (need to get the position of the listItem.)
    var pos = listItem.getPos();
    //alert ('pos ' + pos);
    
    var divDragContainer = nD();
    listItem.div = divDragContainer;
    
    liPlaceHolder = nE('li');
    liPlaceHolder.id = 'liPlaceHolder';
    setClass(liPlaceHolder, 'liPlaceHolder');
    listItem.el.parentNode.insertBefore(liPlaceHolder, listItem.el);
    cacheLIDragDropPositions();
    
    divDragContainer.appendChild(listItem.el);
    
    dragOffsetX = posX - pos[0];
    dragOffsetY = posY - pos[1];
    
    dragCorrectionX = 0;
    dragCorrectionY = 0;
    
    var w = listItem.el.offsetWidth;
    var h = listItem.el.offsetHeight;
    
    sectionBeingDragged = listItem;
    divDragContainer.style.position = 'absolute';
    
    //alert ('dragOffsetX ' + dragOffsetX + ', dragOffsetY ' + dragOffsetY);
    //alert ('posX ' + posX + ', posY ' + posY);
    
    var x = posX - dragOffsetX;
    var y = posY - dragOffsetY;
    
    //alert ('x ' + x + ', y ' + y);
    
    divDragContainer.style.left = x + 'px';
    divDragContainer.style.top = y + 'px';
    
    
    // hide the expander_node_content
    
    var expanderNodeContent = getChildNodeByCssClass(listItem.el, 'expander_node_content');
    if (expanderNodeContent) expanderNodeContent.style.display = 'none';
    
    document.body.appendChild(divDragContainer);
    
    //cache the positions of the various droppable areas
    //cacheDroppableListItemNodesPositions();
    cacheLIDragDropPositions();
}

cacheLIDragDropPositions = function() {
    cacheLIPlaceHolderPosition();
    cacheDroppableListItemNodesPositions();
    cacheDraggableListItemNodesPositions();
}

cacheLIPlaceHolderPosition = function() {
    if (liPlaceHolder && liPlaceHolder.parentNode) {
        liPlaceHolderPos = findPos(liPlaceHolder);
        liPlaceHolderWidth = liPlaceHolder.offsetWidth;
        liPlaceHolderHeight = liPlaceHolder.offsetHeight;
    }
}

cacheDroppableListItemNodesPositions = function() {
    for (var c = 0; c < droppableListItemNodes.length; c++) {
        droppableListItemNodes[c].cachePosition();
    }
}
cacheDraggableListItemNodesPositions = function() {
    for (var c = 0; c < draggableListItemNodes.length; c++) {
        draggableListItemNodes[c].cachePosition();
    }
}

var linInMiddleOf;

// need to make this so that it responds to the mouse hovering over it.

DraggableListItemNode = function(el) {
    this.el = el;
    this.init();
}
var p = DraggableListItemNode.prototype;
p.init = function() {
    // there are two numerical components that need to be got from the el's id.
    draggableListItemNodes.push(this);
    this.el.ctrl = this;
    var pos1 = this.el.id.lastIndexOf('_');
    var i2 = parseInt(this.el.id.substring(pos1 + 1));
    var pos2 = this.el.id.lastIndexOf('_', pos1 - 1);
    var i1 = parseInt(this.el.id.substring(pos2 + 1, pos1));
    // set us up the node
    //this.el.onmousedown = function(e) {this.ctrl.el_onmousedown(); return false;}
    this.spanLabel = this.el.getElementsByTagName('span')[0];
    // want to get the span text?
    
    this.spanLabelText = this.spanLabel.getElementsByTagName('span')[0];
    // need to get child span, not span itself.
    //alert ('this.spanLabelText is this.spanLabel ' + (this.spanLabelText == this.spanLabel));
    //alert ('this.spanLabelText is this.spanLabel ' + (this.spanLabelText.parentNode == this.spanLabel));
    this.expanderNodeContent = getChildNodeByCssClass(this.el, 'expander_node_content'); 
    
    //alert ('this.expanderNodeContent ' + this.expanderNodeContent);
    // should only begin the drag from clicking on the span. 
    this.spanLabelText.onmousedown = function(e) {this.parentNode.parentNode.ctrl.el_onmousedown(); return false;}
    // while dragging, either remove the node from the DOM or create a ghost clone of it.
    
    this.el.onmouseup = function(e) {
        // stop the drag
        if(!e) var e=window.event;
        if (sectionBeingDragged) {
            if (e.button != 2) {
                endLiDrag(sectionBeingDragged);
                document.oncontextmenu=null;
            }
        }
        
        
    }
}

// isExpandable?

//p.elHasChildNodes = fun

p.getIsExpanded = function() {
    if (!this.expanderNodeContent) return false;
    // look at the display property of this.expanderNodeContent
    
    var display;
    var height;
    
    if (this.expanderNodeContent.currentStyle){
        display = this.expanderNodeContent.currentStyle['display'];
        height = toInt(this.expanderNodeContent.currentStyle['height']);
    }
    if (window.getComputedStyle) {
        display = window.getComputedStyle(this.expanderNodeContent, '').getPropertyValue('display');
        height = toInt(window.getComputedStyle(this.expanderNodeContent, '').getPropertyValue('height'));
    }
    
    if (display == 'none') return false;
    if (height == 0) return false;
    
    return true;
    
}

p.getSpanLabelContainmentInformation = function(x, y) {
    // do not count it as being contained if this is the sectionBeingDragged.
    var res = new Object();
    if (this == sectionBeingDragged) return false;
    var bottom = this.spanLabelPos[1] + this.spanLabelHeight;
    if ((y >= this.spanLabelPos[1]) && (y <= bottom)) {
        //this.spanLabel.style.backgroundColor = '#ABCDEF';
        res.offsetTop = y - this.spanLabelPos[1];
        res.offsetBottom = bottom - y;
    }
    return res;
}

//p.getContainmentInformation = function(x, y) {
//    // not just is it contained within this, is this within the top or bottom margin?
//    
//    // maybe is it just outside its top or bottom?
//    
//    var res = new Object();
//    
//    var bottom = this.pos[1] + this.height;
//    if ((y >= this.pos[1]) && (y <= bottom)) {
//        this.el.style.backgroundColor = '#ABCDEF';
//        res.offsetTop = y - this.pos[1];
//        res.offsetBottom = bottom - y;
//    }
//    
//    return res;
//}

p.cachePosition = function() {
    //this.pos = this.getPos();
    //this.width = this.el.offsetWidth;
    //this.height = this.el.offsetHeight;
    
    this.spanLabelPos = findPos(this.spanLabel);
    this.spanLabelWidth = this.spanLabel.offsetWidth;
    this.spanLabelHeight = this.spanLabel.offsetHeight;
    
    // these may have multiple lines.
    // we are most interested in the top line label.
    // also need to store information about wh
}

var containmentArea;

p.indicateContainment = function(continmentInfo) {
    if (!liPlaceHolder) {
        //dPlaceHolder = nD();
        liPlaceHolder = nE('li');
        liPlaceHolder.id = 'liPlaceHolder';
        setClass(liPlaceHolder, 'liPlaceHolder');
    }
    
    var isAtTop = continmentInfo.offsetTop <= 2;
    var isAtBottom = continmentInfo.offsetBottom <= 3;
    var isWithin = continmentInfo.offsetBottom || continmentInfo.offsetBottom == 0 || continmentInfo.offsetTop || continmentInfo.offsetTop == 0;
    
    var isInMiddle = isWithin && (!(isAtTop || isAtBottom));
    
    var ilph = isWithinLiPlaceHolder(posX, posY);
    if (ilph) isWithin = false;
    
    if (isWithin) {
        
        var canHaveSiblingDropped;
        if (isAtTop || isAtBottom) {
            //alert('looking');
            canHaveSiblingDropped = this.getCanHaveSiblingDropped();
            //linInMiddleOf = null;
        }
        
        if (isAtBottom) {
            //setStatusHTML ('isAtBottom');
            containmentArea = 'bottom';
        }
        if (isAtTop) {
            //setStatusHTML ('isAtTop');
            containmentArea = 'top';
        }
        
        if (isInMiddle) {
            containmentArea = 'middle';
            // raise an event?
            // a countdown has to begin.
            //setStatusHTML ('isInMiddle');
            // it gets stopped if the mouse moves out of the middle.
            // needs to work with the onmousemove / ondrag.
            
            if (linInMiddleOf == this) {
                // but need to respond if it has been put in the middle having been moved away from middle
                //setStatusHTML ('same middle');
                // but there may not be a timeout going.
                
                if (!overMiddleCountdownIsGoing) {
                    this.onovermiddle();
                }
                
                // the countdown will proceed.
                
                // may need to set linInMiddleOf to null at the correct point - when the item is put above or below.

            } else {
                //setStatusHTML ('over middle of DraggableListItemNode');
                linInMiddleOf = this;
                this.onovermiddle();
            }
        } else {
            //setStatusHTML ('not over middle of DraggableListItemNode');
            //linInMiddleOf = null;
        }
        
        if (canHaveSiblingDropped && isAtTop) {
            
            var previousSibling = getPreviousSibling(this.el);
            //previousSibling.style.backgroundColor = '#000000';
            if (!liPlaceHolder) {
                //dPlaceHolder = nD();
                liPlaceHolder = nE('li');
                liPlaceHolder.id = 'liPlaceHolder';
                setClass(liPlaceHolder, 'liPlaceHolder');
            }
            if (previousSibling && previousSibling != liPlaceHolder) {
                //setStatusHTML ('');
                this.el.parentNode.insertBefore(liPlaceHolder, this.el);
                cacheLIDragDropPositions();
            }
            if (!previousSibling) {
                // place it first.
                //setStatusHTML ('');
                this.el.parentNode.insertBefore(liPlaceHolder, this.el);
                cacheLIDragDropPositions();
            }
            
            //setStatusHTML ('overMiddleCountdownIsGoing ' + overMiddleCountdownIsGoing);
        }
        
        if (canHaveSiblingDropped && isAtBottom) {
            // act differently if the li is expanded.
            var nextSibling = getNextSibling(this.el);
            //previousSibling.style.backgroundColor = '#000000';
            // need to insert after (before the next sibling)
            if (nextSibling && nextSibling != liPlaceHolder) {
                //insert after this one, before the next
                //setStatusHTML ('');
                this.el.parentNode.insertBefore(liPlaceHolder, nextSibling);
                cacheLIDragDropPositions()
            }
            if (!nextSibling) {
                // place it first.
                //setStatusHTML ('');
                this.el.parentNode.appendChild(liPlaceHolder);
                cacheLIDragDropPositions();
            }
            //setStatusHTML ('overMiddleCountdownIsGoing ' + overMiddleCountdownIsGoing);
        } 
    }
}

var timeoutOverMiddle;
var overMiddleCountdownIsGoing = false;


beenOverMiddle = function() {
    linInMiddleOf.onbeenovermiddle();
    overMiddleCountdownIsGoing = false;
    
}

p.expand = function() {
    var expanderNodeContent = this.expanderNodeContent;
    if (!(expanderNodeContent.isResizing || expanderNodeContent.isExpanded)) {
        expanderNodeContent.isResizing = true;
        if (!expanderNodeContent.expandComplete) expanderNodeContent.expandComplete = function() {
            //alert ('expandComplete');
            expanderNodeContent.isResizing = false;
            expanderNodeContent.isExpanded = true;
            cacheLIDragDropPositions();
        }
        expandDiv(expanderNodeContent);
        return false;
    }
        //alert ('(!expanderNodeContent.isResizing) && (expanderNodeContent.isExpanded)' + (!expanderNodeContent.isResizing) && (!expanderNodeContent.isExpanded));
}

p.collapse = function() {
    var expanderNodeContent = this.expanderNodeContent;
    if ((!expanderNodeContent.isResizing) && (expanderNodeContent.isExpanded)) {
        // then contract it.
        expanderNodeContent.isResizing = true;
        if (!expanderNodeContent.collapseComplete) expanderNodeContent.collapseComplete = function() {
            //alert ('expandComplete');
            
            expanderNodeContent.isResizing = false;
            expanderNodeContent.isExpanded = false;
            cacheLIDragDropPositions();
        }
        collapseDiv(expanderNodeContent);
        return false;
    }
}

p.onovermiddle = function() {
    // start a countdown so that if the cursor stays over then 
    clearTimeout(timeoutOverMiddle);
    timeoutOverMiddle = setTimeout(beenOverMiddle, 400);
    overMiddleCountdownIsGoing = true;
    //setStatusHTML('start ' + timeoutOverMiddle);
}

p.getContainsExpanderNodeWithContent = function() {
    if (this.containsExpanderNodeWithContent != null) return this.containsExpanderNodeWithContent;
    var res = true;
    if (!this.expanderNodeContent) res = false;
    if (res) var ul = this.expanderNodeContent.getElementsByTagName('ul')[0];
    if (!ul) res = false;
    if (res) var li = this.expanderNodeContent.getElementsByTagName('li')[0];
    if (!li) {
        res = false;
    } else {
        if (li.id == 'liPlaceHolder' && ul.childNodes.length == 1) res = false;
    }
    
    this.containsExpanderNodeWithContent = res;
    return res;
}

p.onbeenovermiddle = function() {
    //alert('onbeenovermiddle');
    // expand this to show there is going to be a node placed here below.
    
    // if there is already a group of childNodes, and it is open, do nothing.
    
    // if there is a group of childnodes and it is closed / hidden, expand it.
    //alert ('this.getIsExpanded() ' + this.getIsExpanded());
    
    if (containmentArea == 'middle') {
        setStatusHTML('this.getIsExpanded() ' + this.getIsExpanded());
        
        if (!this.getIsExpanded()) {
            //alert('this.expanderNodeContent ' + this.expanderNodeContent);
            if (this.getContainsExpanderNodeWithContent()) {
                this.expand();
            } else {
                // we make it so that this indicates the placeholder as a child node of this.
                //alert ('child placeholder');
                this.showNewChildPlaceholder();
            }
        } else {
            // perhaps it should not be expanded ever.
            // but if the child node has been removed, we need to make it show the child node again.
            
            // we want to immediately collapse it, then add the placeholder, then expand it.
            
            //this.expanderNodeContent.style.height = '0px';
            this.showNewChildPlaceholder();
            //this.expand();
            //this.showNewChildPlaceholder();
            
            
        }
    }
    // if there are no childnodes, we need to indicate that a childnode will be created.
}

p.showNewChildPlaceholder = function() {
    // would be nice to put the stuff into the DOM then expand the node.
    if (!this.expanderNodeContent) {
        this.expanderNodeContent = nD();
        setClass(this.expanderNodeContent, 'expander_node_content');
        this.el.appendChild(this.expanderNodeContent);
        //expander_node_content
    }
    
    var ul = this.expanderNodeContent.getElementsByTagName('ul')[0];
    if (!ul) ul = nE('ul');
    this.expanderNodeContent.appendChild(ul);
    //var li = nE
    
    if (!liPlaceHolder) {
        //dPlaceHolder = nD();
        liPlaceHolder = nE('li');
        liPlaceHolder.id = 'liPlaceHolder';
        setClass(liPlaceHolder, 'liPlaceHolder');
    }
    ul.appendChild(liPlaceHolder);
    cacheLIDragDropPositions();
    this.expand();
    this.isShowingNewChildPlaceholder = true;
    
    //this.expanderNodeContent.display = 'block';
    
    
    // then make it so that this collapses if the mouse moves out of the central section.
    
}


p.onmiddleout = function() {
    //alert('onmiddleout');
    // stop the countdown.
    //setStatusHTML('stop ' + timeoutOverMiddle);
    
    //linInMiddleOf = null;
    
    clearTimeout(timeoutOverMiddle);
    
    // only collapse it if it is showing the placeholder and no other child nodes.
    
    // we want to count something: the LI, is there more then one?
    // if there is only one, is it the placeholder.
    
    
    var containsExpanderNodeWithContent = this.getContainsExpanderNodeWithContent();
    
    
    if (this.isShowingNewChildPlaceholder) {
        if (!containsExpanderNodeWithContent) {
            this.collapse();
        } else {
            // remove the child place holder from the dom?
            
            
        }
        
        
        
        this.isShowingNewChildPlaceholder = false;
    }
    // if this is showing the liPlaceHolder as a child, then collapse.
    
    
    
}



p.getCanHaveSiblingDropped = function() {
    // for some reason, is not possible to get reference to the ctrl through the dom, looking
    // at the parents.
    
    // look at the cssClass of the great grandparent.
    var ancestorClass = getElClassName(this.el.parentNode.parentNode.parentNode);
    //setStatusHTML('ancestorClass ' + ancestorClass);
    
    var res = true;
    if (!ancestorClass) res = false;
    return res;
    
}

p.getPos = function() {
    return findPos(this.el)
}
p.el_onmousedown = function() {
    //alert('el_onmousedown');
    beginListItemDrag(this);
}
p.type = 'draggableListItemNode';

var droppableListItemNodes = new Array();
var draggableListItemNodes = new Array();

// This also needs to respond to items being dragged within it, because items may be put above / below
// this.

// Droppables should NOT be draggable.

DroppableListItemNode = function(el) {
    this.el = el;
    this.init();
}
var p = DroppableListItemNode.prototype;
p.init = function() {
    // Droppable nodes are always draggable too.
    droppableListItemNodes.push(this);
    this.el.ctrl = this;
    
    //alert('this.el.tagName ' + this.el.tagName)
    
    var pos1 = this.el.id.lastIndexOf('_');
    var i2 = parseInt(this.el.id.substring(pos1 + 1));
    var pos2 = this.el.id.lastIndexOf('_', pos1 - 1);
    var i1 = parseInt(this.el.id.substring(pos2 + 1, pos1));
    this.spanLabel = this.el.getElementsByTagName('span')[0];
    // want to get the span text?
    
    this.spanLabelText = this.spanLabel.getElementsByTagName('span')[0];
    this.expanderNodeContent = getElementByCssClass(this.el, 'expander_node_content');
    
    this.containsExpanderNodeWithContent = null;
    
    // should only begin the drag from clicking on the span.     
    //this.spanLabelText.onmousedown = function(e) {this.parentNode.parentNode.ctrl.el_onmousedown(); return false;}
}

p.getIsExpanded = function() {
    // look at this.expanderNodeContent. does it have a height set in CSS? if not it is at full height.
    var div = this.expanderNodeContent;
    var res = true;
    //setStatusHTML(this.expanderNodeContent.style.height);
    //if (this.expanderNodeContent.style.height) res = false;
    var height;
    
    if (div.currentStyle){
        height = toInt(div.currentStyle['height']);
    }
    
    if (window.getComputedStyle) {
        height = toInt(window.getComputedStyle(div, '').getPropertyValue('height'));
    }
    //setStatusHTML(height);
    if (!height) res = false;
    return res;
}
// expansion state

p.getCanHaveSiblingDropped = function() {
    var ancestorClass = getElClassName(this.el.parentNode.parentNode.parentNode);
    //setStatusHTML('ancestorClass ' + ancestorClass);
    var res = true;
    if (!ancestorClass) res = false;
    if (ancestorClass == 'droppableNode') return true;
    
    return res;
}

p.getContainsExpanderNodeWithContent = function() {
    if (this.containsExpanderNodeWithContent != null) return this.containsExpanderNodeWithContent;
    var res = true;
    if (!this.expanderNodeContent) res = false;
    if (res) var ul = this.expanderNodeContent.getElementsByTagName('ul')[0];
    if (!ul) res = false;
    if (res) var li = this.expanderNodeContent.getElementsByTagName('li')[0];
    if (!li) res = false;
    this.containsExpanderNodeWithContent = res;
    return res;
}

// indicates that the item being dragged is above / within / below this.
p.indicateContainment = function(continmentInfo) {
    // let's deal with siblings first
    // if it is before:
    // is there a previous sibling?
    
    // if so put a placeholder there.
    // at the top
    //alert('this.el.ctrl ' + this.el.ctrl);
    
    if (!liPlaceHolder) {
        //dPlaceHolder = nD();
        liPlaceHolder = nE('li');
        liPlaceHolder.id = 'liPlaceHolder';
        setClass(liPlaceHolder, 'liPlaceHolder');
    }
    
    var isAtTop = continmentInfo.offsetTop <= 3;
    var isAtBottom = continmentInfo.offsetBottom <= 3;
    var isWithin = continmentInfo.offsetBottom || continmentInfo.offsetBottom == 0 || continmentInfo.offsetTop || continmentInfo.offsetTop == 0;
    var isInMiddle = isWithin &! isAtTop &! isAtBottom;
    
    var ilph = liPlaceHolder.parentNode && isWithinLiPlaceHolder(posX, posY);
    if (ilph) {
        isWithin = false;
        //$('spnStatus').innerHTML = 'ilph';
    }
    
//    if (isInMiddle && isAtTop) {
//        alert('isInMiddle && isAtTop');
//    }
//    if (isInMiddle && isAtBottom) {
//        alert('isInMiddle && isAtBottom');
//    }
    
    if (isWithin) {
        if (!liPlaceHolder) {
            //dPlaceHolder = nD();
            liPlaceHolder = nE('li');
            liPlaceHolder.id = 'liPlaceHolder';
            setClass(liPlaceHolder, 'liPlaceHolder');
        }
        
        var canHaveSiblingDropped;
        
        if (isInMiddle) {
            // raise an event?
            // a countdown has to begin.
            
            // it gets stopped if the mouse moves out of the middle.
            // needs to work with the onmousemove / ondrag.
            
            if (linInMiddleOf == this) {
                // the countdown will proceed.
                
            } else {
                // only begin the countdown if it will potentially expand, if it is expandable.
                // there must be this.expanderNodeContent
                // perhaps that needs content inside it too.
                
                // a function to see if it contains expandable content.
                
                //if (this.getContainsExpanderNodeWithContent()) {
                    //setStatusHTML('enc');
                    linInMiddleOf = this;
                    this.onovermiddle();
                //} else {
                    //setStatusHTML('no enc');
                    
                //}
            }
        }
        
        if (isAtTop || isAtBottom) {
            //alert('looking');
            canHaveSiblingDropped = this.getCanHaveSiblingDropped();
            
        }
        
        if (canHaveSiblingDropped && isAtTop) {
            //$('spnStatus').innerHTML = 'isAtTop, continmentInfo.offsetBottom ' + continmentInfo.offsetBottom;
            //alert('isAtTop');
            var previousSibling = getPreviousSibling(this.el);
            //previousSibling.style.backgroundColor = '#000000';
            
            if (previousSibling && previousSibling != liPlaceHolder) {
                //previousSibling.style.backgroundColor = '#000000';
                this.el.parentNode.insertBefore(liPlaceHolder, this.el);
                cacheLIDragDropPositions();
            }
            if (!previousSibling) {
                // place it first.
                this.el.parentNode.insertBefore(liPlaceHolder, this.el);
                cacheLIDragDropPositions();
            }
            //alert('isAtTop');
        }
        
        if (canHaveSiblingDropped && isAtBottom) {
            //$('spnStatus').innerHTML = 'isAtBottom, continmentInfo.offsetBottom ' + continmentInfo.offsetBottom;;
            var nextSibling = getNextSibling(this.el);
            //previousSibling.style.backgroundColor = '#000000';
            // need to insert after (before the next sibling)
            // if the node is expanded don't show the placeholder as the next sibling.
            var isExpanded = this.getIsExpanded();
            if (!isExpanded) {
                if (nextSibling && nextSibling != liPlaceHolder) {
                    //insert after this one, before the next
                    this.el.parentNode.insertBefore(liPlaceHolder, nextSibling);
                    cacheLIDragDropPositions();
                }
                if (!nextSibling) {
                    // place it first.
                    this.el.parentNode.appendChild(liPlaceHolder);
                    cacheLIDragDropPositions();
                }
            }
        }
        
        if (isInMiddle) {
            // remove the liPlaceholder.
            //$('spnStatus').innerHTML = 'isInMiddle, continmentInfo.offsetBottom ' + continmentInfo.offsetBottom;;
            //alert ('isInMiddle');
            if (removeLIPlaceHolderWhenInMiddle) {
                if (liPlaceHolder.parentNode) {
                    removeNode(liPlaceHolder);
                    cacheLIDragDropPositions();
                    
                }
            }
        }
    }
    
    // at the bottom
//    
//    if (isInMiddle) {
//    //if (!(continmentInfo.offsetBottom <= 3 || continmentInfo.offsetTop <= 3)) {
//        //alert ('continmentInfo.offsetBottom ' + continmentInfo.offsetBottom);
//        //alert ('continmentInfo.offsetTop <= 3 ' +  continmentInfo.offsetTop <= 3);
//        
//        // make sure that the node being dragged does not have the placeholder as a sibling.
//        // / placeholder not in doc.
//        //alert ('isInMiddle');
//        
//        //if (liPlaceHolder.parentNode) removeNode(liPlaceHolder);
//        
//    }
    
    //cacheDroppableListItemNodesPositions();
    
//    if (!isAtTop &! isAtBottom && ) {
//        if (liPlaceHolder.parentNode) removeNode(liPlaceHolder);
//    }
    
}

p.getSpanLabelContainmentInformation = function(x, y) {
    var res = new Object();
    if (this == sectionBeingDragged) return false;
    //var bottom = this.spanLabelPos[1] + this.spanLabelHeight;
    // thought +1 could help.
    var bottom = this.spanLabelPos[1] + this.spanLabelHeight + 1;
    if ((y >= this.spanLabelPos[1]) && (y <= bottom)) {
        //this.spanLabel.style.backgroundColor = '#ABCDEF';
        
        res.offsetTop = y - this.spanLabelPos[1];
        res.offsetBottom = bottom - y;
    }
    
    return res;
}

//p.getContainmentInformation = function(x, y) {
//    // not just is it contained within this, is this within the top or bottom margin?
//    
//    // maybe is it just outside its top or bottom?
//    
//    var res = new Object();
//    
//    var bottom = this.pos[1] + this.height;
//    if ((y >= this.pos[1]) && (y <= bottom)) {
//        this.el.style.backgroundColor = '#ABCDEF';
//        res.offsetTop = y - this.pos[1];
//        res.offsetBottom = bottom - y;
//    }
//    
//    return res;
//}


p.cachePosition = function() {
    //this.pos = this.getPos();
    //this.width = this.el.offsetWidth;
    //this.height = this.el.offsetHeight;
    
    this.spanLabelPos = findPos(this.spanLabel);
    this.spanLabelWidth = this.spanLabel.offsetWidth;
    this.spanLabelHeight = this.spanLabel.offsetHeight;
    
    // these may have multiple lines.
    // we are most interested in the top line label.
    
    // also need to store information about wh
}
p.getPos = function() {
    return findPos(this.el)
}
p.el_onmousedown = function() {
    //alert('el_onmousedown');
    beginListItemDrag(this);
}

p.expand = function() {
    var expanderNodeContent = this.expanderNodeContent;
    //alert('expanderNodeContent.isExpanded ' + expanderNodeContent.isExpanded);
    if (!(expanderNodeContent.isResizing || expanderNodeContent.isExpanded)) {
        expanderNodeContent.isResizing = true;
        if (!expanderNodeContent.expandComplete) expanderNodeContent.expandComplete = function() {
            //alert ('expandComplete');
            expanderNodeContent.isResizing = false;
            expanderNodeContent.isExpanded = true;
            
            cacheLIDragDropPositions();
        }
        expandDiv(expanderNodeContent);
        return false;
    }
    //alert ('(!expanderNodeContent.isResizinsg) && (expanderNodeContent.isExpanded)' + (!expanderNodeContent.isResizing) && (!expanderNodeContent.isExpanded));
    
    // don't collapse it.
//    if ((!expanderNodeContent.isResizing) && (expanderNodeContent.isExpanded)) {
//        // then contract it.
//        expanderNodeContent.isResizing = true;
//        if (!expanderNodeContent.collapseComplete) expanderNodeContent.collapseComplete = function() {
//            //alert ('expandComplete');
//            expanderNodeContent.isResizing = false;
//            expanderNodeContent.isExpanded = false;

//            cacheLIDragDropPositions();
//        }
//        collapseDiv(expanderNodeContent);
//        return false;
//    }
}

p.onovermiddle = function() {
    // start a countdown so that if the cursor stays over then
    
    //alert('onovermiddle');
    
    clearTimeout(timeoutOverMiddle);
    timeoutOverMiddle = setTimeout(beenOverMiddle, 400);
    //setStatusHTML('start ' + timeoutOverMiddle);
    //alert('started');
}
p.onbeenovermiddle = function() {
    //alert('onbeenovermiddle');
    // expand this to show there is going to be a node placed here below.
    // if there is already a group of childNodes, and it is open, do nothing.
    
    // if there is a group of childnodes and it is closed / hidden, expand it.
    //alert ('this.getIsExpanded() ' + this.getIsExpanded());
    if (!this.getIsExpanded()) {
        //alert('this.expanderNodeContent ' + this.expanderNodeContent);
        if (this.getContainsExpanderNodeWithContent()) {
            this.expand();
        }
    }
    
    // if there are no childnodes, we need to indicate that a childnode will be created.
}

p.onmiddleout = function() {
    //alert('onmiddleout' + fsdagjnklfg.dfgdfs.sdrfgasd);
    // stop the countdown.
    //setStatusHTML('stop ' + timeoutOverMiddle);
    //linInMiddleOf = null;
    
    clearTimeout(timeoutOverMiddle);
}

p.type = 'droppableListItemNode';

template_expander_onclick = function() {
    toggleDivExpansion(document.getElementById('add-template-body'));
    
}