Ext.BLANK_IMAGE_URL = 'ext/resources/images/default/s.gif';

//make components only save state when told to by stateful = true

Ext.override(Ext.Component, {
    saveState : function(){
        if(Ext.state.Manager && this.stateful !== false){
            var state = this.getState();
            if(this.fireEvent('beforestatesave', this, state) !== false){
                Ext.state.Manager.set(this.stateId || this.id, state);
                this.fireEvent('statesave', this, state);
            }
        }
    },
 
    stateful : false
 
});
                 
//decodeValue erroneously decodes empty arrays and objects
//empty arrays return as [undefined]
//empty objects (untested) probably fail or return as {undefined: undefined}
//either way, a simple check for empty value portions alleviates this issue
Ext.override(Ext.state.Provider, {
    decodeValue : function(cookie){
        var re = /^(a|n|d|b|s|o)\:(.*)$/;
        var matches = re.exec(unescape(cookie));
        if(!matches || !matches[1]) return; // non state cookie
        var type = matches[1];
        var v = matches[2];
        switch(type){
            case "n":
                return parseFloat(v);
            case "d":
                return new Date(Date.parse(v));
            case "b":
                return (v == "1");
            case "a":
                var all = [];
                if (v) {
                    var values = v.split("^");
                    for(var i = 0, len = values.length; i < len; i++){
                        all.push(this.decodeValue(values[i]));
                    }
                }
                return all;
           case "o":
                var all = {};
                if (v) {
                    var values = v.split("^");
                    for(var i = 0, len = values.length; i < len; i++){
                        var kv = values[i].split("=");
                        all[kv[0]] = this.decodeValue(kv[1]);
                    }
                }
                return all;
           default:
                return v;
        }
    }
});


/*******************************************************************************
 * Custom Time field for EditableGrids
 */ 
Ext.ns("Ext.ux");
Ext.ux.timeColumn = function(increment){
   
   var times = [], lastDate = new Date();
   lastDate.setHours(0,0,0);
   while(lastDate.getDayOfYear() == new Date().getDayOfYear()) {            
       times.push([lastDate.format('h:i A')]);
       lastDate = lastDate.add(Date.MINUTE, increment);
   }
   
   Ext.ux.timeColumn.superclass.constructor.call(this, {
       autoCreate: {tag: "input", type: "text", size: "8", autocomplete: "off"}, 
       store: new Ext.data.SimpleStore({ fields : ['times'], data : times }),
       displayField:'times',
       valueField:'times',
       typeAhead: true,
       mode: 'local',
       triggerAction: 'all',
       emptyText:'Time...',
       selectOnFocus:true,
       validator: function(value){ return isNaN(Date.parse('1/1/2007 ' + value)) ? 'Not a valid time' : true; },
       maskRe: /a|p|A|P|:|s|d/,
       value: new Date().format('h:i A'),
       listeners : {
         change : function(){ 
              var time = Date.parse('1/1/2007 ' + this.getValue()); 
              if(!isNaN(time)) this.setRawValue((new Date(time)).format('h:i A')); 
         }
       }
   });
}
Ext.extend(Ext.ux.timeColumn, Ext.form.ComboBox);

function timeRenderer(combo){
	return function(value){
	  if (!value) {
      return '';
    }
		var record = combo.findRecord(combo.valueField || combo.displayField, value);
		var time = (record ? record.get(combo.displayField) : (combo.valueNotFoundText?combo.valueNotFoundText:value));
		if(isNaN(time)) time = Date.parse('1/1/2007 ' + time); 
		return (new Date(time)).format('h:i A');
	}
}

/*******************************************
 * FROM: http://extjs.com/forum/showthread.php?t=17580
 */ 
Ext.grid.CheckColumn = function(config) {
	if(!this.id){
		this.id = Ext.id();
	}
	
	this.addEvents({
		click: true
	});
	
	Ext.grid.CheckColumn.superclass.constructor.call(this);

	Ext.apply(this, config, {
		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, a, nextIndex, doClick){
			if(nextIndex != undefined || t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1) {
			    if (nextIndex == undefined){
             e.stopEvent();
             var index = this.grid.getView().findRowIndex(t);
             var record = this.grid.store.getAt(index); 
             doClick = !record.data[this.dataIndex];
             if (doClick) {
             Ext.get(t).removeClass('x-grid3-check-col');
             Ext.get(t).addClass('x-grid3-check-col-on');
             } else {
             Ext.get(t).removeClass('x-grid3-check-col-on');
             Ext.get(t).addClass('x-grid3-check-col');
             }
               
             //record.set(this.dataIndex, doClick);
             if (this.grid.view.viewType != undefined && this.grid.view.viewType == 'EditorTreeGrid') {
                  record.data[this.dataIndex] = doClick;
             } else {
                  record.set(this.dataIndex, doClick);
             }
             //this.grid.getView().refreshRow(record);     
             //this.fireEvent('click', this, e, record);
          } else {
            index = nextIndex;
          }
              record = this.grid.store.getAt(index);
              view = this.grid.getView();
             if (record.node != undefined) {
                for (i=0; i < record.node.childNodes.length; i++){
                  index++;
                  record = this.grid.store.getAt(index);
                  if (this.grid.view.viewType != undefined && this.grid.view.viewType == 'EditorTreeGrid') {
                     record.data[this.dataIndex] = doClick;
                  } else {
                     record.set(this.dataIndex, doClick);
                  }
                  col = view.cm.findColumnIndex(this.dataIndex);
                  el = Ext.get(view.getCell(index,col)).last().last();
                   if (doClick) {
                    el.removeClass('x-grid3-check-col');
                    el.addClass('x-grid3-check-col-on');
                   } else {
                    el.removeClass('x-grid3-check-col-on');
                    el.addClass('x-grid3-check-col');
                   }                  
                  
                  //this.grid.getView().refreshRow(record);
                  this.onMouseDown(null, null, null, index, doClick);
                }
                
             }       
			} 
		},

		renderer : function(v, p, record){
        p.css += ' x-grid3-check-col-td';
        return '<div style="margin:0 !important; padding:0 !important;" class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
        

		}
	});

	this.renderer = this.renderer.createDelegate(this);
};
Ext.extend(Ext.grid.CheckColumn, Ext.util.Observable);




Ext.grid.RadioColumn = function(config) {
	if(!this.id){
		this.id = Ext.id();
	}
	
	this.addEvents({
		click: true
	});
	
	Ext.grid.RadioColumn.superclass.constructor.call(this);

	Ext.apply(this, config, {
		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){
         e.stopEvent();
         var field = this.dataIndex;
         var index = this.grid.getView().findRowIndex(t);
         var record = this.grid.store.getAt(index);
         if (!record.data[field]) {
            var i=0;
            Ext.each(this.grid.store.data.items, function(rec) {
               if (i != index) {
                  record.data[this.dataIndex] = false;
               }
               i++;
            }, this);
             record.data[this.dataIndex] = true;
             Ext.get(t).removeClass('x-grid3-radio-col');
             Ext.get(t).addClass('x-grid3-radio-col-on');  
            this.fireEvent('click', this, e, record);
         }
		},

		renderer : function(value, metadata, record, rowIndex, colIndex, store){
         metadata.css += ' radioColCell'
         return '<input type="radio" value="value" class="x-grid3-radio-col'+(value?'-on':'')+' x-grid3-rb-'+this.id+'" name="rb-'+this.id+'-'+colIndex+'" ' + (value ? ' checked="checked"' : '') + '>';
		}
	});
	

	this.renderer = this.renderer.createDelegate(this);
};
Ext.extend(Ext.grid.RadioColumn, Ext.util.Observable);




/*******************************************************************************
 * Support for Printing specified Ext.Element's with specified parameters
 * 
 * @version 0.4
 * @author nerdydude81
 */
Ext.override(Ext.Element, {
    // @cfg {string} css A string containing a css style text.
    css: ''
    // @cfg {string} printCSS The file path of a CSS file for printout.
    ,printCSS: "css/print.css"
    //// @cfg {string} addHeader Additional HTML text to add a the top.
    //, addHeader: { tpl: '<h1><IMG src="http://www.schooldynamics.net/images/icon_infodirect_logo.png" /> INFO<span>DIRECT</span></h1>\n<h3>{schoolname}</h3>', store: schoolnamestore}
    //// @cfg {string} addFooter Additional HTML text to add a the bottom.
    //, addFooter: ''//<div id="footer"><h4><em>Powered by</em> InfoDirect</h4><div><h6>Copyright &copy; {year} School Dynamics<h6></div></div>
    // @cfg {Boolean} printStyle Copy the style attribute of this element to the print iframe.
    , printStyle: true
    // @property {string} printTitle Page Title for printout. 
    , printTitle: document.title
    // @cfg {Boolean} printLandscape Set the default orientation for this print job.
    , printLandscape: false
    
    /* handles a messagebox for reports designed for landscape view before printing the report.
     * @param config {object} (optional)
     */
    , print: function(config) {
         Ext.apply(this, config);
         
         var el = this; // gives the alert's callback function access to the current "this" variable
         
         if (this.printLandscape == true) {
            Ext.MessageBox.show({
               title: "School Dynamics", 
               msg: "This report prints best in Landscape view.",
               buttons: Ext.MessageBox.OK,
               fn: function(btn) {
                  el._print(config);
               }
            });
         } else {
            this._print(config);
         }
    }
    
    /* Prints this element.
     * @param config {object} (optional)
     */
    , _print: function(config) {
        Ext.apply(this, config);
        
        var el = Ext.get(this.id).dom;
            
        var c = document.getElementById('printcontainer');
        var iFrame = document.getElementById('printframe');
        
         var oRequest = new XMLHttpRequest();
         if (typeof oRequest == 'undefined') oRequest = new ActiveXObject("Msxml2.XMLHTTP"); //IE fix
         if (typeof oRequest == 'undefined') oRequest = new ActiveXObject("Microsoft.XMLHTTP"); //IE fix
         if (window.location.href.indexOf("schooldynamics")==-1) {
            oRequest.open("GET","http://localhost/v3/print.html",false);
         } else {
            oRequest.open("GET","https://www.schooldynamics.net/v3/print.html",false);
         }
         oRequest.setRequestHeader("User-Agent",navigator.userAgent);
         oRequest.send(null);
         if (oRequest.status!=200) {
            Ext.Msg.alert('Status', 'Error printing document!');
            return;
         }
         
        var strTemplate = oRequest.responseText;
        var strLinkTpl = '<link rel="stylesheet" type="text/css" href="{0}" media="print" />';
        var strAttr = '';
        var strFormat;
        var strHTML;
        
        
        //Get rid of the old crap so we don't copy it to our iframe
        //if (iFrame!=undefined)if (iFrame != null) c.removeChild(iFrame);
        //if (c != null) el.removeChild(c);
        
        //Copy attributes from this element.
        for (var i = 0; i < el.attributes.length; i++) {
            if (Ext.isEmpty(el.attributes[i].value) || el.attributes[i].value.toLowerCase() != 'null') {
                strFormat = Ext.isEmpty(el.attributes[i].value)? '{0}="true" ': '{0}="{1}" ';
                if (this.printStyle? this.printStyle: el.attributes[i].name.toLowerCase() != 'style') strAttr += String.format(strFormat, el.attributes[i].name, el.attributes[i].value);
            }
        }
        
        var strLink ='';
        if(this.printCSS){
            if(!Ext.isArray(this.printCSS))this.printCSS = [this.printCSS];
            for(var i=0; i<this.printCSS.length; i++) strLink += String.format(strLinkTpl, this.printCSS[i]);
        }

        strHTML = strTemplate;
        strHTML = strHTML.replace("{css}",strLink);
        strHTML = strHTML.replace("{title}",this.printTitle);
        strHTML = strHTML.replace("{title}",this.printTitle);//IE Fix
        strHTML = strHTML.replace("{schoolname}",SchoolDynamics.SchoolName);
        strHTML = strHTML.replace("{onload}",(Ext.isIE? 'document.execCommand(\'print\');': 'window.print();'));
        strHTML = strHTML.replace("{customizations}",strAttr);
        strHTML = strHTML.replace("{content}",(el.innerHTML?el.innerHTML:(el.value?el.value:"")));
        strHTML = strHTML.replace(' disabled="false" ',''); //IE fix
        
        //I coun't get FF to print a hidden iframe, so I encapsulated it in a hidden div.
        c = document.createElement('div');
        c.setAttribute('style','width:0px;height:0px;' + (Ext.isSafari? 'display:none;': 'visibility:hidden;'));
        c.setAttribute('id', 'printcontainer');
        el.appendChild(c);
        
        //IE doesn't like style attributes anymore?
        if (Ext.isIE) c.style.display = 'none';
        
        iFrame = document.createElement('iframe');
        iFrame.setAttribute('id', 'printframe');
        iFrame.setAttribute('name', 'printframe');
        c.appendChild(iFrame);
        
        //Write our new document to the iframe
        iFrame.contentWindow.document.open();        
        iFrame.contentWindow.document.write(strHTML);
        iFrame.contentWindow.document.close();
        //console.log(strHTML);
    }
});

Ext.override(Ext.Component, {
    printEl: function(config) {
        this.el.print(Ext.isEmpty(config)? this.initialConfig: config);
    }
    , printBody: function(config) {
        this.body.print(Ext.isEmpty(config)? this.initialConfig: config);
    }
}); 
Ext.override(Ext.Panel, {
    printEl: function(config) {
        this.el.print(Ext.isEmpty(config)? this.initialConfig: config);
    }
    , printBody: function(config) {
        this.body.print(Ext.isEmpty(config)? this.initialConfig: config);
    }
});

/******************************************************************************/
/*
Ext.override(Ext.form.DateField, {
    hide: function(datefield) {
         this.fireEvent('change',datefield, datefield.getValue() );
         this.fireEvent('hide',datefield );
    }
});
*/
Ext.form.DateField.prototype.onMenuHide = function(){
   this.fireEvent('change',this, this.getValue() );
   this.fireEvent('blur',this);
};


//**************************************************
// add observable properties to Ext.data.Connection
Ext.util.Observable.observeClass = function(c) {
   Ext.apply(c, new Ext.util.Observable());
   c.prototype.fireEvent = function() { return (c.fireEvent.apply(c, arguments) !== false) && (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false); };
};
Ext.override(Ext.util.Observable, {
   addListener: Ext.util.Observable.prototype.addListener.createInterceptor(function() { if (!this.events) { this.events = {}; } })
});
Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
Ext.util.Observable.observeClass(Ext.data.Connection);
//**************************************************
var loadingstarted=0, loadingfinished=0, loadingprogress=0;
function updateProgress() {
   loadingfinished++;
   loadingprogress = loadingfinished/loadingstarted;
   Ext.MessageBox.updateProgress((isNaN(loadingprogress)?1:loadingprogress),  (isNaN(Math.round(100*loadingprogress))?'100':Math.round(100*loadingprogress))+'% completed');
   //Ext.get(document.body).mask('<h1>Loading...</h1><p>'+(isNaN(Math.round(100*loadingprogress))?'100':Math.round(100*loadingprogress))+'% complete</p>');
   if(loadingstarted == loadingfinished){
      loadingstarted=0, loadingfinished=0;
         //Ext.MessageBox.hide();
         //Ext.get(document.body).unmask();
   }
}

Ext.data.Connection.on('beforerequest', function(con, options) {
   loadingstarted++;
   //Ext.MessageBox.show({ title: 'Please Wait', progressText: 'Loading...', width:300, progress:true, closable:false, modal: false });
   //Ext.get(document.body).mask('<h1>Loading...</h1><p>0% complete</p>');
});
Ext.data.Connection.on('requestcomplete', function(con, response, options) {
   updateProgress();
   
   if ((response.responseText.match("<title>School Dynamics Login</title>"))!=null) {
      //redirect to login
      if (window.location.href.match("localhost")!=null) {
         window.location = "http://localhost/v2/login.php?schoolid="+SchoolDynamics.schoolid;
      } else {
         window.location = "https://www.schooldynamics.net/"+SchoolDynamics.schoolid+"/";
      }
   }
});
Ext.lib.Ajax.request = Ext.lib.Ajax.request.createInterceptor(function(method, uri, cb, data, options){
    //run universal code on "failure"
    cb.failure = cb.failure.createInterceptor(function(response){ updateProgress(); });
});


Ext.link = Ext.extend(Ext.Button, {
  constructor: function(config) {   
      this.cls = 'x-link';             
      Ext.link.superclass.constructor.apply(this, arguments);                 
  }
});
Ext.ComponentMgr.registerType('link', Ext.link);


//if a combo's store isn't loaded when combo.setValue() is called, load the store and select the value
Ext.override(Ext.form.ComboBox, {
   minChars : 1,
   allowBlank: true,    
   valueNotFoundText:'', 
   typeAhead: true,
   triggerAction: 'all',
   setValue : function(v){
        var oldValue = this.value;
        var text = v;
        if(this.valueField){
            if(this.mode == 'remote' && !Ext.isDefined(this.store.totalLength)){
                this.store.on('load', this.setValue.createDelegate(this, arguments), null, {single: true});
                if(this.store.lastOptions === null){
                    var params;
                    if(this.valueParam){
                        params = {};
                        params[this.valueParam] = v;
                    }else{
                        var q = this.allQuery;
                        this.lastQuery = q;
                        this.store.setBaseParam(this.queryParam, q);
                        params = this.getParams(q);
                    }
                    this.store.load({params: params});
                }
                this.mode = 'local';
                this.firstLoad = true;
                return;
            }
            var r = this.findRecord(this.valueField, v);
            if(r){
                text = r.data[this.displayField];
            }else if(this.valueNotFoundText !== undefined){
                text = this.valueNotFoundText;
            }
        }
        this.lastSelectionText = text;
        if(this.hiddenField){
            this.hiddenField.value = v;
        }
        Ext.form.ComboBox.superclass.setValue.call(this, text);
        this.value = v;
        
        if (this.firstLoad != undefined && this.firstLoad){
          this.fireEvent('select', this, r, this.selectedIndex); //fire the select event, so there is an event to handle the value when it is first set
          this.firstLoad = false;
        }
        
        if (this.value != oldValue) this.fireEvent('setValue', this, this.value, oldValue);
        
        return this;
    },
    onViewClick: function() {
        var index = this.view.getSelectedIndexes()[0],
        s = this.store,
        r = s.getAt(index);
        if(r){
            this.onSelect(r, index);
        }else {
            this.collapse();
        }
    },
    focus: function(selectText, delay){
        if(delay){
            this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]);
            return;
        }
        if(this.rendered){
            this.el.focus();
            if(selectText === true){
                this.el.dom.select();
            }
        }
        if (this.getValue() == '') {
          this.expand();
        }
        return this;    
    },
    onTriggerClick : function(){
        if(this.disabled){
            return;
        }
        if(this.isExpanded()){
            this.collapse();
            this.el.focus();
        }else {
            this.onFocus({});
            if(this.triggerAction == 'all') {
               var tempmode=undefined;
               if (!(this.store.data.length<1)) {
                  tempmode = this.mode;
                  this.mode='local';
               }
               this.doQuery(this.allQuery, true );
               if (tempmode!=undefined) {
                  this.mode=tempmode;
               }
            } else {
                this.doQuery(this.getRawValue());
            }
            this.el.focus();
        }
    },
    initEvents : function(){
        Ext.form.ComboBox.superclass.initEvents.call(this);
        this.keyNav = new Ext.KeyNav(this.el, {
            "up" : function(e){
                this.inKeyMode = true;
                this.selectPrev();
            },
            "down" : function(e){
                if(!this.isExpanded()){
                    this.onTriggerClick();
                }else{
                    this.inKeyMode = true;
                    this.selectNext();
                }
            },
            "enter" : function(e){
                this.onViewClick();
            },
            "esc" : function(e){
                this.collapse();
            },
            "tab" : function(e){
                this.collapse();
                return true;
            },
            scope : this,
            doRelay : function(e, h, hname){
                if(hname == 'down' || this.scope.isExpanded()){
                    var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
                    if(!Ext.isIE && Ext.EventManager.useKeydown){
                        this.scope.fireKey(e);
                    }
                    return relay;
                }
                return true;
            },

            forceKeyDown : true,
            defaultEventAction: 'stopEvent'
        });
        this.queryDelay = Math.max(this.queryDelay || 10,
                this.mode == 'local' ? 10 : 250);
        this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
        if(this.typeAhead){
            this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
        }
        if(!this.enableKeyEvents){
            this.mon(this.el, 'keyup', this.onKeyUp, this);
        }
        if (this.store != undefined) {               
          if (this.store.combos == undefined) {
            this.store.combos = [];
          }
          this.store.combos.push(this);
        }
    }    
}); 


Ext.util.Observable.observeClass(Ext.data.Store);
Ext.data.Store.on('load', function(store, records) {
   if (records.length > 0 && store.combos != undefined) {
     for (i = 0; i < store.combos.length; i++){
        store.combos[i].mode = 'local';
     }
   }
});

Ext.override(Ext.data.Store,{
   copyData: function(destStore) {
      var data = new Array();
      for (var i=0, ilen=this.data.items.length; i<ilen; i++) {
         var rec = this.data.items[i];
         var fields = rec.fields.items;
         
         var record = new Array();
         for (var j=0, jlen=fields.length; j < jlen; j++) {
            record.push(rec.data[fields[j].name]);
         }
         data.push(record);
      }
      destStore.loadData(data, false);
   },
   moveData: function(destStore) {
      this.copyData(destStore);
      this.removeAll();
   },
   
   
	addField: function(field){
		field = new Ext.data.Field(field);
		this.recordType.prototype.fields.replace(field);
		if(typeof field.defaultValue != 'undefined'){
			this.each(function(r){
				if(typeof r.data[field.name] == 'undefined'){
					r.data[field.name] = field.defaultValue;
				}
			});
		}
      delete this.reader.ef;
      this.reader.buildExtractors();

	},
	removeField: function(name){
		this.recordType.prototype.fields.removeKey(name);
		this.each(function(r){
			delete r.data[name];
			if(r.modified){
				delete r.modified[name];
			}
		});
      delete this.reader.ef;
      this.reader.buildExtractors();
	},
	removeAllFields: function() {
      if (this.fields != undefined) {
         var fldnames = this.fields.keys;
         for (var i=0, len=fldnames.length; i<len; len--) {
            this.removeField(fldnames[len]);
         }
      }
   }
});


Ext.override(Ext.grid.ColumnModel,{
	addColumn: function(column, colIndex){
		if(typeof column == 'string'){
			column = {header: column, dataIndex: column};
		}
		var config = this.config;
		this.config = [];
		if(typeof colIndex == 'number'){
			config.splice(colIndex, 0, column);
		}else{
			colIndex = config.push(column);
		}
		this.setConfig(config);
		return colIndex;
	},
	removeColumn: function(colIndex){
		var config = this.config;
		this.config = [config[colIndex]];
		config.splice(colIndex, 1);
		this.setConfig(config);
	}
});
Ext.override(Ext.grid.GridPanel,{
	addColumn: function(field, column, colIndex){
		if(!column){
			if(field.dataIndex){
				column = field; 
				field = field.dataIndex;
			} else{
				column = field.name || field;
			}
		}
		this.store.addField(field); 
		return this.colModel.addColumn(column, colIndex);
	},
	removeColumn: function(name, colIndex){
		this.store.removeField(name);
		if(typeof colIndex != 'number'){
			colIndex = this.colModel.findColumnIndex(name);
		}
		if(colIndex >= 0){
			this.colModel.removeColumn(colIndex);
		}
	},
   removeAllColumns: function() {
      var fldnames = this.store.fields.keys;
      for (var i=0, len=fldnames.length; i<len; len--) {
         this.removeColumn(fldnames[len]);
      }
   }
});

 
Ext.grid.CheckboxSelectionModel.override({
    initEvents: function()
    {
        Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
        this.grid.on('render', function()
        {
            var view = this.grid.getView();
            view.mainBody.on('mousedown', this.onMouseDown, this);
            Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
        }, this);
    }
});

Ext.grid.GridDragZone.override({
    handleMouseDown: function(e, t)
    {
        if(t.className == 'x-grid3-row-checker')
            return false;
        Ext.grid.GridDragZone.superclass.handleMouseDown.apply(this, arguments);
    }
});      
      
                
/********************************************************************** 
* add mouseclick focus() and blur() functionality to the Ext.Panel 
*/   

Ext.override(Ext.Panel, { 
   hasFocus:false, 
   handleDocMouseDown : function(e) { 
      if (!e.within(this.getEl()) && this.hasFocus) { 
         this.hasFocus=false; 
         this.fireEvent('blur', this); 
      } else if (e.within(this.getEl()) && !this.hasFocus) { 
         this.hasFocus=true; 
         this.fireEvent('focus', this); 
      } 
   } 
}); 
 
Ext.util.Observable.observeClass(Ext.Panel); 
Ext.Panel.on('beforerender', function(panel) { 
   Ext.getDoc().on("mousedown", panel.handleDocMouseDown, panel); 
   panel.addEvents({ 
      focus: true, 
      blur: true 
   }); 
});                                 
/*********************************************************************/       

Ext.grid.EditorGridPanel.override({ 
   clicksToEdit: 2 
}); 

// CellSelectionModel override to move edited cell down/up on Shift+Enter key
Ext.grid.GridPanel.override({ ltrTab: true });
Ext.override(Ext.grid.CellSelectionModel, {
   handleKeyDown : function(e){
        if(!e.isNavKeyPress()){
            return;
        }
        var g = this.grid, s = this.selection;
        if(!s){
            e.stopEvent();
            var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
            if(cell){
                this.select(cell[0], cell[1]);
            }
            return;
        }
        var sm = this;
        
        var walk = function(row, col, step, ltr){
            if (!ltr) {
               var numrows = g.store.data.length;
               var numcols = g.colModel.columns.length;
               if (0<step && row==numrows) { // down
                  row=0;
                  col=(numcols-col!=1 ? col+1 : 0);
               }
               if (step<0 && row<0) { // up
                  row=numrows - (col!=0? 1 : 0 );
                  col--;
               }
            }
            return g.walkCells(row, col, step, sm.isSelectable,  sm);
        };
        
        var k = e.getKey(), r = s.cell[0], c = s.cell[1];
        var newCell;

        switch(k){
             case e.TAB:
                 if(e.shiftKey){
                     newCell = walk(r-(g.ltrTab?0:1), c-(g.ltrTab?1:0), -1, g.ltrTab);
                 }else{
                     newCell = walk(r+(g.ltrTab?0:1), c+(g.ltrTab?1:0), 1, g.ltrTab);
                 }
             break;
             case e.DOWN:
                 newCell = walk(r+1, c, 1, false);
             break;
             case e.UP:
                 newCell = walk(r-1, c, -1, false);
             break;
             case e.RIGHT:
                 newCell = walk(r, c+1, 1, true);
             break;
             case e.LEFT:
                 newCell = walk(r, c-1, -1, true);
             break;
             case e.ENTER:
                 if(g.isEditor && !g.editing){
                    g.startEditing(r, c);
                    e.stopEvent();
                    return;
                }
             break;
        }
        if(newCell){
            this.select(newCell[0], newCell[1]);
            if(g.isEditor && k==e.TAB){
            g.stopEditing();
               g.startEditing(newCell[0], newCell[1]);
            }
            e.stopEvent();
        }
    }
});

Ext.override(Ext.tree.TreeNode, {
  findDescendant: function(attribute, value) {
    var children = this.childNodes;
    for(var i = 0, l = children.length; i < l; i++) {
      if(children[i].attributes[attribute] == value){
        return children[i];
      } else if(node = children[i].findDescendant(attribute, value)) {
        return node;
      }
    }
    return null;
  }
}); 


Ext.util.Observable.observeClass(Ext.grid.CheckboxSelectionModel); 
Ext.grid.CheckboxSelectionModel.on('rowselect', function(sm, rowIndex, record) {
   var i=1;
   if (record.node != undefined) {
      Ext.each(record.node.childNodes, function(cnode) {
         sm.selectRow(rowIndex+i, true);
         i++;
      }, this);
   }
});
Ext.grid.CheckboxSelectionModel.on('rowdeselect', function(sm, rowIndex, record) {
   var i=1;
   if (record.node != undefined) {
      Ext.each(record.node.childNodes, function(cnode) {
         sm.deselectRow(rowIndex+i);
         i++;
      }, this);
   }
});

Ext.grid.RadioSelectionModel = Ext.extend(Ext.grid.CheckboxSelectionModel, {
    singleSelect: true,
    header: '',
    constructor : function(){
        Ext.grid.RadioSelectionModel.superclass.constructor.apply(this, arguments);
    },
    renderer : function(v, p, record){
        return '<div class="x-grid3-row-checker x-grid3-radio-checker">&#160;</div>';
    }    
});

Ext.grid.GroupingView.override ({
        toggleGroup : function(group, expanded){
            var gel = Ext.get(group);
            expanded = Ext.isDefined(gel.selected) ? gel.selected : (Ext.isDefined(expanded) ? expanded : gel.hasClass('x-grid-group-collapsed'));
            if (this.fireEvent('beforetoggleGroup', this, gel, expanded) === false) return false;
            if(this.state[gel.id] !== expanded){
                this.grid.stopEditing(true);
                this.state[gel.id] = expanded;
                gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
                this.fireEvent('toggleGroup', this, gel, expanded);
            }
        }
    });

Ext.form.Field.override({
    setValue : function(v){
        var oldValue = this.value;
        this.value = v;
        if(this.rendered){
            this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
            this.validate();
        }
        if (this.xtype != 'combo') {
           this.fireEvent('setValue', this, this.value, oldValue);
        } 
        return this;
    }
});

/***************************************
 * Custom mouse cursors for gripanels
 */
 Ext.override(Ext.grid.GridView, {
    getColumnData : function(){
        
        var cs = [], cm = this.cm, colCount = cm.getColumnCount();
        for(var i = 0; i < colCount; i++){
            var name = cm.getDataIndex(i);
            cs[i] = {
                name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
                renderer : cm.getRenderer(i),
                scope: cm.getRendererScope(i),
                id : cm.getColumnId(i),
                cls: cm.config[i].cls || '', // ADDED
                style : this.getColumnStyle(i)
            };
        }
        return cs;
    },
    doRender : function(cs, rs, ds, startRow, colCount, stripe){
        var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
        var tstyle = 'width:'+this.getTotalWidth()+';';
        
        var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
        for(var j = 0, len = rs.length; j < len; j++){
            r = rs[j]; cb = [];
            var rowIndex = (j+startRow);
            for(var i = 0; i < colCount; i++){
                c = cs[i];
                p.id = c.id;
                p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
                p.css += ' ' + c.cls || ''; // ADDED
                p.attr = p.cellAttr = '';
                p.value = c.renderer.call(c.scope, r.data[c.name], p, r, rowIndex, i, ds);
                p.style = c.style;
                if(Ext.isEmpty(p.value)){
                    p.value = ' ';
                }
                if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
                    p.css += ' x-grid3-dirty-cell';
                }
                cb[cb.length] = ct.apply(p);
            }
            var alt = [];
            if(stripe && ((rowIndex+1) % 2 === 0)){
                alt[0] = 'x-grid3-row-alt';
            }
            if(r.dirty){
                alt[1] = ' x-grid3-dirty-row';
            }
            rp.cols = colCount;
            if(this.getRowClass){
                alt[2] = this.getRowClass(r, rowIndex, rp, ds);
            }
            rp.alt = alt.join(' ');
            rp.cells = cb.join('');
            buf[buf.length] =  rt.apply(rp);
        }
        return buf.join('');
    }

}); 

Ext.util.Observable.observeClass(Ext.grid.GridPanel); 
Ext.grid.GridPanel.on('beforerender', function(grid) {
   if (grid.enableDragDrop) {
      grid.addClass('draggable_rows');
   }
   var columns = grid.colModel.config;
   for (var i=0, len=columns.length; i<len; i++ ) {
      if (columns[i].editor != undefined) {
         if (columns[i].editor != null) {
            //column is editable
            columns[i].cls = 'editable';
         }
         //if the columns renderer is powered by a store in the editor, load the store.
         if (columns[i].editor.field != undefined && columns[i].editor.field.store != undefined && columns[i].editor.field.store.data.length < 1) {
            columns[i].editor.field.store.load({
               callback: function(records, options, success) {
                  // the grid's view must be refreshed for the renderer to be re-called
                  this.view.refresh();
               },
               scope: grid,
               add: false
            });
         }
      }
   }
});
/************************************************/


Ext.data.MotherStore = Ext.extend(Ext.data.Store, {
    childStores: [],
    constructor : function(){
        Ext.data.MotherStore.superclass.constructor.apply(this, arguments);
        this.on("load", this.onLoad, this);
    },
    createChildStore: function(filterProperty, filterValue){
      var childStore = new Ext.data.Store({
        filterProperty: filterProperty,
        filterValue: filterValue,
        motherStore: this,
         reader: new Ext.data.ArrayReader({},this.fields.items),
        load: function(){
           records = this.motherStore.query(this.filterProperty, this.filterValue);
           this.removeAll(false, true);
           if (records.length > 0) this.add(records.items, true);
        },
        removeAll : function(silent, blockDeletes){
            var items = [];
            this.each(function(rec){
                items.push(rec);
            });
            this.clearData();
            if(this.snapshot){
                this.snapshot.clear();
            }
            if(this.pruneModifiedRecords){
                this.modified = [];
            }
            if (silent !== true) {  // <-- prevents write-actions when we just want to clear a store.
                this.fireEvent('clear', this, items, blockDeletes);
            }
        },        
        add : function(records, isSilent){
            records = [].concat(records);
            if(records.length < 1){
                return;
            }
            for(var i = 0, len = records.length; i < len; i++){
                records[i].join(this);
            }
            var index = this.data.length;
            this.data.addAll(records);
            if(this.snapshot){
                this.snapshot.addAll(records);
            }
              this.fireEvent('add', this, records, index, isSilent);

        },
        setFilter: function(filterProperty, filterValue){
          this.filterProperty = filterProperty;
          this.filterValue = filterValue;
        },    
        listeners: {
          add: function(store, records, index, isSilent){
            if (isSilent == undefined || !isSilent){
               this.motherStore.add(records);
            }
          },
          remove: function(store, records, index){
             this.motherStore.remove(records);
          },
          clear: function(store, records, blockDeletes){
             if (blockDeletes == undefined || !blockDeletes) {
              this.motherStore.remove(records);    
             }    
          }            
        },
    afterEdit : function(record){
        if(this.modified.indexOf(record) == -1){
            this.modified.push(record);
        }
        this.fireEvent('update', this, record, Ext.data.Record.EDIT);
        this.motherStore.afterEdit(record);
    },
    afterReject : function(record){
        this.modified.remove(record);
        this.fireEvent('update', this, record, Ext.data.Record.REJECT);
        this.motherStore.afterReject(record);
    },
    afterCommit : function(record){
        this.modified.remove(record);
        this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
        this.motherStore.afterCommit(record);
    }                
      });
      this.childStores.push(childStore);
      childStore.load();
      return childStore;
    },
    loadChildren: function(){
      for(i = 0; i < this.childStores.length; i++){
        this.childStores[i].load();
      }
    },
    onLoad: function(){
        this.loadChildren();
    }
});

//FROM: http://www.extjs.com/forum/showthread.php?21913-SOLVED-Grid-Drag-and-Drop-reorder-rows
Ext.namespace('Ext.ux.dd');
Ext.ux.dd.GridReorderDropTarget = function(grid, config) {
    this.target = new Ext.dd.DropTarget(grid.getEl(), {
        ddGroup: grid.ddGroup || 'GridDD'
        ,grid: grid
        ,gridDropTarget: this
        ,notifyDrop: function(dd, e, data){
            // determine the row
            var t = Ext.lib.Event.getTarget(e);
            var rindex = this.grid.getView().findRowIndex(t);
            if (rindex === false) return false;
            if (rindex == data.rowIndex) return false;

            // fire the before move/copy event
            if (this.gridDropTarget.fireEvent(this.copy?'beforerowcopy':'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections) === false) return false;

            // update the store
            var ds = this.grid.getStore();
            if (!this.copy) {
                for(i = 0; i < data.selections.length; i++) {
                    ds.remove(ds.getById(data.selections[i].id));
                }
            }
            ds.insert(rindex,data.selections);

            // re-select the row(s)
            var sm = this.grid.getSelectionModel();
            if (sm) sm.selectRecords(data.selections);

            // fire the after move/copy event
            this.gridDropTarget.fireEvent(this.copy?'afterrowcopy':'afterrowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections);

            return true;
        }
        ,notifyOver: function(dd, e, data) {
            var t = Ext.lib.Event.getTarget(e);
            var rindex = this.grid.getView().findRowIndex(t);
            if (rindex == data.rowIndex) rindex = false;

            return (rindex === false)? this.dropNotAllowed : this.dropAllowed;
        }
    });
    if (config) {
        Ext.apply(this.target, config);
        if (config.listeners) Ext.apply(this,{listeners: config.listeners});
    }

    this.addEvents({
        "beforerowmove": true
        ,"afterrowmove": true
        ,"beforerowcopy": true
        ,"afterrowcopy": true
    });

    Ext.ux.dd.GridReorderDropTarget.superclass.constructor.call(this);
};

Ext.extend(Ext.ux.dd.GridReorderDropTarget, Ext.util.Observable, {
    getTarget: function() {
        return this.target;
    }
    ,getGrid: function() {
        return this.target.grid;
    }
    ,getCopy: function() {
        return this.target.copy?true:false;
    }
    ,setCopy: function(b) {
        this.target.copy = b?true:false;
    }
});  

Ext.override(Ext.form.Field, {
    initEvents : function(){
        this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey,  this);
        this.mon(this.el, 'focus', this.onFocus, this);
        this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);

        if (this.label != undefined) {
          this.on({
          'hide' : this.onHide2,
          'show' : this.onShow2,
           scope: this
          });  
          
          if (this.hidden){
            this.hide();
          } 
        } 

    },
    onHide2 : function(){
      this.label.hide();
    },
    onShow2 : function(){
      this.label.show();
    }    
});


Ext.override(Ext.data.GroupingStore, {
   getByGroupId: function(groupid) {
      var records = new Array();
      Ext.each(this.data.items, function(rec) {
         if (rec._groupId == groupid) records.push(rec);
      },this);
      return records;
   },
   
   findInGroup: function(groupid, field, value) {
      var gRecs = this.getByGroupId(groupid), records = new Array();
      Ext.each(gRecs, function(rec) {
         if (in_array(value, rec.data)) records.push(rec);
      },this);
      return records;
   }
});


Ext.override(Ext.chart.Chart, {
    refresh : function(){
        var styleChanged = false;
        // convert the store data into something YUI charts can understand
        var data = [], rs = this.store.data.items;
        for(var j = 0, len = rs.length; j < len; j++){
            data[j] = rs[j].data;
        }
        //make a copy of the series definitions so that we aren't
        //editing them directly.
        var dataProvider = [];
        var seriesCount = 0;
        var currentSeries = null;
        var i = 0;
        if(this.series){
            seriesCount = this.series.length;
            for(i = 0; i < seriesCount; i++){
                currentSeries = this.series[i];
                var clonedSeries = {};
                for(var prop in currentSeries){
                    if(prop == "style" && currentSeries.style !== null){
                        clonedSeries.style = Ext.encode(currentSeries.style);
                        styleChanged = true;
                        //we don't want to modify the styles again next time
                        //so null out the style property.
                        // this causes issues
                        // currentSeries.style = null;
                    } else{
                        clonedSeries[prop] = currentSeries[prop];
                    }
                }
                dataProvider.push(clonedSeries);
            }
        }

        if(seriesCount > 0){
            for(i = 0; i < seriesCount; i++){
                currentSeries = dataProvider[i];
                if(!currentSeries.type){
                    currentSeries.type = this.type;
                }
                currentSeries.dataProvider = data;
            }
        } else{
            dataProvider.push({type: this.type, dataProvider: data});
        }
        
        //this.swf.setDataProvider(dataProvider);
        if(this.swf && this.swf.setDataProvider) {        // fix
          this.swf.setDataProvider(dataProvider);
        }
    }
});

//**************************************************
// add observable properties to Ext.data.Connection
Ext.util.Observable.observeClass = function(c) {
   Ext.apply(c, new Ext.util.Observable());
   c.prototype.fireEvent = function() { return (c.fireEvent.apply(c, arguments) !== false) && (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false); };
};
Ext.override(Ext.util.Observable, {
   addListener: Ext.util.Observable.prototype.addListener.createInterceptor(function() { if (!this.events) { this.events = {}; } })
});
Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
Ext.util.Observable.observeClass(Ext.data.Connection);
//**************************************************
var loadingstarted=0, loadingfinished=0, loadingprogress=0;
function updateProgress() {
   loadingfinished++;
   loadingprogress = loadingfinished/loadingstarted;
   Ext.MessageBox.updateProgress((isNaN(loadingprogress)?1:loadingprogress),  (isNaN(Math.round(100*loadingprogress))?'100':Math.round(100*loadingprogress))+'% completed');
   if(loadingstarted <= loadingfinished){
      loadingstarted=0, loadingfinished=0;
      Ext.MessageBox.hide();
   }
}

Ext.data.Connection.on('beforerequest', function(con, options) {
   loadingstarted++;
   if (!Ext.MessageBox.isVisible())
      Ext.MessageBox.show({ title: 'Please Wait', /*msg: 'Loading...',*/ progressText: 'Loading...', width:300, progress:true, closable:false, modal: false });
});
Ext.data.Connection.on('requestcomplete', function(con, response, options) {
   updateProgress();
});
Ext.lib.Ajax.request = Ext.lib.Ajax.request.createInterceptor(function(method, uri, cb, data, options){
    //run universal code on "failure"
    cb.failure = cb.failure.createInterceptor(function(response){ updateProgress(); });
});
          
Ext.override(Ext.data.Store, {
  batchRemove: function(records){
    if (this.autoSave){
      this.autoSave = false;
      this.remove(records);
      this.autoSave = true;
      this.save();      
    } else {
      this.remove(records);
    }
  }
});

Ext.override(Ext.tree.TreeNodeUI, {
    renderElements : function(n, a, targetNode, bulkRender){
        // add some indent caching, this helps performance when rendering a large tree
        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';

        var cb = Ext.isBoolean(a.checked),
            nel,
            href = a.href ? a.href : Ext.isGecko ? "" : "#",
            buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
            '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
            '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
            cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
            '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
            '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
             a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
            "</li>"].join('');

        if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
            this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
        }else{
            this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
        }

        this.elNode = this.wrap.childNodes[0];
        this.ctNode = this.wrap.childNodes[1];
        var cs = this.elNode.childNodes;
        this.indentNode = cs[0];
        this.ecNode = cs[1];
        this.iconNode = cs[2];
        var index = 3;
        if(cb){ 
            this.iconNode = cs[3];
            this.checkbox = cs[2];
            // fix for IE6
            this.checkbox.defaultChecked = this.checkbox.checked;
            index++;
	    Ext.get(this.checkbox).addListener('click',
		function(){
			if(n.ownerTree.selModel.isSelected(n)){
				n.ownerTree.selModel.unselect(n);
			} else {
				n.ownerTree.selModel.selectNode(n);
			}
		}
	    );
        }
        this.anchor = cs[index];
        this.textNode = cs[index].firstChild;
    }
});                    
                 

Ext.override(Ext.grid.CheckboxSelectionModel, {
    selectAll : function(){
        if(this.isLocked()){
            return;
        }
        this.selections.clear();
        for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
            this.selectRow(i, true);
        }
        
        if (this.grid.view.showGroupCheckBoxes) {
           var groups = this.grid.view.getGroups();
           for (i=0; i<groups.length; i++) {
               Ext.get(this.grid.id+'-'+groups[i].id+'-checkbox').addClass('x-grid3-row-selected');
           }
        } 
    },
    clearSelections : function(fast){
        if(this.isLocked()){
            return;
        }
        if(fast !== true){
            var ds = this.grid.store;
            var s = this.selections;
            s.each(function(r){
                this.deselectRow(ds.indexOfId(r.id));
            }, this);
            s.clear();
        }else{
            this.selections.clear();
        }
        this.last = false;
        
        if (this.grid.view.showGroupCheckBoxes) {
           var groups = this.grid.view.getGroups();
           for (i=0; i<groups.length; i++) {
               Ext.get(this.grid.id+'-'+groups[i].id+'-checkbox').removeClass('x-grid3-row-selected');
           }
        } 
    }
});
