﻿/// <reference path="lib_core.js"/>

CDiv = function(container, options) {
    this.container = container;
    //this.className = className;
    this.options = options;
    this.init(0);
}
var p = CDiv.prototype;
p.inheritFrom(Control);

p.init = function(depth) {
    // parameter validation to throw an error if the parameters are not valid.
    
    this.superInit(depth);
    this.el = nD();
    this.el.control = this;
    if(this.options.cssClass) this.setClass(this.options.cssClass);
    if(this.options.cssFloat) {
        //alert ('this.options.cssFloat ' + this.options.cssFloat);
        this.setFloat(this.options.cssFloat);
    }
    if (this.options.width) this.setWidth(this.options.width);
    if (this.options.marginLeft) this.setMarginLeft(this.options.marginLeft);
    
    // Option to 
    this.bindElEvents();
    //this.el.innerHTML = 'yo';
    
    this.addMyEl();
}
p.el_onclick = function() {
    //alert('el_onclick');
    if (this.onclick) return this.onclick();
}
p.bindElEvents = function() {
    this.el.onclick = function() {return this.control.el_onclick();};
}

CA = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = CA.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('a');
    if(this.options.cssClass) this.setClass(this.options.cssClass);
    if(this.options.cssFloat) {
        this.setFloat(this.options.cssFloat);
    }
    this.addMyEl();
}

CSpan = function(container, options) {
    this.container = container;
    //this.className = className;
    this.options = options;
    this.init(0);
}
var p = CSpan.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(0);
    this.el = nE('span');
    if (this.options.innerHTML) this.el.innerHTML = this.options.innerHTML;
    if (this.options.cssClass) {
        setClass(this.el, this.options.cssClass);
    }
    //alert ('this.options.visible ' + this.options.visible);
    //alert ('this.options.visible==false ' + this.options.visible==false);
    
    if (this.options.visible != false) {
        if (!this.options.visible) this.options.visible = true;
    }
    this.setVisibility(this.options.visible);
    this.addMyEl();
}

// set the CSS Class

CDClear = function(container) {
    this.container = container;
    this.init(0);
}
var p = CDClear.prototype;
p.inheritFrom(CDiv);
p.init = function() {
    this.superInit(0);
    this.setClass('clearall');
    this.setStyle('clear', 'all');
    this.addMyDiv();
}

Label = function(container, options) {
    this.container = container;
    this.options = options;
    //this.text = text;
    //this.className = className;
    
    this.init(0);
}
var p = Label.prototype;
p.inheritFrom(Control);
p.init = function(depth){
    this.superInit(depth);
    var s = this.createMySpan(this.options.className);
    this.el.innerHTML = this.options.text;
    
    // currently only will be added into a div.
    //  perhaps better to use obj.el.
    this.addMyEl();
}

CDLabel = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = CDLabel.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    
//    var oL = new Object();
//    //alert ('this.options.text ' + this.options.text);
//    oL.text = this.options.text;
//    oL.className = this.options.className;
    
    var oL = {
        'text':this.options.text /*,
        'cssClass':this.options.cssClass */
    }
    if (this.options.cssClass) {
        this.setClass(this.options.cssClass);
    } else {
        this.setClass('CDLabel');
    }
    
    this.label = new Label(this, oL);
    
    //
    //this.label.setStyle('float', 'left');
    
    if (this.options.width) this.setWidth(this.options.width);
    // perhaps it will have a margin on its right?
    if (this.options.margin) this.setStyle('margin', this.options.margin);
    
    
    
    if (this.options.cssFloat) { 
        //alert ('this.options.cssFloat ' + this.options.cssFloat);
        this.setFloat(this.options.cssFloat);
        
        // but when it is floated, it is expected there is also a margin.
        //  The margin may not be necessary when there is fixed width for the label area.
        //  Perhaps the margin would be disabled when the 
    } else {
        //alert ('this.djsld.djska ' + this.djsld.djska);
    }
    
    this.addMyEl();
}

// need to make it so that it correctly renders its styles both for the first time and when those styles / options get changed.




CheckBox = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = CheckBox.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    
    this.setClass('CheckBox');
    
    // can choose for the bos to be either on the left or the right.
    
    if (!this.options) {
        this.options = new Object();
    }
    if (!this.options.checkboxPosition) this.options.checkboxPosition = 'left';
    
    var iChk = nE('input');
        iChk.control = this;
        iChk.type = 'checkbox';
        this.iChk = iChk;
   //this.el.appendChild(iChk);
    
    
    //iChk.onchange = function() {this.control.processChange();};
    iChk.onclick = function() {this.control.processChange();};
    
    //var sLbl = nE('label');
    var sLbl = nE('span');
    sLbl.innerHTML = this.options.text;
    sLbl.control = this;
    sLbl.onclick = this.sLbl_click;
    
    //alert ('this.options.checkboxPosition ' + this.options.checkboxPosition);
    
    if (this.options.checkboxPosition == 'left') {
        this.el.appendChild(iChk);
        this.el.appendChild(sLbl);
        
    }
    if (this.options.checkboxPosition == 'right') {
        this.el.appendChild(sLbl);
        this.el.appendChild(iChk);
    }
    
    
    
    this.container.el.appendChild(this.el);   
    this.addMyDiv();
}

p.getValue = function() {
    return this.value;
}

p.processChange = function() {
    this.value = this.iChk.checked;
    if (this.onchange) this.onchange(this);
}
p.toggle = function() {
    this.iChk.checked = !this.iChk.checked;
    this.processChange();
}

p.sLbl_click = function() {
    this.control.toggle();
    
    //if (this.control.onclick) this.control.onclick();
}



// would be nice to set the container of this, rather than assign it a div.
// the container is the group.

// What about storing that there is a next item in the option so that the next one gets chosen automatically?
// Using TabOrder?

TextBox = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = TextBox.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit();
    this.el = nE('input');
    this.el.type = 'text';
    this.el.control = this;
    //this.el.onchange = function() {this.control.el_onchange(); return false};
    
    // maybe using closures here would make the most sense.
    
    this.el.onchange = function() {this.control.el_onchange()};
    this.el.onkeydown = function(e) {return this.control.el_onkeydown(e)};
    this.el.onkeyup = function(e) {return this.control.el_onkeyup(e)};
    this.el.onkeypress = function(e) {return this.control.el_onkeypress(e)};
    this.el.onblur = function(e) {return this.control.el_onblur(e)};
    this.setClass('TextBox');
    
    if (!this.options) this.options = new Object();
    if (this.options.width) this.setWidth(this.options.width);
    
    this.addMyEl();
}
p.validate = function() {
    // return a validation result.
    
    var isValid = true;
    if (this.options.isRequired) {
        isValid = isValid && this.getValue().length > 0;
    }
    var res = new ValidationResult({'isValid': isValid});
    
    return res;
}
p.clear = function() {
    this.setValue('');
}
p.setValue = function(value) {
    this.el.value = value;
    this.value = value;
}
p.getValue = function() {
    return this.el.value;
}
p.setAlign = function(value) {
    this.setStyle('textAlign', value);
}
p.selectAll = function(value) {
    this.el.focus();
    this.el.select();
}

p.setFocus = function() {
    this.el.focus();
}

p.el_onkeydown = function(e) {
    var key = e?e.which:event.keyCode;
    //alert('key ' + key);
    
    //var cont = this.container;
    //alert ('cont.options.maxLength ' + this.options.maxLength);
    if (this.options.maxLength) {
        //alert ('this.options.maxLength ' + this.options.maxLength);
        //alert ('this.getValue().length ' + this.getValue().length);
        if (this.getValue().length >= (this.options.maxLength)) {
            //if (this.reachedMaxLength) this.reachedMaxLength();
            if (key == 8) {
                
                
            } else {
                return false;
            }
            
            
            
            // also get it to move to the next one, or register that it has done so.
            
            //return false;
        }
    }
    if (this.onkeydown) this.onkeydown();
    return true;
    //return false;
}

p.el_onkeyup = function(e) {
    //var cont = this.container;
    //alert ('cont.options.maxLength ' + this.options.maxLength);
    
    //alert ('el_onkeyup');
    
    var key = e?e.which:event.keyCode;
    //alert('key ' + key);
    
    if (key == 8) {
        // check that the delete has not been done?
        
        
        //var newVal = this.getValue().substring(0, this.getValue().length - 1);
        //alert('newVal ' + newVal);
        //this.setValue(newVal);
        //return false;
        
        // could move to the previous box if there is now nothing in the box.
        
    } else {
        
        if (this.options.maxLength) {
            //alert ('this.options.maxLength ' + this.options.maxLength);
            //alert ('this.getValue().length ' + this.getValue().length);
            if (this.getValue().length >= (this.options.maxLength)) {
                if (this.reachedMaxLength) this.reachedMaxLength();
                
                // also get it to move to the next one, or register that it has done so.
                
                return false;
            }
        }
    }
    
    if (this.onkeyup) this.onkeyup();
    return true;
    //return false;
}

p.el_onkeypress = function() {
    //alert ('el_onkeypress');
    
    if (this.options.maxLength) {
        //alert ('this.options.maxLength ' + this.options.maxLength);
        //alert ('this.getValue().length ' + this.getValue().length);
        if (this.getValue().length >= (this.options.maxLength)) {
            // if longer than max length, cut it down.
            
            var newVal = this.getValue().substring(0, this.options.maxLength);
            //alert('newVal ' + newVal);
            this.setValue(newVal);
            
            return false;
            
//            if (this.reachedMaxLength) this.reachedMaxLength();
//            
//            // also get it to move to the next one, or register that it has done so.
//            
//            return false;
        }
    }
    // I think this processes before the item has been entered in.
    if (this.onkeypress) this.onkeypress();
    return true;
}

// on key press to move to the next one?

p.el_onchange = function() {
    this.value = this.el.value;
    //alert ('this.value ' + this.value);
    if (this.onchange) this.onchange();
    return true;
}
p.el_onblur = function() {
    this.value = this.el.value;
    //alert ('this.value ' + this.value);
    if (this.onblur) this.onblur();
    return true;
}

IntegerTextBox = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = IntegerTextBox.prototype;
p.inheritFrom(TextBox);
p.init = function(depth) {
    this.superInit(depth);
    
}
p.validate = function() {
    // could not have this function but rely on regex validation elsewhere.
    
    var v = this.getValue();
    return parseInt(v).toString() == v;
}

CDTextBox = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = CDTextBox.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    var o = new Object();
    o.width = this.options.width - 4;
    
    //alert ('this.options.maxLength ' + this.options.maxLength);
    
    if (this.options.maxLength) o.maxLength = this.options.maxLength;
    
    this.textBox = new TextBox(this, o);
    //this.textBox.onmovenext = function() {this.container.textBox_onmovenext();}
    this.textBox.onmovenext = function() {this.container.textBox_onmovenext();}
    this.textBox.reachedMaxLength = function() {this.container.textBox_reachedMaxLength();}
    
    this.addMyDiv();
}
p.textBox_reachedMaxLength = function() {
    if (this.options.nextControl) this.options.nextControl.setFocus();
}
p.setFocus = function() {
    this.textBox.setFocus();
}

//p.textBox_onmovenext = function() {
//    
//}
p.clear = function() {
    this.textBox.clear();
}
p.getValue = function() {
    return this.textBox.getValue();
}


// should be able to display the items in different ways.
// [item] [checkbox] [price]

// include items in options?
//  probably best in terms of keeping the same syntax throughout the place. 

CheckBoxMenu = function(container, options) {
    this.container = container;
    this.options = options;
    //this.items = items;
    this.init(0);
}
var p = CheckBoxMenu.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    
    this.setClass('CheckBoxMenu');
    
    this.uid = UID();
    
    if (!this.options.renderMode) this.options.renderMode = 'CheckBoxOnLeft';
    if (!this.options.innerPadding) this.options.innerPadding = 6;
    
    idxControls[this.uid] = this;
    
    //this.idxValues = new Object();
    this.idxAOptions = new Object();
    
    if (!this.options.items) this.options.items = new Array();
    this.addMyEl();
}

p.clear = function() {
    // deselect all items
    
    
    this.deselectAll();
    this.render();
}

p.deselectAll = function() {
    // needs to deselect the items internally, then render itself.
    
    for (var c = 0; c < this.options.items.length; c++) {
        this.options.items[c].setSelected(false);
    }
    this.render();
    
//    for (var c = 0; c < this.el.childNodes.length; c++) {
//        var d = this.el.childNodes[c];
//        var i = d.childNodes[0];
//        //var ao = this.idxAOptions[i.id];
//        i.checked = false;
//        //var checked = i.checked;
//        //alert ('checked ' + checked);
//        //ao.selected = checked;
//    }
    

}

p.allAreChecked = function() {
    var res = true;
    for (var c = 0; c < this.el.childNodes.length; c++) {
        var d = this.el.childNodes[c];
        var i = d.childNodes[0];
        //var ao = this.idxAOptions[i.id];
        if (!i.checked) res = false;
        //var checked = i.checked;
        //alert ('checked ' + checked);
        //ao.selected = checked;
    }
    return res;
}


p.add = function(item) {
    this.options.items.push(item);
    
}
p.render = function() {
    var sb = new StringBuilder();
    // though this could be given a container div. Perhaps that would be an option, enabled by default.
    
    for (var c = 0; c < this.options.items.length; c++) {
        sb.append(this.renderAOption(this.options.items[c]));
    }
    this.el.innerHTML = sb.toString();
}
p.renderAOption = function(aOption) {
    
    // perhaps the div could be given a distinctive id as well.
    //  id naming schemes. Perhaps controls could also be given ids / names, maybe from a GUI like Visual Studio.
    
    // the AOption may also refer to an object.
    //alert ('this.options.renderMode ' + this.options.renderMode);
    
    // the width of the label needs to have style properties set, such as width from options.labelWidth
    //var strLabelWidth = '';
    
    
    var strStyle = ''
    if (this.options.labelWidth) {
        //alert ('this.options.labelWidth ' + this.options.labelWidth);
        strStyle = strStyle + 'width:' + toPx(this.options.labelWidth) + ';'
    }
    
    var strStyleInnerPadding_Margin = '';
    if (this.options.innerPadding) {
        //alert ('this.options.innerPadding ' + this.options.innerPadding);
        //alert ('toPx(this.options.innerPadding) ' + toPx(this.options.innerPadding));
        strStyleInnerPadding_Margin = strStyleInnerPadding_Margin + 'margin-right:' + toPx(this.options.innerPadding) + ';'
        //alert ('strStyleInnerPadding_Margin ' + strStyleInnerPadding_Margin);

    }
    
    if (this.options.renderMode == 'CheckBoxOnLeft') {
        //alert ('aOption.options.text ' + aOption.options.text);
        //alert ('aOption.options.value ' + aOption.options.value);
        var sb = new StringBuilder('<div><input ');
        var tid = UID();
        sb.append('id="' + tid + '" type="checkbox" onchange="RaiseEvent(this, \'onchange\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));" onclick="RaiseEvent(this, \'onclick\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));">')
        sb.append('</input><label style=\'' + strStyle + '\' for="' + tid + '" >' + aOption.options.text + '</label>');
        
        if (aOption.options.appendedLink) {
            sb.append('<a target="_blank" href="' + aOption.options.appendedLink.href + '" >' + aOption.options.appendedLink.text + '</a>');
        }
        
        // we want a way of including a link inside the label.
        
        // best to use an appendedLink option for the moment.
        
        
        
        //alert('aOption.options.isRequired ' + aOption.options.isRequired);
        
        if (aOption.options.isRequired) {
        
            sb.append('<span class="validationFailure">*</span>');
        }
        
        sb.append('</div>');
        
        
        this.idxAOptions[tid] = aOption;
        idxElementsControls[tid] = this.uid;
        return sb.toString();
    }
    if (this.options.renderMode == 'CheckBoxInMiddle') {
        //alert ('aOption.options.object.price ' + aOption.options.object.price);
        // the price goes on the right - though a different field from the object could be shown.
        //alert ('strStyle ' + strStyle);
        //alert ('strStyleInnerPadding_Margin ' + strStyleInnerPadding_Margin);
        var price = aOption.options.object.options.price;
        var tid = UID();
        var sb = new StringBuilder('<div><div style="' + strStyle + ' float:left;"><label class="leftLabel" for="' + tid + '" >' + aOption.options.text + '</label></div><input ');
        
        if (aOption.options.selected) {
            //alert('aOption.options.selected');
            sb.append('checked=\'true\' ');
        }
        
        sb.append('style=\'' + strStyleInnerPadding_Margin + '\' id="' + tid + '" type="checkbox" onchange="RaiseEvent(this, \'onchange\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));" onclick="RaiseEvent(this, \'onclick\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));">')
        sb.append('</input><label for="' + tid + '" >only £' + price + '</label><div style="clear:both;"></div></div>');
        this.idxAOptions[tid] = aOption;
        idxElementsControls[tid] = this.uid;
        return sb.toString();
    }
    if (this.options.renderMode == 'CheckBoxOnRight') {
        //alert ('aOption.options.object.price ' + aOption.options.object.price);
        // the price goes on the right - though a different field from the object could be shown.
        //alert ('strStyle ' + strStyle);
        //alert ('strStyleInnerPadding_Margin ' + strStyleInnerPadding_Margin);
        var price = aOption.options.object.options.price;
        var tid = UID();
        var sb = new StringBuilder('<div><div style="' + strStyle + ' float:left;"><label class="leftLabel" for="' + tid + '" >' + aOption.options.text + '</label></div><input ');
        sb.append('style=\'' + strStyleInnerPadding_Margin + '\' id="' + tid + '" type="checkbox" onchange="RaiseEvent(this, \'onchange\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));" onclick="RaiseEvent(this, \'onclick\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));">')
        sb.append('</input><div style="clear:both;"></div></div>');
        this.idxAOptions[tid] = aOption;
        idxElementsControls[tid] = this.uid;
        return sb.toString();
    }
    
    
}
p.notify = function(sender, eventName, eventArgs) {
    if (eventName == 'onclick') {
        var itemText = eventArgs.value;
        
        // it would be nice if the notification updates a list that says whether or not the item is selected.
        
        if (this.onclick) {
            this.onclick(itemText, sender.value);
            
        }
        
        if (this.onchange) this.onchange(itemText, sender.value);
    }
    if (eventName == 'onchange') {
        var itemText = eventArgs.value;
        
        // it would be nice if the notification updates a list that says whether or not the item is selected.
        // this would make looking at the selection statuses easier.
        
        //alert ('onchange');
        if (this.onchange) {
            this.onchange(itemText, sender.value);
        }
    }
}

p.updateSelectionStatuses = function() {
    // or look at each of the elements.
    // get the id of each element, and then refer to idxAOptions
    
    for (var c = 0; c < this.el.childNodes.length; c++) {
        var d = this.el.childNodes[c];
        var i = d.childNodes[0];
        var ao = this.idxAOptions[i.id];
        var checked = i.checked;
        //alert ('checked ' + checked);
        ao.selected = checked;
    }
    
    //for (var c = 0; c < this.items.length; c++) {
    //    
    //}
}

// can we have a function that returns an array of AOptions, saying if they are selected?


p.getCheckStatuses = function() {
    // return an object that says which ones are selected
    // go through the child nodes?
    for (var c = 0; c < this.el.childNodes.length; c++) {
        var cn = this.el.childNodes[c];
        var input = cn.childNodes[0];
        
        // need to get the associated AOption.
        
        //alert (input.id);
    }
}

//p.getSelectedValues = function() {
//    
//}

// need a function to get the selected objects associated with the AOption.
p.getSelectedValues = function() {
    return this.getValues({'onlySelected':true});
}


p.getValues = function(options) {
    
    // build an array of AOptions based on reading the check statuses from the GUI
    // alert ('getValues');
    
    // for some reason this happens twice.
    
    var res = new Array();
    
    // go through the child nodes, looking a the values and if they are selected.
    
    for (var c = 0; c < this.el.childNodes.length; c++) {
        //var cn = this.el.childNodes[c];
        //var input = cn.childNodes[0];
        //alert (input.id);
        var n = this.el.childNodes[c];
        var i;
        if (this.options.renderMode == 'CheckBoxInMiddle' || this.options.renderMode == 'CheckBoxOnRight') {
            i = n.childNodes[1];
        } else {
            i = n.childNodes[0];
        }
        
        //alert ('i.id ' + i.id)
        //var ao =  this.idxAOptions[n.childNodes[0].childNodes[0].id];
        var ao =  this.idxAOptions[i.id];
        //
        var ao2;
        //if (ao) ao2 = 
        //alert ('i.nodeName ' + i.nodeName);
        // see if any of them are selected.
        //alert ('options.onlySelected ' + options.onlySelected);
        if (options.onlySelected) {
            //alert ('options.onlySelected ' + options.onlySelected);
            var checked = i.checked;
            //alert ('checked ' + checked);
            if (checked) {
                //alert ('ao ' + ao.dasds.das.dsa);
                //if (ao.options.object) {
                //    res.push(ao.options.object);
                //} else {
                    res.push(ao);
                //}
                
            }
        } else {
            //if (ao.options.object) {
            //    res.push(ao.options.object);
            //} else {
                res.push(ao);
            //}
        }
        
        
        //alert ('checked ' + checked);
        
        //var val = ao.value;
        // need to get the input element's id.
        //alert ('val ' + val);
        //res.push(ao);
    }
    
    //for (var c = 0; c < this.items.length; c++) {
        //var i = this.items[c]
        // need to see if the item is selected.
        
        //res.push(i);
        
        
    //}
    //alert ('res.length ' + res.length);
    return res;
    
}


// Or a menu that can be rendered as a radio group, along with other things?
// The menu need to respond to clicks - raise an event when it gets its value changed.

/*
Options
RepeatDirection
    1 Horizontal
    2 Vertical
*/

StyleStringBuilder = function() {
    this.sb = new StringBuilder();
    this.init();
}
var p = StyleStringBuilder.prototype;
p.getValue = function() {
    //return this.getText();
    var res = this.sb.toString();
    if (res.length > 0) {
        return 'style="' + this.sb.getValue() + '" ';
    }
    
    return '';
}
p.addStyle = function(name, value) {
    //if (this.hasAny == true) this.sb
    this.sb.append(name + ':' + value + ';');
}
p.init = function() {
    
}

// This can have an area for validation failures to be shown, with the default as a red astrisk.

RadioButtonMenu = function(container, options, items) {
    this.container = container;
    
    // Perhaps this could inherit from something that maintains an array of AOptions.
    this.options = options;
    this.items = items;
    this.init(0);
}
var p = RadioButtonMenu.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(0);
    
    // create a group name
    if (!this.options.repeatDirection) this.options.repeatDirection = 'vertical';
    
    
    if (this.options.repeatDirection) {
        this.setClass('RadioButtonMenu_' + this.options.repeatDirection);
    } else {
        this.setClass('RadioButtonMenu');
    }
    
    if (!this.options) this.options = new Object();
    this.options.clearAll = true;
    //if (!this.options.clearAll) this.options.clearAll = true;
    
    this.uid = UID();
    // add itself to the list of controls indexed by uid
    idxControls[this.uid] = this;
    
    this.idxAOptions = new Object();
    
    this.csValidationError = new CSpan(this, {
        'innerHTML':'*',
        'cssClass':'validationFailure',
        'visible':false
    })
    
    if (!this.items) this.items = new Array();
    this.addMyEl();
}
p.clear = function() {
    for (var c = 0; c < this.items.length; c++) {
        var i = this.items[c];
        i.setSelected(false);
    }
    this.render();
}
p.setOption = function(optionName, value) {
    this.options[optionName] = value;
    return value;
}
p.setItemPadding = function(value) {
    //this.options.itemPadding = value;
    
    return this.setOption('itemPadding', value);
    
}
p.setRepeatDirection = function(value) {
    this.options.repeatDirection = value;
    
    // maybe render it
    //  need to work on automatically rendering when updates are done.
    
    // this affects if the items are floated - if they are horizontal we want to float left.
    
}

// need to have a method to select an item.
// then need to re-render?
//p.selectByValue = function(value) {
//    
//}

p.validate = function() {
    var res = true;
    if (this.options.isRequired) {
        //alert ('this.options.isRequired ' + this.options.isRequired);
        res = this.hasValue();
    }
    //alert ('res ' + res)
    this.csValidationError.setVisibility(!res);
    return res;
}

p.hasValue = function() {
    var i = this.getSelectedItem();
    //alert ('i ' + i);
    //return (i != null && i != 'undefined');
    if (i) return true; 
    return false;
}
p.itemIsSelected = p.hasValue;

// getSelectedValue = getValue?
// when an item is clicked it is registered as being the selected one.



p.getSelectedValue = function() {
    var i = this.getSelectedItem();
    //alert ('i ' + i);
    //alert ('i.value ' + i.value)
    if (i) {
        var res = i.getValue();
        if (res) return res;
    } else {
        return null;
    }
    
    
    // the item's value???
    //alert ('res ' + res);
    
    //alert ('res ' + res);
    
}

p.getValue = p.getSelectedValue;

p.getSelectedItem = function() {
    // maybe will return an AOption.
    // we can go through the childNodes and 
    var found = false;
    var c = 0;
    var selectedId;
    var res;
    while (c < this.el.childNodes.length && !found) {
        var cn = this.el.childNodes[c];
        //if (cn
        // then need to examine in the div[0], input[0];
        
        // needs to get elements by tag name.
        
        
        
        //var elInput = cn.childNodes[0];
        var elInput = cn.getElementsByTagName('input')[0];
        //alert ('elInput ' + elInput);
        if (elInput && elInput.checked == true) {
            //alert ('elInput.checked');
            // get the object from the id of the element.
            // this is some form of delegation / referencing that should improve performance (as lokg as it does not badly impact memory by storing the references)
            selectedId = elInput.id;
            //alert ('selectedId ' + selectedId);
            var res = this.idxAOptions[selectedId];
            //alert ('ao.value ' + ao.value);
            
            found = true;
        }
        
        c++;
        
    }
    return res;
}

p.setSelectedByValue = function(value) {
    
    
    for (var c = 0; c < this.items.length; c++) {
        var i = this.items[c];
        i.setSelected(i.options.value == value);
    }
}

p.setSelectedValue = p.setSelectedByValue;

p.validateItemSelected = function() {
    for (var c = 0; c < this.el.childNodes.length; c++) {
        var cn = this.el.childNodes[c];
        if (cn.tagName.toLowerCase() == 'input' && cn.type.toLowerCase() == 'radio') {
            if (cn.checked == true) {
                return true;
            }
        }
    }
    
}
p.add = function(item) {
    this.items.push(item);
    // AOption(text, value);
}
p.render = function() {
    // Get HTML from the array of AOptions.
    //var sb = new StringBuilder('<div>');
    this.el.removeChild(this.csValidationError.el);
    
    var sb = new StringBuilder();
    var isFirst = true;
    for (var c = 0; c < this.items.length; c++) {
        sb.append(this.renderAOption(this.items[c], isFirst));
        isFirst = false;
    }
    //return sb.toString()
    
    // there should be something to clear all after that?
    //sb.append('</div><div style="clear:both" />');
    
    // how can this be done within RadioButtonMenu?
    
    // also need to have a span to show the validation error.
    
    if (this.options.clearAll) {
        sb.append('<div class="clearall" />');
    }
    
    
    this.el.innerHTML = sb.toString();
    
    // then need to look inside the element to get the elements to attach the event handlers to.
    var div = this.el.childNodes[0];
    
    var input = div.childNodes[0];
    input.control = this;
    var label = div.childNodes[1];
    label.control = this;
    
    this.el.appendChild(this.csValidationError.el);
    
    
    
    
    
    // Calling a central event procesor from the actions may work well. The event would contain the div that is throwing the event.
    // Perhaps the div would have a unique id.
    // Then it looks up in a table which control processes the event.
    // There is a function that is called on that control (a callback object could be used).
    
    // RadioButtonMenu needs to respond to the events of radio buttons and labels that are not controls. This will hopefully improve efficiency and prevent
    // potential garbage collection problems with IE.
    // Should provide its element, its value and its text. Also present the unique id of its control.
    
    // Unique ids of controls will be used to direct the events in the right direction.
    
    //input.onclick = function() { this.control.input_onclick(); return false;}
    //label.onclick = function() { this.control.label_onclick(); return false;}
}
p.notify = function(sender, eventName, eventArgs) {
    // EventArguments can either be an array or a single item.
    //alert ('notify sender: ' + sender + ', eventName: ' + eventName);
    //alert (sender.value);
    //alert('notify');
    
    if (eventName == 'onclick') {
        //alert ('onclick');
        var itemText = eventArgs.value;
        if (this.onclick) {
            this.onclick(itemText, sender.value);
        }
        
        // the item then becomes selected.
        this.selectedItem = sender;
        //sender.ded.dae.ddsa = 1;
        
    }
    if (eventName == 'onchange') {
        //alert ('onchange');
        if (this.onchange) {
            this.onchange();
        }
    }
    
}
p.input_onclick = function() {
    alert ('input_onclick');
}
p.label_onclick = function() {
    alert ('label_onclick');
}
p.renderAOption = function(aOption, isFirst) {
    // maybe want to know if this is the first one.
    // also need to take into consideration the itemPadding.
    
    
    // Also be able to put the radio button on the right.
    //  buttonSide
    
    //alert ('renderAOption');
    
    // perhaps use a StyleStringBuilder
    // Or a more general Style control that makes the style string.
    var ssb = new StyleStringBuilder();
    //ssb.addStyle('float', 'left');
    if (this.options.repeatDirection == 'horizontal') {
        ssb.addStyle('float', 'left');
    }
    
    
    //var txtStyle = '';
    
    if (!isFirst) {
        if (this.options.itemPadding) {
            if (this.options.repeatDirection == 'horizontal') {
                ssb.addStyle('margin-left', toPx(this.options.itemPadding));
            }
            
        }
        
        
    }
    
    //var txtFloat = '';
    //if (this.options.repeatDirection == 'horizontal') {
    //    txtFloat = ' style="float:left;"';
    //}
    
    var sSelected = '';
    if (aOption.options.selected == true) {
        sSelected = 'checked="true" ';
    }
    //alert ('sSelected ' + sSelected);
    var tid = UID();
    var sbInput = new StringBuilder('<input ' + 'id="' + tid + '" type="radio" name="' + this.uid + '" ' + sSelected + 'value="' + aOption.options.value + '" onchange="RaiseEvent(this,\'onchange\');" onclick="RaiseEvent(this, \'onclick\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));">' + '</input>');
    
    var sb = new StringBuilder();
    
    var strRightSide = ''
    
    var price = aOption.options.price;
    var strPrice = 'only £0.00';
    if (price) {
        strPrice = 'only £' + price.toFixed(2);
    }
    
    if (price || price == 0) strRightSide = '&nbsp;'+ strPrice;
    
    // 
    //alert ('this.options.buttonSide ' + this.options.buttonSide);
    
    if (this.options.buttonSide == 'right') {
        sb.append('<div class="RadioButtonMenuItem" ' + ssb.getValue() + '>' + '<div class="divLabel"><label for="' + tid + '" >' + aOption.options.text + '</label></div>' + sbInput.getValue() + '<label for="' + tid + '" >' + strRightSide + '</label>');
        //sb.append('<img href="/images/BQu_20x20.gif" title="' + this.options.questionMarkText + '" alt="' + this.options.questionMarkText + '" />');
        if (aOption.options.questionMarkText) {
            //alert ('aOption.options.questionMarkText ' + aOption.options.questionMarkText);
            // cool to be able to use a control to get its HTML. Render to HTML string rather than in the normal control way.
            sb.append('<img class="QuestionCircle" src="/images/BQu_13x13.gif" title="' + aOption.options.questionMarkText + '" alt="' + aOption.options.questionMarkText + '" />');
        }
        sb.append('</div>');
    } else {
        sb.append('<div class="RadioButtonMenuItem" ' + ssb.getValue() + '>' + sbInput.getValue());
        sb.append('<label for="' + tid + '" >' + aOption.options.text + '</label></div>');
        
        // then add the question mark if it is there.
        //alert ('this.options.questionMarkText');
        //alert ('this.options.questionMarkText ' + this.options.questionMarkText);
        if (aOption.options.questionMarkText) {
            //alert ('aOption.options.questionMarkText ' + aOption.options.questionMarkText);
            // cool to be able to use a control to get its HTML. Render to HTML string rather than in the normal control way.
            sb.append('<img class="QuestionCircle" src="/images/BQu_13x13.gif" title="' + aOption.options.questionMarkText + '" alt="' + aOption.options.questionMarkText + '" />');
        }
    }
    
    
    
    // the style of the DIVs being set here?
    // use float if it has its repeatDirection to 
    
    // and if the item is selected?
    
    // could do with a function / object to put together event handler strings.
    //alert ('aOption.options.value ' + aOption.options.value);
    
    
    
    
    //sb.append('id="' + tid + '" type="radio" name="' + this.uid + '" ' + sSelected + 'value="' + aOption.options.value + '" onchange="RaiseEvent(this,\'onchange\');" onclick="RaiseEvent(this, \'onclick\', new EventArgument(\'itemText\', \'' + aOption.options.text + '\'));">')
    
    
    // also register in a table that tid is associated with this.uid.
    //  the events will get passed to the control with this.uid.
    idxElementsControls[tid] = this.uid;
    this.idxAOptions[tid] = aOption;
    // and also make an index within this, linking each item to an AOption.
    // we want to be able to get the item values.
    
    
    
    //return '<div class="AOption">' + sb.toString() + '</div>';
    return sb.toString();
}

idxControls = new Object();
idxElementsControls = new Object();



CSelect = function(container, options) {
    this.container = container;
    this.options = options;
    // Items now in the options.
    /* this.items = items; */
    this.init(0);
}
var p = CSelect.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('select');
    
    // the control could ensure there is an options object.
//    alert (this.options);
//    if (this.options == true) {
//        this.options.dgf.gjh = 0;
//    }
    
    //if (!this.options) this.options = new Object();
    //alert ('this.options.multiple ' + this.options.multiple);
    //alert ('this.options.size ' + this.options.size);
    
    if (this.options.multiple) this.el.multiple = this.options.multiple;
    if (this.options.size) this.el.setAttribute('size', this.options.size);
    if (this.options.width) this.setWidth(this.options.width);
    if (!this.options.items) this.options.items = new Array();
    this.attachEvents();
    
    this.setClass('CSelect');
    
    //alert ('this.items.length ' + this.items.length);
    
    
    this.addMyEl();
    
    // why does this not work above this.addMyEl();?
    if (this.options.items) this.render();
}

p.clear = function() {
    // deselect all.
    this.deselectAll();
    // select the first / default???
    this.selectFirst();
    
    this.render();
    
}

p.selectFirst = function() {
    this.options.items[0].setSelected(true);
}

p.deselectAll = function() {
    for (var c = 0; c < this.options.items.length; c++) {
        var i = this.options.items[c];
        i.setSelected(false);
    }
}
p.validate = function() {
    var res = true;
    
    // Unless this gets some validation parameters, it will alway validate.
    
    return res;
}

p.getValue = function() {
    // need to look at the element.
    // look through the child nodes?
    //alert ('this.el ' + this.el);
    //alert ('this.el.value ' + this.el.value);
    return this.el.value;
}

p.setValue = function(value) {
    //alert('CSelect set value ' + value);
    var res = this.setSelectedByValue(value);
    
    this.render();
    return res;
}

p.setSelectedByValue = function(value) {
    //var ao = this.getAOptionByValue(value);
    //if (ao) ao
    var found = false;
    
    for (var c = 0; c < this.options.items.length; c++) {
        var i = this.options.items[c];
        i.setSelected(i.options.value == value);
        if (i.options.value == value) found = true;
        
        //if (found) alert('found');
        
        //alert('i.options.value ' + i.options.value);
    }
    return found;
    
}

p.getAOptionByValue = function(value) {
    var found = false;
    var res;
    var c = 0;
    while (!found && c < this.options.items.length) {
        found = (this.options.items.value == value);
        res = this.options.items[c];
    }
    return res;
}

p.attachEvents = function() {
    this.el.onchange = function() { this.control.el_onchange(); return false; }
}
p.el_onchange = function() {
    //alert ('el_onchange');
    this.value = this.el.value;
    if (this.onchange) {
        this.onchange();
    }
}
p.add = function(aOption) {
    //this.el.appendChild(dropDownListItem.el);
    
    this.options.items.push(aOption);
    
}
p.addNumberSequence = function(minValue, maxValue) {
    for (var c = minValue; c <= maxValue; c++) {
        var i = new AOption({'text':c, 'value':c});
        this.add(i);
    }
}
p.render = function() {
    //MS KB 276228: If you must use innerHTML, a workaround is to use a Div object to wrap the SELECT element and then set the innerHTML property for the Div object.
    
    //alert('render');
    
    var sb = new StringBuilder();
    
    for(var c = 0; c < this.options.items.length; c++) {
        var i = this.options.items[c];
        sb.append('<option value="');
        sb.append(i.options.value);
        
        sb.append('"');
        if (i.options.selected == true) {
            sb.append(' selected="selected"');
        }
        sb.append('>');
        sb.append(i.options.text);
        sb.append('</option>');
    }
    
    if (ieVersion && ieVersion < 8) {
        var sPlaceholder = nS();
        // swap the element from its parent node with a placeholder.
        if (this.el.parentNode) {
            //
            var pn = this.el.parentNode;
            this.el.parentNode.insertBefore(sPlaceholder, this.el);
            var dT = nD();
            dT.appendChild(this.el);
            var multipleString = '';
            var sizeString = '';
            if (this.options.multiple) var multipleString = ' multiple="' + this.options.multiple + '"';
            if (this.options.size) var sizeString = ' size="' + this.options.size + '"';
            this.el.outerHTML = '<select onchange="this.control.el_onchange(); return false;"' + multipleString + sizeString +  '>' + sb.toString() + '</select>';
            this.el = dT.childNodes(0);
            pn.replaceChild(this.el, sPlaceholder);
            this.el.control = this;
        }
        // then need to get the element.
        //this.el.onchange = function() { alert('onchange'); this.control.el_onchange(); return false; }
        //this.el.attachEvent('onclick', function() { alert('onchange'); this.control.el_onchange(); return false; });
        this.attachEvents();
    } else {
        this.el.innerHTML = sb.toString();
    }
    //alert ('sb.toString' + sb.toString());
    
}


AOption = function(options) {
    //this.text = text;
    //this.value = value;
    //this.selected = selected;
    //this.object = object;
    this.options = options;
}
var p = AOption.prototype;
p.setSelected = function(value) {
    this.options.selected = value;
}
p.getValue = function() {
    return this.options.value;
}


DropDownList = function(container, options) {
    //alert ('container ' + container);
    this.container = container;
    this.options = options;
    //this.items = items;
    //this.name = '
    this.init(0);
}
var p = DropDownList.prototype;
p.inheritFrom(CSelect);
p.init = function(depth) {
    this.superInit(depth);
    //this.items = new Array();
    //this.el = nE('select');
    //this.el.control = this;
    
    //this.attachEvents();
    
    //var cd = this.getContainerEl();
    //alert ('cd ' + cd);
    //this.setClass('DropDownList');
    //cd.appendChild(this.el);
    this.addMyEl();
}
//p.setComboBoxWidth = function(value) {
//    //this.el.style.width.setComboBoxWidth(value);
//}


CDDropDownList = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = CDDropDownList.prototype;
p.inheritFrom(CDiv);

// This needs various functions from CSelect.




p.init = function(depth) {
    this.superInit(depth);
    this.dropDownList = new DropDownList(this);
    this.addMyEl();
}
p.attachEvents = function() {
    //this.el.onchange = function() { this.control.el_onchange(); return false; }
    this.dropDownList.attachEvents();
}
p.getValue = function() {
    return this.dropDownList.getValue();
}
p.clear = function() {
    this.dropDownList.clear();
}
//p.el_onchange = function() {
//    this.value = this.el.value;
//    //alert ('this.value ' + this.value);
//    if (this.onchange) {
//        this.onchange();
//    }
//}
p.add = function(aOption) {
    //this.el.appendChild(dropDownListItem.el);
    this.dropDownList.add(aOption);
    
}
p.addNumberSequence = function(minValue, maxValue) {
    this.dropDownList.addNumberSequence(minValue, maxValue);
}
p.render = function() {
    this.dropDownList.render();
    
}
// An abstract DDL item may be good - not a control itself.


DropDownListItem = function(container, text, value) {
    this.container = container;
    this.text = text;
    this.value = value;
    this.init(0);
}
var p = DropDownListItem.prototype;
p.inheritFrom(Control);

p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('option');
    this.el.value = this.value;
    this.el.innerHTML = this.text;
    
    this.getContainerEl().appendChild(this.el);
}



// This could contain CTr items, or ATr items.
//  Perhasp CTr items could be used in the same way, operating in a different mode.
//  Not for the moment though, don't make things too complex.

// Be able to style CButton like a link.
// Easy ways to choose style here would be good.
// Style tables / having the app aware of what various styles can be used for.


// This should be able to use an image too.
// Perhaps ImageButton...


CButton = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = CButton.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('button');
    this.el.innerHTML = this.options.text;
    this.el.id = UID();
    this.el.onclick = function() {this.control.el_onclick(); return false;};
    // use the longer event system to get the event details from the button?
    this.addMyEl();
}
p.styleAsLink = function() {
    this.setClass('LinkButton');
}
p.el_onclick = function() {
    if (this.onclick) this.onclick();
}

CBr = function(container) {
    this.container = container;
    this.init(0);
}
var p = CBr.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('br');
    
    this.addMyEl();
}

CTable = function(container) {
    this.container = container;
    this.init(0);
}
var p = CTable.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('table');
    
    // this can contain abstract items. (Abstract Table Rows)
    
    this.addMyEl();
}

ATr = function() {
    
}
var p = ATr.prototype;
p.init = function() {
    
}

CTbody = function(container) {
    this.container = container;
    this.init(0);
}
var p = CTbody.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('tbody');
    this.addMyEl();
}


CTr = function(container) {
    // the container must be a table
    //  or an abstract container that can hold anything.
    
    // Maybe later implement restrictions on what a control can hold.
    this.container = container;
    this.init(0);
}
var p = CTr.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('tr');
    
    // this will contain abstract items that can get rendered at a later stage.
    //  don't add lots of element to the document one by one using DOM methods - build up a string of the InnerHTML, then set that.
    //  re-attach event handlers afterwards.
    //  un-attach everything before for successful garbage collection
    
    this.addMyEl();
}


CTd = function(container, content) {
    // the container must be a table
    //  or an abstract container that can hold anything.
    
    // Maybe later implement restrictions on what a control can hold.
    this.container = container;
    this.content = content;
    this.init(0);
}
var p = CTd.prototype;
p.inheritFrom(Control);
p.init = function(depth) {
    this.superInit(depth);
    this.el = nE('td');
    if (this.content) this.el.innerHTML = this.content;
    
    
    this.addMyEl();
}



AProgressStage = function(num, text) {
    this.num = num;
    this.text = text;
    this.init(0);
}
var p = AProgressStage.prototype;
p.init = function(depth) {
    
}
p.setSelected = function(value, container) {
    
    // what if this has not yet been rendered?
    //alert (container);
    var el = this.getEl(container);
    //alert (el);
//    if (this.containerEl) {
//        if (value == true) {
//            setClass(el, 'ProgressStage_On');
//        } else {
//            setClass(el, 'ProgressStage');
//        }
//    }
    this.selected = value;
    
    //this.render();
}
p.getEl = function(container) {
    if (this.elId) return getElementById(container.el, this.elId);
}


p.toHTML = function(isOn) {
    if (!this.elId) this.elId = UID();
    
    var sb = new StringBuilder('<div class="ProgressStage');
    if (this.selected == true || isOn) sb.append('_On');
    sb.append('" id="' + this.elId + '">');
    sb.append(this.text);
    sb.append('</div>');
    
    return sb.toString();
}

getElementById = function(el, id) {
    if (el.id == id) return el;
    for (var c = 0; c < el.childNodes.length; c++) {
        var tEl = getElementById(el.childNodes[c]);
        if (tEl) return tEl;
    }
}


NameAndTitleEntry = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = NameAndTitleEntry.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(0);
    this.setClass('NameAndTitleEntry');
    
    var titles = new Array();
    //text, value, selected, object
    titles.push(new AOption({'text':'Mr.','value':'Mr.','selected':true}));
    titles.push(new AOption({'text':'Miss.','value':'Miss.'}));
    titles.push(new AOption({'text':'Mrs.','value':'Mrs.'}));
    titles.push(new AOption({'text':'Dr.','value':'Dr.'}));
    titles.push(new AOption({'text':'Sir.','value':'Sir.'}));
    //o.push(new AOption('Dr', 'Dr'));
    
    var strVF = '';
    if (this.options.isRequired) strVF = '<span class="validationFailure">*</span>';
    
    var options = {
        'labelText': 'Title:',
        'labelWidth': this.options.labelWidth,
        'items': titles
    }
    //options.itemPadding = 10;
    //var sMargin = '';
    //if (this.options.itemPadding) sMargin = '0 ' + toPx(this.options.itemPadding) + ' 0 0 0';
    //alert ('sMargin ' + sMargin);
    var padding = '0';
    if (this.options.itemPadding) padding = '0 ' + toPx(this.options.itemPadding) + ' 0 0';
    
    var oLabel = {'text':'Contact Name:' + strVF, 'margin': padding, 'cssFloat': 'left'};
    if (this.options.labelWidth) oLabel.width = this.options.labelWidth;
   
    this.cdLabel = new CDLabel(this, oLabel);
    
    this.cdTitle = new CDiv(this);
    this.cdTitle.setFloat('left');
    this.cdTitle.setStyle('marginRight', '8px');
    
    this.ddlTitle = new DropDownList(this.cdTitle, options);
    //this.ddlTitle = new DropDownListField(this, options);
    //this.ddlTitle.render();
//    var odfn = new Object();
//    odfn.labelText = 'First name';
//    
//    
//    var odfn = {
//        'labelText':'First name:',
//        'labelWidth':this.options.labelWidth,
//        'isRequired':true
//        
//    }
//    this.tfFirstName = new TextField(this, odfn);
    this.tbName = new TextBox(this, {'isRequired':true});
      
    
//    this.tfSurname = new TextField(this, {
//        'labelText':'Surname:',
//        'labelWidth':this.options.labelWidth,
//        'isRequired':true
//    });
    
    //this.lblTitle = new CDLabel(this.ct_r1_c1, 'Title');
    //this.lblFirstName = new CDLabel(this.ct_r1_c2, 'First name');
    //this.lblSurname = new CDLabel(this.ct_r1_c3, 'Surname');
    // First name
    //this.tfFirstName = new TextBox(this.ct_r2_c2, 'First name');
    
    // Last name
    //this.tfLastName = new TextBox(this.ct_r2_c3, 'Surname');
    this.cdc = new CDClear(this);
    this.addMyEl();
}
p.validate = function() {
    // this.ddlfFirstName, this.ddlfSurname
    //var res = true;
    //res = this.tfFirstName.validate() && res;
    //alert ('res ' + res);
    //res = this.tfSurname.validate() && res;
    
    // if the name does not validate we need to report a validation error.
    // add it to an array of validation errors somewhere.
    
    // the validate() function could provide validation errors when it does not validate successfully.
    //  (rather than true).
    
    // could return ValidationResult object.
    
    var isValid = true;
    
    var res1;
    res1 = this.tbName.validate();
    //res =  && res;
    isValid = res1.options.isValid;
    
    var res = new ValidationResult({'isValid': isValid, 'fieldName': 'Contact Name'});
    //alert ('res.isValid ' + res.isValid);
    //alert ('res ' + res);
    
    return res;
}


p.getValue = function() {
    return this.ddlTitle.getValue() + ' ' + this.txtFirstName.getValue() + ' ' + this.txtLastName.getValue();
}


ContactEntry = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = ContactEntry.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    this.setClass('ContactEntry');
    // need to include title, name, address
    
    this.nameAndTitle = new NameAndTitleEntry(this, {'labelWidth':this.options.labelWidth, 'itemPadding': 12, 'isRequired':true});
    var o = new Object();
    if (this.options.labelWidth) o.labelWidth = this.options.labelWidth;
    o.getAddressFromPostcode = true;
    
    if (this.options.type) {
        o.type = this.options.type;
    }
    
    
    
    // addressEntry - should be able to put custom question mark text in there.
    // there should be the question marks in the Business Details section, but not the billing details section.
    
    
    this.address = new AddressEntry(this, o);
    
    // then email entry
    // mobile phone number entry

    this.tfEmail = new TextField(this, { 'labelWidth': this.options.labelWidth, 'labelText': 'Email address:', 'questionMarkText': 'This will be used should we need to contact you on certain occoasions.' });
    this.tfMobile = new TextField(this, {'labelWidth':this.options.labelWidth,'labelText':'Mobile number:', 'questionMarkText': 'This will be used should we need to contact you on certain occoasions.'});
    
    this.addMyEl();
}
p.validate = function() {
    // (validate that there is a first name)
    // validate the name and title entry.
    
    // Do the validation on all the controls / fileds so that if more than one is not valid it spots which is are not valid and 
    
    // when there are any validation errors they must be shown in a validationError container, which could either be a CDiv, a div or
    // a specific ValidationErrorsDisplay
    
    var res = new ValidationResult();
    
    var isValid = true;
    var res1;
    
    //return true;
    //var res = true;
    res1 = this.nameAndTitle.validate();
    //alert ('res1 ' + res1);
    //alert ('res1 ' + res1.options.isValid);
    isValid = isValid && res1.options.isValid;
    res.add(res1);
    //if (res) res = this.address.validate();
    //alert ('res ' + res);
    res1 = this.address.validate();
    //alert ('res1 ' + res1);
    //alert ('res1 ' + res1.options.isValid);
    
    isValid = isValid && res1.options.isValid;
    res.add(res1);
    //alert ('res ' + res);
    res1 = this.tfEmail.validate();
    //alert ('res1 ' + res1);
    //alert ('res1 ' + res1.options.isValid);
    isValid = isValid && res1.options.isValid;
    //alert ('isValid ' + isValid);
    res.add(res1);
    // 
    res.options.isValid = isValid;
    
    
    return res;
}

// Options: color: gray, blue, brown, green, red

// Changing the CSS or styles within the elements to that for the correct color?
// Now will be replacing these with images.

// Want to keep the same code format for the moment.


ImageButton = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = ImageButton.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    this.setInnerHTML('<img src="' + this.options.src + '" />');
    this.el.onclick = function() { this.control.el_onclick(); return false; };
    this.addMyEl();
}


BoldButton = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = BoldButton.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    this.superInit(depth);
    this.setClass('buttonwrapper_' + this.options.buttonColor);
        
    this.a = new CA(this, {'cssClass': 'boldbuttons_' + this.options.buttonColor});
    //this.a.onclick = function() {this.container.a_onclick();}
    
    this.a.s = new CSpan(this.a);
    this.a.s.setInnerHTML(this.options.text);
    
    this.el.onclick = function() {this.control.el_onclick(); return false;};
    //this.a.onclick = function() {this.control.control.el_onclick(); return false;};
    
    this.addMyEl();
}
p.setText = function(value) {
    this.options.text = value;
    this.a.s.setInnerHTML(this.options.text);
}
p.a_onclick = function() {
    alert('a_onclick');
    if (this.onclick) this.onclick();
}


InfoBubble = function(container, options) {
    this.container = container;
    this.options = options;
    this.init(0);
}
var p = InfoBubble.prototype;
p.inheritFrom(CDiv);
p.init = function(depth) {
    
}
