PDA

View Full Version : Persian Datepicker -- full work



vahid4134
3 Dec 2009, 8:56 AM
Hi
For this you need
http://www.extjs.com/forum/showthread.php?p=396593#post396593
Update 4 Dec 2009, some fix for IE


Ext.override(Ext.DatePicker, {
PmonthNames: Date.PmonthNames,
PersianType: true,

// private
getShortMonthName: function(m) {
return Date[(this.PersianType == true) ? "getPShortMonthName": "getShortMonthName"](m);
},

addDate: function(k, v) {
return this.activeDate[(this.PersianType == true) ? "Padd": "add"](k, v);
},

// private
onRender: function(container, position) {
var m = [
'<table cellspacing="0">',
'<tr>',
'<td class="x-date-left"><a href="#" title="', this.prevText, '">&#160;</a></td>',
'<td class="x-date-middle" align="center"></td>',
'<td class="x-date-right"><a href="#" title="', this.nextText, '">&#160;</a></td>',
'</tr>',
'<tr>',
'<td colspan="3">',
'<table class="x-date-inner" cellspacing="0">',
'<thead>',
'<tr>'
],
dn = this.dayNames,
i;

for (i = 0; i < 7; i++) {
var d = this.startDay + i;
if (d > 6) {
d = d - 7;
}
m.push('<th><span>', dn[d].substr(0, 1), '</span></th>');
}

m[m.length] = '</tr></thead><tbody><tr>';

for (i = 0; i < 42; i++) {
if (i % 7 === 0 && i !== 0) {
m[m.length] = '</tr><tr>';
}
m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
}

m.push(
'</tr></tbody></table></td></tr>',
this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',
'</table><div class="x-date-mp"></div>'
);

var el = document.createElement('div');
el.className = 'x-date-picker';
el.innerHTML = m.join('');

container.dom.insertBefore(el, position);

this.el = Ext.get(el);
this.eventEl = Ext.get(el.firstChild);

this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
handler: this.showPrevMonth,
scope: this,
preventDefault: true,
stopDefault: true
});

this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
handler: this.showNextMonth,
scope: this,
preventDefault: true,
stopDefault: true
});

this.monthPicker = this.el.down('div.x-date-mp');
this.monthPicker.enableDisplayMode('block');

this.keyNav = new Ext.KeyNav(this.eventEl, {
'left': function(e) {
if (e.ctrlKey) {
this.showPrevMonth();
} else {
this.update(this.addDate('d', -1));
}
},

'right': function(e) {
if (e.ctrlKey) {
this.showNextMonth();
} else {
this.update(this.addDate('d', 1));
}
},

'up': function(e) {
if (e.ctrlKey) {
this.showNextYear();
} else {
this.update(this.addDate('d', -7));
}
},

'down': function(e) {
if (e.ctrlKey) {
this.showPrevYear();
} else {
this.update(this.addDate('d', 7));
}
},

'pageUp': function(e) {
this.showNextMonth();
},

'pageDown': function(e) {
this.showPrevMonth();
},

'enter': function(e) {
e.stopPropagation();
return true;
},

scope: this
});

this.el.unselectable();

this.cells = this.el.select('table.x-date-inner tbody td');
this.textNodes = this.el.query('table.x-date-inner tbody span');

this.mbtn = new Ext.Button({
text: '&#160;',
tooltip: this.monthYearText,
renderTo: this.el.child('td.x-date-middle', true)
});
this.mbtn.el.child('em').addClass('x-btn-arrow');

if (this.showToday) {
this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday, this);
var today = (new Date()).dateFormat(this.format);
this.todayBtn = new Ext.Button({
renderTo: this.el.child('td.x-date-bottom', true),
text: String.format(this.todayText, today),
tooltip: String.format(this.todayTip, today),
handler: this.selectToday,
scope: this
});
}
this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
this.mon(this.eventEl, 'click', this.handleDateClick, this, {
delegate: 'a.x-date-date'
});
this.mon(this.mbtn, 'click', this.showMonthPicker, this);
this.onEnable(true);
},

// private
createMonthPicker: function() {
if (!this.monthPicker.dom.firstChild) {
var buf = ['<table border="0" cellspacing="0">'];
for (var i = 0; i < 6; i++) {
buf.push(
'<tr>',
'<td class="x-date-mp-month"><a href="#">', this.getShortMonthName(i), '</a></td>',
'<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.getShortMonthName(i + 6), '</a></td>',
i === 0 ? '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' : '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
);
}
buf.push(
'<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">', this.okText, '</button>',
'<button type="button" class="x-date-mp-cancel">', this.cancelText, '</button></td></tr>',
'</table>'
);
this.monthPicker.update(buf.join(''));

this.mon(this.monthPicker, 'click', this.onMonthClick, this);
this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);

this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
this.mpYears = this.monthPicker.select('td.x-date-mp-year');

this.mpMonths.each(function(m, a, i) {
i += 1;
if ((i % 2) === 0) {
m.dom.xmonth = 5 + Math.round(i * 0.5);
} else {
m.dom.xmonth = Math.round((i - 1) * 0.5);
}
});
}
},

// private
showMonthPicker: function() {
if (!this.disabled) {
this.createMonthPicker();
var size = this.el.getSize();
this.monthPicker.setSize(size);
this.monthPicker.child('table').setSize(size);
if (this.PersianType == true) {
this.mpSelMonth = (this.activeDate || this.value).getPMonth();
this.mpSelYear = (this.activeDate || this.value).getPFullYear();
} else {
this.mpSelMonth = (this.activeDate || this.value).getMonth();
this.mpSelYear = (this.activeDate || this.value).getFullYear();
}
this.updateMPMonth(this.mpSelMonth);
this.updateMPYear(this.mpSelYear);

this.monthPicker.slideIn('t', {
duration: 0.2
});
}
},

// private
onMonthClick: function(e, t) {
e.stopEvent();

var el = new Ext.Element(t),
pn;
if (el.is('button.x-date-mp-cancel')) {
this.hideMonthPicker();
} else if (el.is('button.x-date-mp-ok')) {
var d = new Date().clearTime();
if (this.PersianType == true) {
d.setPFullYear(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getPDate());
} else {
d.setFullYear(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
}

if (d[(this.PersianType == true) ? "getPMonth": "getMonth"]() != this.mpSelMonth) {
// 'fix' the JS rolling date conversion if needed
if (this.PersianType == true) {
d.setPDate(1);
d = d.getPLastDateOfMonth();
} else {
d.setDate(1);
d = getLastDateOfMonth();
}
}
this.update(d);
this.hideMonthPicker();
} else if ((pn = el.up('td.x-date-mp-month', 2))) {
this.mpMonths.removeClass('x-date-mp-sel');
pn.addClass('x-date-mp-sel');
this.mpSelMonth = pn.dom.xmonth;
} else if ((pn = el.up('td.x-date-mp-year', 2))) {
this.mpYears.removeClass('x-date-mp-sel');
pn.addClass('x-date-mp-sel');
this.mpSelYear = pn.dom.xyear;
} else if (el.is('a.x-date-mp-prev')) {
this.updateMPYear(this.mpyear - 10);
} else if (el.is('a.x-date-mp-next')) {
this.updateMPYear(this.mpyear + 10);
}
},

// private
onMonthDblClick: function(e, t) {
e.stopEvent();
var el = new Ext.Element(t),
pn;
var d = new Date().clearTime();
if ((pn = el.up('td.x-date-mp-month', 2))) {
if (this.PersianType == true) {
d.setPFullYear(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getPDate());
} else {
d.setFullYear(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate());
}
} else if ((pn = el.up('td.x-date-mp-year', 2))) {
if (this.PersianType == true) {
d.setPFullYear(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getPDate());
} else {
d.setFullYear(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate());
}
}
this.update(d);
this.hideMonthPicker();
},

// private
showPrevMonth: function(e) {
this.update(this.addDate('mo', -1));
},

// private
showNextMonth: function(e) {
this.update(this.addDate('mo', 1));
},

// private
showPrevYear: function() {
this.update(this.addDate('y', -1));
},

// private
showNextYear: function() {
this.update(this.addDate('y', 1));
},

// private
update: function(date, forceRefresh) {
if (this.rendered) {
var vd = this.activeDate,
vis = this.isVisible();
this.activeDate = date;
if (!forceRefresh && 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 (c.dom.firstChild.dateValue == t) {
c.addClass('x-date-selected');
if (vis) {
Ext.fly(c.dom.firstChild).focus(50);
}
return false;
}
});
return;
}
}

var isP = this.PersianType;
var days = date[(isP) ? "getPDaysInMonth": "getDaysInMonth"](),
firstOfMonth = date[(isP) ? "getPFirstDateOfMonth": "getFirstDateOfMonth"](),
startingPos = firstOfMonth.getDay() - this.startDay;

if (startingPos < 0) {
startingPos += 7;
}

days += startingPos;

var pm = date[(isP) ? "Padd": "add"]('mo', -1),
prevStart = pm[(isP) ? "getPDaysInMonth": "getDaysInMonth"]() - startingPos,
cells = this.cells.elements,
textEls = this.textNodes,
// convert everything to numbers so it's fast
d = (new Date()).clearTime();
if (isP) d.setPFullYear(pm.getPFullYear(), pm.getPMonth(), prevStart);
else d.setFullYear(pm.getFullYear(), pm.getMonth(), prevStart);
day = 86400000,
today = new Date().clearTime().getTime(),
sel = date.clearTime(true).getTime(),
min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
ddMatch = this.disabledDatesRE,
ddText = this.disabledDatesText,
ddays = this.disabledDays ? this.disabledDays.join('') : false,
ddaysText = this.disabledDaysText,
format = this.format;

if (this.showToday) {
var td = new Date().clearTime(),
disable = (td < min || td > max || (ddMatch && format && ddMatch.test(td.dateFormat(format))) || (ddays && ddays.indexOf(td.getDay()) != -1));

if (!this.disabled) {
this.todayBtn.setDisabled(disable);
this.todayKeyListener[disable ? 'disable': 'enable']();
}
}

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 (t == sel) {
cell.className += ' x-date-selected';
if (vis) {
Ext.fly(cell.firstChild).focus(50);
}
}
// disabling
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++) {
var 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]);
}
if (isP) {
this.mbtn.setText(this.PmonthNames[date.getPMonth()] + ' ' + date.getPFullYear());
} else {
this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
}

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

// opera does not respect the auto grow header center column
// then, after it gets a width opera refuses to recalculate
// without a second pass
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]);
}
}
}
}

});

Ext.override(Ext.form.DateField, {
PersianType: true,

initComponent: function() {
Ext.form.DateField.superclass.initComponent.call(this);

this.addEvents(
/**
* @event select
* Fires when a date is selected via the date picker.
* @param {Ext.form.DateField} this
* @param {Date} date The date that was selected
*/
'select');

if (Ext.isString(this.minValue)) {
this.minValue = this.parseDate(this.minValue);
}
if (Ext.isString(this.maxValue)) {
this.maxValue = this.parseDate(this.maxValue);
}
this.disabledDatesRE = null;
this.initDisabledDays();
},

onTriggerClick: function() {
if (this.disabled) {
return;
}
if (this.menu == null) {
this.menu = new Ext.menu.DateMenu({
hideOnClick: false
});
}
this.onFocus();
Ext.apply(this.menu.picker, {
minDate: this.minValue,
maxDate: this.maxValue,
disabledDatesRE: this.disabledDatesRE,
disabledDatesText: this.disabledDatesText,
disabledDays: this.disabledDays,
disabledDaysText: this.disabledDaysText,
format: this.format,
showToday: this.showToday,
PersianType: this.PersianType,
minText: String.format(this.minText, this.formatDate(this.minValue)),
maxText: String.format(this.maxText, this.formatDate(this.maxValue))
});
this.menu.picker.setValue(this.getValue() || new Date());
this.menu.show(this.el, "tl-bl?");
this.menuEvents('on');
},

parseDate: function(value) {
if (!value || Ext.isDate(value)) {
return value;
}
var isP = this.PersianType;
var v = Date[(isP) ? "PparseDate": "parseDate"](value, this.format);
if (!v && this.altFormats) {
if (!this.altFormatsArray) {
this.altFormatsArray = this.altFormats.split("|");
}
for (var i = 0, len = this.altFormatsArray.length; i < len && !v; i++) {
v = Date[(isP) ? "PparseDate": "parseDate"](value, this.altFormatsArray[i]);
}
}
return v;
},

formatDate: function(date) {
return Ext.isDate(date) ? date[(this.PersianType) ? "PdateFormat": "dateFormat"](this.format) : date;
}
});


Example


Ext.onReady(function(){

Ext.QuickTips.init();

// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';

var bd = Ext.getBody();

/*
* ================ Simple form =======================
*/
bd.createChild({tag: 'h2', html: 'Form 1 - Very Simple'});


var simple = new Ext.FormPanel({
labelWidth: 75, // label settings here cascade unless overridden
url:'save-form.php',
frame:true,
title: 'Simple Form',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',

items: [{
fieldLabel: 'Persian Date picker',
name: 'pdatepicker',
allowBlank:false,
PersianType:true,
format:"d-m-Y F",
xtype:"datefield"
}
],

buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});

simple.render(document.body);


});

mrlayeghi
17 Jan 2010, 12:34 AM
Thanks.