﻿// Global Namespaces
$.ui = {};
$.data = {};
$.base = {};
$.Events = {};

$.base.Eventable = {
    registerEvent: function(eventName, delegate) {
        // If event doesn't exist...create it
        if ((Object.isUndefined(this[eventName]))) {
            this[eventName] = $A();
        }

        // ...then push the new delegate
        if (delegate)
            this[eventName].push(delegate);

        // If the parent has an event registry...update it
        if (this.parent) {
            if (this.parent.eventRegistry) {
                this.parent.eventRegistry.Update(eventName);
            }
        }
    },

    removeEvent: function(eventName, delegate) {

        // If event doesn't exist...create it
        if (!(Object.isUndefined(this[eventName]))) {
            delegateIndex = this[eventName].indexOf(delegate);

            if (delegateIndex != -1) {
                this[eventName] = this[eventName].without(this[eventName][delegateIndex]);
            }
        }
    },

    fireEvent: function(eventName, message, inputElement) {
        if (!(Object.isUndefined(this[eventName]))) {
            if (Object.isUndefined(this[eventName])) throw 'Event Error';

            // Delegates are called with different context/scope depending on 
            // if they are an input element or a form-level event
            if (Object.isUndefined(inputElement)) {
                this[eventName].each(function(delegate) {
                    delegate.call(this, eventName, message);
                });
            }
            else {
                this[eventName].each(function(delegate) {
                    delegate.call(inputElement, eventName, message);
                });
            }
        }
    }
};

// Extend the DOM Element with the ability to manage 'custom events'
Element.addMethods({
    registerEvent: function(element, eventName, delegate) {
        // If event doesn't exist...create it
        if ((Object.isUndefined(this[eventName]))) {
            element[eventName] = $A();
        }

        // ...then push the new delegate
        element[eventName].push(delegate);

        // keep track of how many events...
        if (Object.isUndefined(element.eventRegistry))
            element.eventRegistry = $A();

        // Do not add event to the registry if already exists
        if (element.eventRegistry.indexOf(eventName) == -1)
            element.eventRegistry.push(eventName);

    },
    fireEvent: function(element, eventName, message, inputElement) {
        if (!(Object.isUndefined(element[eventName]))) {
            if (Object.isUndefined(element[eventName])) throw 'Event Error';

            // Delegates are called with different context/scope depending on 
            // if they are an input element or a form-level event
            if (Object.isUndefined(inputElement)) {
                element[eventName].each(function(delegate) {
                    delegate.call(element, eventName, message);
                });
            }
            else {
                element[eventName].each(function(delegate) {
                    delegate.call(inputElement, eventName, message);
                });
            }
        }
    },
    removeErrorMessage: function(element, eventName) {
        var id = element.id + eventName;
        var messageBox = $(id);

        if (messageBox) messageBox.remove();
    },

    hasIllegalChars: function(content) {
        var validChars = /[\u0021-\u002f\u003a-\u0040\u005b-\u005e\u0060\u007b-\u007e]/g;

        if (validChars.test(content)) {
            return true;
        } else {
            return false;
        }
    },

    stripIlegalChars: function(content) {
        var validChars = /[^a-zA-Z0-9\[{}\]#\.\\\/:$@&,()~_+=?!¡¿;<> %\'\"]/g;

        if (validChars.test(content)) {
            return content.replace(validChars, '*');
        } else {
            return content;
        }
    },

    stripHTMLTag: function(tag) {
        var validChars = /< + tag + [^>]+\>/g;

        if (validChars.test(content)) {
            return content.replace(validChars, '');
        } else {
            return content;
        }
    },

    stripImageTag: function() {
        var validChars = /<img[^>]+\>/g;

        if (validChars.test(content)) {
            return content.replace(validChars, '');
        } else {
            return content;
        }
    },
    // Expresion Methods
    inAll: function(list) {
    },
    inAny: function(list) {
    },
    equal: function(value) {
        return (this.getValue() == value ? true : false);
    },
    greaterThan: function(number) {
        return (this.getValue() > number ? true : false);
    },
    lessThan: function(number) {
        return (this.getValue() < number ? true : false);
    },
    greaterThanOrEqual: function(number) {
        return (this.getValue() >= number ? true : false);
    },
    lessThanOrEqual: function(number) {
        return (this.getValue() <= number ? true : false);
    },
    between: function(valueFrom, valueTo) {
        return (this.getValue() > valueFrom && this.getValue() < valueTo ? true : false);
    },
    range: function(lowValue, highValue) {
        return (this.getValue() >= valueFrom && this.getValue() <= valueTo ? true : false);
    },
    disable: function() {
        this.disabled = true;
    },
    enable: function() {
        this.disabled = false;
    },

    isUnescapeHTML: function() {
        stringvalue = this.innerHTML;

        args = ['&lt;', '&gt;'];

        for (i = 0; i < args.length; i++) {
            if (stringvalue.indexOf(args[1]) != -1) {
                return true;
            }
        }

        return false;
    },

    deepClone: function(element) {
        var newElement = element.clone(true); //clone the node 
        //stop observing all events on the newElement (IE (and some others?) copies them) 
        newElement.stopObserving();

        //copy storage 
        Element.getStorage(newElement);
        (Element.Storage[newElement._prototypeUID[0]] = new Hash(Element.getStorage(element))).unset('prototype_event_registry');

        //copy the events on the parent 
        if (!Object.isUndefined(registry = Element.retrieve(element, 'prototype_event_registry'))) {
            registry.each(function(pair) {
                var eventName = pair.key, responders = pair.value;
                responders.each(function(r) {
                    Element.observe(newElement, eventName, r.handler);
                });
            });
        }

        //get all of the descendants 
        var srcdecend = element.descendants();
        var dstdecend = newElement.descendants();
        var numdecend = srcdecend.length;

        //foreach of the descendants 
        for (var i = 0; i < numdecend; ++i) {
            //stop observing all events on the newElement (IE (and some others?) copies them) 
            dstdecend[i].stopObserving();

            //copy storage 
            Element.getStorage(dstdecend[i]);

            (Element.Storage[dstdecend[i]._prototypeUID[0]] = Element.getStorage(srcdecend[i]).clone()).unset('prototype_event_registry');

            //copy the events on each child to it's new corrisponding child 
            var registry = Element.retrieve(srcdecend[i], 'prototype_event_registry');
            if (!Object.isUndefined(registry)) {
                registry.each(function(pair) {
                    var eventName = pair.key, responders = pair.value;
                    responders.each(function(r) {
                        Element.observe(dstdecend[i], eventName, r.handler);
                    });
                });
            }
        }

        return newElement;
    }
});


// Extend Array
Object.extend(Array.prototype, {
    stringify: function(delimeter) {
        if (this.length == 0) {
            return '';
        }
        
        first = this.first();

        if (!delimeter)
            delimeter = ',';

        pArray = this.without(first);

        _stringify = first;

        pArray.each(function(item) {
            _stringify = _stringify + delimeter + item;
        }, this);

        return _stringify.strip();
    }
});



/***********************************
* ENUMERATIONS
************************************/
formStates = {
    Save: 'SaveWithPost',
    Insert: 'Insert',
    Update: 'Update',
    Delete: 'DeleteWithGet',
    Retrieve: 'Retrieve',
    Readonly: 'Readonly',
    AutoBackup: 'AutoBackup',
    SaveAndReturn: 'SaveAndReturn'
}

controlModes = {
    Hosted: 'Hosted',       // The developer provides the HTML for the control
    Assisted: 'Assisted'    // The developer requests the HTML be created by the control
}

sortOrder = {
    Asc: 'asc',
    Dsc: 'dsc',
    None: 'none'
}


/***********************************
* BASE CLASSES
************************************/
$.base.AutoSavable = {
    isAutoSavable: true,

    _autoBackup: function(key) {
        this.autoSaveCookieKey = key;

        dataBag = this.query.dataConnector.dto.toDTO(this.query.dataConnector.command);

        window.page.destroyCookie(key);
        window.page.setCookie(key, dataBag);

        this.query.bindAsDataConnectorEventListener('$.query.success', this._handleAutoSaveQuerySuccess.bind(this));
    },

    _restorBackup: function() {
        dataBag = window.page.getCookie(this.autosaveCookieKey);

        if (dataBag) {
            var json = dataBagescapeHTML().evalJSON();
            this._handleOnSuccess('Restore', $H(json));
        }
    },

    _isFormDirty: function(cookieKey) {
        cookieKey = window.page.getCookie(cookieKey);

        if (cookieKey)
            return true;
        else
            return false;
    },

    _handleAutoSaveQuerySuccess: function(memo, data) {
        switch (memo) {
            case 'Save':
            case 'SaveAndReturn':
            case 'Delete':
                window.page.destroyCookie(this.autoSaveCookieKey);
                break;
            case 'Restore':
                alert('Record has been restored from memoy.');
                break;
        }
    }
};


$.base.Dirtiable = {
    isDirtiable: true,
    isDirty: false,

    _setDirtyListeners: function() {
        // Listen on root control..if necessary
        switch (this.container.type) {
            case 'select-one':
                this.container.observe('click', this._handleDirtyEvent.bind(this));
                break;
            default:
                break;
        }

        // Listen on child controls..
        if (Prototype.Browser.IE) {
            $$Select(this.container, null, 'input', 'text').invoke('observe', 'change', this._handleDirtyEvent.bind(this));
            $$Select(this.container, null, 'input', 'checkbox').invoke('observe', 'click', this._handleDirtyEvent.bind(this));
            $$Select(this.container, null, 'select').invoke('observe', 'click', this._handleDirtyEvent.bind(this));

            $$Select(this.container, null, 'textarea').each(function(textarea) {
                Editor = $F(textarea.id);

                if (Editor.isYUIEditor()) {
                    Editor.observe('editorKeyDown', this._handleDirtyEvent.bind(this));
                    Editor.observe('editorMouseUp', this._handleDirtyEvent.bind(this));
                    Editor.observe('editorMouseDown', this._handleDirtyEvent.bind(this));
                } else {
                    $(textarea.id).observe('blur', this._handleDirtyEvent.bind(this));
                }
            }, this);
        } else {
            this.container.select('input[type=text]').invoke('observe', 'change', this._handleDirtyEvent.bind(this));
            this.container.select('input[type=checkbox]').invoke('observe', 'click', this._handleDirtyEvent.bind(this));
            this.container.select('select').invoke('observe', 'click', this._handleDirtyEvent.bind(this));

            this.container.select('textarea').each(function(textarea) {
                Editor = $F(textarea.id);

                if (Editor.isYUIEditor()) {
                    Editor.observe('editorKeyDown', this._handleDirtyEvent.bind(this));
                    Editor.observe('editorMouseUp', this._handleDirtyEvent.bind(this));
                    Editor.observe('editorMouseDown', this._handleDirtyEvent.bind(this));
                } else {
                    $(textarea.id).observe('blur', this._handleDirtyEvent.bind(this));
                }
            }, this);
        }

        if ((this.isDirtiable) && (this.isNestable)) {
            this._bindToNestedControlDirtyEvent();
        }

        if (this.isQueriable) {
            // Listen to Important Query (AJAX) events...and be ready to react...
            this.query.bindAsDataConnectorEventListener('$.query.success', this._handleUnDirtyEvent.bind(this));
        }
    },

    _stopDirtyListeners: function() {
        // Stop listening on root control..if necessary
        switch (this.container.type) {
            case 'select-one':
                this.container.stopObserving('click', this._handleDirtyEvent.bind(this));
                break;
            default:
                break;
        }

        this.container.select('input[type=text]').invoke('stopObserving', 'change', this._handleDirtyEvent.bind(this));
        this.container.select('input[type=checkbox]').invoke('stopObserving', 'click', this._handleDirtyEvent.bind(this));
        this.container.select('select').invoke('stopObserving', 'click', this._handleDirtyEvent.bind(this));

        this.container.select('textarea').each(function(textarea) {
            Editor = $F(textarea.id);

            if (Editor.isYUIEditor()) {

            } else {
                $(textarea.id).stopObserving('blur', this._handleDirtyEvent.bind(this));
            }
        }, this);

        if ((this.isDirtiable) && (this.isNestable)) {
            this._unBindNestedControlDirtyEvent();
        }
    },

    _bindToNestedControlDirtyEvent: function() {
        this.nestedControls.each(function(nestedControl) {
            if (nestedControl.isDirtiable) {
                nestedControl.registerEvent('NestedControlDirtyEvent', this._handleDirtyEvent.bind(this));
                nestedControl.registerEvent('NestedControlUndirtyEvent', this._handleUnDirtyEvent.bind(this));
            }
        }, this);
    },

    _unBindNestedControlDirtyEvent: function() {
        this.nestedControls.each(function(nestedControl) {
            if (nestedControl.isDirtiable) {
                nestedControl.removeEvent('NestedControlDirtyEvent', this._handleDirtyEvent.bind(this));
                nestedControl.removeEvent('NestedControlUndirtyEvent', this._handleUnDirtyEvent.bind(this));
            }
        }, this);
    },

    _handleDirtyEvent: function() {
        if (!this.isDirty) {
            this.isDirty = true;
            this._stopDirtyListeners.call(this);

            this.fireEvent('NestedControlDirtyEvent');
        }
    },

    _handleUnDirtyEvent: function() {
        if (this.isDirty) {
            this.isDirty = false;
            this._setDirtyListeners.call(this);

            this.fireEvent('NestedControlUndirtyEvent');
        }
    },

    _handleEndDirtyEvent: function() {
        this.isDirty = false;
        this._setDirtyListeners.call(this);
    }
}

$.base.StandardEvents = {
    isStandardEvents: true,

    _attachStandardEvent: function(eventName, element) {
        switch (eventName.toLowerCase()) {
            case 'mouseover':
                element.stopObserving('mouseover');
                element.observe('mouseover', this._handleMouseOverEvent.bind(this));
                break;
            case 'mouseout':
                element.stopObserving('mouseout');
                element.observe('mouseout', this._handleMouseOutEvent.bind(this));
                break;
            default:
                break;
        }
    },

    _handleMouseOverEvent: function(event) {
        if (Prototype.Browser.IE) {
            event.srcElement.setStyle({
                cursor: 'hand'
            });
        } else {
            event.currentTarget.setStyle({
                cursor: 'pointer'
            });
        }
    },

    _handleMouseOutEvent: function(event) {
        if (Prototype.Browser.IE) {
            event.srcElement.setStyle({
                cursor: 'default'
            });
        } else {
            event.currentTarget.setStyle({
                borderStyle: 'none',
                cursor: 'default'
            });
        }
    }
}

// Methods: Insert, Update, Delete, SelectSingle, SelectMany, ExecuteStoredProcedure, SubmitHandler
$.base.Persistable = {
    isPersitable: true,
    update: function(dataBag) {
        this.formDTO = this.query.dataConnector.Update(dataBag);
    },

    insert: function(dataBag) {
        this.query.dataConnector.Insert(dataBag);
    },

    delete$: function() {
        this.query.dataConnector.Delete();
    },

    save: function(dataBag) {
        this.query.dataConnector.Save(dataBag);
    },

    savewithpost: function(dataBag) {
        this.query.dataConnector.SaveWithPost(dataBag);
    },

    saveAndReturnwithpost: function(dataBag) {
        this.query.dataConnector.SaveAndReturnWithPost(dataBag);
    },

    deletewithget: function() {
        this.query.dataConnector.DeleteWithGet();
    },

    autoBackup: function() {
        this.query.dataConnector.AutoBackup(dataBag);
    },

    submitHandler: function(state, dataBag) {
        switch (state) {
            case 'Insert':
                this.insert(dataBag);
                break;
            case 'Delete':
                this.delete$();
                break;
            case 'Update':
                this.update(dataBag);
                break;
            case 'Save':
                this.save(dataBag);
                break;
            case 'SaveWithPost':
                this.savewithpost(dataBag);
                break;
            case 'SaveAndReturn':
                this.saveAndReturnwithpost(dataBag);
                break;
            case 'DeleteWithGet':
                this.deletewithget();
                break;
            case 'ExecuteStoredProcedure':
                this.executeStoreProcedure('needtohandlestoreprocedure');
                break;
            case 'AutoBackup':
                this.autoBackup(dataBag);
                break;
            default:
                break;
        }

        return true;

    }
};

$.base.Alertable = {
    isAlertable: true,

    configuration: (function() {
        this.fileLoadingImage = 'images/loading.gif';
        this.fileBottomNavCloseImage = 'images/closelabel.gif';

        this.overlayOpacity = 0.8;   // controls transparency of shadow overlay

        this.animate = true;        // toggles resizing animations
        this.resizeSpeed = 7;        // controls the speed of the image resizing animations (1=slowest and 10=fastest)

        this.borderSize = 10;         //if you adjust the padding in the CSS, you will need to update this variable

        // When grouping images this is used to write: Image # of #.
        // Change it for non-english localization
        this.labelImage = "Image";
        this.labelOf = "of";
    })(),

    //    _showError: function(error) {

    //    },

    _showBusyIndicator: function(message) {
        // Privat Event Handler
        var _handleQueryReturn = function(event) {
            $('busyIndicator').fade({ duration: 2 });
        };

        busyIndicator = $('busyIndicator');

        if (busyIndicator == null) {
            busyIndicator = $div(
                                  { id: 'busyIndicator' }, message,
                                    $img(
                                            { id: 'busyAjax', src: 'javascript/framework/images/rotating_arrow.gif' }
                                        )
                                );

            $$('body')[0].appendChild(busyIndicator);
        }

        busyIndicator.setStyle({ position: 'absolute' });
        busyIndicator.setStyle({ display: 'none' });
        busyIndicator.style.top = '0px';
        busyIndicator.style.left = '0px';
        busyIndicator.setStyle({ opacity: 0.5 });
        busyIndicator.setStyle({ backgroundColor: '#C1C1C1' });
        busyIndicator.style.zIndex = 1000;
        busyIndicator.setStyle({ width: '200px' });
        busyIndicator.setStyle({ height: '30px' });

        busyIndicator.show();
        // Listen to the query complete event..to turn off indicator automatically..
        if (this.isQueriable) {
            // Listen to Important Query (AJAX) events...and be ready to react...
            this.query.bindAsDataConnectorEventListener('$.query.success', _handleQueryReturn.bind(this));
            this.query.bindAsDataConnectorEventListener('$.query.failure', _handleQueryReturn.bind(this));
            this.query.bindAsDataConnectorEventListener('$.query.exception', _handleQueryReturn.bind(this));
        }
    },

    _showProgressBar: function() {
        this.overlay = this._buildContainerOverlay(this.container);

        if (this.overlay != null && this.overlay.getWidth() > 0) {
            var busyIndicator = $img({ id: 'busyAjax', src: 'javascript/framework/images/rotating_arrow.gif' });

            var xTop = 5 + 'px';
            var xLeft = 5 + 'px';

            busyIndicator.position = 'absolute';
            busyIndicator.style.top = xTop;
            busyIndicator.style.left = '100px';
            //            busyIndicator.style.width = (this.overlay.getWidth() / 10) + 'px';
            //            busyIndicator.style.height = (this.overlay.getWidth() / 10) + 'px';
            busyIndicator.style.zIndex = 1000;
            ajaxIndicator = this.overlay.select('img#busyAjax');

            // Privat Event Handler
            var _handleQueryReturn = function(event) {
                $(this.overlay.id).fade({ duration: 2 });
                //                $(this.overlay.id).remove.delay();
            };

            if (ajaxIndicator.length == 0) {
                this.overlay.appendChild(busyIndicator);
                //                busyIndicator.style.display = 'block';

                // Listen to the query complete event..to turn off indicator automatically..
                if (this.isQueriable) {
                    // Listen to Important Query (AJAX) events...and be ready to react...
                    this.query.bindAsDataConnectorEventListener('$.query.success', _handleQueryReturn.bind(this));
                    this.query.bindAsDataConnectorEventListener('$.query.failure', _handleQueryReturn.bind(this));
                    this.query.bindAsDataConnectorEventListener('$.query.exception', _handleQueryReturn.bind(this));
                }
            }
        }
    },
    _showDialog: function(dialog, container) {
        viewPortOffsets = document.viewport.getScrollOffsets();
        viewPortDimensions = document.viewport.getDimensions();

        //        window.removeEventListener('scroll', this._scrollHandler.bind(this), false);
        //        window.addEventListener('scroll', this._scrollHandler.bind(this), false);

        this.dialogContainer = container;
        this.dialog = dialog;
        this.frameWork = this._buildFramework(dialog, container);
        this.overlay = this._buildOverlay(container, dialog);

        dialog.style.display = 'none';
        dialog.style.position = 'absolute';
        dialog.style.opacity = '1';

        //dialog.style.top = viewPortOffsets['top'] + 15 + 'px';
        dialog.style.top = parseInt(this.frameWork.style.top) + ((parseInt(this.frameWork.style.height) - dialog.getHeight()) / 2) + 'px';
        dialog.style.left = (viewPortDimensions['width'] - dialog.getWidth()) / 2 + 'px';

        //        dialog.style.top = frameWork.style.top + 10 + 'px';
        //        dialog.style.left = frameWork.style.left + 10 + 'px';
        dialog.style.zIndex = '110';

        if ($(dialog.id) == null) {
            container.appendChild(dialog);
        }

        $(dialog.id).slideDown();
    },

    _buildFramework: function(dialog, container, IsRedraw) {
        // Create only if necessary...
        if ($('frameWork') == null) {
            var frameWork = $div({ id: 'frameWork' },
                $img(
                    { id: 'closeFrameWork'
                      , src: 'javascript/framework/images/close.gif'
                      , alt: 'Close Dialog'
                      , style: 'position:absolute; width:15px; height: 15px;'
                    }), $div());

            container.appendChild(frameWork);
        } else {

            var frameWork = $('frameWork');
        }

        frameWork.style.width = dialog.getWidth() + 50 + 'px';
        frameWork.style.height = dialog.getHeight() + 50 + 'px';

        frameWork.style.display = 'none';
        frameWork.style.position = 'absolute';
        frameWork.style.opacity = '0.7';
        frameWork.style.backgroundColor = 'gray';
        frameWork.style.top = viewPortOffsets['top'] + 5 + 'px';
        frameWork.style.left = (viewPortDimensions['width'] - frameWork.getWidth()) / 2 + 'px';
        frameWork.style.zIndex = '101';

        // Calculate coordinates for 'X' ...that is 'Close Framework' button
        var xTop = 5 + 'px';
        var xLeft = (frameWork.getWidth() - 30) + 'px';

        $('closeFrameWork').style.position = 'absolute';
        $('closeFrameWork').style.top = xTop;
        $('closeFrameWork').style.left = xLeft;

        // Attach the Close dialog behaivior...
        $('closeFrameWork').observe('click', this._handleCloseFrameworkClick.bind(this));


        if (Object.isUndefined(IsRedraw))
            $(frameWork.id).slideDown();
        else {
            frameWork.style.display = 'block';
        }

        return frameWork;
    },

    _buildOverlay: function(container, dialog) {
        var containerDimensions = container.getBoundingClientRect();
        var dialogDimensions = dialog.getBoundingClientRect();
        var viewPortOffsets = document.viewport.getScrollOffsets();
        var viewPortDimensions = document.viewport.getDimensions();

        // This measurements are re-usable...
        containerWidth = container.getWidth();
        containerHeight = container.getHeight();
        containerTop = containerDimensions['top'];
        containerLeft = containerDimensions['left'];
        vPortTop = viewPortOffsets['top'];
        vPortLeft = viewPortOffsets['left'];
        vPortWidth = viewPortDimensions['width'];
        vPortHeight = viewPortDimensions['height'];
        vDialogWidth = dialog.getWidth();
        vDialogHeight = dialog.getHeight();

        // Create only if necessary...
        if ($('overlay') == null) {
            var overlay = $div({ id: 'overlay' });
            container.appendChild(overlay);
        } else {
            var overlay = $('overlay');
        }

        overlay.style.position = 'absolute';
        overlay.style.backgroundColor = 'gray';
        overlay.style.opacity = '0.1';
        overlay.style.top = (vPortHeight / 4) + 'px';
        overlay.style.left = (vPortWidth / 4) + 'px';
        overlay.style.width = (containerWidth / 2) + 'px';
        overlay.style.height = (containerHeight / 2) + 'px';

        overlay.style.zIndex = 100;

        // Resize overlay to fit the screen
        newWidth = containerWidth;
        newTop = containerTop + vPortTop;
        newLeft = containerLeft + vPortLeft;

        //...height is tricky...hence...
        if ((containerHeight - (vPortHeight + vPortTop)) < 0) {
            newHeight = (containerHeight + (vDialogHeight / 2));
        } else {
            if ((vPortHeight - vDialogHeight) < 30) {
                newHeight = containerHeight + ((vDialogHeight - (containerHeight - (vPortTop + vPortHeight))) / 2);
            } else {
                newHeight = containerHeight;
            }
        }

        var morphEffect = 'width:' + newWidth + 'px; ' + 'height:' + newHeight + 'px; ' + 'top:' + newTop + 'px; ' + 'left:' + newLeft + 'px; opacity:0.5;';

        overlay.style.display = 'block';
        $(overlay.id).morph(morphEffect);

        return overlay;
    },

    _buildViewPortOverlay: function(container) {
        var viewPortOffsets = document.viewport.getScrollOffsets();
        var viewPortDimensions = document.viewport.getDimensions();

        // Create only if necessary...
        if ($('overlay') == null) {
            var overlay = $div({ id: 'overlay' });
            container.appendChild(overlay);
        } else {
            var overlay = $('overlay');
        }

        overlay.style.position = 'absolute';
        overlay.style.width = viewPortDimensions['width'] - 5 + 'px';
        overlay.style.height = viewPortDimensions['height'] - 5 + 'px';
        overlay.style.opacity = '0.5';
        overlay.style.backgroundColor = 'gray';
        overlay.style.top = viewPortOffsets['top'] + 3 + 'px';
        overlay.style.left = viewPortOffsets['left'] + 3 + 'px';

        container.appendChild(overlay);

        var morphEffect = 'width:' + newWidth + 'px; ' + 'height:' + newHeight + 'px; ' + 'top:' + newTop + 'px; ' + 'left:' + newLeft + 'px; opacity:0.5;';

        $('overlay').morph(morphEffect);

        return overlay;

    },

    _buildContainerOverlay: function(container) {
        if (container && container.style.display != 'none') {
            var containerDimensions = container.getBoundingClientRect();
            var containerOffsets = container.positionedOffset();
            var viewPortDimensions = document.viewport.getDimensions();
            var viewPortOffsets = document.viewport.getScrollOffsets();

            containerWidth = containerDimensions['width'];
            containerHeight = containerDimensions['height'];
            containerLeft = containerDimensions['left'];
            containerRight = containerDimensions['right'];
            containerTop = containerDimensions['top'];
            containerBottom = containerDimensions['bottom'];
            containerOffsetTop = containerOffsets['top'];
            containerOffsetLeft = containerOffsets['left'];

            viewportWidth = viewPortDimensions['width'];
            viewportHeight = viewPortDimensions['height'];
            viewportTop = viewPortOffsets['top'];
            viewportLeft = viewPortOffsets['left'];

            // Calculate Container Width if necessary
            if (containerWidth == 0)
                containerWidth = viewportWidth - containerLeft - containerRight;

            // Calculate Container Height if necessary
            if (containerHeight == 0)
                containerHeight = viewportHeight - containerTop - containerBottom + viewportTop;

            // Create only if necessary...
            if ($('overlay') == null) {
                var overlay = $div({ id: 'overlay' });
                $$('body')[0].appendChild(overlay);
            } else {
                var overlay = $('overlay');
            }

            overlay.style.position = 'absolute';
            overlay.style.display = 'none';
            overlay.style.width = containerWidth + 'px';
            overlay.style.height = containerHeight + 'px';
            overlay.style.opacity = '0.3';
            overlay.style.backgroundColor = 'gray';
            overlay.style.top = containerTop + viewportTop + 'px';
            overlay.style.left = containerOffsetLeft + 'px';

            $('overlay').grow();

            return overlay;
        }

        return null;
    },
    _handleCloseFrameworkClick: function() {
        $(this.frameWork.id).blindUp();
        $(this.dialog.id).blindUp();
        $(this.overlay.id).fade();

        window.removeEventListener('scroll', this._scrollHandler.bind(this), false);

        // this.frameWork.parentNode.removeChild.defer(this.frameWork);
        // this.dialog.style.display = 'none';
        // this.overlay.parentNode.removeChild.defer(this.overlay);
    },

    _scrollHandler: function(event) {
        currentPortOffsets = document.viewport.getScrollOffsets();

        containerLimit = this.overlay.getHeight() - (currentPortOffsets['top'] + this.dialog.getHeight()) - 30;

        if (containerLimit >= 0) {
            this.frameWork.style.top = currentPortOffsets['top'] + 5 + 'px';
        }

        newTop = parseInt(this.frameWork.style.top) + ((parseInt(this.frameWork.style.height) - this.dialog.getHeight()) / 2);

        this.dialog.style.top = newTop + 'px';
    }
};

$.base.Queriable = {
    isQueriable: true,
    isModalDisplayed: false,
    selectSingle: function(key) {
        this.query.dataConnector.SelectSingle(key);
    },

    selectMany: function() {
        this.query.dataConnector.SelectMany();
    },

    executeStoreProcedure: function() {
        this.query.dataConnector.ExecuteStoredProcedure;
    },

    _handleQueryFailure: function() {
        //        // hide ajax loading image        
        //        if (this.controlFormat) {
        //            if (this.controlFormat.loadingImg) {
        //                Element.hide(this.controlFormat.loadingImg);
        //            }
        //        }

        //        if (!this.isModalDisplayed) {
        //            Modalbox.show('warning.html', {
        //                title: 'Oops...',
        //                width: 300,
        //                afterLoad: this._setObservers.bind(this),
        //                onHide: this._removeObservers.bind(this)
        //            });
        //        }
    },

    _setObservers: function() {
        this.isModalDisplayed = true;
    },

    _removeObservers: function() {
        this.isModalDisplayed = false;
    },

    _handleQueryException: function() {
        // hide ajax loading image
        //        if (this.controlFormat) {
        //            if (this.controlFormat.loadingImg) {
        //                Element.hide(this.controlFormat.loadingImg);
        //            }
        //        }

        //        if (!this.isModalDisplayed) {
        //            Modalbox.show('warning.html', {
        //                title: 'Oops...',
        //                width: 300,
        //                afterLoad: _setObservers.bind(this),
        //                onHide: _removeObservers.bind(this)
        //            });
        //        }
    },

    _bind: function(innerControl) {
        if (this.isAlertable) {
            this._showBusyIndicator('Loading...');
        }

        if (innerControl == true) {
            this.query.executeSQLtoJSON();
        }
        else {
            // If no key was provided...we literally have nothing to retrieve

            globalDelay = 0.5; // default

            if (this.isNestable) {
                globalDelay = this.nestedControls.length * 1;
            }

            this.query.executeSQLtoJSON.bind(this.query).delay(globalDelay);

        }

    },

    _isExecutable: function() {
        // If query requires parameters...and these are null then don't databind
        try {
            if (this.query.dataConnector.command.parameters) {
                var nullParam = false;

                this.query.dataConnector.command.parameters.each(function(parameters) {
                    nullParam = true;
                }, this);

                return nullParam;
            }
        } catch (err) {
            return true;
        }
    },

    _query: function(param) {
        switch (param) {
            case 'SelectSingle':
                this.selectSingle(dataBag);
                break;
            case 'SelectMany':
                this.selectMany();
                break;
            case 'DataByFilter':
                this.DataByFilter();
        }
    },

    _hashLength: function(hash) {
        // The hash object has a bug ...it reports the length to be the data + the number of methods in the object..weirl!!!!
        hashLength = 0;

        hash.each(function(object) {
            if (!isNaN(parseInt(object.key)))
                hashLength++;

        }, this);

        return hashLength;
    }
};

$.base.Nestable = {
    isNestable: true,
    nestedControls: $A(),

    _findControlByName: function(controlName) {
        if (this.nestedControls) {
            if (Object.isArray(this.nestedControls)) {
                var control = this.nestedControls.find(function(nestedControl) {
                    return nestedControl.id == controlName;
                });

                if (control)
                    return control;
            }
        }
    },

    _findControlByID: function(controlID) {
        if (this.innerControls) {
            var controlSpec = this.innerControls.find(function(controlSpec) {
                return controlSpec.controlParts.container == controlID;
            });

            if (this.nestedControls) {
                if (Object.isArray(this.nestedControls)) {
                    var control = this.nestedControls.find(function(nestedControl) {
                        return nestedControl.id == controlSpec.controlName;
                    });

                    if (control)
                        return control;
                    else
                        return null;
                }
            }
        }
    },
    _bindInnerControls: function() {
        if (this.innerControls) {
            var count = 0;
            if (this.innerControls.each) {
                this.innerControls.each(function(control) {
                    if (control.controlClass) {
                        var activatedControl = new control.controlClass(control.controlKeys, control.controlParts, control.commandButtons, control.controlFormat, control.controlQuery, control.nestedControls);
                        if (activatedControl) {
                            // We need to take count of who is awaiting for an AJAX callback
                            activatedControl.id = control.controlName;
                            activatedControl.parentControl = this;

                            if (activatedControl.isQueriable) {
                                activatedControl.bindingActive = true;
                                activatedControl.query.bindAsDataConnectorEventListener('$.query.success', this._handleNestedControlQueryComplete.bind(this, activatedControl.id));
                                activatedControl.query.bindAsDataConnectorEventListener('$.query.failure', this._handleNestedControlQueryComplete.bind(this, activatedControl.id));
                                activatedControl.query.bindAsDataConnectorEventListener('$.query.exception', this._handleNestedControlQueryComplete.bind(this, activatedControl.id));
                                activatedControl._bind.call(activatedControl, true);
                            } else {
                                activatedControl.bindingActive = true;
                            }

                            this.nestedControls.push(activatedControl);

                            count++;

                        } else {
                            throw new $.base.Exception('ErrorDefiningInnerControl', 'Error creating ' + control.controlName);
                        }
                    }
                }, this);
            }
        }
    },

    _handleNestedControlQueryComplete: function(id, transport) {
        var firstBinding = this.nestedControls.find(function(control) {
            return control.id == id;
        }, this);

        if (firstBinding)
            firstBinding.bindingActive = false;
    },

    _isBindingActive: function() {
        // This function inspects the innerBindingList to determine if there inner controls that are
        // still awaiting for an AJAX call back. Technically a container control that needs to call
        // its own binding must wait for its inner controls to finish their own binding calls
        var firstBinding = this.nestedControls.find(function(control) {
            return control.bindingActive == true;
        }, this);

        if (!firstBinding) return firstBinding.bindingActive;
    }
}

$.base.Clickable = {
    isClickable: true,
    _attachButtonBehaivior: function() {

        if (this.commandButtons) {
            // Attach first standard button behaviors
            // Save Button
            if (this.commandButtons.SaveButton) {
                if (Prototype.Browser.IE) {
                    var saveButtons = $$Select(null, this.commandButtons.SaveButton);
                } else {
                    var saveButtons = $$('#' + this.commandButtons.SaveButton);
                }

                // Inject the SaveButton standard behavior
                saveButtons.each(function(saveButton) {
                    saveButton.stopObserving('click');
                    saveButton.observe('click', this._saveHandler.bind(this));

                }, this);
            }

            // Save & Return
            if (this.commandButtons.SaveAndReturn) {
                if (Prototype.Browser.IE) {
                    var saveAndReturnButtons = $$Select(null, this.commandButtons.SaveAndReturn);
                } else {
                    var saveAndReturnButtons = $$('#' + this.commandButtons.SaveAndReturn);
                }

                // Inject the SaveAndReturnBehavior
                saveAndReturnButtons.each(function(saveAndReturnButton) {
                    saveAndReturnButton.stopObserving('click');
                    saveAndReturnButton.observe('click', this._saveAndReturnHandler.bind(this));
                }, this);
            }

            // Insert Button
            if (this.commandButtons.InsertButton) {
                if (Prototype.Browser.IE) {
                    var insertButtons = $$Select(null, this.commandButtons.InsertButton);
                } else {
                    var insertButtons = $$('#' + this.commandButtons.InsertButton);
                }

                // Inject the InsertButton standard behavior
                insertButtons.each(function(insertButton) {
                    insertButton.stopObserving('click');
                    insertButton.observe('click', this._insertHandler.bind(this));

                }, this);
            }

            // Delete Button
            if (this.commandButtons.DeleteButton) {
                if (Prototype.Browser.IE) {
                    var deleteButtons = $$Select(null, this.commandButtons.DeleteButton);
                } else {
                    var deleteButtons = $$('#' + this.commandButtons.DeleteButton);
                }

                deleteButtons.each(function(deleteButton) {
                    deleteButton.stopObserving('click');
                    deleteButton.observe('click', this._deleteHandler.bind(this));

                    switch (this.query.dataConnector.dto.State) {
                        case formStates.Insert:
                            deleteButton.disabled = true;
                            break;

                        case formStates.Update:

                            deleteButton.disabled = false;

                            break;
                    }
                }, this);
            }

            // Cancel / Reset Button
            if (this.commandButtons.CancelButton) {
                if (Prototype.Browser.IE) {
                    var cancelButtons = $$Select(null, this.commandButtons.CancelButton);
                } else {
                    var cancelButtons = $$('#' + this.commandButtons.CancelButton);
                }

                // TODO: Develop cancelHandler function
                cancelButtons.each(function(cancelButton) {
                    cancelButton.stopObserving('click');
                    cancelButton.observe('click', this._cancelHandler.bind(this));

                }, this);
            }

            // Browse Upload File button
            if (this.commandButtons.BrowseUploadButton) {
                if (Prototype.Browser.IE) {
                    var browseUploadButtons = $$Select(null, this.commandButtons.BrowseUploadButton);
                } else {
                    var browseUploadButtons = $$('#' + this.commandButtons.BrowseUploadButton);
                }

                // TODO: Develop cancelHandler function
                browseUploadButtons.each(function(browseUploadButton) {
                    browseUploadButton.stopObserving('click');
                    browseUploadButton.observe('click', this._browseUploadHandler.bind(this));

                    switch (this.query.dataConnector.dto.State) {
                        case formStates.Insert:
                            if (!Object.isUndefined(this.commandButtons.BrowseUploadButton.insertState))
                                browseUploadButton.disabled = true;

                            break;

                        case formStates.Update:
                            if (!Object.isUndefined(this.commandButtons.BrowseUploadButton.insertState))
                                browseUploadButton.disabled = false;

                            break;
                    }

                }, this);
            }

            // More command buttons in form?
            if (this.commandButtons.ExtraButtons) {
                this.commandButtons.ExtraButtons.each(function(buttonMap) {
                    if (Prototype.Browser.IE) {
                        var extraButtons = $$Select(null, buttonMap.buttonName);
                    } else {
                        var extraButtons = $$('#' + buttonMap.buttonName);
                    }

                    extraButtons.each(function(extraButton) {
                        if (!Object.isUndefined(buttonMap.buttonType)) {
                            switch (buttonMap.buttonType) {
                                case 'Link':
                                    extraButton.setAttribute('href', buttonMap.buttonURL);
                                    //extraButton.setAttribute('target', '_blank');
                                    break;
                                default:
                                    {
                                        if (extraButton.type == 'submit')
                                            extraButton.type = 'button';

                                        if (buttonMap.buttonContext) {
                                            switch (buttonMap.buttonContext) {
                                                case 'context':
                                                    extraButton.stopObserving('click');
                                                    extraButton.observe('click', buttonMap.command.bind(this));

                                                    break;
                                                case 'control':
                                                    extraButton.stopObserving('click');
                                                    extraButton.observe('click', buttonMap.command.bind(extraButton));

                                                    break;
                                                default:
                                                    // Set by the developer at the page configuration script

                                                    extraButton.stopObserving('click');
                                                    extraButton.observe('click', buttonMap.command);
                                                    break;
                                            }

                                        } else {
                                            // Set by the developer at the page configuration script
                                            extraButton.stopObserving('click');
                                            extraButton.observe('click', buttonMap.command);
                                        }

                                        switch (this.query.dataConnector.dto.State) {
                                            case formStates.Insert:
                                                if (!Object.isUndefined(buttonMap.insertState))
                                                    extraButton.disabled = !buttonMap.insertState;
                                                break;

                                            case formStates.Update:
                                                if (!Object.isUndefined(buttonMap.updateState))
                                                    extraButton.disabled = !buttonMap.updateState;
                                                break;
                                            default:
                                                break;
                                        }
                                    }
                                    break;
                            }
                        }
                    }, this);
                }, this);
            }
        }
    },

    _updateButtonState: function(button) {
        switch (this.query.dataConnector.dto.State) {
            case formStates.Insert:
                button.disabled = !this.commandButtons.SaveButton.insertState;
                break;

            case formStates.Update:
                button.disabled = !this.commandButtons.SaveButton.updateState;
                break;
        }
    },
    _browseUploadHandler: function() {
        // TODO: Venkata
        // TODO: We need a way to handle the directory structure here...which come to think of it
        // is a pretty standard parameter for a butto like this one. We just need to make generic
        // enough to be reusable...
        var upload = new $.ui.UploadFile(this);

        // Subscribe to the OnComplete Method..and trick the form into refreshing its content.
        upload.bindAsFileUploadEventListener('$.query.complete', this._handleQuerySuccess.bind(this));

        upload._BrowseUploadHandler(this.controlParts.uploadDlg);
    },

    _saveAndReturnHandler: function() {
        if (this.isDirty) {
            // Run form level validation
            var isValidRequired = ValidationRuleFactory.GenerateRequiredRuleValidation.call(this, null);
            var isValidFields = ValidationRuleFactory.GenerateFormLevelValidationRule.call(this, null);
            // TODO: var isValidated = isValidated && ValidationRuleFactory.GenerateDependenciesValidation.call(this, null);
            // TODO: var isValidated = isValidated && ValidationRuleFactory.GenerateBehaivioralValidation.call(this, null);

            var dataBag = null

            if (isValidRequired && isValidFields) {
                // If this is a persitable object then a MergeDTO must be provided otherwise pass a null
                // Collect the data from the form...remember we generated a MergeDTO in the very beginning?
                dataBag = this.query.dataConnector.dto.toDTO(this.query.dataConnector.command);

                var isProcessed = this.submitHandler(formStates.SaveAndReturn, dataBag);
            }
            else {
                throw new $.base.Exception('ValidationFailed', 'Form failed validation');
            }
        } else {
            alert("Nothing to Save. Record was not added/modified");

            if (this.commandButtons.CancelButton) {
                this._cancelHandler.call(this);
            }
        }
    },

    _saveHandler: function() {
        // Don't save if form is not dirty
        if (this.isDirty) {
            // Run form level validation
            var isValidRequired = ValidationRuleFactory.GenerateRequiredRuleValidation.call(this, null);
            var isValidFields = ValidationRuleFactory.GenerateFormLevelValidationRule.call(this, null);
            // TODO: var isValidated = isValidated && ValidationRuleFactory.GenerateDependenciesValidation.call(this, null);
            // TODO: var isValidated = isValidated && ValidationRuleFactory.GenerateBehaivioralValidation.call(this, null);

            var dataBag = null

            if (isValidRequired && isValidFields) {
                // If this is a persitable object then a MergeDTO must be provided otherwise pass a null
                // Collect the data from the form...remember we generated a MergeDTO in the very beginning?
                dataBag = this.query.dataConnector.dto.toDTO(this.query.dataConnector.command);

                var isProcessed = this.submitHandler(formStates.Save, dataBag);


            }
            else {
                throw new $.base.Exception('ValidationFailed', 'Form failed validation');
            }
        } else {

        }
    },

    _insertHandler: function() {
        this.container.formState = 'Insert';
        // Run form level validation
        var isValidated = ValidationRuleFactory.GenerateRequiredRuleValidation.call(this, null);
        // TODO: var isValidated = isValidated && ValidationRuleFactory.GenerateDependenciesValidation.call(this, null);
        // TODO: var isValidated = isValidated && ValidationRuleFactory.GenerateBehaivioralValidation.call(this, null);        

        if (isValidated) {
            // If this is a persitable object then a MergeDTO must be provided otherwise pass a null
            // Collect the data from the form...remember we generated a MergeDTO in the very beginning?
            var dataBag = null;
            if (this.query.dataConnector) {
                if (this.query.dataConnector.MergeDTO) {
                    // Collect Data then...
                    dataBag = this.query.dataConnector.MergeDTO.toDTO.call(this, null);
                }
            }

            var isInserted = this.submitHandler(dataBag);

            if (!isInserted) {
                alert('TODO: Failed to save form in spite of all fields having passed validation. Please open a Support Ticket to further research the issue');
            }
        }
        else {
            alert('TODO: Form failed validation, no data was submitted');
        }
    },

    _deleteHandler: function(formState) {
        // Ask nicely: 'You mean you really want to delete this record?'
        // Get the Key field ID
        if (confirm('Record will be deleted permanently. Are you sure you want to proceed?', 'Delete Confirmation')) {
            // Delete
            var passed = this.submitHandler(formStates.Delete);
        }
        else {
            alert('Delete has been canceled by the user.')
        };
    },

    _cancelHandler: function() {
        result = 1;

        if (this.isDirtiable) {
            if (this.isDirty)
                result = confirm("Changes will be lost. Do you really want to continue?", 'Cancel Confirmation');
        }

        if (result == 1)
            location.href = this.commandButtons.cancelpage;
    }
};

$.base.Excepitonable = {};

$.base.Exception = Class.create();

$.base.Exception.prototype = {
    initialize: function(exceptionName, message) {
        alert(message);
    }
};

$.base.Collectable = {
    toForm: function(dataBag, form) {
        if (dataBag.size() != 0) {

            this.dataBag = dataBag;

            this.fieldSet.each(function(fieldMap) {
                //                if (this.dataBag.get(fieldMap.dbName) != null) {
                if (fieldMap.entity) {
                    if (Prototype.Browser.IE) {
                        if (fieldMap.map)
                            var elements = $$Select(form, fieldMap.inputName, null, fieldMap.map);
                        else
                            var elements = $$Select(form, fieldMap.inputName);
                    } else {
                        var elements = $$('#' + fieldMap.inputName);
                    }

                    if (elements) {
                        if (elements.length == 1) {

                            var childDTO = this.dataBag.get(fieldMap.dbName);

                            if (childDTO != null) {
                                var thisElement = $F(fieldMap.inputName);

                                if (thisElement)
                                    thisElement.setValue(childDTO[fieldMap.fkKey]);

                                // Enabled??
                                thisElement = $(fieldMap.inputName);

                                if (thisElement) {
                                    if (this.State == formStates.Update) {
                                        if (!Object.isUndefined(fieldMap.enabled)) {
                                            if (fieldMap.enabled) {
                                                $(fieldMap.inputName).disabled = false;
                                            } else {
                                                $(fieldMap.inputName).disabled = true;
                                            }
                                        } else {
                                            $(fieldMap.inputName).disabled = false;
                                        }
                                    }
                                }
                            }
                        } else {
                            // Handle Multiple Response controls (checkboxes, multi-part text boxes, select-many comboboxes)

                            // Clear all prior responses

                            switch (fieldMap.map) {
                                case 'checkbox':
                                    elements.each(function(element) {
                                        element.checked = false;
                                    }, this);
                                    break;
                                default:
                                    break;
                            }

                            var childDTOs = this.dataBag.get(fieldMap.dbName);

                            if (childDTOs != null) {
                                if (childDTOs.each) {
                                    childDTOs.each(function(childDTO) {
                                        var foundElement = elements.find(function(element) {
                                            dbValue = childDTO[fieldMap.fkKey];
                                            fldValue = element.value;

                                            if ((!Object.isUndefined(dbValue)) && (!Object.isUndefined(fldValue))) {
                                                dbValue = String(dbValue).toUpperCase();
                                                fldValue = String(fldValue).toUpperCase();

                                                return (dbValue == fldValue);
                                            }
                                        });

                                        if (foundElement) {
                                            foundElement.checked = true;
                                        }

                                    }, this);
                                } else {
                                    var thisElement = $F(fieldMap.inputName);

                                    if (thisElement)
                                        thisElement.setValue(childDTOs[fieldMap.fkKey]);
                                }
                            }
                        }
                    }

                } else {
                    if (fieldMap.map) {
                        switch (fieldMap.map.toUpperCase()) {
                            case 'IMAGE':
                                imageElement = $(fieldMap.inputName);

                                if (imageElement) {
                                    // Look for the field specification
                                    var fieldSpec = this.fieldPresentationRules.find(function(fieldSpec) {
                                        return (fieldSpec.inputID == imageElement.id);
                                    });

                                    if (fieldSpec) {
                                        // Resolve path template
                                        if (fieldSpec.path) {
                                            // #1 Set Template up
                                            template = new Template(fieldSpec.path);
                                            tempVar = $H();

                                            // #2 Retrieve Key values
                                            keyHash = this._getKeys(fieldSpec.path);

                                            // #3 Look up the value
                                            keyHash.each(function(key) {
                                                tempVar.set(key, dataBag.get(key));
                                            }, this);

                                            imageElement.writeAttribute('src', template.evaluate(tempVar) + dataBag.get(fieldMap.dbName));

                                        } else {
                                            // Update the elements SCR property
                                            imageElement.writeAttribute('src', dataBag.get(fieldMap.dbName));
                                        }
                                    } else {
                                        // Update the elements SCR property
                                        imageElement.writeAttribute('src', dataBag.get(fieldMap.dbName));
                                    }

                                    if (fieldSpec && fieldSpec.defaultPicture) {
                                        imageElement.onerror = function() {
                                            this.src = fieldSpec.defaultPicture;
                                        };
                                    }
                                }
                                break;
                            case 'RADIOOPTION':
                                var thisElement = $F(fieldMap.inputName);

                                if (thisElement)
                                    thisElement.setValue(dataBag.get(fieldMap.dbName));

                                break;
                            case 'CHECKBOX':
                                var thisElement = $F(fieldMap.inputName);

                                if (thisElement)
                                    thisElement.setValue(dataBag.get(fieldMap.dbName));

                                break;
                            case 'SINGLECHECKBOX':
                                var thisElement = $(fieldMap.inputName);

                                if (thisElement)
                                    thisElement.checked = dataBag.get(fieldMap.dbName);
                                break;
                            case 'LINKTEMPLATE':
                                if (fieldMap.URLTemplate) {
                                    template = new Template(fieldMap.URLTemplate);

                                    var thisElement = $(fieldMap.inputName);

                                    if (thisElement.tagName.toUpperCase() == 'A') {
                                        thisElement.setAttribute("href", template.evaluate(dataBag));
                                        //el.setAttribute("target", "_blank");
                                    } else {
                                        hRef = thisElement.descendants('a');

                                        if (hRef) {
                                            // an Array...get only the first one
                                            if (Object.isArray(hRef)) {
                                                hRef = hRef.first();
                                            }

                                            hRef.setAttribute("href", template.evaluate(dataBag));
                                            //hRef.setAttribute("target", "_blank");

                                        }
                                    }

                                }
                                break;

                            default:
                                var thisElement = $F(fieldMap.inputName);

                                if (thisElement)
                                    thisElement.setValue(dataBag.get(fieldMap.dbName));

                                break;
                        }
                    } else {

                        var thisElement = $F(fieldMap.inputName);

                        if (thisElement)
                            thisElement.setValue(dataBag.get(fieldMap.dbName));

                        if (fieldMap.primaryKey)
                            this.primaryKeys.set(fieldMap.dbName, this.dataBag.get(fieldMap.dbName));


                        // Enabled??
                        thisElement = $(fieldMap.inputName);

                        if (thisElement) {
                            if (this.State == formStates.Update) {
                                if (!Object.isUndefined(fieldMap.enabled)) {
                                    if (fieldMap.enabled) {
                                        $(fieldMap.inputName).disabled = false;
                                    } else {
                                        $(fieldMap.inputName).disabled = true;
                                    }
                                } else {
                                    $(fieldMap.inputName).disabled = false;
                                }
                            }
                        }
                    }
                }
                // }

            }, this);
        }

        if (this.isQueriable) {
            if (this.query.dataConnector.dto.State == formStates.readonly) {
                if (this._setFormToReadonly)
                    this._setFormToReadonly();
            }
        }
    },

    toDTO: function(command, fkName, fkValue) {
        // 
        this.dataBag = $H();

        // Save command for internal use
        this.command = command;

        // If this is a ChildDTO then a fkName was passed as parameter
        if (!fkName) {

            // ...otherwise this is a parentDTO
            // Start collecting data...and storing it in DTO.
            this.fieldSet.each(function(fieldMap) {

                // if inputName is not provided or empty for the field
                // then it is not required to validate or collect data for...obviously
                if ((fieldMap.inputName) && (fieldMap.inputName != '')) {
                    // Retrieve the element
                    var element = $F(fieldMap.inputName);

                    // If field is an entity --meaning it must be collected in its own DTO
                    if (fieldMap.entity) {
                        result = element.getValue();
                        // if the element is an array it means the information has to be collected
                        // from controls that allow multiple responses

                        if (result.each) {
                            // What type of control was this data collected from?
                            if (fieldMap.map) {
                                switch (fieldMap.map) {
                                    case 'checkbox':    // multiple responses are possible (checked)..collect them
                                        var childDTOs = $A();

                                        result.each(function(elementValue) {
                                            var proxyDTO = new fieldMap.entity();
                                            var childDTO = proxyDTO.toDTO(this.command, fieldMap.fkKey, elementValue);

                                            childDTOs.push(childDTO);

                                        }, this);

                                        this.dataBag.set(fieldMap.dbName, childDTOs);

                                        break;
                                    case 'textbox':     // a multiple-part text input--ex: areacode-phonenumber
                                        var childDTOs = $A();

                                        result.each(function(elementValue) {
                                            var proxyDTO = new fieldMap.entity();
                                            var childDTO = proxyDTO.toDTO(this.command, fieldMap.fkKey, elementValue);

                                            childDTOs.push(childDTOs);

                                        }, this);

                                        this.dataBag.set(fieldMap.dbName, childDTOs);

                                        break;
                                    default:
                                        break;
                                }
                            } else {
                                // Missing input field mapping..data cannot be collected
                                throw new $.base.Exception('MissingFieldMapDefinition', fieldMap.inputName + ' has multiple responses but has not being mapped to a valid multiple response input control such as a checkbox or a multiple part text box');
                            }
                        } else {
                            // ...otherwise data is collected from single response controls including radiooption option buttons
                            var proxyDTO = new fieldMap.entity();
                            var childDTO = proxyDTO.toDTO(this.command, fieldMap.fkKey, result);

                            this.dataBag.set(fieldMap.dbName, childDTO);
                        }
                    }
                    else {
                        // if element was found
                        if (element) {
                            // Handle special cases...
                            // When a Bool is represented as a checkbox
                            if ((fieldMap.type == 'bool') && (fieldMap.map == 'singlecheckbox')) {
                                var result = element.getValue();

                                if (result.length == 0)
                                    this.dataBag.set(fieldMap.dbName, 'false');
                                else
                                    this.dataBag.set(fieldMap.dbName, 'true');

                            } else if (fieldMap.type == 'integer') {

                                if (!Object.isUndefined(fieldMap.primaryKey)) {
                                    if (fieldMap.primaryKey) {
                                        fieldValue = element.getValue();

                                        if (Object.isUndefined(fieldValue)) {
                                            this.dataBag.set(fieldMap.dbName, 0);
                                        } else {
                                            this.dataBag.set(fieldMap.dbName, fieldValue);
                                        }
                                    }
                                }
                                else {
                                    // Check if the value was passed by parameter
                                    if (this.command.parameters) {
                                        if (!Object.isUndefined(this.command.parameters.QueryString[fieldMap.inputName])) {
                                            var parameterValue = this.command.parameters.QueryString[fieldMap.inputName];
                                            this.dataBag.set(fieldMap.dbName, parameterValue);
                                        } else {
                                            // Get the value from the element itself ..otherwise null
                                            elementValue = element.getValue();

                                            if (elementValue)
                                                this.dataBag.set(fieldMap.dbName, elementValue);
                                            else
                                                this.dataBag.set(fieldMap.dbName, null);
                                        }
                                    }



                                }
                            } else {
                                this.dataBag.set(fieldMap.dbName, element.getValue());
                            }
                        } else {
                            // Check if the value was passed by parameter
                            if (this.command.parameters) {
                                if (!Object.isUndefined(this.command.parameters.QueryString[fieldMap.inputName])) {
                                    var parameterValue = this.command.parameters.QueryString[fieldMap.inputName];
                                    this.dataBag.set(fieldMap.dbName, parameterValue);
                                } else {
                                    this.dataBag.set(fieldMap.dbName, null);
                                }
                            }

                            // Value may part of the PrimaryKeys collection
                            if (this.dataBag.get(fieldMap.dbName) == null) {
                                if (this.primaryKeys) {
                                    if (!Object.isUndefined(this.primaryKeys.get(fieldMap.inputName))) {

                                        var primaryKeyValue = this.primaryKeys.get(fieldMap.inputName);
                                        this.dataBag.set(fieldMap.dbName, primaryKeyValue);

                                    } else {
                                        this.dataBag.set(fieldMap.dbName, null);
                                    }
                                }
                            }

                            // Value may have been passed as foreignKey
                            if (this.dataBag.get(fieldMap.dbName) == null) {
                                if (fkName) {

                                    if (fkName == fieldMap.dbName) {
                                        this.dataBag.set(fieldMap.dbName, fkValue);
                                    }
                                    else {
                                        this.dataBag.set(fieldMap.dbName, null);
                                    }
                                }
                            }

                            // if the field:
                            //      (1) is null
                            //      (2) is a primaryKey or part of a key
                            // &&   (3) is not an identity
                            if (this.dataBag.get(fieldMap.dbName) == null) {
                                if (fieldMap.primaryKey) {
                                    // A primary key is always required, if not provided either as a field
                                    // on the form or as a parameter then the PrimaryKey must be an Identity
                                    // column in the database...otherwise.. this is an error

                                    if (fieldMap.identity) {
                                        // ...if a value for a primaryKey is not provided but the column is an identity
                                        // then this can only be a NEW RECORD hence...formStates.Insert
                                        this.dataBag.set(fieldMap.dbName, -1);
                                        this.dataBag.State = formStates.Save;
                                    } else {
                                        // ...otherwise is an error
                                        throw new $.base.Exception("RequiredPrimaryKey", fieldMap.inputName + ' is required but no value was collected');
                                    }
                                } else if (fieldMap.fkKey) {
                                    // A foreignKey is required only if this is a weak-entity...that is one
                                    // without a primaryKey of its own but rather identified by its foreignKeys
                                    if (fieldMap.weakEntity) {
                                        throw new $.base.Exception("RequiredPrimaryKey", fieldMap.inputName + ' is required to identify this entity');
                                    }
                                }
                                else {
                                    //...not a primaryKey or a foreignKey but required?
                                    if (fieldMap.required) {
                                        throw new $.base.Exception("RequiredFieldException", fieldMap.inputName + ' is a required field');
                                    } else {
                                        // Assign the defaulValue if available
                                        if (!Object.isUndefined(fieldMap.defaultValue)) {
                                            this.dataBag.set(fieldMap.dbName, fieldMap.defaultValue);
                                        } else {
                                            this.dataBag.set(fieldMap.dbName, null);
                                        }
                                    }
                                }
                            } else {
                                if (fieldMap.primaryKey) {
                                    //..if the value for a primary key is provided this could either be and Inser or an Update
                                    // the decision is left to the Data Layer
                                    this.dataBag.State = formStates.Save;
                                }
                            }
                        }
                    }
                }
            }, this);

        } else {
            this.fieldSet.each(function(fieldMap) {
                // Check if the value was passed by parameter
                if (this.command.parameters) {
                    if (!Object.isUndefined(this.command.parameters.QueryString[fieldMap.inputName])) {
                        var parameterValue = this.command.parameters.QueryString[fieldMap.inputName];
                        this.dataBag.set(fieldMap.dbName, parameterValue);
                    } else {
                        this.dataBag.set(fieldMap.dbName, null);
                    }
                }

                // Value may have been passed as foreignKey
                if (this.dataBag.get(fieldMap.dbName) == null) {
                    if (fkName) {

                        if (fkName == fieldMap.dbName) {
                            this.dataBag.set(fieldMap.dbName, fkValue);
                        }
                        else {
                            // Look for a Default Value
                            if (fieldMap.defaultValue) {
                                this.dataBag.set(fieldMap.dbName, fieldMap.defaultValue);
                            } else {
                                // let the Server decide what to do 
                                this.dataBag.set(fieldMap.dbName, -1);
                            }
                        }
                    }
                }
            }, this);

            return this.dataBag;

        }

        return this.dataBag;
    },

    _getKeys: function(commandString) {
        // Returns a hash with {key} = value ...
        // the {key} is in the commandString
        // the value is in the command.parameters
        var keyCommand = commandString;
        var templateKeys = $A();

        this.fieldSet.each(function(fieldMap) {
            if (commandString.toUpperCase().indexOf(fieldMap.dbName.toUpperCase()) != -1) {
                templateKeys.push(fieldMap.inputName);
            }
        }, this);

        return templateKeys;
    }
}


$.base.Pageable = Class.create();

Object.extend($.base.Pageable, {
    isPageable: true,
    _setPageNo: function(pageNo, memo, data) {
        this.pageNo = pageNo;
        this._handleQuerySuccess(memo, data);
    },

    _createPageNumberSpan: function(text, pageNo, memo, data) {
        var thisObj = this;
        var span = new Element('span', {});
        Element.update(span, text);

        Event.observe(span, 'click', function() {
            thisObj._setPageNo(pageNo, memo, data);
        });
        return span;
    },

    _createPageHandler: function(memo, data, controlFormat, pagerContainer) {
        // Do nothing if there are no records to display..
        if (Object.isArray(data)) {
            if (data.length == 0) return;
        }

        var recordsetPaging = controlFormat.recordsetPaging;
        var pageSize = controlFormat.pageSize;
        var recordCount = data.length;
        var pageCount = Math.ceil(recordCount / pageSize);
        var pagerContainer = $(pagerContainer);

        // clear pager container
        Element.childElements(pagerContainer).each(function(ele) {
            Event.stopObserving(ele);
            Element.remove(ele);
        });
        Element.update(pagerContainer, '');

        // create first/last, previous/next links
        Object.extend(pagerContainer, {
            recordsetPaging: recordsetPaging,
            pageCount: pageCount,
            firstPage: this._createPageNumberSpan.call(this, '&lt;&lt;', 0, memo, data),
            previousPage: this._createPageNumberSpan.call(this, '&lt;', this.pageNo - 1, memo, data),
            nextPage: this._createPageNumberSpan.call(this, '&gt;', this.pageNo + 1, memo, data),
            lastPage: this._createPageNumberSpan.call(this, '&gt;&gt;', pageCount - 1, memo, data),
            pages: []
        });

        // insert first & previous
        Element.insert(pagerContainer, pagerContainer.firstPage);
        Element.insert(pagerContainer, pagerContainer.previousPage);

        var _insertPageNumber = function(i) {
            if (i == this.pageNo) {
                Element.insert(pagerContainer, '<strong>' + (i + 1) + '</strong>');
            }
            else {
                var span = this._createPageNumberSpan.call(this, i + 1, i, memo, data);
                pagerContainer.pages[i] = span;
                Element.insert(pagerContainer, span);
            }
        };

        // insert page numbers
        if (pageCount <= recordsetPaging) {
            for (var i = 0; i < pageCount; i++) {
                _insertPageNumber.call(this, i);
            }
        }
        else {
            var lowerBound = Math.max(0, Math.ceil(this.pageNo - recordsetPaging / 2));
            var upperBound = Math.min(lowerBound + recordsetPaging, pageCount);

            if (lowerBound > 0) {
                Element.insert(pagerContainer, '...');
            }

            for (var i = lowerBound; i < upperBound; i++) {
                _insertPageNumber.call(this, i);
            }

            if (lowerBound + recordsetPaging < pageCount) {
                Element.insert(pagerContainer, '...');
            }
        }

        // insert next & last
        Element.insert(pagerContainer, pagerContainer.nextPage);
        Element.insert(pagerContainer, pagerContainer.lastPage);

        this.pagerContainer = pagerContainer;
        // show pager
        Element.show(pagerContainer);

        // show/hide previous/next & first/last links
        (this.pageNo == 0 ? Element.hide : Element.show)(this.pagerContainer.firstPage);
        (this.pageNo == 0 ? Element.hide : Element.show)(this.pagerContainer.previousPage);
        (this.pageNo == this.pagerContainer.pageCount - 1 ? Element.hide : Element.show)(this.pagerContainer.nextPage);
        (this.pageNo == this.pagerContainer.pageCount - 1 ? Element.hide : Element.show)(this.pagerContainer.lastPage);
    }
});

$.base.Filterable = Class.create();

Object.extend($.base.Filterable, {
    isFilterable: true,
    _filter: function(filterStruct, data) {
        if (!filterStruct) {
            throw new $.base.Exception('something happened');
        }

        // HACK: Sometimes data contains a single record...this functions expects an Array.
        if (!Object.isArray(data)) {
            var aData = $A();

            if (Object.isHash(data)) {
                aData.push(data.toObject());
            } else {
                aData.push(data);
            }

            data = aData;
        }

        if (data) {
            if (Object.isHash(data)) {
                toFilterData = data.values();
            } else if (Object.isArray(data)) {
                toFilterData = data;
            } else {
                toFilterData = $A(data);
            }
        } else {
            throw new $.base.Exception('something happened');
        }

        var filteredData = toFilterData;

        filterStruct.each(function(filteredField) {

            var resultData = filteredData.findAll(function(dataRecord) {

                if (!Object.isFunction(dataRecord)) {
                    fieldName = filteredField.get('fieldName');
                    fieldValue = filteredField.get('fieldValue');

                    // We need to determine the type of recordValue
                    var fieldSpec = this.query.dataConnector.dto.fieldSet.find(function(field) {
                        return (fieldName.toUpperCase() == field.dbName.toUpperCase());
                    });

                    if (fieldSpec) {
                        recordValue = dataRecord[fieldSpec.dbName];

                        switch (fieldSpec.type.toUpperCase()) {
                            case 'BOOL':
                                if (Object.isString(fieldValue))
                                    fieldValue = (fieldValue.toUpperCase() == 'TRUE') ? true : false;

                                return (recordValue == fieldValue);

                                break;
                            default:
                                recordValue = String(recordValue);
                                recordValue = recordValue.toUpperCase();

                                fieldValue = String(fieldValue);
                                fieldValue = fieldValue.toUpperCase();


                                if (fieldValue.indexOf('!') == -1) {
                                    isMatch = (recordValue.indexOf(fieldValue) != -1);
                                } else {
                                    fieldValue = fieldValue.gsub('!', '')
                                    isMatch = !(recordValue.indexOf(fieldValue) != -1);
                                }

                                return isMatch;

                                break;
                        }
                    } else {
                        alert('Filter is disabled. No Field Specification found');
                    }
                } else {
                    return false;
                }
            }, this);

            filteredData = resultData;

        }, this);

        return filteredData;
    },

    _sort: function(sortField, toSortData, order) {
        if (!sortField) {
            throw new $.base.Exception('something happened');
        }

        if (toSortData) {
            if (Object.isHash(toSortData)) {
                toSortData = data.values();
            } else if (Object.isArray(toSortData)) {
                toSortData = toSortData;
            } else {
                toSortData = $A(toSortData);
            }
        } else {
            throw new $.base.Exception('something happened');
        }


        if (order == sortOrder.Asc) {
            sortedData = toSortData.sort(function(a, b) {
                aValue = a[sortField];
                bValue = b[sortField];

                if (Object.isString(aValue)) {
                    aValue = aValue.toUpperCase();
                    bValue = bValue.toUpperCase();
                }

                return aValue > bValue;
            });
        } else {
            sortedData = toSortData.sort(function(a, b) {

                aValue = a[sortField];
                bValue = b[sortField];

                if (Object.isString(aValue)) {
                    aValue = aValue.toUpperCase();
                    bValue = bValue.toUpperCase();
                }

                return aValue <= bValue;
            });
        }

        return sortedData;

    }
});

/***********************************
* UTILITY FUNCTIONS
************************************/

function isUnescapeHTML2(fragment) {
    stringvalue = fragment;

    args = ['&lt;', '&gt;', '&amp;lt;', '&amp;gt;'];

    for (i = 0; i < args.length; i++) {
        if (stringvalue.indexOf(args[i]) != -1) {
            return true;
        }
    }

    return false;
}

// An HTML string is handled by both the front and the backend. Each of this ends may be driven
// by scripting/programming languages that differ in its use of html encoding...
// this function will unescape HTML no matter how many times it has to run the unescapeHTML function
// until all that remains is actual HTML
function unescapeHTML2(stringvalue) {
    if (Object.isString(stringvalue)) {

        loopString = stringvalue;

        while (isUnescapeHTML2(loopString)) {
            loopString = loopString.unescapeHTML();
        }

        return loopString;
    }

    return stringvalue;
}

function stripHTMLTag(value, tag) {
    var validChars = new RegExp('<' + tag + '[^>]+\>', 'g');

    if (validChars.test(value)) {
        return value.replace(validChars, '');
    } else {
        return value;
    }
}

function stripImageTag(fieldValue) {
    var validChars = /<img[^>]+\>/i;

    if (validChars.test(fieldValue)) {
        return content.replace(fieldValue, '');
    } else {
        return fieldValue;
    }
}

function $$Select(rootElement, elementName, tagName, type) {
    if (Prototype.Browser.IE) {
        var allElements = $A();

        if (elementName == null) {
            if (Object.isUndefined(rootElement) || rootElement == null)
                var elements = $$('body')[0].getElementsByTagName(tagName);
            else
                var elements = rootElement.getElementsByTagName(tagName);

            // HACK: Standardize types...
            switch (type) {
                case 'radiooption': type = 'radio'; break;
                default: break;
            }

            if (type) {
                var elementSet = $A(elements).findAll(function(element) {
                    return (element.type.toLowerCase() == type.toLowerCase());
                });
            } else {
                var elementSet = $A(elements);
            }

            allElements.mergeElements(elementSet);
        }
        else if (type) {
            if (Object.isUndefined(rootElement) || rootElement == null)
                var elements = $$('body')[0].getElementsByTagName('input');
            else
                var elements = rootElement.getElementsByTagName('input');

            // HACK: Standardize types...
            switch (type) {
                case 'radiooption': type = 'radio'; break;
                default: break;
            }

            var elementSet = $A(elements).findAll(function(element) {
                return (element.type.toLowerCase() == type.toLowerCase()) && (element.id.toLowerCase() == elementName.toLowerCase());
            });

            allElements.mergeElements(elementSet);

        } else if (tagName) {
            if (Object.isUndefined(rootElement) || rootElement == null)
                var elements = $$('body')[0].getElementsByTagName(tagName.toLowerCase());
            else
                var elements = rootElement.getElementsByTagName(tagName.toLowerCase());

            var elementSet = $A(elements).findAll(function(element) {
                return element.id == elementName;
            });

            allElements.mergeElements(elementSet);
        } else {
            var sample = $(elementName);

            if (sample) {
                if (Object.isUndefined(rootElement) || rootElement == null)
                    var elements = $A($$('body')[0].getElementsByTagName(sample.tagName));
                else
                    var elements = $A(rootElement.getElementsByTagName(sample.tagName));

                // Depth First Search...
                var subElements = $A();

                var elementSet = elements.each(function(element) {
                    elementChildren = $$Select(element, elementName);

                    subElements.mergeElements(elementChildren);

                }, this);

                var elementSet = elements.findAll(function(element) {
                    return element.id == elementName;
                });

                elementSet.mergeElements(subElements);

                allElements.mergeElements(elementSet);
            }
        }

        return allElements.uniq();
        
    } else {
        if (rootElement != null) {
            if (Object.isUndefined(tagName) || tagName == null)
                return rootElement.select('#' + elementName);
            else
                return rootElement.select(tagName + '#' + elementName);
        } else {
            if (Object.isUndefined(tagName) || tagName == null)
                return $$('body')[0].select('#' + elementName);
            else
                return $$('body')[0].select(tagName + '#' + elementName);
        }
    }
}

/***********************************
* JAVASCRIPT / PROTOTYPE EXTENSIONS
************************************/
Object.extend(Array.prototype, {
    merge: function(array2) {
        array2.each(function(item) {
            this.push(item);
        }, this);
    },
    mergeElements: function(array2) {
        array2.each(function(item) {
            this.push($(item));
        }, this);
    }
});


/***********************************
* ELEMENT WRAPPERS
************************************/

function $F(elementName){
    // Extract all the input elements from the form
    var element = $(elementName);
    
    if (element) {
        switch (element.type) {
            case 'text': 
            case 'file':
            case 'hidden':            
                return $FText(elementName);
            case 'textarea':
                return $FTextarea(elementName);
            case 'image':
                return $FImage(elementName);
            case 'checkbox':
                return $FCheckBox(elementName)
            case 'radio':
                return $FRadioOption(elementName)
            case 'password':
                return $FText(elementName);
            case 'select-one':
                return $FComboBox1(elementName);
            default:
                {
                    switch (element.tagName.toUpperCase()) {
                        case 'IMG':
                            return $FImage(elementName);
                        case 'SPAN':
                            return $FGenericTag(elementName);
                    }
                }
        }
    }
}

function $FText(element){
    return {
        constructor: '',
        getValue: function() {
            var rValue = $(element).getValue();
            if (!Object.isString(rValue)) {
                var isDate = new Date(rValue);
            }
            //            if (isDate == NaN)
            //                return rValue;
            if (isDate) {
                if (isDate != 'Invalid Date')
                    return isDate;
                else
                    return rValue;
            }
            else { return rValue; }

        },
        setValue: function(value) {
            if (Object.isString(value))
                var isDate = value.indexOf('/Date(');
            else
                isDate = -1;

            if (isDate == 0) {
                // Strip date label
                var lastIndex = value.indexOf(')');
                var dateString = value.substr(isDate + 6, value.length - (isDate + 5) - 3);

                $(element).value = dateString;
            }
            else
                var thisElement = $(element);

            textElements = $$('#' + thisElement.id);

            textElements.each(function(textElement) {
                if (value != null)
                    textElement.value = value;
            }, this);

        }
    }
}

function $FTextarea(element){
    return {
        constructor: '',
        isYUIEditor: function() {
            try {
                // check if YUI imported
                if (!Object.isUndefined(YAHOO)) {
                    // get HTML from YI editor object
                    var editor = YAHOO.widget.EditorInfo.getEditorById(element);
                    if (editor != false) {
                        return true;
                    }
                }
                return false;
            } catch (e) {
                return false;
            }
        },

        getValue: function() {
            // HACK: YAHOO is only defined in the YUI libraries...not necessary in all pages            
            try {
                // check if YUI imported
                if (!Object.isUndefined(YAHOO)) {
                    // get HTML from YI editor object
                    var editor = YAHOO.widget.EditorInfo.getEditorById(element);
                    if (!Object.isUndefined(editor)) {
                        return editor.getEditorHTML().escapeHTML();
                    }
                }
            } catch (e) {
                // return HTML element value
                return $(element).getValue();
            }
        },
        setValue: function(value) {
            // HACK: YAHOO is only defined in the YUI libraries...not necessary in all pages            
            try {
                // check if YUI imported
                if (!Object.isUndefined(YAHOO)) {
                    // get HTML from YI editor object
                    var editor = YAHOO.widget.EditorInfo.getEditorById(element);
                    if (!Object.isUndefined(editor)) {
                        value = unescapeHTML2(value);

                        editor.setEditorHTML(value);
                    }
                }
            } catch (e) {
                $(element).value = value;
            }
        },

        observe: function(eventName, eventFunction) {
            // check if YUI imported
            if (!Object.isUndefined(YAHOO)) {
                // get HTML from YI editor object
                var editor = YAHOO.widget.EditorInfo.getEditorById(element);
                if (!Object.isUndefined(editor)) {
                    editor.on(eventName, eventFunction);
                }
            }
        }
    }
}

function $FImage(element){
    return {
        constructor: 'image',
        getValue: function() {
            var imgElement = $(element);

            // Strip all information that is not the file name..
            var srcParts = imgElement.src.split('/');

            return srcParts.last();
        },
        setValue: function(value) {
            $(element).scr = value;
        }
    }
}

function $FGenericTag(element) {
    return {
        constructor: 'Generic HTML Tag',
        getValue: function() {
            var tagElement = $(element);

            return tagElement.innerHTML;
        },
        setValue: function(value) {
            $(element).update(value);
        }
    }
}


function $FRadioOption(element) {
    return {
        constructor: '',
        getValue: function() {
            var optionButtons = $$('#' + element);
            var result = '';

            if (optionButtons.length == 0) {
                return null;
            }
            else {
                optionButtons.each(function(optionButton) {
                    if (optionButton.checked) {
                        result = optionButton.value;
                    }
                }, this);
            }

            return result;
        },
        setValue: function(value) {
            var optionButtons = $$('#' + element);

            optionButtons.each(function(optionButton) {
                if (optionButton.value == String(value))
                    optionButton.checked = true;
                else
                    optionButton.checked = false;
            }, this);
        }
    }
}


function $FComboBox1(element) {
    return {
        constructor: 'combobox',
        getValue: function() {
            var comboBox = $(element);
            var results = '';

            if (comboBox.selectedIndex == -1)
                return null;
            else
                return comboBox.value;
        },
        setValue: function(value) {
            var selectArray = $A();

            var select = $(element);

            if (Prototype.Browser.IE) {
                if (select.options)
                    selectArray.push(select);
                else
                    selectArray = select;
            } else {
                if (!Object.isArray(select)) {
                    selectArray.push(select);
                } else {
                    selectArray = select;
                }
            }

            selectArray.each(function(thisSelect) {
                var foundOption = $A(thisSelect.options).find(function(option) {
                    return option.value.toUpperCase().strip() == String(value).toUpperCase().strip();
                }, this);

                if (foundOption)
                    thisSelect.selectedIndex = foundOption.index;

            }, this);
        }
    }
}

function $FCheckBox(element){
    return {
        constructor: 'checkbox',
        getValue: function() {
            var checkBoxes = $$('#' + element);
            var results = $A();

            if (checkBoxes.length == 0)
                return null;
            else {
                checkBoxes.each(function(checkBox) {
                    if (checkBox.checked) {
                        results.push(checkBox.value);
                    }
                }, this);
            }

            return results;
        },
        setValue: function(values) {
            // Values contains an array of 'values.' If we match the one value to a checkbox then
            // we set the checkbox to the checked state
            if (values.each) {
                values.each(function(value) {
                    var checkBoxes = $$('#' + element);

                    var foundcheckBox = checkBoxes.find(function(checkBox) {
                        return checkBox.value == value;
                    });

                    if (foundcheckBox) {
                        foundcheckBox.checked = true;
                    }
                }, this);
            } else {
                var checkBoxes = $$('#' + element);

                var foundcheckBox = checkBoxes.find(function(checkBox) {
                    return checkBox.value == values;
                });

                if (foundcheckBox) {
                    foundcheckBox.checked = true;
                }
            }
        }
    }
}

Function.prototype.executePeriodically = function() {
    var s = this;
    if (typeof arguments[0].callee != 'undefined') {
        var arquments = arguments[0];
    } else {
        var arquments = arguments;
    }

    var delay = arquments[0];
    var timesToExecute = arquments[1];
    this.__INTERVAL__ = null;

    var args = [];
    for (var i = 2; i < arquments.length; i++) { args.push(arquments[i]); }

    s.apply(this, args);

    if (this.__INTERVAL__)
        clearTimeout(this.__INTERVAL__);

    if (--timesToExecute > 0) {
        this.__INTERVAL__ = setTimeout(function() {
            arquments[1] = timesToExecute;
            s.executePeriodically(arquments);
        }, delay);
    }
    return s;
}
