var util = YAHOO.util;
var Event = YAHOO.util.Event;
var Dom = YAHOO.util.Dom;
var DT = YAHOO.widget.DataTable;
var DS = YAHOO.util.DataSource;
var DDM = YAHOO.util.DragDropMgr;
var lang = YAHOO.lang;
YAHOO.namespace('lts');
var lts = YAHOO.lts;
var gizmo = 'http://www.lt-systems.com/gizmo.xsd';
lts.namespaces = {gizmo: gizmo};

lts.widget = function(){};
lts.util = function(){};
lts.lang = function(){};

var googleLoaded = false;
var googleArgs = {};
loadGoogle = function(oArgs) {
    if (!googleLoaded) {
        googleArgs = oArgs; 
        var script = document.createElement('script');
        script.type = 'text/javascript';
        var keys = {
            'hermes': 'ABQIAAAAXRfyqMUo_tZkd2lM3AJWqRTV_MFY9keqLyTK9_HfqkbRM_4TfBRMRO5rd3XluAVtDSFqihPQwouuzw',
            'maximus': 'ABQIAAAAXRfyqMUo_tZkd2lM3AJWqRQE7LDh13L6pMN9Rp5K2Z3Fok8vQhR2xQxokM4GTAzQ4muZMOYDEjf5Lg',
            'devel': 'ABQIAAAAXRfyqMUo_tZkd2lM3AJWqRRcbJJVOinNLgs_lDN65pT5JQRFDRScYUeiQExohKoX688qKjDFTUrISg'
        }
        script.src = 'http://www.google.com/jsapi?key=' + keys['devel'] + '&callback=googleReady';
        document.body.appendChild(script);
        googleLoaded = true;
    } else {
        oArgs.method.call(oArgs.context);
    }
}
googleReady = function() {
    google.load("maps", "2", {"callback": googleCallback});
}
googleCallback = function() {
    googleArgs.method.call(googleArgs.context);
}

lts.util.showError = function(o, status, message) {
    if (o.responseXML) {
        error = lts.util.parseGizmoError(o.responseXML).join('<br/>');
    } else if (o.responseText !== undefined) {
        error = o.responseText.match(/message="([^"]*)"/)[1];
    }
    if (!error) {
        error = message;
    }
    window.scrollTo(0, 0);
    status.showError(error);
}

lts.util.createLookup = function(input, oConfigs) {
    var lookup = document.createElement('a');
    lookup.className = 'magnify';
    Dom.insertAfter(lookup, input);
    oConfigs = oConfigs || {};
    oConfigs.target = input;
    return new lts.widget.LookupPanel(lookup, oConfigs);
};
lts.widget.LookupPanel = function(id, oConfigs) {
    lts.widget.Map.superclass.constructor.call(this, id, oConfigs);
    this.clickEl = Dom.get(id);
    this.inputEl = Dom.get(oConfigs.target);
    this.dialogId = null;
    this.configs = oConfigs || {};
    Event.on(this.clickEl, 'click', this.eventClick, this, true);
    this.force = this.inputEl.getAttribute('forceSelection');
    if (this.force) {
        Event.on(this.inputEl, 'blur', this.enforceSelection, this, true);
    }
};
YAHOO.extend(lts.widget.LookupPanel, YAHOO.util.Element, {
    enforceSelection: function() {
        var oldData = this.inputEl.value;
        if (oldData != '') {
            if (!this.lookup) {
                this.render();
                this.hide();
            }
            Dom.addClass(this.inputEl, 'working');
            this.lookup.dataSource.sendRequest('?exact=1&' + this.inputEl.name + '=' + oldData, {
                success: function(sQuery, oResponse, oPayload) {
                    Dom.removeClass(this.inputEl, 'working');
                    if (oResponse.results.length > 0) {
                        this.applyData(oResponse.results[0]);
                    } else {
                        this.inputEl.value = this.defaultValue || '';
                    }
                },
                failure: function(err) {
                    Dom.removeClass(this.inputEl, 'working');
                    this.inputEl.value = this.defaultValue || '';
                },
                scope: this 
            });
        } else {
            this.inputEl.value = this.defaultValue || '';
        }
    },

    hide: function() {
        this.dialog.hide();
    },

    eventClick: function(event) {
        if (!this.dialogId) {
            this.render();
        }
        this.dialog.show();
    },

    applyData: function(data) {
        var applyMapping = function(el) {
            var mapping = el.getAttribute('mapping');
            el.value = YAHOO.lang.substitute(mapping, data, function(key, value){
                return value || '';
            });
        };
        Dom.getElementsBy(this.validMapping, 'INPUT', document, applyMapping);
        Dom.getElementsBy(this.validMapping, 'TEXTAREA', document, applyMapping);
        Dom.getElementsBy(this.validMapping, 'SELECT', document, applyMapping);
    },

    eventSelect: function() {
        var dt = this.lookup.dataTable;
        var selected = dt.getSelectedRows();
        if (selected.length > 0) {
            var record = dt.getRecord(selected[0]);
            this.applyData(record.getData());
        }
        this.dialog.hide();
    },

    validMapping: function(el, oData) {
        return (el.getAttribute('mapping')) ? el.value.replace(/\s/g, '') == '' : false;
    },

    render: function() {
        var cid = this.inputEl.id || this.inputEl.name;
        this.dialogId = cid  + '-lookup-panel-container';
        this.inputId = cid  + '-lookup-panel-input';
        this.queryId = cid  + '-lookup-panel-input-result';
        this.searchId = cid  + '-lookup-panel-search';
        this.dialog = new YAHOO.widget.Dialog(this.dialogId, {
            context: [this.clickEl, 'tl', 'bl'],
            draggable: true,
            close: true,
            fixedcenter: true,
            zIndex: 10000
        });
        this.dialog.setHeader('Lookup');

        bodyDiv = document.createElement('div');
        bodyDiv.style.width = '800px';
        bodyDiv.style.width = '600px';
        inputDiv = bodyDiv.appendChild(document.createElement('div'));
        inputDiv.className = 'input';
        inputDiv.style.marginBottom = '1em';
        label = inputDiv.appendChild(document.createElement('label'));
        label.innerHTML = Dom.getPreviousSiblingBy(this.inputEl,
            function(node) {
                return node.tagName == 'LABEL';
            }
        ).innerHTML;
        var size = (label.innerHTML.length * .75);
        label.style.width = size + 'em';
        inputEl = inputDiv.appendChild(document.createElement('input'));
        inputEl.name = this.inputEl.name;
        inputEl.id = this.inputId
        inputEl.style.width = 'auto';
        var queryEl = bodyDiv.appendChild(document.createElement('div'));
        queryEl.id = this.queryId;
        queryEl.style.display = 'none';
        var searchEl = bodyDiv.appendChild(document.createElement('div'));
        searchEl.id = this.searchId;
        this.dialog.setBody(bodyDiv);
        var btnOk = document.createElement('input');
        btnOk.type = 'button';
        btnOk.className = 'ok';
        btnOk.value = 'ok';
        this.dialog.setFooter(btnOk);
        this.dialog.render(this.configs.container || document.body);

        var oConfigs = {resultNode: 'record', initialLoad: false, table: this.configs.table}; 
        if (YAHOO.widget.Paginator) {
            oConfigs.paginator = new YAHOO.widget.Paginator({rowsPerPage: 7});
        }
        this.lookup = new lts.widget.LookupTable(this.inputId, this.searchId,
            [{key: 'nothing', label: 'Lookup'}],
            oConfigs);
        var dt = this.lookup.dataTable;
        dt.set('selectionMode', 'single');
        dt.subscribe('rowClickEvent', dt.onEventSelectRow);
        dt.subscribe('rowDblclickEvent', this.eventSelect, this, true);
        Event.on(btnOk, 'click', this.eventSelect, this, true);

        this.dialog.show();

        Event.on(document, 'click', function(e) {
            var el = Event.getTarget(e);
            var dialogEl = this.dialog.element;
            if (el != dialogEl.element && !Dom.isAncestor(dialogEl, el) && el != this.clickEl && !Dom.isAncestor(this.clickEl, el)) {
                this.dialog.hide();
            }
        }, this, true);
    }
});

lts.util.createMap = function(input, oConfigs) {
    var map = document.createElement('a');
    map.className = 'compass';
    map.title = 'View Map of Property Address & Land Title Office Locations';
    Dom.insertAfter(map, input);
    oConfigs = oConfigs || {};
    oConfigs.target = input;
    return new lts.widget.Map(map, oConfigs);
};
lts.widget.Map = function(id, oConfigs) {
    lts.widget.Map.superclass.constructor.call(this, id, oConfigs);
    this.clickEl = Dom.get(id);
    this.inputEl = Dom.get(oConfigs.target);
    this.dialogId = null;
    this.mapId = null;
    this.configs = oConfigs || {};
    Event.on(this.clickEl, 'click', this.eventClick, this, true);
};
YAHOO.extend(lts.widget.Map, YAHOO.util.Element, {
    renderGoogle: function() {
        this.map = new google.maps.Map2(Dom.get(this.mapId));
        this.geo = new google.maps.ClientGeocoder();
        if (this.configs.center) {
            this.centerFromInputs(this.configs.center);
        }
        this.map.addControl(new google.maps.LargeMapControl());
        this.map.addControl(new google.maps.MapTypeControl());
        this.fireEvent('mapLoadEvent', {map: this});
    },

    centerFromInputs: function(options) {
        var address = Dom.get(options.address).value;
        var zip = Dom.get(options.zip).value;
        var state = Dom.get(options.state).value;
        return this.center(address + ' ' + state + ', ' + zip);
    },

    center: function(query) {
        var self = this;
        this.geo.getLatLng(query, function(latLng) {
            self.map.setCenter(latLng, 12)
            if (self.configs.center.callback) {
                self.configs.center.callback.call(self, latLng);
            }
        });
    },

    eventClick: function(event) {
        if (!this.dialogId) {
            this.render();
        }
        this.dialog.show();
    },

    render: function() {
        var cid = this.inputEl.id || this.inputEl.name;
        this.dialogId = cid  + '-map-container';
        this.mapId = cid  + '-map';
        this.dialog = new YAHOO.widget.Dialog(this.dialogId, {
            context: [this.clickEl, 'tl', 'bl'],
            draggable: true,
            close: true,
            fixedcenter: true
        });
        this.dialog.setHeader('Office Locations');
        this.dialog.setBody('<div id="' + this.mapId + '" class="map"></div>'); 
        this.dialog.setFooter('&nbsp;');
        this.dialog.render(document.body);
        this.dialog.show();

        Event.on(document, 'click', function(e) {
            var el = Event.getTarget(e);
            var dialogEl = this.dialog.element;
            if (el != dialogEl.element && !Dom.isAncestor(dialogEl, el) && el != this.clickEl && !Dom.isAncestor(this.clickEl, el)) {
                this.dialog.hide();
            }
        }, this, true);
        loadGoogle({method:this.renderGoogle, context: this});
    }
});


if (YAHOO.widget.AutoComplete) {
    lts.widget.AutoComplete = function(elInput, elContainer, oDataSource, oConfigs) {
        if (document.all && !oConfigs.useIFrame && !window.XMLHttpRequest) {
            oConfigs.useIFrame = true;
        }
        lts.widget.AutoComplete.superclass.constructor.call(this, elInput, elContainer, oDataSource, oConfigs);
        this._events = [];
        this.oConfigs = oConfigs;
    };
    YAHOO.extend(lts.widget.AutoComplete, YAHOO.widget.AutoComplete, {
        formatResult: function(oResultData, sQuery, sResultMatch) {
            return YAHOO.lang.substitute(this.oConfigs.resultLabel, oResultData[1]);
        },

        _sendQuery: function(sQuery) {
            if (this.blockLookup && this.blockLookup()) {
                return;
            } else {
                return lts.widget.AutoComplete.superclass._sendQuery.call(this, sQuery);
            }
        },

        _clearSelection: function() {
            var sValue = this._elTextbox.value
            var ret = lts.widget.AutoComplete.superclass._clearSelection.call(this);
            this.fireEvent('selectionClearEvent', {ac: this, oldData: sValue});
            return ret;
        }
    });
    YAHOO.lang.augmentProto(lts.widget.AutoComplete, YAHOO.util.Element, false);

    lts.widget.AbsAutoComplete = function(elInput, elContainer, oDataSource, oConfigs) {
        lts.widget.AbsAutoComplete.superclass.constructor.call(this, elInput, elContainer, oDataSource, oConfigs);
        elContainer = this.getContainerEl();
        elInput = this.getInputEl();
        elContainer.style.position = 'absolute';
        elContainer.style.top = Dom.getY(elInput) + elInput.offsetHeight + 'px';
        elContainer.style.left = Dom.getX(elInput) + 'px';
        elContainer.style.width = elInput.offsetWidth + 'px';
    };
    YAHOO.extend(lts.widget.AbsAutoComplete, YAHOO.widget.AutoComplete, {
    });

    lts.widget.Lookup = function(searchEl, oConfigs) {
        this.input = Dom.get(searchEl);
        var resultsEl = document.createElement('div')
        document.body.appendChild(resultsEl);
        this.results = resultsEl;
        this.oConfigs = oConfigs;
        this.dataSource = new lts.util.AutoXHRDataSource(oConfigs.uri || 'search');
        this.dataSource.responseType = YAHOO.util.XHRDataSource.TYPE_XML;
        this.dataSource.connXhrMode = 'cancelStaleRequests';
        this.dataSource.maxCacheEntries = oConfigs.maxCacheEntries || 20;
        this.dataSource.scriptQueryParam = this.input.name;
        this.input.style.position = 'static';
        this.input.style.width = 'auto';
        this.ac = new lts.widget.AbsAutoComplete(this.input, this.results, this.dataSource, {queryDelay: .5, minQueryLength: 3, resultLabel: oConfigs.resultLabel});
        this.ac.forceSelection = true;
        this.ac.formatResult = function(oResultData, sQuery, sResultMatch) {
//        Dom.insertAfter(resultsEl, this.input);
        };
        this.ac.itemSelectEvent.subscribe(this.eventItemSelected, this, true);
    },

    lts.widget.Lookup.prototype = {
        validMapping: function(el, oData) {
            return (el.getAttribute('mapping')) ? true : false;
        },

        eventItemSelected: function(elListItem, oArgs) {
            var oData = oArgs[2][1];
            var applyMapping = function(el) {
                var mapping = el.getAttribute('mapping');
                if (oData[mapping]) {
                    el.value = oData[mapping];
                }
            };
            Dom.getElementsBy(this.validMapping, 'INPUT', document, applyMapping);
            Dom.getElementsBy(this.validMapping, 'TEXTAREA', document, applyMapping);
            Dom.getElementsBy(this.validMapping, 'SELECT', document, applyMapping);
        }
    };

    lts.widget.LookupTable = function(searchEl, resultEl, oColumnDefs, oConfigs) {
        if (oConfigs === undefined) {
            oConfigs = {};
        }
        this.configs = oConfigs;
        searchEl = Dom.get(searchEl);
        resultEl = Dom.get(resultEl);
        this.searchEl = searchEl;
        this.resultEl = resultEl;
        this.dataSource = new lts.util.UDXHRDataSource(oConfigs.uri || 'search');
        this.dataSource.responseType = YAHOO.util.XHRDataSource.TYPE_XML;
        this.dataSource.connXhrMode = 'cancelStaleRequests';
        var fields = []
        for (var i=0; i<oColumnDefs.length; i++) {
            fields.push(oColumnDefs[i].key);
        }
        this.dataSource.responseSchema = {
            resultNode: oConfigs.resultNode || 'record',
            fields: fields,
            columnDefs: oConfigs.columnDefs || {node: 'column', key: 'attr', label: 'heading'},
            metaFields: {totalRecords: 'totalRecords'},
            metaNode: oConfigs.resultNode || 'meta'
        };
        this.dataSource.maxCacheEntries = oConfigs.maxCacheEntries || 20;

        this.dataTable = new YAHOO.widget.DataTable(resultEl, oColumnDefs, this.dataSource, oConfigs);

        var self = this;
        var searchName = searchEl.name;
        var search = function(query) {
            Dom.addClass(self.searchEl, 'working');
            var queryStr = '?' + searchName + '=' + query;
            if (self.configs.table) {
                queryStr += '&table=' + self.configs.table;
            }
            self.dataSource.sendRequest(queryStr, {
                success: function(sRequest, oResponse, oPayload) {
                    var dt = this.dataTable;
                    if (oResponse.columnDefs) {
                        var allColumns = dt.getColumnSet().keys;
                        var cDefs = oResponse.columnDefs;
                        for (var i=0, len=allColumns.length; i<len; i++) {
                            var col = allColumns[i];
                            var key = col.getKey();
                            var hasKey = false;
                            for (var ci=0,clen=cDefs.length; ci<clen; ci++) {
                                if (key == cDefs[ci].key) {
                                    hasKey = true;
                                    break;
                                }
                            }
                            if (!hasKey) {
                                dt.removeColumn(col.getKey());
                            }
                        }
                        for (var i=0,len=cDefs.length; i<len; i++) {
                            var hasKey = false;
                            for (var ci=0,clen=allColumns.length; ci<clen; ci++) {
                                if (cDefs[i].key == allColumns[ci].getKey()) {
                                    hasKey = true;
                                    break;
                                }
                            }
                            if (!hasKey) {
                                dt.insertColumn(cDefs[i], i);
                            }
                        }
                    }
                    dt.onDataReturnInitializeTable(sRequest, oResponse, oPayload);
                    var pag = dt.get('paginator');
                    if (pag) {
                        pag.set('totalRecords', oResponse.results.length);
                    }
                    Dom.removeClass(this.searchEl, 'working');
                },
                scope: self}
            );
        }

        this.searchDS = new YAHOO.util.FunctionDataSource(search)
        this.searchAC = new YAHOO.widget.AutoComplete(searchEl, searchEl.id + '-result', this.searchDS, {queryDelay: .5, minQueryLength: 3});
    };

    lts.util.AutoXHRDataSource = function(oLiveData, oConfigs) {
        this.dataType = DS.TYPE_XHR;
        this.connMgr = this.connMgr || util.Connect;
        oLiveData = oLiveData || "";
        lts.util.AutoXHRDataSource.superclass.constructor.call(this, oLiveData, oConfigs);
    };
    
    YAHOO.extend(lts.util.AutoXHRDataSource, YAHOO.util.XHRDataSource, {
makeConnection : function(oRequest, oCallback, oCaller) {
    var oRawResponse = null;
    var tId = DS._nTransactionId++;
    this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller});

    // Set up the callback object and
    // pass the request in as a URL query and
    // forward the response to the handler
    var oSelf = this;
    var oConnMgr = this.connMgr;
    var oQueue = this._oQueue;

    /**
     * Define Connection Manager success handler
     *
     * @method _xhrSuccess
     * @param oResponse {Object} HTTPXMLRequest object
     * @private
     */
    var _xhrSuccess = function(oResponse) {
        // If response ID does not match last made request ID,
        // silently fail and wait for the next response
        if(oResponse && (this.asyncMode == "ignoreStaleResponses") &&
                (oResponse.tId != oQueue.conn.tId)) {
            return null;
        }
        // Error if no response
        else if(!oResponse) {
            this.fireEvent("dataErrorEvent", {request:oRequest,
                    callback:oCallback, caller:oCaller,
                    message:DS.ERROR_DATANULL});

            // Send error response back to the caller with the error flag on
            DS.issueCallback(oCallback,[oRequest, {error:true}], true, oCaller);

            return null;
        }
        // Forward to handler
        else {
            // Try to sniff data type if it has not been defined
            if(this.responseType === DS.TYPE_UNKNOWN) {
                var ctype = (oResponse.getResponseHeader) ? oResponse.getResponseHeader["Content-Type"] : null;
                if(ctype) {
                    // xml
                    if(ctype.indexOf("text/xml") > -1) {
                        this.responseType = DS.TYPE_XML;
                    }
                    else if(ctype.indexOf("application/json") > -1) { // json
                        this.responseType = DS.TYPE_JSON;
                    }
                    else if(ctype.indexOf("text/plain") > -1) { // text
                        this.responseType = DS.TYPE_TEXT;
                    }
                }
            }
            this.handleResponse(oRequest, oResponse, oCallback, oCaller, tId);
        }
    };

    /**
     * Define Connection Manager failure handler
     *
     * @method _xhrFailure
     * @param oResponse {Object} HTTPXMLRequest object
     * @private
     */
    var _xhrFailure = function(oResponse) {
        this.fireEvent("dataErrorEvent", {request:oRequest,
                callback:oCallback, caller:oCaller,
                message:DS.ERROR_DATAINVALID});

        // Backward compatibility
        if(lang.isString(this.liveData) && lang.isString(oRequest) &&
            (this.liveData.lastIndexOf("?") !== this.liveData.length-1) &&
            (oRequest.indexOf("?") !== 0)){
        }

        // Send failure response back to the caller with the error flag on
        oResponse = oResponse || {};
        oResponse.error = true;
        DS.issueCallback(oCallback,[oRequest,oResponse],true, oCaller);

        return null;
    };

    /**
     * Define Connection Manager callback object
     *
     * @property _xhrCallback
     * @param oResponse {Object} HTTPXMLRequest object
     * @private
     */
     var _xhrCallback = {
        success:_xhrSuccess,
        failure:_xhrFailure,
        cache: false,
        scope: this
    };

    // Apply Connection Manager timeout
    if(lang.isNumber(this.connTimeout)) {
        _xhrCallback.timeout = this.connTimeout;
    }

    // Cancel stale requests
    if(this.connXhrMode == "cancelStaleRequests") {
            // Look in queue for stale requests
            if(oQueue.conn) {
                if(oConnMgr.abort) {
                    oConnMgr.abort(oQueue.conn);
                    oQueue.conn = null;
                }
                else {
                }
            }
    }

    // Get ready to send the request URL
    if(oConnMgr && oConnMgr.asyncRequest) {
        var sLiveData = this.liveData;
        var isPost = this.connMethodPost;
        var sMethod = (isPost) ? "POST" : "GET";
        // Validate request
        var sUri = (isPost || !lang.isValue(oRequest)) ? sLiveData : sLiveData+oRequest;
        var sRequest = (isPost) ? oRequest : null;

        // Send the request right away
        if(this.connXhrMode != "queueRequests") {
            oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, _xhrCallback, sRequest);
        }
        // Queue up then send the request
        else {
            // Found a request already in progress
            if(oQueue.conn) {
                var allRequests = oQueue.requests;
                // Add request to queue
                allRequests.push({request:oRequest, callback:_xhrCallback});

                // Interval needs to be started
                if(!oQueue.interval) {
                    oQueue.interval = setInterval(function() {
                        // Connection is in progress
                        if(oConnMgr.isCallInProgress(oQueue.conn)) {
                            return;
                        }
                        else {
                            // Send next request
                            if(allRequests.length > 0) {
                                // Validate request
                                sUri = (isPost || !lang.isValue(allRequests[0].request)) ? sLiveData : sLiveData+allRequests[0].request;
                                sRequest = (isPost) ? allRequests[0].request : null;
                                oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, allRequests[0].callback, sRequest);

                                // Remove request from queue
                                allRequests.shift();
                            }
                            // No more requests
                            else {
                                clearInterval(oQueue.interval);
                                oQueue.interval = null;
                            }
                        }
                    }, 50);
                }
            }
            // Nothing is in progress
            else {
                oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, _xhrCallback, sRequest);
            }
        }
    }
    else {
        // Send null response back to the caller with the error flag on
        DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller);
    }

    return tId;
},

        parseXMLData: function(oRequest, oFullResponse) {
            return {results: lts.util.parseXMLData(oFullResponse)};
        }
    });

    lts.util.UDXHRDataSource = function(oLiveData, oConfigs) {
        this.dataType = DS.TYPE_XHR;
        this.connMgr = this.connMgr || util.Connect;
        oLiveData = oLiveData || "";
        lts.util.AutoXHRDataSource.superclass.constructor.call(this, oLiveData, oConfigs);
    };
    
    YAHOO.extend(lts.util.UDXHRDataSource, YAHOO.util.XHRDataSource, {
        parseXMLData: function(oRequest, oFullResponse) {
            var oResult = {};
            var schema = this.responseSchema;
            var results = [];
            var xmlNodes = oFullResponse.getElementsByTagName(schema.resultNode);
            for (var i=0, len=xmlNodes.length; i<len; i++) {
                results.push(lts.util.parseXMLResult(xmlNodes[i]));
            }
            oResult.results = results;
            if (schema.columnDefs) {
                var xmlNodes = oFullResponse.getElementsByTagName(schema.columnDefs.node);
                var columnDefs = [];
                for (var i=0, len=xmlNodes.length; i<len; i++) {
                    var columnDef = lts.util.parseXMLResult(xmlNodes[i]);
                    columnDefs.push({key: columnDef[schema.columnDefs.key], label: columnDef[schema.columnDefs.label]});
                }
                oResult.columnDefs = columnDefs;
            }
            return oResult;
        }
    });
}

lts.util.parseXMLData = function(xmlDoc) {
    var results = [];
    var xmlNode = xmlDoc.firstChild.firstChild;
    while (xmlNode) {
        results.push(lts.util.parseXMLResult(xmlNode));
        xmlNode = xmlNode.nextSibling;
    }
    return results;
}

lts.util.parseUDXMLResult = function(result) {
    var oResult = {};
    var data;
    var xmlNode = result.firstChild;
    if (xmlNode === null) return oResult;
    function getValue(node) {
        return (node.text) ? node.text : (node.textContent) ? node.textContent : null;
    }
    while (xmlNode) {
        var oRowResult = {}
        var name = null;
        for(var j=0, len=xmlNode.childNodes.length; j<len; j++) {
            var childNode = xmlNode.childNodes[j];
            var value = getValue(childNode);
            if (childNode.nodeName == 'attr') {
                name = value;
            }
            if (value && childNode.nodeName) {
                oRowResult[childNode.nodeName] = value;
            }
        }
        var xmlNode = xmlNode.nextSibling;
        oResult[name] = oRowResult;
    }
    return oResult;
}

lts.util.parseXMLResult = function(result) {
    var oResult = {};
    var data;
    var xmlNode = result.firstChild;
    if (xmlNode === null) return oResult;
    function getValue(node) {
        return (node.text) ? node.text : (node.textContent) ? node.textContent : null;
    }
    while (xmlNode) {
        oResult[xmlNode.nodeName] = getValue(xmlNode);
        if (xmlNode.attributes) {
            for (var i=0; i<xmlNode.attributes.length; i++) {
                var attr = xmlNode.attributes[i];
                oResult[xmlNode.nodeName + '/@' + attr.nodeName] = attr.value;
            }
        }
        for(var j=0, len=xmlNode.childNodes.length; j<len; j++) {
            var childNode = xmlNode.childNodes[j];
            var value = getValue(childNode);
            if (value && childNode.nodeName) {
                oResult[childNode.nodeName] = value;
            }
            if (childNode.attributes) {
                for (var i=0; i<childNode.attributes.length; i++) {
                    var attr = childNode.attributes[i];
                    oResult[childNode.nodeName + '/@' + attr.nodeName] = attr.value;
                }
            }
        }
        var xmlNode = xmlNode.nextSibling;
    }
    for (var i=0; i<result.attributes.length; i++) {
        var attr = result.attributes[i];
        oResult['@' + attr.nodeName] = attr.value;
    }
    return oResult;
}

lts.widget.SelectLookup = function(lookupEl, resultEl, oConfigs) {
    oConfigs = oConfigs || {};
    lookupEl = Dom.get(lookupEl);
    resultEl = Dom.get(resultEl);
    
    this.config = oConfigs;
    this.lookupEl = lookupEl;
    this.resultEl = resultEl;
    this.dataSource = new lts.util.AutoXHRDataSource(oConfigs.uri || 'search');
    this.dataSource.responseType = YAHOO.util.XHRDataSource.TYPE_XML;
    var self = this;
    var search = function(query) {
        self.dataSource.sendRequest('?' +  self.lookupEl.name + '=' + query, {success: self.onDataReturn, scope: self});
    }
    this.searchDS = new YAHOO.util.FunctionDataSource(search);
    Event.on(lookupEl, 'change', this.onChange, this, true);
    var tmp = lookupEl.value;
    lts.widget.SelectLookup.superclass.constructor.call(this, lookupEl, oConfigs);
    // YAHOO.util.Element loses its selection in IE
    lookupEl.value = tmp;
}
YAHOO.extend(lts.widget.SelectLookup, YAHOO.util.Element, {
    onChange: function() {
        this.resultEl.innerHTML = '';
        this.searchDS.sendRequest(this.lookupEl.value);
    },

    onDataReturn: function(query, data) {
        if (this.config.allowNone) {
            var option = document.createElement('option');
            option.value = '' 
            option.innerHTML = this.config.allowNone; 
            this.resultEl.appendChild(option);
        }
        for (var i=0, len=data.results.length; i<len; i++) {
            result = data.results[i];
            var option = document.createElement('option');
            option.value = YAHOO.lang.substitute(this.config.value, result);
            option.innerHTML = YAHOO.lang.substitute(this.config.label, result);
            this.resultEl.appendChild(option);
        }
    }
});

lts.util.getNodeValue = function(xmlNode) {
    var data = (xmlNode.text) ? xmlNode.text : (xmlNode.textContent) ? xmlNode.textContent : null;
    if (!data) {
        var datapieces = [];
        for(var j=0, len=xmlNode.childNodes.length; j<len; j++) {
            if(xmlNode.childNodes[j].nodeValue) {
                datapieces[datapieces.length] = xmlNode.childNodes[j].nodeValue;
            }
        }
        if(datapieces.length > 0) {
            data = datapieces.join("");
        }
    }
    return data;
}

if (YAHOO.widget.Tooltip) {
    lts.widget.Preview = function(id, config) {
        lts.widget.Preview.superclass.constructor.call(this, id, config);
    };

    YAHOO.extend(lts.widget.Preview, YAHOO.widget.Tooltip, {
        onContextMouseMove: function(e, obj) {
            lts.widget.Preview.superclass.onContextMouseMove.call(this, e, obj);
            clearTimeout(obj.positionId);
            var context = this;
            obj.positionId = setTimeout(function() {
                var percentX = ((obj.pageX - 5 - Dom.getX(context)) / context.offsetWidth) * 100;
                var percentY = ((obj.pageY - 5 - Dom.getY(context)) / context.offsetHeight) * 100;
                obj.body.style.backgroundPosition = percentX + '% ' + percentY + '%';
            }, 25);
        },

        /**
        * Processes the showing of the Tooltip by setting the timeout delay
        * and offset of the Tooltip.
        * @method doShow
        * @param {DOMEvent} e The current DOM event
        * @return {Number} The process ID of the timeout function associated
        * with doShow
        */
        doShow: function (e, context) {
            var me = this;
            var background = "url(" + context.src.replace(/thumbnail/, "preview") + ")";
            background = background.replace(/http:..[^\/]*/, "");
            var style = me.body.style;
            if (style.backgroundImage != background) {
                style.backgroundImage = background;
            }

            var xPos = Dom.getX(context) - me.body.offsetWidth - 12;
            var yPos = Dom.getY(context) + (context.offsetHeight / 2) - 200;

            if (YAHOO.env.ua.opera && context.tagName &&
                context.tagName.toUpperCase() == "A") {
                yPos += 12;
            }

            return setTimeout(function () {
                me.moveTo(xPos, yPos);

                if (me.cfg.getProperty("preventoverlap")) {
                    me.preventOverlap(me.pageX, me.pageY);
                }

                Event.removeListener(context, "mousemove",
                    me.onContextMouseMove);

                me.show();
            }, this.cfg.getProperty("showdelay"));
        }
    });
}


if (YAHOO.widget.Panel) {
    lts.widget.WorkingPanel = function(heading) {
        oConfigs = {
            width: "240px",
            fixedcenter: true,
            close: false,
            draggable: false,
            zindex: 999,
            modal: true,
            visible: false
        };
        lts.widget.WorkingPanel.superclass.constructor.call(this, "working-panel", oConfigs);
        this.setHeader(heading);
        this.setBody('<img src="/style/loading.gif"/>');
        this.render(document.body);
    };
    YAHOO.extend(lts.widget.WorkingPanel, YAHOO.widget.Panel, {
        show: function(message) {
            this.setHeader(message);
            this.cfg.setProperty("visible", true);
        }
    });
}

lts.widget.Calendar = function(id, oConfigs) {
    this.clickEl = Dom.get(id);
    this.inputEl = oConfigs.target;
    this.dialogId = null;
    this.calendarId = null;
    Event.on(this.clickEl, 'click', this.eventClick, this, true);
};

lts.widget.Calendar.prototype = {
    eventClick: function(event) {
        if (!this.dialogId) {
            this.render();
        }
        this.dialog.show();
    },

    render: function() {
        var cid = this.inputEl.id || this.inputEl.name;
        this.dialogId = cid  + '-cal-container';
        this.calendarId = cid  + '-calendar';
        this.dialog = new YAHOO.widget.Dialog(this.dialogId, {
            context: [this.clickEl, 'tl', 'bl'],
            draggable: true,
            close: true,
            zIndex: 99999
        });
        this.dialog.setHeader('Pick a Date');
        this.dialog.setBody('<div id="' + this.calendarId + '"></div>'); 
        this.dialog.setFooter('&nbsp;');
        this.dialog.render(document.body);
        Dom.addClass(this.dialog.element, 'lt-calendar');

        Event.on(document, 'click', function(e) {
            var el = Event.getTarget(e);
            var dialogEl = this.dialog.element;
            if (el != dialogEl.element && !Dom.isAncestor(dialogEl, el) && el != this.clickEl && !Dom.isAncestor(this.clickEl, el)) {
                this.dialog.hide();
            }
        }, this, true);

        this.calendar = new YAHOO.widget.Calendar(this.calendarId, {
            iframe: false
        });
        this.calendar.render();
        this.calendar.selectEvent.subscribe(function() {
            var calendar = this.calendar;
            if (calendar.getSelectedDates().length > 0) {
                var value = calendar.getSelectedDates()[0];
                this.inputEl.value = lts.util.formatDate(value);
                var validator = lts.util.validatorManager.get(this.inputEl.id);
                if (validator) {
                    validator.validate();
                }
            } else {
                this.inputEl.value = "";
            }
            this.dialog.hide();
        }, this, true);
    }
};

lts.util.formatDate = function(date) {
    if (!date) return '';
    var month = date.getMonth() + 1;
    if (month < 10) {
        month = '0' + month;
    }
    var day = date.getDate();
    if (day < 10) {
        day = '0' + day;
    }
    return month + '/' + day + '/' + date.getFullYear();
}


lts.widget.Collapse = function(id, oConfigs) {
    oConfigs = oConfigs || {};
    this.labelEl = Dom.get(id);
    this.containerEl = Dom.get(oConfigs.target) || Dom.getNextSibling(this.labelEl.parentNode); 
    Event.on(this.labelEl, 'click', this.toggle, this, true);
    if (oConfigs.state == 'collapsed') {
        this.collapse();
    } else {
        this.expand();
    }
};
lts.widget.Collapse.prototype = {
    toggle: function(event) {
        Event.preventDefault(event);
        if (Dom.hasClass(this.labelEl, 'expand')) {
            this.expand();
        } else {
            this.collapse();
        }
    },
    expand: function(event) {
        Dom.removeClass(this.labelEl, 'expand');
        Dom.addClass(this.labelEl, 'collapse');
        Dom.setStyle(this.containerEl, 'display', 'block');
    },
    collapse: function() {
        Dom.removeClass(this.labelEl, 'collapse');
        Dom.addClass(this.labelEl, 'expand');
        Dom.setStyle(this.containerEl, 'display', 'none');
    }
};

lts.util.createCollapsibleBox = function(node) {
    var link = document.createElement('a');
    var label = Dom.getFirstChild(node);
    label.insertBefore(link, label.firstChild);
    var oConfigs = {};
    if (Dom.hasClass(node, 'collapsed')) {
        oConfigs.state = 'collapsed';
    }
    return new lts.widget.Collapse(link, oConfigs);
};

lts.widget.Status = function(id) {
    this.srcEl = Dom.get(id);
    if (!this.srcEl) {
        throw("Could not find " + id);
    }
    this.srcEl.innerHTML = '';
}

lts.widget.Status.prototype = {
    _show: function(message, fadeOut) {
        this.srcEl.innerHTML = message;
        this.fadeIn();
        if (fadeOut) {
            this.fadeOut(fadeOut);
        }
    },

    getMessage: function() {
        return this.srcEl.innerHTML;
    },

    showMessage: function(message, fadeOut) {
        this.srcEl.className = 'alert';
        this._show(message, fadeOut);
    },

    showError: function(message, fadeOut) {
        this.srcEl.className = 'error';
        this._show(message, fadeOut);
    },

    fadeIn: function() {
        var anim = new YAHOO.util.Anim(this.srcEl, {opacity: {to: 1}}, 0.1);
        anim.animate();
        return anim;
    },

    _fadeOut: function() {
        var anim = new YAHOO.util.Anim(this.srcEl, {opacity: {to: 0}}, 0.4);
        anim.animate();
        return anim;
    },

    fadeOut: function(delay) {
        if (delay) {
            var self = this;
            setTimeout(function() {self._fadeOut();}, delay);
        } else {
            this.hide();
        }
    },

    hide: function() {
        this._fadeOut();
    }
};

lts.widget.Table = function(node, columnDefs, oConfigs) {
    node = Dom.get(node);
    dsNode = Dom.get(node.id + '-data');
    if (!dsNode) return;

    this.id = node.id;
    if (!columnDefs) {
        columnDefs = [];
        var thead = dsNode.tHead.rows[0];
        var tbody = dsNode.tBodies[0].rows[0];
        var field, parser, partial, label;
        for (var i=0; i<thead.cells.length; i++) {
            var th = thead.cells[i];
            label = th.innerHTML;
            partial = Dom.hasClass(th, 'hidden');
            field = {label: label, key: label};
            if (partial) {
                field['hidden'] = true;
            } 
            columnDefs.push(field);
        }
    }
    oConfigs = oConfigs || {};
    if (!oConfigs.caption && dsNode.caption) {
        oConfigs['caption'] = dsNode.caption.innerHTML;
    }
    this.dataSource = new YAHOO.util.DataSource(dsNode);
    this.dataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
    this.dataSource.responseSchema = {
        fields: columnDefs
    };
    this.dataTable = new YAHOO.widget.DataTable(node, columnDefs, this.dataSource, oConfigs);

    this.dataTable.subscribe("rowMouseoverEvent", this.dataTable.onEventHighlightRow);
    this.dataTable.subscribe("rowMouseoutEvent", this.dataTable.onEventUnhighlightRow);
    this.dataTable.subscribe("rowClickEvent", this.dataTable.onEventSelectRow);
}

lts.widget.Table.prototype = {
    attachDataSource: function(ds) {
        var old = this.dataSource;
        this.dataSource = ds;
        this.dataTable._oDataSource = ds;
        return old;
    }
};

lts.widget.MultiValue = function(node, columnDefs, oConfigs) {
    dsNode = Dom.get(node.id + '-data');
    if (!dsNode) return;

    this.id = node.id;
    if (!columnDefs) {
        columnDefs = [];
        var thead = dsNode.tHead.rows[0];
        var tbody = dsNode.tBodies[0].rows[0];
        var field, parser, type, options, editor, key, label, oldType, validator, partial, mapping, resultLabel, forceSelection, table;
        for (var i=0; i<thead.cells.length; i++) {
            var th = thead.cells[i];
            label = th.innerHTML;
            options = [];
            key = null;
            type = null;
            table = null;
            validator = null;
            mapping = null;
            resultLabel = null;
            forceSelection = null;
            partial = Dom.hasClass(th, 'hidden');
            if (tbody) {
                Dom.getElementsBy(function(input) {
                    oldType = type;
                    if (!input.type) {
                        return;
                    }
                    type = input.getAttribute('format') || input.type;
                    key = input.name;
                    if (oldType && type != oldType) {
                        alert("Multiple inputs foud");
                        throw("Multiple inputs foud");
                    }
                    switch(type) {
                        case 'select-one':
                        case 'select-multiple':
                            for (var oi=0; oi<input.options.length; oi++) {
                                options.push({label: input.options[oi].innerHTML, value: input.options[oi].value});
                            }
                            break;
                        case 'radio':
                            options.push({label: input.value, value: input.value});
                            break;
                        case 'checkbox':
                            options.push({label: input.value, value: input.value});
                            break;
                        case 'lookup':
                            table = input.getAttribute('table');
                        default:
                            break;
                    }
                    if (input.getAttribute('validate')) {
                        validator = input.getAttribute('validate');
                    }
                    if (input.getAttribute('mapping')) {
                        mapping = input.getAttribute('mapping');
                    }
                    if (input.getAttribute('resultLabel')) {
                        resultLabel = input.getAttribute('resultLabel');
                    }
                    if (input.getAttribute('forceSelection')) {
                        forceSelection = true;
                    }
                }, null, tbody.cells[i]);
            }
            field = {label: label, key: key || label, parser: lts.util.HTMLParser, mapping: mapping};
            if (validator) {
                validator = new lts.util.Validators(validator);
            }
            switch (type) {
                case 'select-one':
                case 'select-multiple':
                    field['editor'] = new lts.widget.DropdownCellEditor({validator: validator, dropdownOptions: options, disableBtns: false});
                    field['formatter'] = lts.util.dropdownFormatter;
                    break;
                case 'radio':
                    field['editor'] = new YAHOO.widget.RadioCellEditor({radioOptions: options, disableBtns: true});
                    break;
                case 'checkbox':
                    if (options.length == 1) {
                        field['parser'] = lts.util.BooleanParser;
                        field['formatter'] = lts.util.booleanFormatter;
                        var newOptions = [{label: 'yes', value: options[0]['value']}, {label: 'no', value: ''}];
                        field['editor'] = new lts.widget.BooleanCellEditor({radioOptions: newOptions}); 
                    } else {
                        field['editor'] = new YAHOO.widget.CheckboxCellEditor({checkboxOptions: options});
                    }
                    break;
                case 'date':
                    field['parser'] = lts.util.HTMLDateParser;
                    field['formatter'] = YAHOO.widget.DataTable.formatDate;
                    field['editor'] = new lts.widget.DateCellEditor();
                    break;
                case 'hidden':
                    field['hidden'] = true;
                    break;
                case null:
                    field['parser'] = null;
                    break;
                case 'lookup':
                    field['editor'] = new lts.widget.LookupCellEditor({validator: validator, resultLabel: resultLabel, key: key, forceSelection: forceSelection, table: table});
                    break;
                default:
                    field['editor'] = new lts.widget.TextboxCellEditor({validator: validator});
            }
            if (partial) {
                field['hidden'] = true;
            } 
            columnDefs.push(field);
        }
    }
    oConfigs = oConfigs || {};
    if (!oConfigs.caption && dsNode.caption) {
        oConfigs['caption'] = dsNode.caption.innerHTML;
    }
    this.dataSource = new lts.util.DataSource(dsNode);
    this.dataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
    this.dataSource.responseSchema = {
        fields: columnDefs
    };
    this.dataTable = new lts.widget.DataTable(node, columnDefs, this.dataSource, oConfigs);
    if (oConfigs.form) {
        Event.addListener(oConfigs.form, 'submit', this.onEventBeforeSubmit, this);
    }
    dsNode.parentNode.removeChild(dsNode);
}


YAHOO.lang.augmentObject(lts.widget.MultiValue, {
    CLASS_ADD: 'add',
    CLASS_NEW: 'new-row',
    CLASS_REMOVE: 'remove',
    CLASS_OPTIONS: 'row-options',
    CLASS_INFO: 'row-info',
    CLASS_RESTORE: 'restore',
    CLASS_DELETED: 'deleted',
    CLASS_COL_EDIT: 'mv-col-edit',
    CLASS_COL_HORIZONTAL: 'mv-col-horizontal',
    CLASS_COL_VERTICAL: 'mv-col-vertical',
    CLASS_HORIZONTAL: 'mv-row-horizontal',
    CLASS_VERTICAL: 'mv-row-vertical',
    CLASS_QUICK: 'mv-quick',
    CLASS_HIGHLIGHTED: 'mv-highlighted'
});

lts.widget.MultiValue.prototype = {
    addToForm: function(form, skipValidation) {
        var data = this.getParsedData(skipValidation);
        var container = Dom.get(this.id + '-form');
        if (!container) {
            container = document.createElement('div');
            container.style.display = 'none';
            container.id = this.id + '-form';
            form.appendChild(container);
        } else {
            container.innerHTML = '';
        }

        for (var key in data) {
            values = data[key];
            for (var i=0; i<values.length; i++) {
                var value = values[i];
                var input = document.createElement('input');
                input.type = 'hidden';
                input.name = key;
                input.value = value;
                container.appendChild(input);
            }
        }
    },

    getParsedData: function(skipValidation) {
        return this.dataTable.getParsedData({}, skipValidation);
    },

    onEventBeforeSubmit: function(event, self) {
        var form = Event.getTarget(event);
        self.addToForm(form);
    }
};
var MV = lts.widget.MultiValue;

lts.util.parseGizmoError = function(errorXml) {
    errors = [];
    if (errorXml.documentElement) {
        errorXml = errorXml.documentElement;
    }
    if (errorXml.getElementsByTagNameNS) {
        var messages = errorXml.getElementsByTagNameNS(gizmo, 'error');
    } else {
        var messages = errorXml.getElementsByTagName('ns0:error');
        if (messages.length == 0) {
            messages = errorXml.getElementsByTagName('gizmo:error');
        }
        if (messages.length == 0) {
            messages = errorXml.getElementsByTagName('error');
        }
    }
    for (var i = 0; i<messages.length; i++) {
        errors.push(messages[i].getAttribute('message'));
    }
    return errors;
};

RowMixIn = function(){};
RowMixIn.prototype = {
    keyPress: function(el) {
        // Save on enter by default
        // Bug: 1802582 Set up a listener on each textbox to track on keypress
        // since SF/OP can't preventDefault on keydown
        Event.addListener(el, "keypress", function(v){
            if((v.keyCode === 13)) {
                // Prevent form submit
                Event.preventDefault(v);
                this.save();
            }
        }, this, true);
    },

    attach: function(oDataTable, elCell, oColumn, oRecord) {
        // Validate
        if(oDataTable instanceof YAHOO.widget.DataTable) {
            this._oDataTable = oDataTable;

            // Validate cell
            elCell = oDataTable.getTdEl(elCell);
            if(elCell) {
                this._elTd = elCell;

                // Validate Column
                if (!oColumn)
                    var oColumn = oDataTable.getColumn(elCell);
                if(oColumn) {
                    this._oColumn = oColumn;

                    // Validate Record
                    if (!oRecord)
                        var oRecord = oDataTable.getRecord(elCell);
                    if(oRecord) {
                        this._oRecord = oRecord;
                        var value = oRecord.getData(this.getColumn().getKey());
                        this.value = (value !== undefined) ? value : this.defaultValue;
                        return true;
                    }
                }
            }
        }
        return false;
    },

    renderBtns: function() {
        return;
    },

    setLabel: function(label) {
        this._labelEl.innerHTML = label;
    },

    doAfterRender: function() {
        var containerEl = this.getContainerEl();
        if (Dom.hasClass(containerEl, 'mv-lookup')) {
            Dom.addClass(containerEl, 'input');
        } else {
            containerEl.className = 'input';
        }

        if (this.validator && this.validator.required) {
            var reqEl = document.createElement('span');
            reqEl.className = 'required';
            reqEl.innerHTML = '&nbsp;';
            containerEl.appendChild(reqEl);
        }
        var errorEl = document.createElement('strong');
        errorEl.style.display = 'none';
        errorEl.className = 'error';
        containerEl.appendChild(errorEl);

        var labelEl = document.createElement('label');
        containerEl.insertBefore(labelEl, containerEl.firstChild);
        containerEl.tabIndex = -1;
        labelEl.className = 'mv';
        this._labelEl = labelEl;
        this._errorEl = errorEl; 
    },

    showError: function(message) {
        this._errorEl.innerHTML = message;
        this._errorEl.style.display = '';
    },

    hideError: function() {
        this._errorEl.style.display = 'none';
    },

    inlineValidate: function() {
        var value = this.validate();
        if (value !== undefined) {
            this.setValue(value);
        }
    },

    valueChanged: function(value) {
        value = value || this.getInputValue();
        if (!value) return false;
        return (value.replace(/\s/g, '') != '' && value != this.defaultValue);
    },

    validate: function() {
        var inputValue = this.getInputValue();
        var validValue = inputValue;
        this.validValue = undefined;

        // Validate new value
        if(this.validator) {
            try {
                validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
            } catch (error) {
                this.showError(error.message || error);
                return undefined;
            }
            if(validValue === undefined ) {
                this.resetForm();
                this.fireEvent("invalidDataEvent",
                        {editor:this, oldData:this.value, newData:inputValue});
                return undefined;
            }
        }
        this.hideError();
        this.validValue = validValue
        return validValue;
    },

    saveValidated: function() {
        var validValue = this.validValue;
        if (validValue === undefined) {
            throw("Has not been validated yet");
        }
        this.validValue = undefined;
        var oSelf = this;
        var finishSave = function(bSuccess, oNewValue) {
            var oOrigValue = lts.util.copy(oSelf.value);
            if(bSuccess) {
                // Update new value
                oSelf.value = oNewValue;
                oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);

                // Hide CellEditor
                oSelf.getContainerEl().style.display = "none";
                oSelf.isActive = false;
                oSelf.getDataTable()._oCellEditor =  null;

                oSelf.fireEvent("saveEvent",
                        {editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
            }
            else {
                oSelf.resetForm();
                oSelf.fireEvent("revertEvent",
                        {editor:oSelf, oldData:oOrigValue, newData:oNewValue});
            }
            oSelf.unblock();
        };

        this.block();
        if(YAHOO.lang.isFunction(this.asyncSubmitter)) {
            this.asyncSubmitter.call(this, finishSave, validValue);
        }
        else {
            finishSave(true, validValue);
        }
    },

    inRow: function() {
        var container = this.getContainerEl();
        return (!Dom.hasClass(container, DT.CLASS_EDITOR) && Dom.hasClass(container.parentNode.parentNode, DT.CLASS_EDITOR));
    },

    save: function() {
        if (this.inRow()) {
            return this.getDataTable().saveRow();
        }
        // Get new value
        var validValue = this.validate();
        if (validValue === undefined) {
            return undefined;
        } else {
            return this.saveValidated();
        } 
    }
};


lts.widget.DateCellEditor = function(oConfigs) {
    lts.widget.DateCellEditor.superclass.constructor.call(this, oConfigs);
};
YAHOO.extend(lts.widget.DateCellEditor, YAHOO.widget.DateCellEditor, {
    focus: function() {
        try {
            lts.widget.DateCellEditor.superclass.focus.call(this);
        } catch (er) {
            return;
        }
    },

    renderForm : function() {
        // Calendar widget
        if(YAHOO.widget.Calendar) {
            var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
            calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
            // Enable navigator with a custom configuration
            var navConfig = {
                strings : {
                    month: "Choose Month",
                    year: "Enter Year",
                    submit: "OK",
                    cancel: "Cancel",
                    invalidYear: "Please enter a valid year"
                },
                monthFormat: YAHOO.widget.Calendar.SHORT,
                initialFocus: "year"
            };
            var calendar =
                    new YAHOO.widget.Calendar(this.getId() + "-date",
                    calContainer.id, {navigator: navConfig});
            calendar.render();
//            calContainer.style.cssFloat = "none";

            if(YAHOO.env.ua.ie) {
                var calFloatClearer = this.getContainerEl().appendChild(document.createElement("br"));
                calFloatClearer.style.clear = "both";
            }

            this.calendar = calendar;

            if(this.disableBtns) {
                this.handleDisabledBtns();
            }
        }
        else {
        }
    },

    resetForm: function() {
        this.hideError();
        var value = this.value;
        var selectedValue = lts.util.formatDate(value);
        this.calendar.cfg.setProperty("selected",selectedValue,false);
        this.calendar.cfg.setProperty("pagedate",value,false);
        this.calendar.render();
    }
});
YAHOO.lang.augmentProto(lts.widget.DateCellEditor, RowMixIn, true);

lts.widget.DropdownCellEditor = function(oConfigs) {
    if (!oConfigs.defaultValue) {
        try {
            oConfigs.defaultValue = oConfigs.dropdownOptions[0].value;
        } catch(er) {}
    }
    lts.widget.DropdownCellEditor.superclass.constructor.call(this, oConfigs);
};
YAHOO.extend(lts.widget.DropdownCellEditor, YAHOO.widget.DropdownCellEditor, {
    focus: function() {
        try {
            lts.widget.DropdownCellEditor.superclass.focus.call(this);
        } catch (er) {
            return;
        }
    },

    resetForm: function() {
        this.hideError();
        lts.widget.DropdownCellEditor.superclass.resetForm.call(this);
    },

    renderForm: function() {
        lts.widget.DropdownCellEditor.superclass.renderForm.call(this);
        this.keyPress(this.dropdown);
    },

    setValue: function(value) {
        this.dropdown.value = value;
    }
});
YAHOO.lang.augmentProto(lts.widget.DropdownCellEditor, RowMixIn, true);

lts.widget.BooleanCellEditor = function(oConfigs) {
    lts.widget.BooleanCellEditor.superclass.constructor.call(this, oConfigs);
};
YAHOO.extend(lts.widget.BooleanCellEditor, YAHOO.widget.RadioCellEditor, {
    focus: function() {
        try {
            lts.widget.BooleanCellEditor.superclass.focus.call(this);
        } catch (er) {
            return;
        }
    },

    resetForm: function() {
        this.hideError();
        for(var i=0, j=this.radios.length; i<j; i++) {
            var elRadio = this.radios[i];
            if (this.value === null && elRadio.value == '' || this.value === elRadio.value) {
                elRadio.checked = true;
                return;
            }
        }
    },

    renderForm: function() {
        if(lang.isArray(this.radioOptions)) {
            var radioOption, radioValue, radioId, elLabel;
            

            // Create the radio buttons in an IE-friendly way
            for(var i=0, len=this.radioOptions.length; i<len; i++) {
                radioOption = this.radioOptions[i];
                radioValue = lang.isValue(radioOption.value) ?
                        radioOption.value : radioOption;
                radioId = this.getId() + "-radio" + i;
                this.getContainerEl().innerHTML += "<input type=\"radio\"" +
                        " name=\"" + this.getId() + "\"" +
                        " value=\"" + radioValue + "\"" +
                        " id=\"" +  radioId + "\" />"; // Needed for label

                // Create the labels in an IE-friendly way
                if (document.all) {
                    elLabel = this.getContainerEl().appendChild(document.createElement("span"));
                } else {
                    elLabel = this.getContainerEl().appendChild(document.createElement("label"));
                    elLabel.htmlFor = radioId;
                }
                elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
                        radioOption.label : radioOption;
                elLabel.style.width = 'auto';
                elLabel.style.cssFloat = 'none';
            }

            // Store the reference to the checkbox elements
            var allRadios = [],
                elRadio;
            for(var j=0; j<len; j++) {
                elRadio = this.getContainerEl().childNodes[j*2];
                allRadios[allRadios.length] = elRadio;
            }
            this.radios = allRadios;

            if(this.disableBtns) {
                this.handleDisabledBtns();
            }
        }
        else {
        }
    },

    getInputValue: function() {
        for(var i=0, j=this.radios.length; i<j; i++) {
            if(this.radios[i].checked) {
                var value = this.radios[i].value;
                return (value != '') ? value : null;
            }
        }
    }
});
YAHOO.lang.augmentProto(lts.widget.BooleanCellEditor, RowMixIn, true);

lts.widget.LookupCellEditor = function(oConfigs) {
    oConfigs = oConfigs || {};
    this.oConfigs = oConfigs;
    lts.widget.LookupCellEditor.superclass.constructor.call(this, "lookup", oConfigs);
};

YAHOO.extend(lts.widget.LookupCellEditor, YAHOO.widget.BaseCellEditor, {
    renderForm: function() {
        var oConfigs = this.oConfigs;
        var elContainer = this.getContainerEl();
        var elInput = elContainer.appendChild(document.createElement('input'));
        elInput.name = oConfigs.key;
        this.input = elInput;
        oConfigs.container = elContainer;
        this.lookup = lts.util.createLookup(elInput, oConfigs);
        var self = this;
        this.lookup.eventSelect = function() {
            var dt = this.lookup.dataTable;
            var selected = dt.getSelectedRows();
            if (selected.length > 0) {
                var record = dt.getRecord(selected[0]);
                var data = record.getData();
                self.setValue('');
                self.getDataTable().fireEvent('valueChangeEvent', {dataTable: self.getDataTable(), editor: self, resultData: data});
            }
            this.dialog.hide();
        }
        Event.on(this.input, 'blur', this.enforceSelection, this, true);
        this.keyPress(this.elInput);
    },

    enforceSelection: function() {
        var oldData = this.input.value;
        if (oldData != '') {
            if (!this.lookup.lookup) {
                this.lookup.render();
                this.lookup.hide();
            }
            Dom.addClass(this.input, 'working');
            var queryStr = '?exact=1&' + this.oConfigs.key + '=' + oldData;
            if (this.oConfigs.table) {
                queryStr += '&table=' + this.oConfigs.table;
            }
            this.lookup.lookup.dataSource.sendRequest(queryStr, {
                success: function(sQuery, oResponse, oPayload) {
                    Dom.removeClass(this.input, 'working');
                    this.input.value = this.defaultValue || '';
                    if (oResponse.results.length > 0) {
                        this.getDataTable().setEditorData(oResponse.results[0]);
                    }
                },
                failure: function(err) {
                    Dom.removeClass(this.input, 'working');
                    this.input.value = this.defaultValue || '';
                    throw(err);
                },
                scope: this 
            });
        } else {
            this.input.value = this.defaultValue || '';
        }
    },

    focus: function() {
        try {
            this.input.focus();
            this.input.select();
        } catch (er) {
            return;
        }
    },

    setValue: function(value) {
        this.input.value = value;
    },

    getInputValue: function() {
        return this.input.value;
    },

    resetForm: function() {
        this.hideError();
        this.input.value = this.value;
    }
});
YAHOO.lang.augmentProto(lts.widget.LookupCellEditor, RowMixIn, true);

lts.widget.TextboxCellEditor = function(oConfigs) {
    lts.widget.TextboxCellEditor.superclass.constructor.call(this, oConfigs);
};

YAHOO.extend(lts.widget.TextboxCellEditor, YAHOO.widget.TextboxCellEditor, {
    focus: function() {
        try {
            lts.widget.TextboxCellEditor.superclass.focus.call(this);
        } catch (er) {
            return;
        }
    },

    renderForm: function() {
        lts.widget.TextboxCellEditor.superclass.renderForm.call(this);
        Event.on(this.textbox, 'change', this.inlineValidate, this, true); 
        this.textbox.className = 'input';
    },

    resetForm: function() {
        this.hideError();
        lts.widget.TextboxCellEditor.superclass.resetForm.call(this);
    },

    setValue: function(value) {
        this.textbox.value = value;
    }
});
YAHOO.lang.augmentProto(lts.widget.TextboxCellEditor, RowMixIn, true);

lts.util.DataSource = function(oLiveData, oConfigs) {
    lts.util.DataSource.superclass.constructor.call(this, oLiveData, oConfigs);
    this.templateData = null;
}


YAHOO.extend(lts.util.DataSource, YAHOO.util.DataSourceBase, {
    parseHTMLTableData: function(oRequest, oFullResponse) {
        var bError = false;
        var elTable = oFullResponse;
        var fields = this.responseSchema.fields;
        var oParsedResponse = {results:[]};

        var templates = Dom.getElementsByClassName('template', 'tbody', elTable);
        if (templates.length) {
            var elRow = templates[0].rows[0];
            var oResult = {};

            for(var k=fields.length-1; k>-1; k--) {
                var field = fields[k];
                var key = (YAHOO.lang.isValue(field.key)) ? field.key : field;
                var data = elRow.cells[k].innerHTML;

                // Backward compatibility
                if(!field.parser && field.converter) {
                    field.parser = field.converter;
                }
                var parser = (typeof field.parser === 'function') ?
                    field.parser :
                    YAHOO.util.DataSource.Parser[field.parser+''];
                if(parser) {
                    data = parser.call(this, data, elRow.cells[k]);
                }
                // Safety measure
                if(data === undefined) {
                    data = null;
                }
                oResult[key] = data;
            }
            this.templateData = oResult;
            elTable.removeChild(templates[0]); 
        } else {
            this.templateData = {};
        }

        // Iterate through just the first TBODY - edited from all
        if (elTable.tBodies.length == 0) {
            return oParsedResponse;
        }
        var elTbody = elTable.tBodies[0];

        // Iterate through each TR
        for(var j=elTbody.rows.length-1; j>-1; j--) {
            var elRow = elTbody.rows[j];
            var oResult = {};

            for(var k=fields.length-1; k>-1; k--) {
                var field = fields[k];
                var key = (YAHOO.lang.isValue(field.key)) ? field.key : field;
                var data = elRow.cells[k].innerHTML;

                // Backward compatibility
                if(!field.parser && field.converter) {
                    field.parser = field.converter;
                }
                var parser = (typeof field.parser === 'function') ?
                    field.parser :
                    YAHOO.util.DataSource.Parser[field.parser+''];
                if(parser) {
                    data = parser.call(this, data, elRow.cells[k]);
                }
                // Safety measure
                if(data === undefined) {
                    data = null;
                }
                oResult[key] = data;
            }
            oParsedResponse.results[j] = oResult;
        }
        if(bError) {
            oParsedResponse.error = true;
        }
        else {
        }
        return oParsedResponse;
    }
});

lts.widget.iFrame = function(parent) {
    parent = parent || document.body;
    var iFrame = document.createElement('iframe');
    iFrame.style.display = 'none';
    iFrame.style.position = 'absolute';
    if (YAHOO.env.ua.ie) {
        iFrame.style.filter = "alpha(opacity=0)";
        /*
             Need to set the "frameBorder" property to 0
             supress the default <iframe> border in IE.
             Setting the CSS "border" property alone
             doesn't supress it.
        */
        iFrame.frameBorder = 0;
    }
    iFrame.style.border = "none";
    iFrame.style.margin = "0";
    iFrame.style.padding = "0";
    parent.appendChild(iFrame);
    this.srcEl = iFrame;
};


lts.widget.iFrame.prototype = {
    show: function(targetEl) {
        this.srcEl.style.display = 'block';
        this.srcEl.style.width = targetEl.offsetWidth + 'px';
        this.srcEl.style.height = targetEl.offsetHeight + 'px';
        this.srcEl.style.left = Dom.getX(targetEl) + 'px';
        this.srcEl.style.top = Dom.getY(targetEl) + 'px';
        this.srcEl.style.zIndex = targetEl.style.zIndex - 1;
    },

    hide: function() {
        this.srcEl.style.display = 'none';
    }
};


// Copy static members to LocalDataSource class
YAHOO.lang.augmentObject(lts.util.DataSource, YAHOO.util.DataSourceBase);

lts.util.Options = function(dataTable, config) {
    lts.util.Options.superclass.constructor.call(this, dataTable, config);

    this.over = false;
    this.dataTable = dataTable
    infoEl = document.createElement('div');
    infoEl.className = MV.CLASS_INFO;
    document.body.appendChild(infoEl);
    this.infoEl = infoEl;
    optionsEl = document.createElement('div');
    optionsEl.className = MV.CLASS_OPTIONS; 
    document.body.appendChild(optionsEl);
    this.iFrame = new lts.widget.iFrame();
    this.optionsEl = optionsEl;
    this.hide();
    this.addable = config.add;
    if (config.add) {
        var addEl = document.createElement('a');
        addEl.className = MV.CLASS_ADD;
        addEl.innerHTML = ' ';
        addEl.title = 'Add Row';
        this.addEl = addEl;
        optionsEl.appendChild(addEl);
        Event.addListener(addEl, 'click', this.eventAddRow, this);
    }
    this.removeable = config.remove;
    if (config.remove) {
        var removeEl = document.createElement('a');
        removeEl.className = MV.CLASS_REMOVE;
        removeEl.innerHTML = ' ';
        removeEl.title = 'Remove Row';
        this.removeEl = removeEl;
        optionsEl.appendChild(removeEl);
        Event.addListener(removeEl, 'click', this.eventRemoveRow, this);
    }
    Event.on(this.infoEl, "click", this.onInfoClick, this, true);
    Event.addListener(infoEl, "mouseout", this.onMouseout, this);
    Event.addListener(infoEl, "mouseover", this.onMouseover, this);
    Event.addListener(optionsEl, "mouseout", this.onMouseout, this);
    Event.addListener(optionsEl, "mouseover", this.onMouseover, this);
};

YAHOO.extend(lts.util.Options, YAHOO.util.Element, {
    onInfoClick: function() {
        if (this.infoTarget) {
            var dt = this.dataTable;
            dt._preventBlur = true;
            dt.showRowEditor(this.infoTarget);
            setTimeout(function(){dt._preventBlur = false;}, 100);
        }
    },

    addRow: function() {
        var elRow = this.dataTable.getTrEl(this.assocEl);
        var index = elRow ? this.dataTable.getTrIndex(elRow) + 1 : -1;
        this.dataTable.addRow(null, index, {isNew: true});
    },

    eventAddRow: function(e, self) {
        self.addRow();
    },

    removeRow: function(trEl) {
        var quickRow = this.dataTable.isMaxLength();
        var elRow = this.dataTable.getTrEl(trEl);
        if (Dom.hasClass(elRow, MV.CLASS_NEW)) {
            this.dataTable.deleteRow(elRow);
        } else if (this.dataTable.disableRow(elRow)) {
            this.removeEl.className = MV.CLASS_RESTORE;
            this.removeEl.title = 'Restore Row';
        } else {
            this.removeEl.className = MV.CLASS_REMOVE;
            this.removeEl.title = 'Remove Row';
        }
        if (quickRow && !Dom.hasClass(this.dataTable.getLastTrEl(), MV.CLASS_QUICK)) {
            this.dataTable.addQuickRow();
        }
    },

    eventRemoveRow: function(e, self) {
        self.removeRow(self.assocEl);
    },

    getDataTable: function() {
        return this.dataTable;
    },

    getOptionsEl: function() {
        return this.optionsEl;
    },

    showInfo: function(targetEl, message) {
        this.infoTarget = targetEl;
        var x = Dom.getX(targetEl);
        var y = Dom.getY(targetEl);
        var w = targetEl.offsetWidth;
        var h = targetEl.offsetHeight;
        this.infoEl.innerHTML = message;
        this.infoEl.style.display = 'block';

        var yOffset = (h - this.infoEl.offsetHeight) / 2;
        var xOffset = (w - this.infoEl.offsetWidth) / 2;

        this.infoEl.style.left = x + xOffset + 'px';
        this.infoEl.style.top = y + yOffset + 'px';
        this.infoEl.style.zIndex = 9999;
    },

    position: function(targetEl) {
        var dt = this.getDataTable();
        var maxLength = dt.isMaxLength();
        var lastEl = dt.getLastTrEl();
        var isLast = targetEl == lastEl;
        if (isLast && Dom.hasClass(targetEl, MV.CLASS_QUICK) && Dom.hasClass(lastEl, MV.CLASS_QUICK)) {
            this.hide();
            this.showInfo(targetEl, "Begin New Row");
            return;
        } else {
            this.hideInfo();
        }
        if (this.assocEl && this.assocEl != targetEl) {
            this.dataTable.unhighlightRow(this.assocEl, true);
        }
        this.assocEl = targetEl;
        if (Dom.hasClass(targetEl, MV.CLASS_DELETED)) {
            this.removeEl.className = MV.CLASS_RESTORE;
        } else {
            this.removeEl.className = MV.CLASS_REMOVE;
        }

        if (this.addEl && maxLength) {
            this.addEl.style.display = 'none';
        } else if (this.addEl) {
            this.addEl.style.display = '';
        }
        var x = Dom.getX(targetEl);
        var y = Dom.getY(targetEl);
        var w = targetEl.offsetWidth;
        var h = targetEl.offsetHeight;

        var padding = (h - 10) / 2;
        this.optionsEl.style.paddingTop = padding + 'px'; 
        this.optionsEl.style.paddingBottom = padding + 'px'; 
        this.optionsEl.style.left = (x+w+1) + 'px';
        this.optionsEl.style.top = y + 'px';
        this.optionsEl.style.height = (h - padding) + 'px';
        this.optionsEl.style.display = 'block';
        this.optionsEl.style.zIndex = 9999;
        this.iFrame.show(this.optionsEl);
    },

    hideInfo: function() {
        this.infoTarget = null;
        this.infoEl.style.display = 'none';
    },

    hide: function() {
        this.assocEl = null;
        this.optionsEl.style.display = 'none';
        this.iFrame.hide();
        this.hideInfo();
    },

    onMouseover: function(oArgs, self) {
        self.over = true;
    },

    onMouseout: function(e, self) {
        var target = Event.getTarget(e);
        var relTarget = Event.getRelatedTarget(e);
        if (!(relTarget == self.optionsEl || Dom.isAncestor(self.optionsEl, relTarget) || relTarget == self.infoEl || Dom.isAncestor(self.infoEl, relTarget))) {
            self.over = false;
            self.clearRow = setTimeout(function() {
                if(!self.dataTable.overRow && self.assocEl) {
                    self.dataTable.unhighlightRow(self.assocEl, true);
                    self.hide();
                }
            }, 10);
        }
    }
});


lts.util.RowEditor = function(dataTable, oConfigs) {
    this.shown = false;
    this.editOrientation = oConfigs.editOrientation;
    this.editAlignment = oConfigs.editAlignment;
    this.dataTable = dataTable;
    this._iFrame = new lts.widget.iFrame();
    this._el = null;
    this._editors = [];
    this._oEditors = {};
    this.label = Dom.getPreviousSiblingBy(dataTable.getContainerEl(),
        function(node) {
            return node.tagName == 'LABEL';
        }
    ).innerHTML;
    this.renderRowEditor();
    lts.util.RowEditor.superclass.constructor.call(this, this._el, oConfigs); 
    this.subscribe("blurEvent", this.onBlurEvent, this, true);
};
YAHOO.extend(lts.util.RowEditor, YAHOO.util.Element, {
    onBlurEvent: function(oArgs) {
        if (!this.dataTable._preventBlur) {
            this.save();
        }
    },

    renderRowEditor: function() {
        var elContainer = document.createElement("div");
        this._el = elContainer;
        elContainer.id = this.dataTable.getId() + "-container"; // Needed for tracking blur event
        elContainer.style.display = "none";
        elContainer.tabIndex = 0;
        if (this.editOrientation == 'horizontal') {
            elContainer.className = DT.CLASS_EDITOR + ' ' + MV.CLASS_HORIZONTAL + ' fieldset';
        } else {
            elContainer.className = DT.CLASS_EDITOR + ' ' + MV.CLASS_VERTICAL + ' fieldset';
        }
        elContainer.style.zIndex = 9999;
        elContainer.style.padding = 0;
        elContainer.style.backgroundColor = 'white';
        document.body.appendChild(elContainer);
//        Dom.insertAfter(elContainer, this.dataTable.getTableEl());
//        document.body.insertBefore(elContainer, document.body.firstChild);
        this.headingEl = elContainer.appendChild(document.createElement('h2'));
        this.headingEl.innerHTML = '<span class="right"></span>' + this.label;
        if (document.all) {
            this.headingEl.style.width = '45em'
        }
        this.rowLabelEl = this.headingEl.firstChild;
        this.contentEl = elContainer.appendChild(document.createElement('div'));
        this.contentEl.className = 'content';

        var column = null, columns = this.dataTable.getColumns();
        for (var i=0; i<columns.length; i++) {
            column = columns[i];
            
            if (column.editor instanceof YAHOO.widget.BaseCellEditor) {
                var colContainer = column.editor.getContainerEl();
                Dom.addClass(colContainer, MV.CLASS_COL_EDIT);
                if (this.editOrientation == 'horizontal') {
                    Dom.addClass(colContainer, MV.CLASS_COL_HORIZONTAL);
                } else {
                    Dom.addClass(colContainer, MV.CLASS_COL_VERTICAL);
                }
                this.contentEl.appendChild(colContainer);
            }
        }
        Event.addListener(elContainer, 'keydown', function(e) {
            if (e.keyCode == 27) {
                this.cancel();
            }
        }, this, true);
        this.renderRowBtns();
    },

    renderRowBtns : function() {
        // Buttons
        var elBtnsDiv = this.contentEl.appendChild(document.createElement("div"));
        elBtnsDiv.className = DT.CLASS_BUTTON;

        // Save button
        var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
        elSaveBtn.className = DT.CLASS_DEFAULT;
        elSaveBtn.innerHTML = 'Save'; 
        Event.addListener(elSaveBtn, "click", function(e) {
            this.save();
            Event.preventDefault(e);
        }, this, true);
        this._elSaveBtn = elSaveBtn;

        // Cancel button
        var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
        elCancelBtn.innerHTML = 'Cancel'; 
        Event.addListener(elCancelBtn, "click", function(e) {
            this.cancel();
            Event.preventDefault(e);
        }, this, true);
        this._elCancelBtn = elCancelBtn;

        if (!this.dataTable.rowOptions.removeable) {
            Event.addListener(elCancelBtn, "keypress", function(e) {
                if (e.keyCode === 9) {
                    this.save();
                }
            }, this, true);
        } else {
            // Delete button
            var elDeleteBtn = elBtnsDiv.appendChild(document.createElement("button"));
            elDeleteBtn.innerHTML = 'Delete'; 
            Event.addListener(elDeleteBtn, "click", function(e) {
                this.deleteRow();
                Event.preventDefault(e);
            }, this, true);
            Event.addListener(elDeleteBtn, "keypress", function(e) {
                if (e.keyCode === 9) {
                    this.save();
                }
            }, this, true);
            this._elDeleteBtn = elDeleteBtn;
        }
    },

    getContainerEl: function() {
        return this._el;
    },

    show: function(elRow, newRow) {
        elRow = this.dataTable.getTrEl(elRow);
        var record = this.dataTable.getRecord(elRow);
        if (!record) return;
        this.dataTable._oCellEditor = this;
        this._editNewRow = newRow;
        var data = record.getData();
        var show = false;
        
        this.record = record;
        this.elRow = elRow;

        var column = null, columns = this.dataTable.getColumns();
        for (var i=0; i<columns.length; i++) {
            column = columns[i];
            if (column.editor instanceof YAHOO.widget.BaseCellEditor) {
                var cell = this.dataTable.getTdEl({record: record, column: column});
                var ok = column.editor.attach(this.dataTable, cell, column, record);
                if (ok) {
                    ok = this.dataTable.doBeforeShowCellEditor(column.editor);
                    if (ok) {
                        if (column.editor.setLabel) {
                            column.editor.setLabel(column.label);
                        }
                        show = true;
                        column.editor.show();
                        this._editors.push(column.editor);
                        this._oEditors[column.getKey()] = column.editor;
                    }
                }
            }
        }
        if (show) {
            this.dataTable.rowOptions.hide();
            this.shown = true;
            this.move()
            this.rowLabelEl.innerHTML = 'Row #' + (this.dataTable.getRecordIndex(record) + 1);
            this._el.style.display = '';
            this._iFrame.show(this._el);
            for (var i=0, len=columns.length; i<len; i++) {
                if (columns[i].editor) {
                    columns[i].editor.focus();
                    break;
                }
            }
        }
    },

    move: function(elRow) {
        elRow = elRow || this.elRow;
        var x = Dom.getX(elRow);
        var y = Dom.getY(elRow);
        var elContainer = this._el;

        // SF doesn't get xy for cells in scrolling table
        // when tbody display is set to block
        if(isNaN(x) || isNaN(y)) {
            var elTbody = this.dataTable.getTbodyEl();
            x = elRow.offsetLeft + // cell pos relative to table
                    Dom.getX(elTbody.parentNode) - // plus table pos relative to document
                    elTbody.scrollLeft; // minus tbody scroll
            y = elRow.offsetTop + // cell pos relative to table
                    Dom.getY(elTbody.parentNode) - // plus table pos relative to document
                    elTbody.scrollTop + // minus tbody scroll
                    this.dataTable.getTheadEl().offsetHeight; // account for fixed THEAD cells
        }
        if (this.editAlignment == 'right') {
            elContainer.style.right = (Dom.getViewportWidth() - (x + elRow.offsetWidth)) + 'px';
        } else {
            elContainer.style.left = x + "px";
        }
        elContainer.style.top = y + "px";
    },

    center: function () {
        var nViewportOffset = YAHOO.widget.Overlay.VIEWPORT_OFFSET,
            _elWidth = this._el.offsetWidth,
            _elHeight = this._el.offsetHeight,
            viewPortWidth = Dom.getViewportWidth(),
            viewPortHeight = Dom.getViewportHeight(),
            x,
            y;

        if (_elWidth < viewPortWidth) {
            x = (viewPortWidth / 2) - (_elWidth / 2) + Dom.getDocumentScrollLeft();
        } else {
            x = nViewportOffset + Dom.getDocumentScrollLeft();
        }

        if (_elHeight < viewPortHeight) {
            y = (viewPortHeight / 2) - (_elHeight / 2) + Dom.getDocumentScrollTop();
        } else {
            y = nViewportOffset + Dom.getDocumentScrollTop();
        }
        this._el.style.left = x + 'px';
        this._el.style.top = y + 'px';
    },

    isLastRecord: function() {
        return this.elRow == this.dataTable.getLastTrEl();
    },

    save: function() {
        var invalid = false;
        var changed = false;
        var empty = true;
        var record = this.dataTable.getRecord(this.elRow);
        for (var i=0; i<this._editors.length; i++) {
            var editor = this._editors[i];
            if (editor.validate) {
                validValue = editor.validate();
                if (!changed && validValue != editor.value && editor.value != editor.getInputValue()) {
                    changed = true;
                }
                if (validValue === undefined) {
                    invalid = true;
                }
                if (empty && editor.valueChanged(validValue)) {
                    empty = false;
                }
            } else if (!changed && editor.getInputValue() != editor.value) {
                if (!(!editor.getInputValue() && !editor.value)) {
                    changed = true;
                }
            }
        }
        var lastRecord = this.isLastRecord();
        if (!this.dataTable.saveNull) {
            if (!lastRecord && empty && this.dataTable.rowOptions.removeable) {
                this.cancel();
                return this.dataTable.removeRow(this.elRow);
            }
            if (empty || !changed) {
                return this.cancel();
            }
        }
        if (!invalid) {
            while(this._editors.length > 0) {
                var editor = this._editors.shift();
                (editor.saveValidated) ? editor.saveValidated() : editor.save();
            }
            record.changed = true;
            this._oEditors = {};
            this.hide(); 
            Dom.removeClass(this.elRow, MV.CLASS_QUICK);
            Dom.removeClass(this.elRow, MV.CLASS_HIGHLIGHTED);
            this.dataTable.fireEvent('rowSaveEvent', {record: record}); 
            if (lastRecord) {
                record.inserted = true;
                if (this.dataTable._rapidEntry) {
                    this.dataTable.addQuickRow(null, -1, {edit: true});
                } else {
                    this.dataTable.addQuickRow(null, -1);
                }
            }
        }
        return !invalid;
    },

    deleteRow: function() {
        this.cancel();
        var dt = this.dataTable;
        if (dt.rowOptions.removeable) {
            var last = this.isLastRecord();
            dt.deleteRow(this.elRow);
            if (last) {
                dt.addQuickRow();
            }
        }
    },

    cancel: function() {
        this._el.style.display = 'none';
        this._iFrame.hide();
        while(this._editors.length > 0) {
            this._editors.shift().cancel();
        }
        this._oEditors = {};

        if (this._editNewRow) {
            this.dataTable.deleteRow(this.elRow);
            this._editNewRow = false;
        }
        this.hide();    
        this.cancelled = true;
        var self = this;
        setTimeout(function() {
            self.cancelled = false;
        }, 200);
        
    },

    hide: function() {
        this.shown = false;
        this.dataTable._oCellEditor = null;
        this._editNewRow = false;
        this._el.style.display = 'none';
        this._iFrame.hide();
    }

});


lts.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
    oConfigs = oConfigs || {};
    if (oConfigs.add) {
        oConfigs['MSG_EMPTY'] = 'No records available.  Double click here to create a new record';
    }
    this.name = elContainer.getAttribute('name');
    lts.widget.DataTable.superclass.constructor.call(this, elContainer, aColumnDefs, oDataSource, oConfigs);
    this.id = elContainer.id;
    this._flagInsert = oConfigs.flagInsert;
    this._ignoreUnchanged = oConfigs.ignoreUnchanged;
    this._flagChanged = oConfigs.flagChanged;
    this._flagDeleted = oConfigs.flagDeleted;
    this._rapidEntry = oConfigs.rapidEntry;
    this.rowOptions = new lts.util.Options(this, oConfigs);
    this._preventBlur = false;
    this.saveNull = oConfigs.saveNull;
    this.minRecords = oConfigs.minRecords;
    this.maxRecords = oConfigs.maxRecords;

    this.subscribe("rowMouseoverEvent", this.onEventHighlightRow); 
    this.subscribe("rowMouseoutEvent", this.onEventUnhighlightRow); 
    this.subscribe("rowHighlightEvent", this.onEventAddRowOptions);
    this.subscribe("rowUnhighlightEvent", this.onEventRemoveRowOptions);
    this.subscribe("tableMsgShowEvent", this.onEventTableMsgShow);
    this.subscribe("rowClickEvent", this.onEventShowRowEditor);
    this.subscribe("rowAddEvent", this.editNewRow);
    this.subscribe("tableFocusEvent", this.onEventTableFocus);
    this.subscribe("valueChangeEvent", this.onEventValueChange, this, true);
    if (oConfigs.reorderable) {
        this.initalizeDragDrop();
        this.subscribe("rowAddEvent", this.enableDragDrop);
        this.subscribe("rowsAddEvent", this.enableManyDragDrop);
    }
    if (oConfigs.add) {
        this.addQuickRow();
    }
    this.rowEditor = new lts.util.RowEditor(this, oConfigs);
};
YAHOO.extend(lts.widget.DataTable, YAHOO.widget.DataTable, {
    updateCell: function(oRecord, oColumn, oData) {
        // Validate Column and Record
        oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
        if(oColumn && oColumn.getKey() && (oRecord instanceof YAHOO.widget.Record)) {
            // Copy data from the Record for the event that gets fired later
            var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());

            // Update Record with new data
            this._oRecordSet.updateRecordValue(oRecord, oColumn.getKey(), oData);

            // Update the TD only if row is on current page
            var elTd = this.getTdEl({record: oRecord, column: oColumn});
            if(elTd) {
                this._oChainRender.add({
                    method: function() {
                        if((this instanceof DT) && this._sId) {
                            this.formatCell(elTd.firstChild, oRecord, oColumn);
                            this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
                        }
                    },
                    scope: this,
                    timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
                });
                this._runRenderChain();
            }
            else {
                this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
            }
        }
    },

    onEventValueChange: function(oArgs) {
        this.setEditorData(oArgs.resultData);
    },

    setEditorData: function(data) {
        for (var i in this.rowEditor._oEditors) {
            var editor = this.rowEditor._oEditors[i];
            var mapping = editor.getColumn().mapping;
            if (mapping && !editor.valueChanged()) {
                editor.setValue(YAHOO.lang.substitute(mapping, data, function(key, value){
                    return value || '';
                }));
                editor.inlineValidate()
            }
        }
    },

    removeRow: function(trEl) {
        return this.rowOptions.removeRow(trEl);
    },

    onEventTableFocus: function(event) {
        var self = this;
        setTimeout(function() {
            if (!self.rowEditor.shown && !self.rowEditor.cancelled) {
                var elRow = self.getFirstTrEl();
                self.showRowEditor(elRow);
            }
        }, 200);
    },

    onEventShowRowEditor: function(oArgs) {
        try {
        this.showRowEditor(oArgs.target);
        } catch(er) {alert('Error: ' + er.message);}
    },

    getColumns: function() {
        var columns = [];
        var trEl = this.getFirstTrEl();
        if (!trEl) {
            return columns;
        }
        for (var i=0, length=trEl.childNodes.length; i < length; i++) {
            columns.push(this.getColumn(i));
        }
        return columns;
    },

    saveRow: function() {
        return this.rowEditor.save();
    },

    showRowEditor: function(elRow, newRow) {
        this.rowEditor.show(elRow, newRow);
    },

    editNewRow: function(oArgs) {
        if (this._suppressNewRowEditor) return;
        var rowEl = this.getTrEl(oArgs.record);
        var keepRow = this.keepRow;
        this.keepRow = false;
        this.showRowEditor(rowEl, !keepRow);
    },

    focusEditor: function(oArgs) {
        oArgs.editor.focus();
    },

    initalizeDragDrop: function() {
        var trEl = this.getFirstTrEl();
        while (trEl) {
            var record = this.getRecord(trEl);
            trEl.dd = new YAHOO.lts.util.MVDD(trEl, this.getId(), {dataTable: this, record: record});
            trEl = this.getNextTrEl(trEl);
        }
    },

    enableManyDragDrop: function(oArgs) {
        var records = oArgs.records;
        for (var i = 0; i < records.length; i++) {
            this.enableDragDrop({record: records[i]});
        }
    },

    enableDragDrop: function(oArgs) {
        var record = oArgs.record;
        var trEl = this.getTrEl(oArgs.record);
        trEl.dd = new YAHOO.lts.util.MVDD(trEl, this.getId(), {dataTable: this, record: record});
    },

    disableRow: function(elRow) {
        var deleted = !Dom.removeClass(elRow, MV.CLASS_DELETED);
        if (deleted) {
            Dom.addClass(elRow, MV.CLASS_DELETED);
        }
        this.getRecord(elRow).deleted = deleted;
        return deleted;
    },

    getDefaults: function() {
        data = {};
        templateData = this.getDataSource().templateData;
        var data = {};

        var oColumnSet = this.getColumnSet();
        for(var j=0; j<oColumnSet.keys.length; j++) {
            var oColumn = oColumnSet.keys[j].getKey();
            var value = templateData[oColumn] || "";
            data[oColumn] = value; 
        }
        return data;
    },

    isMaxLength: function() {
        return (lang.isNumber(this.maxRecords) && this.getRecordSet().getLength() >= this.maxRecords);
    },

    addRow: function(data, index, options) {
        if (this.isMaxLength()) return;
        options = options || {};
        if (!index && index != 0) index = -1;
        if (index < -1) {
            var len = this.getRecordSet().getLength();
            index = len + index + 1;
            if (index < 0) index = 0;
        }
        if (!data) {
            data = this.getDefaults();
        } else {
            defaults = this.getDefaults();
            for (var key in defaults) {
                if (!data[key]) {
                    data[key] = defaults[key];
                }
            }
        }
        this.keepRow = options.keepRow;
        lts.widget.DataTable.superclass.addRow.call(this, data, index);
        var newRow = (index == -1) ? this.getLastTrEl() : this.getTrEl(index);

        if (options.quick && options.empty) {
            // Add the "quick row"
            Dom.addClass(newRow, MV.CLASS_NEW);
            var allTds = newRow.childNodes;
            for (var i=0,len=allTds.length; i<len; i++) {
                allTds[i].firstChild.innerHTML = '';
            }
        }
        if (options.isNew) {
            this._preventBlur = true;
            var self = this;
            setTimeout(function(){self._preventBlur = false;}, 100);
        }
        return newRow;
    },

    addQuickRow: function(data, index, oArgs) {
        oArgs = oArgs || {};
        if (!oArgs.edit) {
            this._suppressNewRowEditor = true;
        }
        var newRow = this.addRow(data, index, {empty: !data, quick:true, keepRow: true, isNew: true});
        Dom.addClass(newRow, MV.CLASS_QUICK);
        if (oArgs.highlight) {
            Dom.addClass(newRow, MV.CLASS_HIGHLIGHTED);
        }
        if (!oArgs.edit) {
            this._suppressNewRowEditor = false;
        }
    },

    onEventHighlightEditableCell: function(oArgs) {
        var elCell = oArgs.target;
        if(Dom.hasClass(elCell, DT.CLASS_EDITABLE)) {
            this.highlightCell(elCell);
        }
    },

    onEventTableMsgShow: function(oArgs) {
        switch (oArgs.className) {
            case DT.CLASS_EMPTY: 
                var msgEl = this._elMsgTd;
                Event.addListener(msgEl, 'dblclick', this.onEventCreateFirst, this);
                break;
        }
    },

    createFirst: function() {
        var oColumnSet = this.getColumnSet();
        var data = {};
        for(var j=0; j<oColumnSet.keys.length; j++) {
            var oColumn = oColumnSet.keys[j].getKey();
            data[oColumn] = "";
        }
        this.addRow(data, -1);
    },

    onEventCreateFirst: function(e, self) {
        Event.removeListener(self._elMsgTd, 'dblclick');
        self.createFirst();
    },

    unhighlightRow: function(row, suppress) {
        var elRow = this.getTrEl(row);

        if(elRow) {
            var oRecord = this.getRecord(elRow);
            Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
            if (!suppress) {
                this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
            }
            return;
        }
    },

    onEventUnhighlightRow: function(oArgs) {
        this.overRow = false;
        if(!Dom.isAncestor(oArgs.target, Event.getRelatedTarget(oArgs.event))) {
            var self = this;
            var target = oArgs.target;
            self.clearOptions = setTimeout(function() {
                if (!self.rowOptions.over) {
                    self.unhighlightRow(target);
                }
            }, 100);
        }
    },

    onEventHighlightRow: function(oArgs) {
        this.overRow = true;
        lts.widget.DataTable.superclass.onEventHighlightRow.call(this, oArgs);
    },

    onEventAddRowOptions: function(oArgs) {
        var oRecord = oArgs.record;
        this.rowOptions.position(oArgs.el)
    },

    onEventRemoveRowOptions: function(oArgs) {
        if (!this.overRow) {
            this.rowOptions.hide();
        }
    },

    validate: function(record) {
        var column = null, columns = this.getColumns();
        var hasError = false; 
        for (var i=0; i<columns.length; i++) {
            column = columns[i];
            if (column.editor instanceof YAHOO.widget.BaseCellEditor && column.editor.validator) {
                try {
                    validValue = column.editor.validator.call(this, record.getData(column.getKey()), null, column.editor);
                } catch (error) {
                    if (!hasError) {
                        this.showRowEditor(record);
                        hasError = true;
                    }
                    column.editor.showError(error.message || error);
                }
            }
        }
        if (hasError) {
            return false;
        }
        return true;
    },

    getParsedData: function(data, skipValidation) {
        if (data === undefined) {
            data = {};
        }
        var value, prefix, record, j, oColumn, columnName, changed, newData;
        var records = this.getRecordSet().getRecords();
        var oColumnSet = this.getColumnSet();
        var recordCount = 0;
        var defaults = this.getDefaults();
        for (var i in records) {
            changed = false;
            newData = [];
            recordCount ++;
            prefix = (this.name) ? this.name + '/' + this.name + '[' + recordCount + ']/' : '';
            record = records[i];
            if (record.deleted) {
                if (!this.flagDeleted(record)) {
                    continue;
                } else {
                    dName = prefix + '__deleted';    
                    data[dName] = '1';
                }
            } else {
                if (this._flagInsert && record.inserted) {
                    dName = prefix + '__inserted';    
                    data[dName] = '1';
                }
                if (record.changed) {
                    if (this._flagChanged) {
                        dName = prefix + '__changed';    
                        data[dName] = '1';
                    }
                } else if (this._ignoreUnchanged) {
                    continue;
                }
            }
            for (j=0; j<oColumnSet.keys.length; j++) {
                oColumn = oColumnSet.keys[j].getKey();
                value = record.getData(oColumn);
                if (value instanceof Date) { 
                    value = YAHOO.util.Date.format(value, {format:'MM/DD/YYYY'});
                } else if (value === null || value === undefined) {
                    continue;
                }
                value = value.replace(/&amp;/, '&');
                value = value.replace(/&lt;/, '<');
                value = value.replace(/&gt;/, '>');
                value = value.replace(/&quot;/, '"');
                if (!changed) {
                    changed = defaults[oColumn] != value;
                }
                columnName = prefix + oColumn; //YAHOO.lang.substitute(oColumn, {position: recordCount});
                newData.push([columnName, value]);
            }

            if (changed) {
                if (!skipValidation && !this.validate(record))
                    throw ("Invalid data");
                for (j=0; j<newData.length; j++) {
                    columnName = newData[j][0];
                    value = newData[j][1];
                    if (data[columnName] === undefined) {
                        data[columnName] = [];
                    }
                    data[columnName].push(value);
                }
            } else if (this.flagDeleted(record)) {
                dName = prefix + '__deleted';
                data[dName] = '1';
            }
        }
        return data;
    },

    flagDeleted: function(elRow) {
        if (this._flagDeleted) {
            var elRow = this.getTrEl(elRow);
            return !Dom.hasClass(elRow, MV.CLASS_NEW);
        } else {
            return false;
        }
    }
});


lts.lang.parseElement = function(oElement) {
    oDisabled = oElement.disabled;
    oName     = oElement.name;
    oValue    = oElement.value;
    oType     = oElement.type;

    if (oDisabled || !oName) {
        return null;
    }

    switch(oElement.type)
    {
        // Safari, Opera, FF all default opt.value from .text if
        // value attribute not specified in markup
        case 'select-one':
            if (oElement.selectedIndex > -1) {
                opt = oElement.options[oElement.selectedIndex];
                return (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text;
            }
            break;
        case 'select-multiple':
            var value = [];
            if (oElement.selectedIndex > -1) {
                for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){
                    opt = oElement.options[j];
                    if (opt.selected) {
                        value.push((opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
                    }
                }
            }
            return value
            break;
        case 'radio':
        case 'checkbox':
            return (oElement.checked) ? oValue : null;
            break;
        case 'file':
            // stub case as XMLHttpRequest will only send the file path as a string.
        case undefined:
            // stub case for fieldset element which returns undefined.
        case 'reset':
            // stub case for input type reset button.
        case 'button':
            // stub case for input type button elements.
            break;
        case 'submit':
            return oValue;
            break;
        default:
            return oValue;
    }
};

lts.lang.loadForm = function(oForm, data) {
    var oElement, oName, oValue, value;

    for (var i=0; i<oForm.elements.length; i++){
        oElement = oForm.elements[i];
        oName = oForm.elements[i].name;
        oValue = oForm.elements[i].value;

        // Do not submit fields that are disabled or
        // do not have a name attribute value.
        if(oName && data[oName]) {
            value = data[oName];
            if (!YAHOO.lang.isArray(value)) {
                value = [value];
            }
            switch(oElement.type) {
                case 'select-one':
                case 'select-multiple':
                    for(var j=0; j<oElement.options.length; j++){
                        var option = oElement.options[j];
                        if(window.ActiveXObject){
                            oValue = option.attributes['value'].specified ? option.value : option.text;
                        } else{
                            oValue = option.hasAttribute('value') ? option.value : option.text;
                        }
                        option.selected = false;
                        for (var di=0; di < value.length; di++) {
                            if (value[di] == oValue) {
                                option.selected = true;
                                break;
                            }
                        }

                    }
                    break;
                case 'radio':
                case 'checkbox':
                    oElement.checked = false;
                    for (var di=0; di < value.length; di++) {
                        if (value[di] == oValue) {
                            oElement.checked = true;
                            break;
                        }
                    }
                    break;
                case 'file':
                    // stub case as XMLHttpRequest will only send the file path as a string.
                case undefined:
                    // stub case for fieldset element which returns undefined.
                case 'reset':
                    // stub case for input type reset button.
                case 'button':
                    // stub case for input type button elements.
                    break;
                case 'submit':
                    break;
                default:
                    oElement.value = value.shift();
            }
        }
    }
};

lts.lang.parseForm = function(oForm, data, options) {
    options = options || {};
    if (options.useDict) {
        data = data || {}
        Dom.getElementsBy(function(node) {
            value = lts.lang.parseElement(node);
            if (value !== null) {
                data[node.name] = value
            }
        }, null, oForm);
    } else {
        data = data || []
        Dom.getElementsBy(function(node) {
            value = lts.lang.parseElement(node);
            if (value !== null) {
                data.push([node.name, value]);
            }
        }, null, oForm);
    }
    return data;
};

lts.util.HTMLParser = function(value, node) {
    value = lts.lang.parseForm(node);
    if (value.length) {
        return value[0][1];
    } else {
        return '';
    }
};

lts.util.BooleanParser = function(value, node) {
    value = lts.util.HTMLParser(value, node);
    return (value != '') ? value : null;
};

lts.util.HTMLDateParser = function(value, node) {
    value = lts.util.HTMLParser(value, node);
    return (value != '') ? YAHOO.util.DataSource.parseDate(value) : '';
}

lts.util.hiddenFormatter = function(cell, record, column, data) {
    cell.innerHTML = '';
};

lts.util.booleanFormatter = function(cell, record, column, data) {
    cell.innerHTML = (data !== null) ? 'yes' : 'no';
};

lts.util.dropdownFormatter = function(el, oRecord, oColumn, oData) {
    var selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field);
    var options = (lang.isArray(oColumn.dropdownOptions)) ? oColumn.dropdownOptions : oColumn.editor.dropdownOptions;
    
    if (options) {
        var option;
        for (var i=0,len=options.length; i<len; i++) {
            option = options[i];
            if (option.value == selectedValue) {
                el.innerHTML = option.label;
                return;
            }
        }
        el.innerHTML = '';
    } else {
        el.innerHTML = selectedValue;
    }
};

lts.util.MVDD = function(id, sGroup, config) {
    lts.util.MVDD.superclass.constructor.call(this, id, sGroup, config);
    this.lastY = null;
    this.goingUp = false;
    this.moved = false;
    this.dataTable = config.dataTable;
    this.record = config.record;
};


YAHOO.extend(lts.util.MVDD, YAHOO.util.DDProxy, {
    startDrag: function(x, y) {
        var dragEl = this.getDragEl();
        var clickEl = this.getEl();
        Dom.setStyle(clickEl, "visibility", "hidden");

        Dom.addClass(dragEl, 'yui-dt');
        dragEl.innerHTML = '<table><tbody><tr>' + clickEl.innerHTML + '</tr></tbody></table>';

        Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color"));
        Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor"));
        Dom.setStyle(dragEl, "border", "2px solid gray");
    },
  
    endDrag: function(e) {
        var srcEl = this.getEl();
        var proxy = this.getDragEl();

        if (this.dataTable && this.moved) {
            var dt = this.dataTable;
            dt._setFirstRow();
            dt._setLastRow();
            dt._setRowStripes();

            // Move the record to the new position by removing and readding
            var rs = dt.getRecordSet();
            var recIndex = dt.getRecordIndex(this.record);
            var recData = lts.util.copy(this.record.getData());
            var newIndex = srcEl.sectionRowIndex + dt.getRecordIndex(dt.getRecord(dt.getFirstTrEl()));
            rs.deleteRecord(recIndex);
            this.record = rs.addRecord(recData, newIndex); 
            this.record._sId = this.id;
        }
        this.moved = false;

        // Show the proxy element and animate it to the src element's location
        Dom.setStyle(proxy, "visibility", "");
        var a = new YAHOO.util.Motion(
            proxy, {
                points: {
                    to: Dom.getXY(srcEl)
                }
            },
            0.4,
            YAHOO.util.Easing.easeOut
        )
        var self = this;

        // Hide the proxy and show the source element when finished with the animation
        a.onComplete.subscribe(function() {
            Dom.setStyle(proxy.id, "visibility", "hidden");
            Dom.setStyle(self.id, "visibility", "");
        });
        a.animate();
    },

    onDrag: function(e) {

        // Keep track of the direction of the drag for use during onDragOver
        var y = Event.getPageY(e);

        if (y < this.lastY) {
            this.goingUp = true;
        } else if (y > this.lastY) {
            this.goingUp = false;
        }

        this.lastY = y;
    },

    onDragOver: function(e, id) {
        var srcEl = this.getEl();
        var destEl = Dom.get(id);
        if (destEl.nodeName.toLowerCase() == "tr") {
            var p = destEl.parentNode;
            if (this.goingUp) {
                p.insertBefore(srcEl, destEl); // insert above
            } else {
                p.insertBefore(srcEl, destEl.nextSibling); // insert below
            }
            this.moved = true;
            DDM.refreshCache();
        }
    }
});

lts.util.moneyToFloat = function(money) {
    return parseFloat(money.replace(/[^0-9.]/, ''));
};

lts.util.floatToMoney = function(money) {
    var n = money;
    var s = n < 0 ? "-" : "";
    var i = parseInt(n = Math.abs(+n || 0).toFixed(2)) + "";
    var j = (j = i.length) > 3 ? j % 3 : 0;
    return '$' + s + (j ? i.substr(0, j) + "," : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + ",") + (2 ? '.' + Math.abs(n - i).toFixed(2).slice(2) : "");
};

lts.widget.DDList = function(id, sGroup, config) {
    lts.widget.DDList.superclass.constructor.call(this, id, sGroup, config);
    this._events = [];
    this.createEvent('disableEvent');
    this.createEvent('enableEvent');
    this.createEvent('dropEvent');
    var dragEl = this.getDragEl();
    Dom.setStyle(dragEl, "opacity", 0.60);
    this.goingUp = false;
    this.lastY = 0;

    var el = this.getEl();
    el.DDList = this;
    this.status = '';
    var status, i;
    for (i in config.statuses) {
        status = config.statuses[i];
        if (Dom.hasClass(el, status)) {
            this.status = status;
            break;
        }
    }
    this.permanentDisabledStatuses = config.permanantDisabledStatuses || [];

    this.disabled = false;
    this.disabledNode = config['disable'];
    if (this.disabledNode) {
        Event.on(this.disabledNode, 'click', this.disable, this, true);
    }

    this.previousSibling = Dom.getPreviousSibling(el);
    this.nextSibling = Dom.getNextSibling(el);
};
YAHOO.extend(lts.widget.DDList, YAHOO.util.DDProxy, {
    indexOf: function(obj) {
        if (obj.indexOf) {
            return obj.indexOf;
        } else {
            return function (item) {
                for (var i=0; i<obj.length; i++) {
                    if (obj[i] == item)
                        return i;
                }
                return -1;
            }
        }
    },

    canEnable: function() {
        return this.indexOf(this.permanentDisabledStatuses)(this.status) == -1;
    },

    disable: function(event) {
        if (event) {
            Event.preventDefault(event);
        }
        if (this.disabled) {
            return this.enable(event);
        }
        if (event) {
            this.initStateAnimation(event);
        }
        var srcEl = this.getEl();
        srcEl.parentNode.appendChild(srcEl);
        Dom.addClass(srcEl, 'disabled');
        this.disabled = true;
        if (event) {
            this.endDrag();
        }
        this.fireEvent('disableEvent', {o: this});
        return this.disabled;
    },

    _getFirstDDNode: function(node) {
        if (node.DDList.previousSibling) {
            return this._getFirstDDNode(node.DDList.previousSibling);
        } else {
            return node;
        }
    },

    reorder: function() {
        var parent = this.getEl().parentNode;
        var firstChild = null;

        var nodes = [];
        var node = this._getFirstDDNode(this.getEl())
        while (node && node.DDList.disabled) {
            node = node.DDList.nextSibling;
        }
        while (node) {
            nodes.push(node);
            node = node.DDList.nextSibling;
            while (node && node.DDList.disabled) {
                node = node.DDList.nextSibling;
            }
        }

        while (nodes.length) {
            node = nodes.pop();
            firstChild = Dom.getFirstChild(parent);
            if (node != firstChild) {
                Dom.insertBefore(node, firstChild);
            }
        }
        DDM.refreshCache();
    },

    enable: function(event) {
        if (!this.canEnable()) {
            return false;
        }
        this.initStateAnimation(event);
        var srcEl = this.getEl();
        this.disabled = false;
        Dom.removeClass(srcEl, 'disabled');
        this.reorder();
        this.endDrag();
        this.fireEvent('enableEvent', {o: this});
        return this.disabled;
    },

    initStateAnimation: function(e) {
        this.b4Drag(e);
        this.setStartPosition();
        var oDD = this;

        this.currentTarget = YAHOO.util.Event.getTarget(e);

        this.dragCurrent = oDD;

        var el = oDD.getEl();
        // track start position
        this.startX = YAHOO.util.Event.getPageX(e);
        this.startY = YAHOO.util.Event.getPageY(e);

        this.deltaX = this.startX - el.offsetLeft;
        this.deltaY = this.startY - el.offsetTop;

        this.dragThreshMet = false;
        DDM.startDrag(DDM.startX, DDM.startY);
        this.startDrag();
        this._resizeProxy();
    },

    handleMouseDown: function(e, oDD) {
        if (this.disabled) {
            return false;
        }
        lts.widget.DDList.superclass.handleMouseDown.call(this, e, oDD);
    },

    startDrag: function(x, y) {
        var dragEl = this.getDragEl();
        var clickEl = this.getEl();
        Dom.setStyle(clickEl, "visibility", "hidden");

        dragEl.innerHTML = clickEl.innerHTML;

        Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color"));
        Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor"));
        Dom.setStyle(dragEl, "border", "2px solid gray");
    },

    endDrag: function(e) {
        var srcEl = this.getEl();
        var proxy = this.getDragEl();

        // Show the proxy element and animate it to the src element's location
        Dom.setStyle(proxy, "visibility", "");
        var a = new YAHOO.util.Motion(
            proxy, {
                points: {
                    to: Dom.getXY(srcEl)
                }
            },
            0.4,
            YAHOO.util.Easing.easeOut
        )
        var proxyid = proxy.id;
        var thisid = this.id;
        var thisel = this.getEl();
        var self = this;

        // Hide the proxy and show the source element when finished with the animation
        a.onComplete.subscribe(function() {
            Dom.setStyle(proxyid, "visibility", "hidden");
            Dom.setStyle(thisid, "visibility", "");

            var parent = thisel.parentNode;
            var node = Dom.getFirstChild(parent);
            var dd;
            while (node) {
                dd = DDM.getDDById(node.id);

                /* Reorganize list item positions */
                if (e) {
                    if (dd.previousSibling && dd.previousSibling.id == thisid) {
                        dd.previousSibling = self.previousSibling;
                    } else if (dd.nextSibling && dd.nextSibling.id == thisid) {
                        dd.nextSibling = self.nextSibling;
                    }
                }

                node = Dom.getNextSibling(node);
            }

            /* Reorganize list item positions */
            if (e) {
                nextEl = Dom.getNextSibling(thisel);
                prevEl = Dom.getPreviousSibling(thisel);
                self.nextSibling = nextEl;
                self.previousSibling = prevEl;
                if (nextEl)
                    nextEl.DDList.previousSibling = thisel;
                if (prevEl)
                    prevEl.DDList.nextSibling = thisel;
            }
            try {
                self.fireEvent('dropEvent', {o: this});
            } catch (er) {
                alert(er.message);
            }
        });
        a.animate();
    },

    onDragDrop: function(e, id) {

        // If there is one drop interaction, the li was dropped either on the list,
        // or it was dropped on the current location of the source element.
        if (DDM.interactionInfo.drop.length === 1) {

            // The position of the cursor at the time of the drop (YAHOO.util.Point)
            var pt = DDM.interactionInfo.point;

            // The region occupied by the source element at the time of the drop
            var region = DDM.interactionInfo.sourceRegion;

            // Check to see if we are over the source element's location.  We will
            // append to the bottom of the list once we are sure it was a drop in
            // the negative space (the area of the list without any list items)
            if (!region.intersect(pt)) {
                var destEl = Dom.get(id);
                var srcEl = this.getEl();

                var lSibling = destEl.lastChild;
                while (lSibling) {
                    if (!DDM.getDDById(lSibling.id).disabled) {
                        Dom.insertAfter(srcEl, lSibling);
                        break;
                    }
                    var lSibling = Dom.getPreviousSibling(lSibling);
                }

                DDM.refreshCache();
            }

        }
    },

    onDrag: function(e) {
        // Keep track of the direction of the drag for use during onDragOver
        var y = Event.getPageY(e);

        if (y < this.lastY) {
            this.goingUp = true;
        } else if (y > this.lastY) {
            this.goingUp = false;
        }

        this.lastY = y;
    },

    onDragOver: function(e, id) {
        var srcEl = this.getEl();
        var destEl = Dom.get(id);
        // We are only concerned with list items that are not disabled, we ignore the dragover
        // notifications for the list.
        if (destEl.nodeName.toLowerCase() == "li" && !DDM.getDDById(destEl.id).disabled) {
            var orig_p = srcEl.parentNode;
            var p = destEl.parentNode;

            if (this.goingUp) {
                p.insertBefore(srcEl, destEl); // insert above
            } else {
                p.insertBefore(srcEl, destEl.nextSibling); // insert below
            }

            DDM.refreshCache();
        }
    }
});
YAHOO.lang.augmentProto(lts.widget.DDList, YAHOO.util.EventProvider, false);


lts.widget.FormOverlay = function(id, config) {
    lts.widget.FormOverlay.superclass.constructor.call(this, id, config);
    this.isShown = false;
    this.element.overlay = this;
    this.inputs = [];
    this.state = null;
};
YAHOO.extend(lts.widget.FormOverlay, YAHOO.widget.Overlay, {
    TYPE_MULTIVALUE: 1,
    TYPE_FORM: 2,

    registerInput: function(input, type) {
        this.inputs.push([input, type]);
    },

    _parseMultiValue: function(input, data) {
        return input.dataTable.getParsedData(data);
    },

    _parseForm: function(input, data) {
        data = data || {};
        return lts.lang.parseForm(input, data, {useDict: true});
    },

    _saveStateMultiValue: function(input) {
        var dataTable = input.dataTable;
        var records = dataTable.getRecordSet().getRecords();
        var data = [];
        for (var i in records) {
            var record = records[i];
            data.push(YAHOO.lts.util.copy(record.getData()));
        }
        return data;
    },

    _loadStateMultiValue: function(input, data) {
        var dataTable = input.dataTable;
        dataTable.deleteRows(0, dataTable.getRecordSet().getRecords().length);
        dataTable.addRows(data);
    },

    _saveStateForm: function(input) {
        return this._parseForm(input);
    },

    _loadStateForm: function(oForm, data) {
        return lts.lang.loadForm(oForm, data);
    },

    saveState: function() {
        var state = [];
        var results;
        for (var i in this.inputs) {
            var input = this.inputs[i][0];
            var type = this.inputs[i][1];
            switch (type) {
                case this.TYPE_MULTIVALUE:
                    results = this._saveStateMultiValue(input);
                    break;
                case this.TYPE_FORM:
                    results = this._saveStateForm(input);
                    break;
            }
            state.push(results)
        }
        this.state = state;
        return state;
    },

    loadState: function() {
        var state = this.state;
        if (!state) return false;

        for (var i in this.inputs) {
            var input = this.inputs[i][0];
            var type = this.inputs[i][1];
            var inputState = state[i];
            switch (type) {
                case this.TYPE_MULTIVALUE:
                    results = this._loadStateMultiValue(input, inputState);
                    break;
                case this.TYPE_FORM:
                    results = this._loadStateForm(input, inputState);
                    break;
            }
        }
        return true;
    },

    cancelEditors: function() {
        for (var i in this.inputs) {
            var input = this.inputs[i][0];
            if (input.dataTable && input.dataTable._oCellEditor) {
                input.dataTable.cancelCellEditor();
            }
        }
    },

    parse: function() {
        var data = {};
        for (var i in this.inputs) {
            var input = this.inputs[i][0];
            var type = this.inputs[i][1];
            switch (type) {
                case this.TYPE_MULTIVALUE:
                    data = this._parseMultiValue(input, data);
                    break;
                case this.TYPE_FORM:
                    data = this._parseForm(input, data);
                    break;
            }
        }
        return data;
    },

    buildMask: function () {
        var oMask = this.mask;
        if (!oMask) {
            oMask = Dom.get("overlay-mask");
            if (!oMask) {
                oMask = document.createElement("div");
                oMask.className = "mask";
                oMask.innerHTML = "&#160;";
                oMask.id = "overlay-mask";
                document.body.insertBefore(oMask, document.body.firstChild);
                if (YAHOO.env.ua.gecko && this.platform == "mac") {
                    Dom.addClass(this.mask, "block-scrollbars");
                }
            }

            this.mask = oMask;
        }
        // Stack mask based on the element zindex
        this.stackMask();
        this.sizeMask();
        this.mask.style.display = 'block';
        Event.on(this.mask, 'click', this.cancel, this, true);
    },

    hideMask: function () {
        if (this.mask) {
            this.mask.style.display = "none";
            Event.removeListener(this.mask, 'click');
        }
    },

    stackMask: function() {
        if (this.mask) {
            var panelZ = Dom.getStyle(this.element, "zIndex");
            if (!YAHOO.lang.isUndefined(panelZ) && !isNaN(panelZ)) {
                Dom.setStyle(this.mask, "zIndex", panelZ - 1);
            }
        }
    },

    show: function(event) {
        this.saveState();
        if (this.isShown)
            return;
        this.buildMask();
        this.isShown = true;

        lts.widget.FormOverlay.superclass.show.call(this, event);
        for (var i in this.inputs) {
            var input = this.inputs[i][0];
            if (input.dataTable) {
                input.dataTable._oChainRender.run();
            }
        }
        this.center();
    },

    configVisible: function (type, args, obj) {
        var visible = args[0];
        if (visible) {
            Dom.setStyle(this.element, "display", "block");
        } else {
            Dom.setStyle(this.element, "display", "none");
        }
        lts.widget.FormOverlay.superclass.configVisible.call(this, type, args, obj);
    },

    sizeMask: function () {
        if (this.mask) {
            this.mask.style.height = Dom.getDocumentHeight() + "px";
            this.mask.style.width = Dom.getDocumentWidth() + "px";
        }
    },

    cancel: function(event) {
        this.hide(event);
        this.loadState();
    },

    hide: function(event) {
        this.isShown = false;
        this.hideMask();
        this.cancelEditors();
        lts.widget.FormOverlay.superclass.hide.call(this, event);
    }
});

lts.util.loadValidators = function() {
    var inputs = document.getElementsByTagName('INPUT');
    var textareas = document.getElementsByTagName('TEXTAREA');
    var selects = document.getElementsByTagName('SELECT');
    for (var i=0; i<inputs.length; i++) {
        var input = inputs[i];
        var validator = null;
        if (document.all && (input.type == 'checkbox' || input.type == 'radio')) {
            input.style.border = 'none';
        }
        if (input.getAttribute('validate')) {
            validator = new lts.widget.Validator(input, {submit: true});
        }
        switch (input.getAttribute('format')) {
            case 'date':
                var cal = document.createElement('a');
                cal.className = 'calendar';
                cal.title = 'Open Calendar';
                Dom.insertAfter(cal, input);
                new lts.widget.Calendar(cal, {target: input});
                if (validator) {
                    validator.errorEl.style.marginLeft = '2.5em';
                }
                break;
        }
    }
    for (var i=0; i<textareas.length; i++) {
        if (textareas[i].getAttribute('validate')) {
            new lts.widget.Validator(textareas[i], {submit: true});
        }
    }
    for (var i=0; i<selects.length; i++) {
        if (selects[i].getAttribute('validate')) {
            new lts.widget.Validator(selects[i], {submit: true});
        }
    }
    Dom.getElementsByClassName('collapsible', 'DIV', document, function(node) {
        lts.util.createCollapsibleBox(node);
    });
};


lts.widget.ValidatorManager = function() {
    this.validators = {};
    this.validatorId = 0;
};
lts.widget.ValidatorManager.prototype = {
    genId: function() {
        return 'validator-gen-id-' + this.validatorId++;
    },

    set: function(id, validator) {
        this.validators[id] = validator;
    },

    get: function(id) {
        return this.validators[id];
    },

    validate: function() {
        var validator, result, valid = true;
        for (var i in this.validators) {
            validator = this.validators[i];
            result = validator.validate();
            if (valid && !result) {
                valid = false;
            }
        }
        return valid;
    },

    validateBeforeSubmit: function(form) {
        if (form) {
            form = Dom.get(form).id;
        }
        var validator, result, valid = {valid: true};
        for (var i in this.validators) {
            validator = this.validators[i];
            if (validator.config['submit']) {
                var vEl = validator.getInputEl();
                if (form && vEl.form.id != form) {
                    continue;
                }
                result = validator.validate();
                if (valid['valid'] && !result) {
                    valid['valid'] = false;
                    valid['invalidEl'] = vEl;
                }
            }
        }
        return valid;
    }
};
lts.util.validatorManager = new lts.widget.ValidatorManager();

lts.widget.Validator = function(id, oConfigs) {
    this.config = oConfigs || {};
    this.inputEl = Dom.get(id); 
    this.id = this.inputEl.id;
    if (!this.id) {
        this.inputEl.id = lts.util.validatorManager.genId();
        this.id = this.inputEl.id;
    }
    if (lts.util.validatorManager.get(this.id)) {
        this.inputEl = null;
        this.id = null;
        return;
    }
    var errorEl = document.createElement('strong');
    errorEl.style.display = 'none';
    errorEl.className = 'error';
    Dom.insertAfter(errorEl, this.inputEl);
    this.errorEl = errorEl;

    this.validator = new lts.util.Validators(this.inputEl.getAttribute('validate'));
    if (this.validator.required) {
        var reqEl = document.createElement('span');
        reqEl.className = 'required';
        reqEl.innerHTML = '&nbsp;';
        Dom.insertAfter(reqEl, this.inputEl);
    }
    Event.on(this.inputEl, 'change', this.validate, this, true);
    lts.util.validatorManager.set(this.id, this);
};
lts.widget.Validator.prototype = {
    validate: function() {
        if (this.inputEl.disabled) {
            return true;
        }
        var value = lts.lang.parseElement(this.inputEl);
        try {
            var newValue = this.validator.validate(value);
            this.setValue(newValue);
            this.hideError();
            return true;
        } catch (error) {
            this.showError(error.message || error);
            return false;
        }
    },

    getInputEl: function() {
        return this.inputEl;
    },

    setValue: function(value) {
        this.inputEl.value = value; 
    },

    hideError: function() {
        this.errorEl.innerHTML = '';
        this.errorEl.style.display = 'none';
    },

    showError: function(error) {
        this.errorEl.innerHTML = error;
        this.errorEl.style.display = '';
    }
};


lts.util.Validators = function(types) {
    var type, params, tmp, tmpParam, tmpParams, key, value, index, validator;
    var typesAndParams = types.split(';');
    this.validators = [];
    this.required = false;
    for (var i=0; i<typesAndParams.length; i++) {
        tmp = typesAndParams[i];
        index = tmp.indexOf(':');
        if (index != -1) {
            type = tmp.substr(0, index);
            tmpParams = tmp.substr(index+1).split('&'); 
            params = {};
            for (var t=0; t<tmpParams.length; t++) {
                tmpParam = tmpParams[t];
                index = tmpParam.indexOf('=');
                if (index != -1) {
                    key = tmpParam.substr(0, index);
                    value = tmpParam.substr(index + 1);
                } else {
                    key = tmpParam;
                    value = '';
                }
                params[YAHOO.lang.trim(key)] = YAHOO.lang.trim(value);
            }
        } else {
            type = tmp;
        }
        type = YAHOO.lang.trim(type);
        if (!type) {
            continue;
        }
        validator = new lts.util.Validator(type, params);
        if (validator.type == 'Exists') {
            this.required = true;
        }
        this.validators.push(validator);
    }
};

lts.util.Validators.prototype = {
    validate: function(value, orig) {
        for (var i=0; i<this.validators.length; i++) {
            value = this.validators[i].validate(value, orig);
        }
        return value;
    },

    call: function(datatable, value, orig, editor) {
        return this.validate(value, orig);
    }
};

lts.util.Validator = function(type, params) {
    type = String(type);
    type = type.charAt(0).toUpperCase() + type.substr(1).toLowerCase();
    this.type = type;
    this.params = params || [];
};

lts.util.Validator.prototype = {
    validateWithGizmo: function(type, value, orig) {
        var extra = [];
        for (key in this.params) {
            extra.push('param/' + key + '=' + this.params[key]);
        }
        var extraQuery = (extra.length > 0) ? '&' + extra.join('&') : '';
        var conn = YAHOO.util.Connect.sRequest('POST', '/Expert/validate', 'value=' + value + '&orig=' + orig + '&type=' + type + extraQuery);
        if (conn.status != 200) {
            if (conn.responseXML) {
                throw(lts.util.parseGizmoError(conn.responseXML).join('\n'));
            } else {
                return undefined;
            }
        }
        return conn.responseText;
    },

    validateEmail: function(value, orig) {
        if (this.params['strict']) {
            return this.validateWithGizmo('Email', value, orig);
        } else {
            value = value.replace(/\s/g, '');
            regex = new RegExp(/^.+@[^\.].*\.[a-zA-Z0-9]+$/);
            if (regex.exec(value)) {
                return value;
            } else {
                throw("Invalid email address: " + value);
            }
        }
    },

    validateCase: function(value, orig) {
        switch (this.params['case']) {
            case "lower": return value.toLowerCase();
            case "upper": return value.toUpperCase();
        };
        return value;
    },

    validateExpression: function(value, orig) {
        if (value.match(this.params.pattern)) {
            return value;
        } else {
            throw("Invalid value " + value);
        }
    },

    validatePhone: function(value, orig) {
        var sValue = value;
        value = value.replace(/[^\d]/g, '');
        value = value.replace(/^[01]*/, '');
        if (value.length != 10) {
            throw("Invalid phone number: " + sValue);
        }
        var regex = new RegExp(/^[2-9]\d\d[2-9]\d+/);
        if (regex.exec(value)) {
            return value.substr(0,3) + '-' + value.substr(3, 3) + '-' + value.substr(6);
        } else {
            throw("Invalid phone number: " + sValue);
        }
    },

    validateExists: function(value) {
        var empty = value.replace(/\s/g, '');
        if (empty.length == 0) {
            throw("A value is required");
        }
        return value;
    },

    validateNumber: function(value) {
        var precision = parseFloat(this.params.precision);
        if (isNaN(precision)) {
            precision = 2;
        }
        var newValue = value.replace(/[^\d.-]/g, '');
        newValue = parseFloat(newValue);
        if (isNaN(newValue)) {
            newValue = 0;
        }
        var min = parseFloat(this.params.minimum);
        var max = parseFloat(this.params.maximum);
        if (!isNaN(min) && newValue < min) {
            throw(newValue + " is less than minimum " + min);
        }
        if (!isNaN(max) && newValue > max) {
            throw(newValue + " is less than maximum " + max);
        }
        return newValue.toFixed(precision);
    },

    validateInteger: function(value) {
        var newValue = parseInt(value.replace(/[^\d.-]/g, ''));
        if (isNaN(newValue)) {
            newValue = 0;
        }
        var min = parseFloat(this.params.minimum);
        var max = parseFloat(this.params.maximum);
        if (!isNaN(min) && newValue < min) {
            throw(newValue + " is less than minimum " + min);
        }
        if (!isNaN(max) && newValue > max) {
            throw(newValue + " is less than maximum " + max);
        }
        return newValue;
    },

    validateEquals: function(value) {
        for (var name in this.params) {
            var names = document.getElementsByName(name);
            var valid = false;
            for (var i=0,len=names.length; i<len; i++) {
                if (value == names[i].value) {
                    valid = true;
                    break;
                }
            }
            if (!valid) {
                throw("Does not match " + name);
            }
        }
        return value;
    },

    validateOneof: function(value) {
        names = [];
        for (var id in this.params) {
            var el = Dom.get(id);
            if (!el) continue;
            if (el.value) {
                return value;
            }
            names.push(el.name);
        }
        throw('One of ' + names.join(', ') + ' is required');
    },

    _sumParamIds: function() {
        var sum = 0;
        for (var id in this.params) {
            if (id == 'message') continue;
            var el = Dom.get(id);
            if (!el) continue;
            var value = el.value.replace(/[^\d.-]/g, '');
            value = parseFloat(value);
            if (!isNaN(value))
                sum += value; 
        }
        return sum;
    },

    validateLe: function(value) {
        cValue = parseFloat(value.replace(/[^\d.-]/g, ''));
        var max = this._sumParamIds();
        if (cValue > max)
            throw(value + ' must be less than or equal to ' + max);
        return value;
    },

    validateGe: function(value) {
        cValue = parseFloat(value.replace(/[^\d.-]/g, ''));
        var min = this._sumParamIds();
        if (cValue < min)
            throw(this.params.message || value + ' must be greater than or equal to ' + min);
        return value;
    },

    validateOneofname: function(value) {
        names = [];
        for (var name in this.params) {
            var els = document.getElementsByName(name);
            for (var i=0,len=els.length; i<len; i++) {
                value = lts.lang.parseElement(els[i]);
                if (value !== null) { 
                    return value;
                }
            }
            names.push(name);
        }
        if (names.length == 1) {
            throw(names[0] + ' is required');
        } else {
            throw('One of ' + names.join(', ') + ' is required');
        }
    },

    validate: function(value, orig) {
        if (value === null) value = '';
        value = value.replace(/^\s*/, '');
        value = value.replace(/\s*$/, '');
        var type = this.type;
        if (type != 'Exists' && type != 'Oneof' && type != 'Oneofname' && value == '')
            return value;
        var method = this['validate' + type];
        if (method) {
            value = method.call(this, value, orig);
        } else {
            value = this.validateWithGizmo(type, value, orig);
        }
        return value;
    },

    call: function(datatable, value, orig, editor) {
        return this.validate(value, orig);
    }
};


YAHOO.util.Connect.sRequest = function(method, uri, postData) {
    var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
    if (!o)
        return;

    o.conn.open(method, uri, false);

    // Each transaction will automatically include a custom header of
    // "X-Requested-With: XMLHttpRequest" to identify the request as
    // having originated from Connection Manager.
    if(this._use_default_xhr_header){
        if(!this._default_headers['X-Requested-With']){
            this.initHeader('X-Requested-With', this._default_xhr_header, true);
        }
    }

    //If the transaction method is POST and the POST header value is set to true
    //or a custom value, initalize the Content-Type header to this value.
    if((method.toUpperCase() === 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
        this.initHeader('Content-Type', this._default_post_header);
    }

    //Initialize all default and custom HTTP headers,
    if(this._has_default_headers || this._has_http_headers){
        this.setHeader(o);
    }

    o.conn.send(postData || '');
    return o.conn;
};

lts.util.copy = function(o) {
    if(!lang.isValue(o)) {
        return o;
    }

    var copy = {};

    if(o instanceof YAHOO.widget.BaseCellEditor) {
        copy = o;
    } else if(lang.isFunction(o)) {
        copy = o;
    } else if(lang.isArray(o)) {
        var array = [];
        for(var i=0,len=o.length;i<len;i++) {
            array[i] = DT._cloneObject(o[i]);
        }
        copy = array;
    } else if(o instanceof Date) {
        copy = new Date(o.getTime());
    } else if(lang.isObject(o)) {
        for (var x in o){
            if(lang.hasOwnProperty(o, x)) {
                if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
                    copy[x] = lts.util.copy(o[x]);
                }
                else {
                    copy[x] = o[x];
                }
            }
        }
    } else {
        copy = o;
    }

    return copy;
}


