2 the script only works on "input [type=text]"
\r
3 http://teddevito.com/demos/calendar.php
\r
6 // don't declare anything out here in the global namespace
\r
8 (function($) { // create private scope (inside you can use $ instead of jQuery)
\r
10 // functions and vars declared here are effectively 'singletons'. there will be only a single
\r
11 // instance of them. so this is a good place to declare any immutable items or stateless
\r
12 // functions. for example:
\r
14 var today = new Date(); // used in defaults
\r
15 var months = 'January,February,March,April,May,June,July,August,September,October,November,December'.split(',');
\r
16 var months = l10n_cal_month;
\r
17 var monthlengths = '31,28,31,30,31,30,31,31,30,31,30,31'.split(',');
\r
18 var dateRegEx = /^\d{1,2}\/\d{1,2}\/\d{2}|\d{4}$/;
\r
19 var yearRegEx = /^\d{4,4}$/;
\r
21 // next, declare the plugin function
\r
22 $.fn.simpleDatepicker = function(options) {
\r
24 // functions and vars declared here are created each time your plugn function is invoked
\r
26 // you could probably refactor your 'build', 'load_month', etc, functions to be passed
\r
27 // the DOM element from below
\r
29 var opts = jQuery.extend({}, jQuery.fn.simpleDatepicker.defaults, options);
\r
31 // replaces a date string with a date object in opts.startdate and opts.enddate, if one exists
\r
32 // populates two new properties with a ready-to-use year: opts.startyear and opts.endyear
\r
35 /** extracts and setup a valid year range from the opts object **/
\r
36 function setupYearRange () {
\r
38 var startyear, endyear;
\r
39 if (opts.startdate.constructor == Date) {
\r
40 startyear = opts.startdate.getFullYear();
\r
41 } else if (opts.startdate) {
\r
42 if (yearRegEx.test(opts.startdate)) {
\r
43 startyear = opts.startdate;
\r
44 } else if (dateRegEx.test(opts.startdate)) {
\r
45 opts.startdate = new Date(opts.startdate);
\r
46 startyear = opts.startdate.getFullYear();
\r
48 startyear = today.getFullYear();
\r
51 startyear = today.getFullYear();
\r
53 opts.startyear = startyear;
\r
55 if (opts.enddate.constructor == Date) {
\r
56 endyear = opts.enddate.getFullYear();
\r
57 } else if (opts.enddate) {
\r
58 if (yearRegEx.test(opts.enddate)) {
\r
59 endyear = opts.enddate;
\r
60 } else if (dateRegEx.test(opts.enddate)) {
\r
61 opts.enddate = new Date(opts.enddate);
\r
62 endyear = opts.enddate.getFullYear();
\r
64 endyear = today.getFullYear();
\r
67 endyear = today.getFullYear();
\r
69 opts.endyear = endyear;
\r
72 /** HTML factory for the actual datepicker table element **/
\r
73 // has to read the year range so it can setup the correct years in our HTML <select>
\r
74 function newDatepickerHTML () {
\r
78 // process year range into an array
\r
79 for (var i = 0; i <= opts.endyear - opts.startyear; i ++) years[i] = opts.startyear + i;
\r
81 // build the table structure
\r
82 var table = jQuery('<table class="datepicker" cellpadding="0" cellspacing="0"></table>');
\r
83 table.append('<thead></thead>');
\r
84 table.append('<tfoot></tfoot>');
\r
85 table.append('<tbody></tbody>');
\r
87 // month select field
\r
88 var monthselect = '<select name="month">';
\r
89 for (var i in l10n_cal_month) monthselect += '<option value="'+i+'">'+l10n_cal_month[i]+'</option>';
\r
90 monthselect += '</select>';
\r
92 // year select field
\r
93 var yearselect = '<select name="year">';
\r
94 for (var i in years) yearselect += '<option>'+years[i]+'</option>';
\r
95 yearselect += '</select>';
\r
97 jQuery("thead",table).append('<tr class="controls"><th colspan="7"><span class="prevMonth">«</span> '+monthselect+yearselect+' <span class="nextMonth">»</span></th></tr>');
\r
98 jQuery("thead",table).append('<tr class="days"><th>'+l10n_cal_days[0]+'</th><th>'+l10n_cal_days[1]+'</th><th>'+l10n_cal_days[2]+'</th><th>'+l10n_cal_days[3]+'</th><th>'+l10n_cal_days[4]+'</th><th>'+l10n_cal_days[5]+'</th><th>'+l10n_cal_days[6]+'</th></tr>');
\r
99 jQuery("tfoot",table).append('<tr><td colspan="2"><span class="today">'+l10n_cal_today+'</span></td><td colspan="3"> </td><td colspan="2"><span class="close">'+l10n_cal_close+'</span></td></tr>');
\r
100 for (var i = 0; i < 6; i++) jQuery("tbody",table).append('<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>');
\r
104 /** get the real position of the input (well, anything really) **/
\r
105 //http://www.quirksmode.org/js/findpos.html
\r
106 function findPosition (obj) {
\r
107 var curleft = curtop = 0;
\r
108 if (obj.offsetParent) {
\r
110 curleft += obj.offsetLeft;
\r
111 curtop += obj.offsetTop;
\r
112 } while (obj = obj.offsetParent);
\r
113 return [curleft,curtop];
\r
119 /** load the initial date and handle all date-navigation **/
\r
120 // initial calendar load (e is null)
\r
121 // prevMonth & nextMonth buttons
\r
122 // onchange for the select fields
\r
123 function loadMonth (e, el, datepicker, chosendate) {
\r
125 // reference our years for the nextMonth and prevMonth buttons
\r
126 var mo = jQuery("select[name=month]", datepicker).get(0).selectedIndex;
\r
127 var yr = jQuery("select[name=year]", datepicker).get(0).selectedIndex;
\r
128 var yrs = jQuery("select[name=year] option", datepicker).get().length;
\r
130 // first try to process buttons that may change the month we're on
\r
131 if (e && jQuery(e.target).hasClass('prevMonth')) {
\r
132 if (0 == mo && yr) {
\r
134 jQuery("select[name=month]", datepicker).get(0).selectedIndex = 11;
\r
135 jQuery("select[name=year]", datepicker).get(0).selectedIndex = yr;
\r
138 jQuery("select[name=month]", datepicker).get(0).selectedIndex = mo;
\r
140 } else if (e && jQuery(e.target).hasClass('nextMonth')) {
\r
141 if (11 == mo && yr + 1 < yrs) {
\r
143 jQuery("select[name=month]", datepicker).get(0).selectedIndex = 0;
\r
144 jQuery("select[name=year]", datepicker).get(0).selectedIndex = yr;
\r
147 jQuery("select[name=month]", datepicker).get(0).selectedIndex = mo;
\r
151 // maybe hide buttons
\r
152 if (0 == mo && !yr) jQuery("span.prevMonth", datepicker).hide();
\r
153 else jQuery("span.prevMonth", datepicker).show();
\r
154 if (yr + 1 == yrs && 11 == mo) jQuery("span.nextMonth", datepicker).hide();
\r
155 else jQuery("span.nextMonth", datepicker).show();
\r
157 // clear the old cells
\r
158 var cells = jQuery("tbody td", datepicker).unbind().empty().removeClass('date');
\r
160 // figure out what month and year to load
\r
161 var m = jQuery("select[name=month]", datepicker).val();
\r
162 var y = jQuery("select[name=year]", datepicker).val();
\r
163 var d = new Date(y, m, 1);
\r
164 var startindex = d.getDay();
\r
165 var numdays = monthlengths[m];
\r
167 // http://en.wikipedia.org/wiki/Leap_year
\r
168 if (1 == m && ((y%4 == 0 && y%100 != 0) || y%400 == 0)) numdays = 29;
\r
170 // test for end dates (instead of just a year range)
\r
171 if (opts.startdate.constructor == Date) {
\r
172 var startMonth = opts.startdate.getMonth();
\r
173 var startDate = opts.startdate.getDate();
\r
175 if (opts.enddate.constructor == Date) {
\r
176 var endMonth = opts.enddate.getMonth();
\r
177 var endDate = opts.enddate.getDate();
\r
180 // walk through the index and populate each cell, binding events too
\r
181 for (var i = 0; i < numdays; i++) {
\r
183 var cell = jQuery(cells.get(i+startindex)).removeClass('chosen');
\r
185 // test that the date falls within a range, if we have a range
\r
187 (yr || ((!startDate && !startMonth) || ((i+1 >= startDate && mo == startMonth) || mo > startMonth))) &&
\r
188 (yr + 1 < yrs || ((!endDate && !endMonth) || ((i+1 <= endDate && mo == endMonth) || mo < endMonth)))) {
\r
194 function () { jQuery(this).addClass('over'); },
\r
195 function () { jQuery(this).removeClass('over'); })
\r
196 .click(function () {
\r
197 var chosenDateObj = new Date(jQuery("select[name=year]", datepicker).val(), jQuery("select[name=month]", datepicker).val(), jQuery(this).text());
\r
198 closeIt(el, datepicker, chosenDateObj);
\r
201 // highlight the previous chosen date
\r
202 if (i+1 == chosendate.getDate() && m == chosendate.getMonth() && y == chosendate.getFullYear()) cell.addClass('chosen');
\r
207 /** closes the datepicker **/
\r
208 // sets the currently matched input element's value to the date, if one is available
\r
209 // remove the table element from the DOM
\r
210 // indicate that there is no datepicker for the currently matched input element
\r
211 function closeIt (el, datepicker, dateObj) {
\r
212 if (dateObj && dateObj.constructor == Date)
\r
213 el.val(jQuery.fn.simpleDatepicker.formatOutput(dateObj));
\r
214 datepicker.remove();
\r
216 jQuery.data(el.get(0), "simpleDatepicker", { hasDatepicker : false });
\r
219 // iterate the matched nodeset
\r
220 return this.each(function() {
\r
222 // functions and vars declared here are created for each matched element. so if
\r
223 // your functions need to manage or access per-node state you can defined them
\r
224 // here and use $this to get at the DOM element
\r
226 if ( jQuery(this).is('input') && 'text' == jQuery(this).attr('type')) {
\r
229 jQuery.data(jQuery(this).get(0), "simpleDatepicker", { hasDatepicker : false });
\r
231 // open a datepicker on the click event
\r
232 jQuery(this).click(function (ev) {
\r
234 var $this = jQuery(ev.target);
\r
236 if (false == jQuery.data($this.get(0), "simpleDatepicker").hasDatepicker) {
\r
238 // store data telling us there is already a datepicker
\r
239 jQuery.data($this.get(0), "simpleDatepicker", { hasDatepicker : true });
\r
241 // validate the form's initial content for a date
\r
242 var initialDate = $this.val();
\r
244 if (initialDate && dateRegEx.test(initialDate)) {
\r
245 var chosendate = new Date(initialDate);
\r
246 } else if (opts.chosendate.constructor == Date) {
\r
247 var chosendate = opts.chosendate;
\r
248 } else if (opts.chosendate) {
\r
249 var chosendate = new Date(opts.chosendate);
\r
251 var chosendate = today;
\r
254 // insert the datepicker in the DOM
\r
255 datepicker = newDatepickerHTML();
\r
256 jQuery("body").prepend(datepicker);
\r
258 // position the datepicker
\r
259 var elPos = findPosition($this.get(0));
\r
260 var x = (parseInt(opts.x) ? parseInt(opts.x) : 0) + elPos[0];
\r
261 var y = (parseInt(opts.y) ? parseInt(opts.y) : 0) + elPos[1];
\r
262 jQuery(datepicker).css({ position: 'absolute', left: x, top: y });
\r
264 // bind events to the table controls
\r
265 jQuery("span", datepicker).css("cursor","pointer");
\r
266 jQuery("select", datepicker).bind('change', function () { loadMonth (null, $this, datepicker, chosendate); });
\r
267 jQuery("span.prevMonth", datepicker).click(function (e) { loadMonth (e, $this, datepicker, chosendate); });
\r
268 jQuery("span.nextMonth", datepicker).click(function (e) { loadMonth (e, $this, datepicker, chosendate); });
\r
269 jQuery("span.today", datepicker).click(function () { closeIt($this, datepicker, new Date()); });
\r
270 jQuery("span.close", datepicker).click(function () { closeIt($this, datepicker); });
\r
272 // set the initial values for the month and year select fields
\r
273 // and load the first month
\r
274 jQuery("select[name=month]", datepicker).get(0).selectedIndex = chosendate.getMonth();
\r
275 jQuery("select[name=year]", datepicker).get(0).selectedIndex = Math.max(0, chosendate.getFullYear() - opts.startyear);
\r
276 loadMonth(null, $this, datepicker, chosendate);
\r
286 // finally, I like to expose default plugin options as public so they can be manipulated. one
\r
287 // way to do this is to add a property to the already-public plugin fn
\r
289 jQuery.fn.simpleDatepicker.formatOutput = function (dateObj) {
\r
290 return (dateObj.getMonth() + 1) + "/" + dateObj.getDate() + "/" + dateObj.getFullYear();
\r
293 jQuery.fn.simpleDatepicker.defaults = {
\r
294 // date string matching /^\d{1,2}\/\d{1,2}\/\d{2}|\d{4}$/
\r
295 chosendate : today,
\r
297 // date string matching /^\d{1,2}\/\d{1,2}\/\d{2}|\d{4}$/
\r
298 // or four digit year
\r
299 startdate : today.getFullYear(),
\r
300 enddate : today.getFullYear() + 1,
\r
302 // offset from the top left corner of the input element
\r
303 x : 1, // must be in px
\r
304 y : 18 // must be in px
\r
310 $(document).ready(function(){
\r
311 $('#date_first').simpleDatepicker({startdate: 2005, enddate: 2100});
\r
312 $('#date_second').simpleDatepicker({startdate: 2005, enddate: 2100});
\r
314 $('#date_filter').change(function(){
\r
315 var show = $(this).val() == 'between' ? 'inline' : 'none';
\r
316 $('#date_second').css('display', show);
\r
317 $('#date_and').css('display', show);
\r