PDA

View Full Version : extension datepicker-range (day/week/month)



cocorossello
8 Jan 2008, 3:34 AM
Hi,

Here is a datepicker with support for selecting a single day, week or month.

Its an extension of the datepicker but now you can set a selection mode ('day', 'week', 'month').



Ext.namespace('Ext.ux');


Date.prototype.getFirstDateOfWeek = function(){
var value=this.clearTime();
var semana=this.getWeekOfYear();
while(semana == value.getWeekOfYear())
value=value.add(Date.DAY,-1);
value=value.add(Date.DAY,1);
return value;
}


Ext.ux.DatePickerRange = Ext.extend(Ext.DatePicker, {
selectionMode:'month',
setSelectionMode:function(mode){
this.selectionMode=mode;
this.setValue(this.value);
},
getSelectionMode:function(){
return this.selectionMode();
},

//private
update : function(date){
var vd = this.activeDate;
this.activeDate = date;
if(vd && this.el){
var t = date.getTime();
if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
this.cells.removeClass("x-date-selected");
this.cells.each(function(c){
if(this.isSelected( c.dom.firstChild.dateValue )){
c.addClass("x-date-selected");
}
},this);
return;
}
}
var days = date.getDaysInMonth();
var firstOfMonth = date.getFirstDateOfMonth();
var startingPos = firstOfMonth.getDay()-this.startDay;

if(startingPos <= this.startDay){
startingPos += 7;
}

var pm = date.add("mo", -1);
var prevStart = pm.getDaysInMonth()-startingPos;

var cells = this.cells.elements;
var textEls = this.textNodes;
days += startingPos;


var day = 86400000;
var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
var today = new Date().clearTime().getTime();
var sel = date.clearTime().getTime();
var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
var ddMatch = this.disabledDatesRE;
var ddText = this.disabledDatesText;
var ddays = this.disabledDays ? this.disabledDays.join("") : false;
var ddaysText = this.disabledDaysText;
var format = this.format;



var setCellClass = function(cal, cell){
cell.title = "";
var t = d.getTime();
cell.firstChild.dateValue = t;
if(t == today){
cell.className += " x-date-today";
cell.title = cal.todayText;
}
if(cal.isSelected(cell.firstChild.dateValue)){
cell.className += " x-date-selected";
}

if(t < min) {
cell.className = " x-date-disabled";
cell.title = cal.minText;
return;
}
if(t > max) {
cell.className = " x-date-disabled";
cell.title = cal.maxText;
return;
}
if(ddays){
if(ddays.indexOf(d.getDay()) != -1){
cell.title = ddaysText;
cell.className = " x-date-disabled";
}
}
if(ddMatch && format){
var fvalue = d.dateFormat(format);
if(ddMatch.test(fvalue)){
cell.title = ddText.replace("%0", fvalue);
cell.className = " x-date-disabled";
}
}
};

var i = 0;
for(; i < startingPos; i++) {
textEls[i].innerHTML = (++prevStart);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-prevday";
setCellClass(this, cells[i]);
}

for(; i < days; i++){
intDay = i - startingPos + 1;
textEls[i].innerHTML = (intDay);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-active";
setCellClass(this, cells[i]);
}
var extraDays = 0;
for(; i < 42; i++) {
textEls[i].innerHTML = (++extraDays);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-nextday";
setCellClass(this, cells[i]);
}

this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());

if(!this.internalRender){
var main = this.el.dom.firstChild;
var w = main.offsetWidth;
this.el.setWidth(w + this.el.getBorderWidth("lr"));
Ext.fly(main).setWidth(w);
this.internalRender = true;

if(Ext.isOpera && !this.secondPass){
main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
this.secondPass = true;
this.update.defer(10, this, [date]);
}
}
},
isSelected:function(date){
date=new Date(date);
if(this.selectionMode=='day')
return date.clearTime()==this.value.clearTime();
if(this.selectionMode=='month')
return date.getFirstDateOfMonth().clearTime().getTime ()==this.value.getFirstDateOfMonth().clearTime().getTime ();
if(this.selectionMode=='week'){
return date.getFirstDateOfWeek().clearTime().getTime ()==this.value.getFirstDateOfWeek().clearTime().getTime ();
}
throw 'Illegal selection mode';

}


});

Ext.reg('datepickerrange', Ext.ux.DatePickerRange);




Hope it is useful for someone ;)

tof
9 Jan 2008, 2:14 AM
Nice, thank you !

stevenvegt
15 Jan 2008, 4:49 AM
Thanx for this extension. I use it in a calendar application. I found a small bug:
When in selectionMode == 'day' the chosen day isn't selected. Fixed when you change this line of code:


return date.clearTime() == this.value.clearTime();


into this one:


return date.clearTime().getTime() == this.value.clearTime().getTime();

cocorossello
15 Jan 2008, 10:52 AM
Thx esteven, i forgot that one.

I am doing a calendar application too :P

stevenvegt
16 Jan 2008, 7:10 AM
I extended your extension: When you hold the shiftkey you can select more days or weeks. In my project it wasn't necessary to select more than one month, but it is easy to add this functionality.
There is an public var "amount" which is set. When you select 1 day or week the amount is 1. If you select 2 days or weeks the amount is 2 etc. Simple :) Also the fireEvent sends this extra amount parameter.

Here's the code:


Ext.namespace('Ext.ux');


Date.prototype.getFirstDateOfWeek = function(){

var value = this.clone();
var dayOfWeek = this.getDay();
dayOfWeek = (dayOfWeek + 6) % 7;
value.setDate(value.getDate() - dayOfWeek);

return value;
}


Ext.ux.DatePickerRange = Ext.extend(Ext.DatePicker, {

selectionMode:'month',

amount: 1,

setSelectionMode:function(mode){
this.selectionMode=mode;
this.setValue(this.value);
},

getSelectionMode:function(){
return this.selectionMode();
},

// private
handleDateClick : function(e, t){

e.stopEvent();
if(t.dateValue && !Ext.fly(t.parentNode).hasClass("x-date-disabled")){


if (Ext.EventObject.shiftKey) {
if (t.dateValue && t.dateValue >= this.activeDate) {

var d = new Date(t.dateValue);
var DAY = 86400000;

if(this.selectionMode=='day') {
this.amount = ((d.getTime() - this.activeDate.getTime()) / DAY) + 1;
//if(this.selectionMode=='month')
// this.amount = 1;
} else if(this.selectionMode=='week'){
var days = d.getFirstDateOfWeek().getTime() - this.activeDate.getFirstDateOfWeek().getTime() + DAY;
this.amount = Math.ceil(days / (DAY * 7));
}

this.setValue(new Date(this.value));
}
} else {
this.amount = 1;
this.setValue(new Date(t.dateValue));
}
this.fireEvent("select", this, this.value, this.amount);
}
},
//private
update : function(date){
var vd = this.activeDate;
this.activeDate = date;
if(vd && this.el){
var t = date.getTime();
if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
this.cells.removeClass("x-date-selected");
this.cells.each(function(c){
if(this.isSelected( c.dom.firstChild.dateValue )){
c.addClass("x-date-selected");
}
},this);
return;
}
}
var days = date.getDaysInMonth();
var firstOfMonth = date.getFirstDateOfMonth();
var startingPos = firstOfMonth.getDay()-this.startDay;

if(startingPos <= this.startDay){
startingPos += 7;
}

var pm = date.add("mo", -1);
var prevStart = pm.getDaysInMonth()-startingPos;

var cells = this.cells.elements;
var textEls = this.textNodes;
days += startingPos;


var day = 86400000;
var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
var today = new Date().clearTime().getTime();
var sel = date.clearTime().getTime();
var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
var ddMatch = this.disabledDatesRE;
var ddText = this.disabledDatesText;
var ddays = this.disabledDays ? this.disabledDays.join("") : false;
var ddaysText = this.disabledDaysText;
var format = this.format;



var setCellClass = function(cal, cell){
cell.title = "";
var t = d.getTime();
cell.firstChild.dateValue = t;
if(t == today){
cell.className += " x-date-today";
cell.title = cal.todayText;
}
if(cal.isSelected(cell.firstChild.dateValue)){
cell.className += " x-date-selected";
}

if(t < min) {
cell.className = " x-date-disabled";
cell.title = cal.minText;
return;
}
if(t > max) {
cell.className = " x-date-disabled";
cell.title = cal.maxText;
return;
}
if(ddays){
if(ddays.indexOf(d.getDay()) != -1){
cell.title = ddaysText;
cell.className = " x-date-disabled";
}
}
if(ddMatch && format){
var fvalue = d.dateFormat(format);
if(ddMatch.test(fvalue)){
cell.title = ddText.replace("%0", fvalue);
cell.className = " x-date-disabled";
}
}
};

var i = 0;
for(; i < startingPos; i++) {
textEls[i].innerHTML = (++prevStart);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-prevday";
setCellClass(this, cells[i]);
}

for(; i < days; i++){
intDay = i - startingPos + 1;
textEls[i].innerHTML = (intDay);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-active";
setCellClass(this, cells[i]);
}
var extraDays = 0;
for(; i < 42; i++) {
textEls[i].innerHTML = (++extraDays);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-nextday";
setCellClass(this, cells[i]);
}

this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());

if(!this.internalRender){
var main = this.el.dom.firstChild;
var w = main.offsetWidth;
this.el.setWidth(w + this.el.getBorderWidth("lr"));
Ext.fly(main).setWidth(w);
this.internalRender = true;

if(Ext.isOpera && !this.secondPass){
main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
this.secondPass = true;
this.update.defer(10, this, [date]);
}
}
},
isSelected:function(date){
// date to test
date=new Date(date).clearTime();
// activeDate
ad = new Date(this.value).clearTime();
var DAY = 86400000;

if(this.selectionMode=='day') {
date = date.getTime()
for (var i=0; i<this.amount;++i) {
if (date == ad.add(Date.DAY,i).getTime()) return true;
}
return false;
}
if(this.selectionMode=='month')
return date.getFirstDateOfMonth().getTime() == this.value.getFirstDateOfMonth().getTime();

if(this.selectionMode=='week'){
var refTime = date.getFirstDateOfWeek().getTime();
var ad = ad.getFirstDateOfWeek();

if (refTime == ad.getTime())
return true;

for (var i=1; i<this.amount;++i) {
ad.setDate(ad.getDate() + 7);
if (refTime == ad.getTime())
return true;
}
return false;
}
throw 'Illegal selection mode';

}


});

Ext.reg('datepickerrange', Ext.ux.DatePickerRange);


Any comments are welcome :)

mm_202
17 Jan 2008, 6:45 PM
Excellent work guys!

Just one question (and I really hope that it isnt an obvious answer that I missed), how do I programatically select a date range?

Hopefully Im not the only one needing to do that.. /:)

cocorossello
18 Jan 2008, 12:11 AM
Just set the value. If it's a week or month selection, it will tear your value to the beginning of the week/month,



mydatepicker.setSelectionMode('week');
mydatepicker.setValue(new Date());

mm_202
18 Jan 2008, 12:31 AM
Just set the value. If it's a week or month selection, it will tear your value to the beginning of the week/month,



mydatepicker.setSelectionMode('week');
mydatepicker.setValue(new Date());


Thanks cocorossello,
But I guess I should of made myself more clear. In 'day' mode, I want to be able to programatically select multiple days. Say the 3rd through the 10th.
Im somehow doubting that that is as easy :)

cocorossello
18 Jan 2008, 2:14 AM
Use steven's version, he already did that ;)

I guess you'll have to make do some modifications for your application

stevenvegt
18 Jan 2008, 5:15 AM
Thanks cocorossello,
But I guess I should of made myself more clear. In 'day' mode, I want to be able to programatically select multiple days. Say the 3rd through the 10th.
Im somehow doubting that that is as easy :)

You can do this by setting the amount to 7 and set the date value of the picker to the 3rd.

For instance:


picker.amount = 7;
picker.setValue(new Date())


I optimized the getFirstDateOfWeek method, it was a bit slow.



Date.prototype.getFirstDateOfWeek = function(){
var value = this.clone();
var dayOfWeek = this.getDay();
dayOfWeek = (dayOfWeek + 6) % 7;
value.setDate(value.getDate() - dayOfWeek);

return value;
}

mm_202
18 Jan 2008, 12:21 PM
You can do this by setting the amount to 7 and set the date value of the picker to the 3rd.

For instance:


picker.amount = 7;
picker.setValue(new Date())


I optimized the getFirstDateOfWeek method, it was a bit slow.



Date.prototype.getFirstDateOfWeek = function(){
var value = this.clone();
var dayOfWeek = this.getDay();
dayOfWeek = (dayOfWeek + 6) % 7;
value.setDate(value.getDate() - dayOfWeek);

return value;
}

heh, ok, thanks guys. I was using cocorossello's and assumed it was just a 'better' version. I'll pay more attention next time.

But umm, anyways, great work! :D

hattia
9 Apr 2009, 1:18 AM
hi guys, great work ...
have you an idea on how to use drag rather than maintain the shift key to select range of date

Thanks

Naokai
21 Apr 2009, 2:00 AM
Hi, can someone give me an Example how do i use this Datepicker?

Lars

panosru
2 May 2009, 5:10 PM
great extension! :D

I have:


{
xtype : 'datepickerrange',
fieldLabel: 'Range Date',
selectionMode : 'day',
id : 'date-range'
}

When i do Ext.getCmp('date-range').getValue() I'm getting the first selected date, how can i get all selected days?

Also, I set this to day selection model, some times its needed to select a day range that is between months, eg from 3rd May to 3rd July how can i select these values with shift & click? - i tried and I lost my selection of previous month...-

EDIT:
Btw, it seems to be a bug, if today is 3rd May and you select the 4th, 5th, 6th May by clicking on 4th May hold shift and then click on 6th May and then press Today button, instead of selecting the 3rd May it will select the 3rd, 4th and 5th May...

panosru
2 May 2009, 7:54 PM
I fixed the "bug" with today button, also todayBtn gets its name by selectionMode, on day the button text is "Current Day", on week, "Current Week" and on month "Current Month".

Today Button actions are now: on day, it reset the amount to 1 so it selects the current day, on week it resets the amount to 1 so it selects current week and same for month.

I added the getValues() function that returns the days selected. So if you select a range of days or weeks or a months it will return all the days selected.

I Still don't know how to select multiple months....

Code based on stevenvegt code which is based on cocorossello's code! :D
Thank you both guys!! I needed this for a project I am working on! :D


Ext.namespace('Ext.ux');

Date.prototype.getFirstDateOfWeek = function(){
var value = this.clone();
var dayOfWeek = this.getDay();
dayOfWeek = (dayOfWeek + 6) % 7;
value.setDate(value.getDate() - dayOfWeek);
return value;
}


Ext.ux.DatePickerRange = Ext.extend(Ext.DatePicker, {
selectionMode:'month',
amount: 1,
setSelectionMode:function(mode){
this.selectionMode=mode;
this.setValue(this.value);
//Update todayBtn Text on mode change
this.todayBtn.setText(this._TodayText());
},

getSelectionMode:function(){
return this.selectionMode();
},

handleDateClick : function(e, t){
e.stopEvent();
if(t.dateValue && !Ext.fly(t.parentNode).hasClass("x-date-disabled")){
if (Ext.EventObject.shiftKey) {
if (t.dateValue && t.dateValue >= this.activeDate) {

var d = new Date(t.dateValue);
var DAY = 86400000;

if(this.selectionMode=='day') {
this.amount = ((d.getTime() - this.activeDate.getTime()) / DAY) + 1;
//if(this.selectionMode=='month')
// this.amount = 1;
} else if(this.selectionMode=='week'){
var days = d.getFirstDateOfWeek().getTime() - this.activeDate.getFirstDateOfWeek().getTime() + DAY;
this.amount = Math.ceil(days / (DAY * 7));
}

this.setValue(new Date(this.value));
}
} else {
this.amount = 1;
this.setValue(new Date(t.dateValue));
}
this.fireEvent("select", this, this.value, this.amount);
}
},

//private
update : function(date){
var vd = this.activeDate;
this.activeDate = date;
if(vd && this.el){
var t = date.getTime();
if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
this.cells.removeClass("x-date-selected");
this.cells.each(function(c){
if(this.isSelected( c.dom.firstChild.dateValue )){
c.addClass("x-date-selected");
}
},this);
return;
}
}
var days = date.getDaysInMonth();
var firstOfMonth = date.getFirstDateOfMonth();
var startingPos = firstOfMonth.getDay()-this.startDay;

if(startingPos <= this.startDay){
startingPos += 7;
}

var pm = date.add("mo", -1);
var prevStart = pm.getDaysInMonth()-startingPos;

var cells = this.cells.elements;
var textEls = this.textNodes;
days += startingPos;


var day = 86400000;
var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
var today = new Date().clearTime().getTime();
var sel = date.clearTime().getTime();
var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
var ddMatch = this.disabledDatesRE;
var ddText = this.disabledDatesText;
var ddays = this.disabledDays ? this.disabledDays.join("") : false;
var ddaysText = this.disabledDaysText;
var format = this.format;

var setCellClass = function(cal, cell){
cell.title = "";
var t = d.getTime();
cell.firstChild.dateValue = t;
if(t == today){
cell.className += " x-date-today";
cell.title = cal.todayText;
}
if(cal.isSelected(cell.firstChild.dateValue)){
cell.className += " x-date-selected";
}

if(t < min) {
cell.className = " x-date-disabled";
cell.title = cal.minText;
return;
}
if(t > max) {
cell.className = " x-date-disabled";
cell.title = cal.maxText;
return;
}
if(ddays){
if(ddays.indexOf(d.getDay()) != -1){
cell.title = ddaysText;
cell.className = " x-date-disabled";
}
}
if(ddMatch && format){
var fvalue = d.dateFormat(format);
if(ddMatch.test(fvalue)){
cell.title = ddText.replace("%0", fvalue);
cell.className = " x-date-disabled";
}
}
};

var i = 0;
for(; i < startingPos; i++) {
textEls[i].innerHTML = (++prevStart);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-prevday";
setCellClass(this, cells[i]);
}

for(; i < days; i++){
intDay = i - startingPos + 1;
textEls[i].innerHTML = (intDay);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-active";
setCellClass(this, cells[i]);
}
var extraDays = 0;
for(; i < 42; i++) {
textEls[i].innerHTML = (++extraDays);
d.setDate(d.getDate()+1);
cells[i].className = "x-date-nextday";
setCellClass(this, cells[i]);
}

this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());

if(!this.internalRender){
var main = this.el.dom.firstChild;
var w = main.offsetWidth;
this.el.setWidth(w + this.el.getBorderWidth("lr"));
Ext.fly(main).setWidth(w);
this.internalRender = true;

if(Ext.isOpera && !this.secondPass){
main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
this.secondPass = true;
this.update.defer(10, this, [date]);
}
}
},

isSelected:function(date){
// date to test
date=new Date(date).clearTime();
// activeDate
ad = new Date(this.value).clearTime();
var DAY = 86400000;

if(this.selectionMode=='day') {
date = date.getTime()
for (var i=0; i<this.amount;++i) {
if (date == ad.add(Date.DAY,i).getTime()) return true;
}
return false;
}
if(this.selectionMode=='month')
return date.getFirstDateOfMonth().getTime() == this.value.getFirstDateOfMonth().getTime();

if(this.selectionMode=='week'){
var refTime = date.getFirstDateOfWeek().getTime();
var ad = ad.getFirstDateOfWeek();

if (refTime == ad.getTime())
return true;

for (var i=1; i<this.amount;++i) {
ad.setDate(ad.getDate() + 7);
if (refTime == ad.getTime())
return true;
}
return false;
}
throw 'Illegal selection mode';

},

getValues : function(){
//predefined data
var currentAmount = this.amount;
var currentValue = this.value;
var currentDate = new Date(currentValue);
var date = new Date(currentDate);
//empty data
var selectedDates = new Array();

switch (this.selectionMode) {
case 'day':
for (var i = 0; i < currentAmount; i++) selectedDates.push(currentDate.add(Date.DAY,i).getTime());
break;

case 'week':
currentAmount *= 7;
currentDate = date.getFirstDateOfWeek();
for (var i = 0; i < currentAmount; i++) selectedDates.push(currentDate.add(Date.DAY,i).getTime());
break;

case 'month':
default:
currentAmount *= date.getDaysInMonth();
currentDate = date.getFirstDateOfMonth();
for (var i = 0; i < currentAmount; i++) selectedDates.push(currentDate.add(Date.DAY,i).getTime());
}

return selectedDates;
},

selectToday : function(){
if(this.todayBtn && !this.todayBtn.disabled){
this.amount = 1;
this.setValue(new Date().clearTime());
this.fireEvent("select", this, this.value);
}
},

_TodayText : function () {
var todayText = 'Current ';
switch (this.selectionMode) {
case 'day':
todayText += 'Day';
break;

case 'week':
todayText += 'Week';
break;

case 'month':
default:
todayText += 'Month';
}
return todayText;
},

initComponent: function() {
this.todayText = this._TodayText();
Ext.ux.DatePickerRange.superclass.initComponent.apply(this, arguments);
}


});

Ext.reg('datepickerrange', Ext.ux.DatePickerRange);

panosru
4 May 2009, 6:05 PM
I updated the getValues function. If we call getValues without any parameter it will return us all the days selected. Now, if we set true to first parram by calling the function like this: getValues(true) it will return us the first and last day of our selection. If we set true to second parammeter it will return dates into unix timestamps eg: getValues(false,true) will return all selected days into unix timestamp format, if we want only first and last day selected just set first parammeter to true :)


getValues : function(startEnd, unix_timestamp) {
//arguments
startEnd = (startEnd === undefined)?false:startEnd;
unix_timestamp = (unix_timestamp === undefined)?false:unix_timestamp;
//predefined data
var currentAmount = this.amount;
var currentValue = this.value;
var currentDate = new Date(currentValue);
var date = new Date(currentDate);
//private data
var selectedDates = new Array();
var push = false;
var tmp_date = null;

switch (this.selectionMode) {
case 'week':
currentAmount *= 7;
currentDate = date.getFirstDateOfWeek();
break;

case 'month':
currentAmount *= date.getDaysInMonth();
currentDate = date.getFirstDateOfMonth();
break;

default:
}

for (var i = 0; i < currentAmount; i++) {
push = (startEnd)?((i === 0 || (i + 1) === currentAmount)?true:false):true;
if (push) {
tmp_date = currentDate.add(Date.DAY, i).getTime();
tmp_date = (unix_timestamp)?parseInt(tmp_date / 1000):tmp_date;
selectedDates.push(tmp_date);
tmp_date = null;
}
}

return selectedDates;
}hope someone find my codes usefull :)

digz6666
25 Apr 2012, 7:29 PM
I cannot get it work in extjs 4, it says:
Uncaught TypeError: Cannot call method 'substring' of undefined

Any suggestions and fixes?