/********
* @version: 0.6.0 - Professional Edition (Coolite Commercial License)
* @author: Coolite Inc. http://www.coolite.com/
* @date: 2008-08-05
* @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
* @license: See license.txt and http://www.coolite.com/license/. 
* @website: http://www.coolite.com/
********/

Ext.namespace('Coolite.Ext');

Coolite.Ext.HttpWriteProxy = function (conn) {
    Coolite.Ext.HttpWriteProxy.superclass.constructor.call(this);
    this.conn = conn;
    this.useAjax = !conn || !conn.events;
    if (conn && conn.handleSaveResponseAsXml) {
        this.handleSaveResponseAsXml = conn.handleSaveResponseAsXml;
    }
};
Ext.extend(Coolite.Ext.HttpWriteProxy, Ext.data.HttpProxy, {
    handleSaveResponseAsXml: false,
    save: function (params, reader, callback, scope, arg) {
        if (this.fireEvent("beforesave", this, params) !== false) {
            var o = {
                params: params || {},
                request: {
                    callback: callback,
                    scope: scope,
                    arg: arg
                },
                reader: reader,
                scope: this,
                callback: this.saveResponse
            };
            if (this.useAjax) {
                Ext.applyIf(o, this.conn);
                if (this.activeRequest) {
                    Ext.Ajax.abort(this.activeRequest);
                }
                this.activeRequest = Ext.Ajax.request(o);
            } else {
                this.conn.reequest(o);
            }
        } else {
            callback.call(scope || this, null, arg, false);
        }
    },

    saveResponse: function (o, success, response) {
        delete this.activeRequest;
        if (! success) {
            this.fireEvent("saveexception", this, o, response, {message: response.statusText});
            o.request.callback.call(o.request.scope, null, o.request.arg, false);
            return;
        }
        var result;
        try {
            if (!this.handleSaveResponseAsXml) {
                var json = response.responseText;
                var responseObj = eval("(" + json + ")");
                result = {
                    success: responseObj.Success,
                    msg: responseObj.Msg,
                    data: responseObj.Data
                }; 
            }
            else {
                var doc = response.responseXML;
                var root = doc.documentElement || doc;
                var q = Ext.DomQuery;
                
                var sv = q.selectValue('Success', root, false);
                success = sv !== false && sv !== 'false';                
                var msg = q.selectValue('Msg', root, '');
                
                result = {success: success, msg: msg};                
            }
        } catch (e) {
            this.fireEvent("saveexception", this, o, response, e);
            o.request.callback.call(o.request.scope, null, o.request.arg, false);
            return;
        }
        this.fireEvent("save", this, o, o.request.arg);
        o.request.callback.call(o.request.scope, result, o.request.arg, true);
    }
});

Coolite.Ext.Store = function (config) {
    Ext.apply(this, config);

    this.deleted = [];
    this.addEvents(
        'beforesave',
        'save',
        'saveexception',
        'commitdone',
        'commitfailed');

    if (this.updateProxy) {
        this.relayEvents(this.updateProxy, ["saveexception"]);
    }

    Coolite.Ext.Store.superclass.constructor.call(this);
};

Ext.extend(Coolite.Ext.Store, Ext.data.GroupingStore, {
    pruneModifiedRecords: true,

    warningOnDirty: true,
    dirtyWarningTitle: 'Uncommitted Changes',
    dirtyWarningText: 'You have uncommitted changes.  Are you sure you want to load/reload data?',

    updateProxy: null,

    // 'none' - no refresh after saving
    // 'always' - always refresh after saving
    // 'auto' - auto refresh. If no new records then refresh doesn't perfom. If new records exists then refresh will be perfom for refresh id fields
    refreshAfterSave: 'Auto',

    useIdConfirmation: false,

    refreshIds: function(newRecordsExists, deletedExists) {
        switch (this.refreshAfterSave) {
            case 'None':
                return;
            case 'Always':
                if (!newRecordsExists && !deletedExists) {
                    Coolite.Ext.Store.superclass.reload.call(this);
                }
                else {
                    this.reload();
                }

                break;
            case 'Auto':
                if (newRecordsExists || deletedExists) {
                    Coolite.Ext.Store.superclass.reload.call(this);
                }
                break;
        }
    },

    reload: function(options) {
        if (this.proxy.refreshByUrl) {
            var opts = options || {};
            var prms = opts.params || {};
            this.callbackReload(true, prms);
        } else {
            Coolite.Ext.Store.superclass.reload.call(this, options);
        }
    },

    load: function(options) {
        var loadData = function(store, options) {
            store.deleted = [];
            store.modified = [];
            return Coolite.Ext.Store.superclass.load.call(store, options);
        };

        if (this.warningOnDirty && this.isDirty() && !this.silentMode) {
            this.silentMode = false;
            Ext.MessageBox.confirm(
                this.dirtyWarningTitle,
                this.dirtyWarningText,
                function(btn, text) {
                    return (btn == 'yes') ? loadData(this, options) : false;
                },
                this
            );
        } else {
            return loadData(this, options);
        }
    },

    save: function(options) {
        if (Ext.isEmpty(this.updateProxy)) {
            this.callbackSave();
            return;
        }

        options = options || {};
        if (this.fireEvent("beforesave", this, options) !== false) {
            var json = this.getChangedData();

            if (json.length > 0) {
                var p = Ext.apply(options.params || {}, { data: '{' + json + '}' });
                this.updateProxy.save(p, this.reader, this.recordsSaved, this, options);
            } else {
                this.fireEvent("commitdone", this, options);
            }
        }
    },

    getChangedData: function() {
        var json = '';
        var d = this.deleted, m = this.modified;
        if (d.length > 0) {
            json += '"Deleted":[';
            for (var i = 0; i < d.length; i++) {
                var obj = {};
                if (this.reader.meta.id) {
                    obj[this.reader.meta.id] = d[i].id;
                }
                var list = Ext.apply(obj, d[i].data);
                json += Ext.util.JSON.encode(list) + ',';
            }
            json = json.substring(0, json.length - 1) + ']';
        }

        var jsonUpdated = '';
        var jsonCreated = '';
        for (var j = 0; j < m.length; j++) {
            var obj2 = {};
            if (this.reader.meta.id) {
                obj2[this.reader.meta.id] = m[j].id;
            }

            var list2 = Ext.apply(obj2, m[j].data);
            if (m[j].newRecord) {
                jsonCreated += Ext.util.JSON.encode(list2) + ',';
            }
            else {
                jsonUpdated += Ext.util.JSON.encode(list2) + ',';
            }

        }

        if (jsonUpdated.length > 0) {
            jsonUpdated = jsonUpdated.substring(0, jsonUpdated.length - 1) + ']';
        }

        if (jsonCreated.length > 0) {
            jsonCreated = jsonCreated.substring(0, jsonCreated.length - 1) + ']';
        }


        if (jsonUpdated.length > 0) {
            if (json.length > 0) {
                json += ",";
            }
            json += '"Updated":[';
            json += jsonUpdated;
        }

        if (jsonCreated.length > 0) {
            if (json.length > 0) {
                json += ",";
            }
            json += '"Created":[';
            json += jsonCreated;
        }

        return json;
    },

    getByDataId: function(id) {
        if (!this.reader.meta.id) {
            return undefined;
        }

        var m = this.modified;
        for (var i = 0; i < m.length; i++) {
            if (m[i].data[this.reader.meta.id] == id) {
                return m[i];
            }
        }

        return undefined;
    },

    recordsSaved: function(o, options, success) {
        if (!o || success === false) {
            if (success !== false) {
                this.fireEvent("save", this, options);
            }
            if (options.callback) {
                options.callback.call(options.scope || this, options, false);
            }
            //this.fireEvent("commitfailed", this);
            return;
        }

        var serverSuccess = o.success;
        var msg = o.msg;

        this.fireEvent("save", this, options);
        if (options.callback) {
            options.callback.call(options.scope || this, options, true);
        }

        var serviceResult = o.data || {};

        var newRecordsExists = false;
        var deletedExists = this.deleted.length > 0;
        var m = this.modified;
        for (var j = 0; j < m.length; j++) {
            if (m[j].newRecord) {
                newRecordsExists = true;
                break;
            }
        }

        if (this.useIdConfirmation) {
            if (Ext.isEmpty(serviceResult.confirm)) {
                this.fireEvent("commitfailed", this, 'The confirmation list is absent');
                return;
            }

            var r = serviceResult.confirm;
            var failCount = 0;
            for (var i = 0; i < r.length; i++) {

                if (r[i].s === false) {
                    failCount++;
                }
                else {
                    var record = this.getById(r[i].oldId) || this.getByDataId(r[i].oldId);
                    if (record) {
                        record.commit();
                        if (record.newRecord || false) {
                            delete record.newRecord;
                            var index = this.data.indexOf(record);
                            this.data.removeAt(index);
                            record.id = r[i].newId || r[i].oldId;
                            this.data.insert(index, record);
                        }
                    } else {
                        var d = this.deleted;
                        for (var i2 = 0; i2 < d.length; i2++) {
                            if (this.reader.meta.id && d[i2].id == r[i].oldId) {
                                this.deleted.splice(i2, 1);
                                failCount--;
                                break;
                            }
                        }
                        failCount++;
                    }
                }
            }

            if (!serverSuccess) {
                this.fireEvent("commitfailed", this, msg);
                return;
            }

            if (failCount > 0) {
                this.fireEvent("commitfailed", this, 'Some records have no success confirmation!');
                return;
            }
            this.modified = [];
            this.deleted = [];
        } else {
            if (!serverSuccess) {
                this.fireEvent("commitfailed", this, msg);
                return;
            }

            this.commitChanges();
        }


        this.fireEvent("commitdone", this, options);

        if (serviceResult.data && serviceResult.data != null) {
            this.proxy.refreshData(serviceResult.data);
        }

        this.refreshIds(newRecordsExists, deletedExists);
    },

    getDeletedRecords: function() {
        return this.deleted;
    },

    remove: function(record) {
        if (!record.newRecord) {
            this.deleted.push(record);
        }
        Coolite.Ext.Store.superclass.remove.call(this, record);
    },

    commitChanges: function() {
        Coolite.Ext.Store.superclass.commitChanges.call(this);
        //var d = this.deleted.slice(0);
        this.deleted = [];
        //for (var i = 0, len = d.length; i < len; i++) {
        //    d[i].commit();
        // }
    },

    rejectChanges: function() {
        Coolite.Ext.Store.superclass.rejectChanges.call(this);
        var d = this.deleted.slice(0);
        this.deleted = [];
        for (var i = 0, len = d.length; i < len; i++) {
            d[i].reject();
        }
    },

    isDirty: function() {
        return (this.deleted.length > 0 || this.modified.length > 0) ? true : false;
    },

    prepareCallback: function(context) {
        var options = { params: {} };
        if (context.fireEvent("beforesave", context, options) !== false) {
            var json = context.getChangedData();
            if (json.length > 0) {
                var p = { data: '{' + json + '}', userParams: options.params };
                return p;
            } else {
                context.fireEvent("commitdone", context, options);
            }
        }
        return null;
    },

    callbackHandler: function(response, result, context, type, action, userParams) {
        try {
            var responseObj = result.serviceResponse;
            result = { success: responseObj.Success, msg: responseObj.Msg, data: responseObj.Data };
        } catch (e) {
            context.fireEvent("saveexception", context, {}, response, e);
            return;
        }
        context.recordsSaved(result, {}, true);
    },

    silentMode: false,

    callbackRefreshHandler: function(response, result, context, type, action, userParams) {
        var p = context.proxy;
        try {
            var responseObj = result.serviceResponse;
            result = { success: responseObj.Success, msg: responseObj.Msg || null, data: responseObj.Data || {} };
        } catch (e) {
            context.fireEvent("loadexception", context, {}, response, e);
            return;
        }

        if (result.success === false) {
            context.fireEvent("loadexception", context, {}, response, { message: result.msg });
            return;
        }

        if (p.refreshData) {
            if (result.data.data && result.data.data != null) {
                p.refreshData(result.data.data);
            }
            else {
                p.refreshData({});
            }
        }

        context.silentMode = true;
        Coolite.Ext.Store.superclass.reload.call(context);
    },

    callbackErrorHandler: function(response, result, context, type, action, userParams) {
        context.fireEvent("saveexception", context, {}, response, { message: result.error || response.statusText });
    },

    callbackRefreshErrorHandler: function(response, result, context, type, action, userParams) {
        context.fireEvent("loadexception", context, {}, response, { message: result.error || response.statusText });
    },

    callbackSave: function() {
        var requestObject = this.prepareCallback(this);
        if (requestObject != null) {
            this.ajaxPostBackConfig.success = this.callbackHandler;
            this.ajaxPostBackConfig.failure = this.callbackErrorHandler;
            this.ajaxPostBackConfig.userParams = requestObject.userParams;

            new Coolite.Ext.Callback().makeCallback(this.ajaxPostBackConfig, null, this, Coolite.Ext.AjaxRequestType.PostBack, 'update', requestObject.data);
        }
    },

    callbackReload: function(dirtyConfirm, reloadOptions) {
        var params = reloadOptions || {};
        var lastOpts = this.lastOptions || {};
        Ext.apply(params, lastOpts.params || {});

        var options = { params: params };

        var reload = function(store, options) {
            if (store.fireEvent("beforeload", store, options) !== false) {
                store.ajaxPostBackConfig.success = store.callbackRefreshHandler;
                store.ajaxPostBackConfig.failure = store.callbackRefreshErrorHandler;
                store.ajaxPostBackConfig.userParams = options.params;

                new Coolite.Ext.Callback().makeCallback(store.ajaxPostBackConfig, null, store, Coolite.Ext.AjaxRequestType.PostBack, 'refresh');
            }
        };

        if (dirtyConfirm && this.isDirty()) {
            Ext.MessageBox.confirm(
                this.dirtyWarningTitle,
                this.dirtyWarningText,
                function(btn, text) {
                    if (btn == 'yes') {
                        reload(this, options);
                    }
                },
                this
            );
        }
        else {
            reload(this, options);
        }
    }
});

//------Save mask-------------
Coolite.Ext.SaveMask = function (el, config) {
    this.el = Ext.get(el);
    Ext.apply(this, config);
    if (this.writeStore) {
        this.writeStore.on('beforesave', this.onBeforeSave, this);
        this.writeStore.on('save', this.onSave, this);
        this.writeStore.on('saveexception', this.onSave, this);
        this.writeStore.on('commitdone', this.onSave, this);
        this.writeStore.on('commitfailed', this.onSave, this);
        this.removeMask = Ext.value(this.removeMask, false);
    }
};

Coolite.Ext.SaveMask.prototype = {
    msg: 'Saving...',
    msgCls: 'x-mask-loading',
    disabled: false,
    
    disable: function () {
        this.disabled = true;
    },
    
    enable: function () {
        this.disabled = false;
    },

    onSave: function () {
        this.el.unmask(this.removeMask);
    },

    onBeforeSave: function () {
        if (!this.disabled) {
            this.el.mask(this.msg, this.msgCls);
        }
    },

    show: function () {
        this.onBeforeSave();
    },

    hide: function () {
        this.onSave();    
    },

    destroy: function () {
        if (this.writeStore) {
            this.writeStore.un('beforesave', this.onBeforeSave, this);
            this.writeStore.un('save', this.onSave, this);
            this.writeStore.un('saveexception', this.onSave, this);
            this.writeStore.un('commitdone', this.onSave, this);
            this.writeStore.un('commitfailed', this.onSave, this);
        }
    }
};

//----------------------------

Coolite.Ext.GridPanel = function(config) {
    Ext.apply(this, config);
    this.addEvents('editcompleted');
    Coolite.Ext.GridPanel.superclass.constructor.call(this);
    this.initSelection();
};

Ext.extend(Coolite.Ext.GridPanel, Ext.grid.EditorGridPanel, {
    doSelection: function() {
        var data = this.selModel.selectedData
        
        if (!Ext.isEmpty(data)) {
            if (this.selModel.select) {
                if (!Ext.isEmpty(data.recordID) && !Ext.isEmpty(data.name)) {
                    var rowIndex = this.store.indexOfId(data.recordID);
                    var colIndex = this.getColumnModel().findColumnIndex(data.name);

                    if (rowIndex > -1 && colIndex > -1) {
                        this.selModel.select(rowIndex, colIndex);
                    }
                }
                else if (!Ext.isEmpty(data.rowIndex) && !Ext.isEmpty(data.colIndex)) {
                    this.selModel.select(data.rowIndex, data.colIndex);
                }
            }
            else if (this.selModel.selectRow && data.length > 0) {
                var records = [];
                var record;
                for (var i = 0; i < data.length; i++) {
                    if (!Ext.isEmpty(data[i].recordID)) {
                        record = this.store.getById(data[i].recordID);
                    }
                    else if (!Ext.isEmpty(data[i].rowIndex)) {
                        record = this.store.getAt(data[i].rowIndex);
                    }

                    if (!Ext.isEmpty(record)) {
                        records.push(record);
                    }
                }
                this.selModel.selectRecords(records);
            }
        }
    },

    updateSelectedRows: function() {
        var selectedRecords = this.selModel.getSelections();
        var records = [];
        for (var i = 0; i < selectedRecords.length; i++) {
            records.push({ RecordID: selectedRecords[i].id, RowIndex: this.store.indexOfId(selectedRecords[i].id) });
        }

        this.hField.dom.value = Ext.encode(records);
    },

    updateCellSelection: function(sm, selection) {
        if (selection == null) {
            this.hField.dom.value = '';
        }
    },

    cellSelect: function(sm, rowIndex, colIndex) {
        var r = this.store.getAt(rowIndex);
        var selection = {
            record: r,
            cell: [rowIndex, colIndex]
        };

        var name = this.getColumnModel().getDataIndex(selection.cell[1]);
        var value = selection.record.get(name);
        var id = selection.record.id || '';

        this.hField.dom.value = Ext.encode({ RecordID: id, Name: name, SubmittedValue: value, RowIndex: selection.cell[0], ColIndex: selection.cell[1] });
    },

    initComponent: function() {
        Coolite.Ext.GridPanel.superclass.initComponent.call(this);
        if (!this.record && this.store) {
            this.record = this.store.recordType;
        }

        if (this.disableSelection) {
            if (this.selModel.select) {
                this.selModel.select = Ext.emptyFn;
            }
            else if (this.selModel.selectRow) {
                this.selModel.selectRow = Ext.emptyFn;
            }
        }

        if (this.store.getCount() > 0) {
            this.on('render', this.doSelection, this, { single: true, delay: 100 });
        }
        else {
            this.store.on("load", this.doSelection, this, { single: true, delay: 100 });
        }
    },

    initSelection: function() {
        if (!Ext.isEmpty(this.selModelHidden)) {
            this.hField = Ext.get(this.selModelHidden);

            if (!Ext.isEmpty(this.hField)) {
                if (this.selModel.select) {
                    this.selModel.on('cellselect', this.cellSelect, this);
                    this.selModel.on('selectionchange', this.updateCellSelection, this);
                }
                else if (this.selModel.selectRow) {
                    this.selModel.on('rowselect', this.updateSelectedRows, this);
                    this.selModel.on('rowdeselect', this.updateSelectedRows, this);
                }
            }
        }
    },

    onRender: function(ct, position) {
        Coolite.Ext.GridPanel.superclass.onRender.call(this, ct, position);

        if (this.menu instanceof Ext.menu.Menu) {
            this.on('contextmenu', this.showContextMenu);
            this.on('rowcontextmenu', this.onRowContextMenu);
        }

        this.relayEvents(this.selModel, ['rowselect', 'rowdeselect']);
        this.relayEvents(this.store, ['commitdone', 'commitfailed']);

        this.keymap = new Ext.KeyMap(this.view.el, {
            key: [13, 35, 36],
            scope: this,
            fn: this.handleKeys
        });
    },

    onEditComplete: function(ed, value, startValue) {
        Coolite.Ext.GridPanel.superclass.onEditComplete.call(this, ed, value, startValue);
        if (!ed.record.dirty && ed.record.firstEdit) {
            this.store.remove(ed.record);
        }
        delete ed.record.firstEdit;
        this.fireEvent('editcompleted', ed, value, startValue);
    },

    onRowContextMenu: function(grid, rowIndex, e) {
        e.stopEvent();
        if (!this.selModel.isSelected(rowIndex)) {
            this.selModel.selectRow(rowIndex);
            this.fireEvent('rowclick', this, rowIndex, e);
        }
        this.showContextMenu(e, rowIndex);
    },

    showContextMenu: function(e, rowIndex) {
        e.stopEvent();
        if (rowIndex === undefined) {
            this.selModel.clearSelections();
        }
        if (this.menu) {
            this.menu.showAt(e.getXY());
        }
    },

    handleKeys: function(key, e) {
        e.stopEvent();
        switch (key) {
            case 13:  // return key
                var rowIndex = this.selModel.last;
                var keyEvent = (e.shiftKey === true) ? 'rowdblclick' : 'rowclick';
                this.fireEvent(keyEvent, this, rowIndex, e);
                break;
            case 35:  // end key
                if (this.store.getCount() > 0) {
                    this.selModel.selectLastRow();
                    this.getView().focusRow(this.store.getCount() - 1);
                }
                break;
            case 36:  // home key
                if (this.store.getCount() > 0) {
                    this.selModel.selectFirstRow();
                    this.getView().focusRow(0);
                }
                break;
        }
    },

    reload: function(options) {
        this.store.reload(options);
    },

    isDirty: function() {
        if (this.store.modified.length > 0 || this.store.deleted.length > 0) {
            return true;
        }
        return false;
    },

    hasSelection: function() {
        return this.selModel.hasSelection();
    },

    addRecord: function(values) {
        var rowIndex = this.store.data.length;
        this.insertRecord(rowIndex, values);
        return rowIndex;
    },

    insertRecord: function(rowIndex, values) {
        var f = this.record.prototype.fields, dv = [];
        for (var i = 0; i < f.length; i++) {
            dv[f.items[i].name] = f.items[i].defaultValue;
        }
        var record = new this.record(dv);
        record.firstEdit = true;
        record.newRecord = true;
        this.stopEditing();
        this.store.insert(rowIndex, record);
        values = values || {};
        for (var v in values) {
            record.set(v, values[v]);
        }
    },

    deleteRecord: function(record) {
        this.store.remove(record);
    },

    deleteSelected: function() {
        var s = this.selModel.getSelections();
        for (var i = 0, len = s.length; i < len; i++) {
            this.deleteRecord(s[i]);
        }
    },

    load: function(options) {
        this.store.load(options);
    },

    save: function(options) {
        this.store.save(options);
    },

    clear: function() {
        this.store.removeAll();
    },

    //--------------------------
    saveMask: false,
    initEvents: function() {
        Coolite.Ext.GridPanel.superclass.initEvents.call(this);

        if (this.saveMask) {
            this.saveMask = new Coolite.Ext.SaveMask(this.bwrap,
                    Ext.apply({ writeStore: this.store }, this.saveMask));
        }
    },

    reconfigure: function(store, colModel) {
        Coolite.Ext.GridPanel.superclass.reconfigure.call(this, arguments);
        if (this.saveMask) {
            this.saveMask.destroy();
            this.saveMask = new Coolite.Ext.SaveMask(this.bwrap,
                    Ext.apply({ writeStore: store }, this.initialConfig.saveMask));
        }
    },

    onDestroy: function() {
        if (this.rendered) {
            if (this.saveMask) {
                this.saveMask.destroy();
            }
        }
        Coolite.Ext.GridPanel.superclass.onDestroy.call(this);
    }
});

Ext.reg('coolitegrid', Coolite.Ext.GridPanel);

Ext.data.PagingMemoryProxy = function (data, isUrl) {
	Ext.data.PagingMemoryProxy.superclass.constructor.call(this);
	this.data = data;
	this.isUrl = isUrl || false;		
	this.isNeedRefresh = this.isUrl;
	this.url = this.isUrl ? data : '';	
};

Ext.extend(Ext.data.PagingMemoryProxy, Ext.data.MemoryProxy, {

    refreshData: function (data, store) {
        if (this.isUrl === true) {
            this.isNeedRefresh = true;
        } else {
            if (data && data != null) {
                this.data = data;
            } else {
                store.callbackReload(true);
            }
        }
    },

    refreshByUrl: function (params, reader, callback, scope, arg) {
        var o = {
            method: 'GET',
            request: {
                callback: callback,
                scope: scope,
                arg: arg,
                params: params || {}
            },
            reader: reader,
            url: this.url,
            callback: this.loadResponse,
            scope: this
        };

        if (this.activeRequest) {
            Ext.Ajax.abort(this.activeRequest);
        }
        
        this.activeRequest = Ext.Ajax.request(o);
    },

    loadResponse: function (o, success, response) {
        delete this.activeRequest;
        if (!success) {
            this.fireEvent("loadexception", this, o, response);
            o.request.callback.call(o.request.scope, null, o.request.arg, false);
            return;
        }

        try {
            if (o.reader.getJsonAccessor) {
                this.data = response.responseText;
            } else {
                this.data = response.responseXML;
            }

            if (!this.data) {
                throw { message: "The data doesn't available" };
            }
        } catch (e) {
            this.fireEvent("loadexception", this, o, response, e);
            o.request.callback.call(o.request.scope, null, o.request.arg, false);
            return;
        }

        this.isNeedRefresh = false;
        this.load(o.request.params, o.reader, o.request.callback, o.request.scope, o.request.arg);
    },

    load: function (params, reader, callback, scope, arg) {

        this.fireEvent("beforeload", this, params);
        params = params || {};

        if (this.isNeedRefresh === true) {
            this.refreshByUrl(params, reader, callback, scope, arg);
            return;
        }

        var result;       
        try {
            result = reader.readRecords(this.data);
        } catch (e) {
            this.fireEvent("loadexception", this, arg, null, e);
            callback.call(scope, null, arg, false);
            return;
        }

        if (params.sort !== undefined) {
            var dir = String(params.dir).toUpperCase() == "DESC" ? -1 : 1;

            var fn = function (v1, v2) {
                return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
            };

            result.records.sort(function (a, b) {
                var v = 0;
                if (typeof (a) == "object") {
                    v = fn(a.data[params.sort], b.data[params.sort]) * dir;
                } else {
                    v = fn(a, b) * dir;
                }
                if (v === 0) {
                    v = (a.index < b.index ? -1 : 1);
                }
                return v;
            });
        }

        if (params.start !== undefined && params.limit !== undefined) {
            result.records = result.records.slice(params.start, params.start + params.limit);
        }

        callback.call(scope, result, arg, true);
    }
});

Coolite.Ext.initRefreshPagingToolbar = function (grid) {
    var bBar = grid.getBottomToolbar();
    for (i = 0; i < bBar.items.items.length; ++i) {
        var item = bBar.items.items[i];
        if (item.iconCls == "x-tbar-loading" && item.tooltip == bBar.refreshText) {
            item.setHandler(function () {
                if (grid.getStore().proxy.refreshData) {
                    grid.getStore().proxy.refreshData(null, grid.getStore());
                }
                if (grid.getStore().proxy.isUrl) {
                    item.initialConfig.handler();
                }
            });
            return;
        }
    }
};

Coolite.Ext.PropertyGrid = function () {
    Coolite.Ext.PropertyGrid.superclass.constructor.call(this);	
	this.addEvents('beforesave', 'save', 'saveexception');
};

Coolite.Ext.PropertyGrid = Ext.extend(Ext.grid.PropertyGrid, {

    editable: true,

    initComponent: function () {
        Coolite.Ext.PropertyGrid.superclass.initComponent.call(this);
        if (!this.editable) {
            this.on('beforeedit', function (e) {
                return false;
            });
        }
    },

    callbackHandler: function (response, result, context, type, action, userParams) {
        try {
            var responseObj = result.serviceResponse;
            result = { success: responseObj.Success, msg: responseObj.Msg || null };
        } catch (e) {
            context.fireEvent("saveexception", context, response, e);
            return;
        }

        if (result.success === false) {
            context.fireEvent("saveexception", context, response, { message: result.msg });
            return;
        }

        context.fireEvent("save", context, response);
    },

    callbackErrorHandler: function (response, result, context, type, action, userParams) {
        context.fireEvent("saveexception", context, response, { message: result.error || response.statusText });
    },

    save: function () {
        var options = { params: {} };
        if (this.fireEvent("beforesave", this, options) !== false) {
            this.ajaxPostBackConfig.success = this.callbackHandler;
            this.ajaxPostBackConfig.failure = this.callbackErrorHandler;
            this.ajaxPostBackConfig.userParams = options.params;

            new Coolite.Ext.Callback().makeCallback(this.ajaxPostBackConfig, null, this, Coolite.Ext.AjaxRequestType.PostBack, 'update', Ext.encode(this.getSource()));
        }
    }
});

Ext.reg('coolitepropertygrid', Coolite.Ext.PropertyGrid);

Coolite.Ext.DataSourceProxy = function () {
    Coolite.Ext.DataSourceProxy.superclass.constructor.call(this);
};

Ext.extend(Coolite.Ext.DataSourceProxy, Ext.data.DataProxy, {

    ro: {},

    load: function (params, reader, callback, scope, arg) {
        if (this.fireEvent("beforeload", this, params) !== false) {
            this.ro = {
                params: params || {},
                request: {
                    callback: callback,
                    scope: scope,
                    arg: arg
                },
                reader: reader,
                callback: this.loadResponse,
                scope: this
            };

            scope.ajaxPostBackConfig.success = this.successHandler;
            scope.ajaxPostBackConfig.failure = this.errorHandler;
            scope.ajaxPostBackConfig.userParams = params;

            new Coolite.Ext.Callback().makeCallback(scope.ajaxPostBackConfig, null, scope, Coolite.Ext.AjaxRequestType.PostBack, 'refresh');

        } else {
            callback.call(scope || this, null, arg, false);
        }
    },

    successHandler: function (response, result, context, type, action, userParams) {
        var p = context.proxy;

        try {
            var responseObj = result.serviceResponse;
            result = { success: responseObj.Success, msg: responseObj.Msg || null, data: responseObj.Data || {} };
        } catch (e) {
            context.fireEvent("loadexception", context, {}, response, e);
            p.ro.request.callback.call(p.ro.request.scope, null, p.ro.request.arg, false);
            return;
        }

        if (result.success === false) {
            context.fireEvent("loadexception", context, {}, response, { message: result.msg });
            p.ro.request.callback.call(p.ro.request.scope, null, p.ro.request.arg, false);
            return;
        }

        try {
            var meta = p.ro.reader.meta;
            if (result.data.totalCount && result.data.totalCount > 0) {
                meta.totalProperty = 'totalCount';
            }
            else {
                if (meta.totalProperty) {
                    delete meta.totalProperty;
                }
            }

            if (Ext.isEmpty(meta.root)) {
                meta.root = 'data';
            }

            if (Ext.isEmpty(result.data[meta.root])) {
                result.data[meta.root] = [];
            }

            result = p.ro.reader.readRecords(result.data);

        } catch (ex) {
            p.fireEvent("loadexception", p, p.ro, response, ex);
            p.ro.request.callback.call(p.ro.request.scope, null, p.ro.request.arg, false);
            return;
        }
        p.fireEvent("load", p, p.ro, p.ro.request.arg);
        p.ro.request.callback.call(p.ro.request.scope, result, p.ro.request.arg, true);

    },

    errorHandler: function (response, result, context, type, action, userParams) {
        var p = context.proxy;
        p.fireEvent("loadexception", p, p.ro, response);
        p.ro.request.callback.call(p.ro.request.scope, null, p.ro.request.arg, false);
    }
});

Ext.grid.RowExpander = function (config) {
    Ext.apply(this, config);

    this.addEvents({
        beforeexpand : true,
        expand: true,
        beforecollapse: true,
        collapse: true
    });

    Ext.grid.RowExpander.superclass.constructor.call(this);

    if (this.tpl) {
        if (typeof this.tpl == 'string') {
            this.tpl = new Ext.Template(this.tpl);
        }
        this.tpl.compile();
    }

    this.state = {};
    this.bodyContent = {};
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
    header: "",
    width: 20,
    sortable: false,
    fixed: true,
    menuDisabled: true,
    dataIndex: '',
    id: 'expander',
    lazyRender: true,
    enableCaching: true,
    collapsed: true,

    getRowClass: function (record, rowIndex, p, ds) {
        p.cols = p.cols - 1;
        var content = this.bodyContent[record.id];
        if (!content && !this.lazyRender) {
            content = this.getBodyContent(record, rowIndex);
        }
        if (content) {
            p.body = content;
        }

        if (this.state[record.id] === undefined) {

            if (this.collapsed === false) {
                this.state[record.id] = true;
                if (this.tpl && this.lazyRender) {
                    p.body = this.getBodyContent(record, rowIndex);
                }         
                return 'x-grid3-row-expanded';
            }
            
            return 'x-grid3-row-collapsed';
        }

        return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
    },

    init: function (grid) {
        this.grid = grid;

        var view = grid.getView();
        view.getRowClass = this.getRowClass.createDelegate(this);

        view.enableRowBody = true;

        grid.on('render', function () {
            view.mainBody.on('mousedown', this.onMouseDown, this);
        }, this);
    },

    getBodyContent: function (record, index) {
        if (!this.enableCaching) {
            return this.tpl.apply(record.data);
        }
        var content = this.bodyContent[record.id];
        if (!content) {
            content = this.tpl.apply(record.data);
            this.bodyContent[record.id] = content;
        }
        return content;
    },

    onMouseDown: function (e, t) {
        if (t.className == 'x-grid3-row-expander') {
            e.stopEvent();
            var row = e.getTarget('.x-grid3-row');
            this.toggleRow(row);
        }
    },

    renderer: function (v, p, record) {
        p.cellAttr = 'rowspan="2"';
        return '<div class="x-grid3-row-expander">&#160;</div>';
    },

    beforeExpand: function (record, body, rowIndex) {
        if (this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false) {
            if (this.tpl && this.lazyRender) {
                body.innerHTML = this.getBodyContent(record, rowIndex);
            }
            return true;
        } else {
            return false;
        }
    },

    toggleRow: function (row) {
        if (typeof row == 'number') {
            row = this.grid.view.getRow(row);
        }
        this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
    },

    expandRow: function (row) {
        if (typeof row == 'number') {
            row = this.grid.view.getRow(row);
        }
        var record = this.grid.store.getAt(row.rowIndex);
        var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
        if (this.beforeExpand(record, body, row.rowIndex)) {
            this.state[record.id] = true;
            Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
            this.fireEvent('expand', this, record, body, row.rowIndex);
        }
    },

    collapseRow: function (row) {
        if (typeof row == 'number') {
            row = this.grid.view.getRow(row);
        }
        var record = this.grid.store.getAt(row.rowIndex);
        var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
        if (this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false) {
            this.state[record.id] = false;
            Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
            this.fireEvent('collapse', this, record, body, row.rowIndex);
        }
    }
});

Ext.grid.CheckColumn = function (config) {
    Ext.apply(this, config);
    if (!this.id) {
        this.id = Ext.id();
    }
    this.renderer = this.renderer.createDelegate(this);
};

Ext.grid.CheckColumn.prototype ={
    init : function (grid) {
        this.grid = grid;
        this.grid.on('render', function () {
            var view = this.grid.getView();
            view.mainBody.on('mousedown', this.onMouseDown, this);
        }, this);
    },

    onMouseDown : function (e, t) {
        if (t.className && t.className.indexOf('x-grid3-cc-' + this.id) != -1) {
            e.stopEvent();
            var record = this.grid.store.getAt(this.grid.getView().findRowIndex(t));
            record.set(this.dataIndex, !record.data[this.dataIndex]);
        }
    },

    renderer : function (v, p, record) {
        p.css += ' x-grid3-check-col-td'; 
        return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
    }
};

Ext.grid.TableGrid = function(config) {
    config = config || {};
    Ext.apply(this, config);
    var cf = config.fields || [], ch = config.columns || [];

    if (config.table.isComposite) {
        if (config.table.elements.length > 0) {
            table = Ext.get(config.table.elements[0]);
        }
    }
    else {
        table = Ext.get(config.table);
    }

    var ct = table.insertSibling();

    var fields = [], cols = [];
    var headers = table.query("thead th");
    for (var i = 0, h; h = headers[i]; i++) {
        var text = h.innerHTML;
        var name = 'tcol-' + i;

        fields.push(Ext.applyIf(cf[i] || {}, {
            name: name,
            mapping: 'td:nth(' + (i + 1) + ')/@innerHTML'
        }));

        cols.push(Ext.applyIf(ch[i] || {}, {
            'header': text,
            'dataIndex': name,
            'width': h.offsetWidth,
            'tooltip': h.title,
            'sortable': true
        }));
    }

    var ds = new Ext.data.Store({
        reader: new Ext.data.XmlReader({
            record: 'tbody tr'
        }, fields)
    });

    ds.loadData(table.dom);

    var cm = new Ext.grid.ColumnModel(cols);

    if (config.width || config.height) {
        ct.setSize(config.width || 'auto', config.height || 'auto');
    } else {
        ct.setWidth(table.getWidth());
    }

    if (config.remove !== false) {
        table.remove();
    }

    Ext.applyIf(this, {
        'ds': ds,
        'cm': cm,
        'sm': new Ext.grid.RowSelectionModel(),
        autoHeight: true,
        autoWidth: false
    });
    Ext.grid.TableGrid.superclass.constructor.call(this, ct, {});
};

Ext.extend(Ext.grid.TableGrid, Ext.grid.GridPanel);
Ext.reg("tablegrid", Ext.grid.TableGrid);

if(typeof Sys!=="undefined"){Sys.Application.notifyScriptLoaded();}