[90] | 1 | /* |
---|
| 2 | * Date picker plugin for jQuery |
---|
| 3 | * http://kelvinluck.com/assets/jquery/datePicker |
---|
| 4 | * |
---|
| 5 | * Copyright (c) 2006 Kelvin Luck (kelvnluck.com) |
---|
| 6 | * Licensed under the MIT License: |
---|
| 7 | * http://www.opensource.org/licenses/mit-license.php |
---|
| 8 | * |
---|
| 9 | * $LastChangedDate: 2006-10-08 19:08:47 +0100 (Sun, 08 Oct 2006) $ |
---|
| 10 | * $Rev: 23 $ |
---|
| 11 | */ |
---|
| 12 | |
---|
| 13 | $.datePicker = function() |
---|
| 14 | { |
---|
| 15 | // so that firebug console.log statements don't break IE |
---|
| 16 | if (window.console == undefined) { window.console = {log:function(){}}; } |
---|
| 17 | |
---|
| 18 | var months = ['January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; |
---|
| 19 | var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; |
---|
| 20 | var navLinks = {p:'Prev', n:'Next', c:'Close'}; |
---|
| 21 | var dateFormat = 'dd/mm/yyyy'; |
---|
| 22 | var _firstDate; |
---|
| 23 | var _lastDate; |
---|
| 24 | |
---|
| 25 | var _selectedDate; |
---|
| 26 | var _openCal; |
---|
| 27 | |
---|
| 28 | var _zeroPad = function(num) { |
---|
| 29 | var s = '0'+num; |
---|
| 30 | return s.substring(s.length-2) |
---|
| 31 | //return ('0'+num).substring(-2); // doesn't work on IE :( |
---|
| 32 | }; |
---|
| 33 | var _strToDate = function(dIn) |
---|
| 34 | { |
---|
| 35 | switch (dateFormat.toLowerCase()) { |
---|
| 36 | case 'yyyy-mm-dd': |
---|
| 37 | dParts = dIn.split('-'); |
---|
| 38 | return new Date(dParts[0], Number(dParts[1])-1, dParts[2]); |
---|
| 39 | case 'dd/mm/yyyy': |
---|
| 40 | dParts = dIn.split('/'); |
---|
| 41 | return new Date(dParts[2], Number(dParts[1])-1, Number(dParts[0])); |
---|
| 42 | case 'mm/dd/yyyy': |
---|
| 43 | default: |
---|
| 44 | var parts = parts ? parts : [2, 1, 0]; |
---|
| 45 | dParts = dIn.split('/'); |
---|
| 46 | return new Date(dParts[2], Number(dParts[0])-1, Number(dParts[1])); |
---|
| 47 | } |
---|
| 48 | }; |
---|
| 49 | var _dateToStr = function(d) |
---|
| 50 | { |
---|
| 51 | var dY = d.getFullYear(); |
---|
| 52 | var dM = _zeroPad(d.getMonth()+1); |
---|
| 53 | var dD = _zeroPad(d.getDate()); |
---|
| 54 | switch (dateFormat.toLowerCase()) { |
---|
| 55 | case 'yyyy/mm/dd': |
---|
| 56 | return dY + '/' + dM + '/' + dD; |
---|
| 57 | case 'yyyy-mm-dd': |
---|
| 58 | return dY + '-' + dM + '-' + dD; |
---|
| 59 | case 'dd/mm/yyyy': |
---|
| 60 | return dD + '/' + dM + '/' + dY; |
---|
| 61 | case 'mm/dd/yyyy': |
---|
| 62 | default: |
---|
| 63 | return dM + '/' + dD + '/' + dY; |
---|
| 64 | } |
---|
| 65 | }; |
---|
| 66 | |
---|
| 67 | var _getCalendarDiv = function(dIn) |
---|
| 68 | { |
---|
| 69 | var today = new Date(); |
---|
| 70 | if (dIn == undefined) { |
---|
| 71 | // start from this month. |
---|
| 72 | d = new Date(today.getFullYear(), today.getMonth(), 1); |
---|
| 73 | } else { |
---|
| 74 | // start from the passed in date |
---|
| 75 | d = dIn; |
---|
| 76 | d.setDate(1); |
---|
| 77 | } |
---|
| 78 | // check that date is within allowed limits: |
---|
| 79 | if ((d.getMonth() < _firstDate.getMonth() && d.getFullYear() == _firstDate.getFullYear()) || d.getFullYear() < _firstDate.getFullYear()) { |
---|
| 80 | d = new Date(_firstDate.getFullYear(), _firstDate.getMonth(), 1);; |
---|
| 81 | } else if ((d.getMonth() > _lastDate.getMonth() && d.getFullYear() == _lastDate.getFullYear()) || d.getFullYear() > _lastDate.getFullYear()) { |
---|
| 82 | d = new Date(_lastDate.getFullYear(), _lastDate.getMonth(), 1);; |
---|
| 83 | } |
---|
| 84 | |
---|
| 85 | var calDiv = $.DIV({className:'popup-calendar'}, ''); |
---|
| 86 | var jCalDiv = $(calDiv); |
---|
| 87 | var firstMonth = true; |
---|
| 88 | var firstDate = _firstDate.getDate(); |
---|
| 89 | |
---|
| 90 | // create prev and next links |
---|
| 91 | var prevLinkDiv = ''; |
---|
| 92 | if (!(d.getMonth() == _firstDate.getMonth() && d.getFullYear() == _firstDate.getFullYear())) { |
---|
| 93 | // not in first display month so show a previous link |
---|
| 94 | firstMonth = false; |
---|
| 95 | var lastMonth = new Date(d.getFullYear(), d.getMonth()-1, 1); |
---|
| 96 | var prevLink = $.A({href:'javascript:;'}, navLinks.p); |
---|
| 97 | $(prevLink).click(function() |
---|
| 98 | { |
---|
| 99 | $.datePicker.changeMonth(lastMonth, this); |
---|
| 100 | return false; |
---|
| 101 | }); |
---|
| 102 | prevLinkDiv = $.DIV({className:'link-prev'}, '<', prevLink); |
---|
| 103 | } |
---|
| 104 | |
---|
| 105 | var finalMonth = true; |
---|
| 106 | var lastDate = _lastDate.getDate(); |
---|
| 107 | nextLinkDiv = ''; |
---|
| 108 | if (!(d.getMonth() == _lastDate.getMonth() && d.getFullYear() == _lastDate.getFullYear())) { |
---|
| 109 | // in the last month - no next link |
---|
| 110 | finalMonth = false; |
---|
| 111 | var nextMonth = new Date(d.getFullYear(), d.getMonth()+1, 1); |
---|
| 112 | var nextLink = $.A({href:'javascript:;'}, navLinks.n); |
---|
| 113 | $(nextLink).click(function() |
---|
| 114 | { |
---|
| 115 | $.datePicker.changeMonth(nextMonth, this); |
---|
| 116 | return false; |
---|
| 117 | }); |
---|
| 118 | nextLinkDiv = $.DIV({className:'link-next'}, nextLink, '>'); |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | var closeLink = $.A({href:'javascript:;'}, navLinks.c); |
---|
| 122 | $(closeLink).click(function() |
---|
| 123 | { |
---|
| 124 | $.datePicker.closeCalendar(); |
---|
| 125 | }); |
---|
| 126 | |
---|
| 127 | jCalDiv.append( |
---|
| 128 | $.DIV({className:'link-close'}, closeLink), |
---|
| 129 | $.H3({}, months[d.getMonth()], ' ', d.getFullYear()) |
---|
| 130 | ); |
---|
| 131 | |
---|
| 132 | var headRow = $.TR({}); |
---|
| 133 | for (var i=0; i<7; i++) { |
---|
| 134 | var day = days[i]; |
---|
| 135 | headRow.appendChild( |
---|
| 136 | $.TH({scope:'col', abbr:day, title:day}, day.substr(0, 1)) |
---|
| 137 | ); |
---|
| 138 | } |
---|
| 139 | |
---|
| 140 | var tBody = $.TBODY(); |
---|
| 141 | |
---|
| 142 | var lastDay = (new Date(d.getFullYear(), d.getMonth()+1, 0)).getDate(); |
---|
| 143 | var curDay = -d.getDay(); |
---|
| 144 | |
---|
| 145 | var todayDate = (new Date()).getDate(); |
---|
| 146 | var thisMonth = d.getMonth() == today.getMonth() && d.getFullYear() == today.getFullYear(); |
---|
| 147 | |
---|
| 148 | var w = 0; |
---|
| 149 | while (w++<6) { |
---|
| 150 | var thisRow = $.TR({}); |
---|
| 151 | for (var i=0; i<7; i++) { |
---|
| 152 | var atts = {}; |
---|
| 153 | |
---|
| 154 | if (curDay < 0 || curDay >= lastDay) { |
---|
| 155 | dayStr = ' '; |
---|
| 156 | } else if (firstMonth && curDay < firstDate-1) { |
---|
| 157 | dayStr = curDay+1; |
---|
| 158 | atts.className = 'inactive'; |
---|
| 159 | } else if (finalMonth && curDay > lastDate-1) { |
---|
| 160 | dayStr = curDay+1; |
---|
| 161 | atts.className = 'inactive'; |
---|
| 162 | } else { |
---|
| 163 | d.setDate(curDay+1); |
---|
| 164 | var dStr = _dateToStr(d); |
---|
| 165 | dayStr = $.A({href:'#', rel:dStr}, curDay+1); |
---|
| 166 | $(dayStr).click(function(e) |
---|
| 167 | { |
---|
| 168 | $.datePicker.selectDate($.attr(this, 'rel'), this); |
---|
| 169 | return false; |
---|
| 170 | }); |
---|
| 171 | if (_selectedDate && _selectedDate==dStr) { |
---|
| 172 | $(dayStr).addClass('selected'); |
---|
| 173 | } |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | if (thisMonth && curDay+1 == todayDate) { |
---|
| 177 | atts.className = 'today'; |
---|
| 178 | } |
---|
| 179 | thisRow.appendChild($.TD(atts, dayStr)); |
---|
| 180 | curDay++; |
---|
| 181 | } |
---|
| 182 | tBody.appendChild(thisRow); |
---|
| 183 | } |
---|
| 184 | |
---|
| 185 | jCalDiv.append( |
---|
| 186 | $.TABLE({cellspacing:2}, $.THEAD({}, headRow), tBody), |
---|
| 187 | prevLinkDiv, |
---|
| 188 | nextLinkDiv |
---|
| 189 | ); |
---|
| 190 | |
---|
| 191 | if ($.browser.msie) { |
---|
| 192 | |
---|
| 193 | // we put a styled iframe behind the calendar so HTML SELECT elements don't show through |
---|
| 194 | jCalDiv.append(document.createElement('iframe')); |
---|
| 195 | |
---|
| 196 | } |
---|
| 197 | jCalDiv.css({'display':'block'}); |
---|
| 198 | return calDiv; |
---|
| 199 | }; |
---|
| 200 | var _draw = function(c) |
---|
| 201 | { |
---|
| 202 | // explicitly empty the calendar before removing it to reduce the (MASSIVE!) memory leak in IE |
---|
| 203 | // still not perfect but a lot better! |
---|
| 204 | // Strangely if you chain the methods it reacts differently - when chained opening the calendar on |
---|
| 205 | // IE uses a bunch of memory and pressing next/prev doubles this memory. When you close the calendar |
---|
| 206 | // the memory is freed. If they aren't chained then pressing next or previous doesn't double the used |
---|
| 207 | // memory so only one chunk of memory is used when you open the calendar (which is also freed when you |
---|
| 208 | // close the calendar). |
---|
| 209 | $('div.popup-calendar a', _openCal).unbind(); |
---|
| 210 | $('div.popup-calendar', _openCal).empty(); |
---|
| 211 | $('div.popup-calendar', _openCal).remove(); |
---|
| 212 | _openCal.append(c); |
---|
| 213 | }; |
---|
| 214 | var _closeDatePicker = function() |
---|
| 215 | { |
---|
| 216 | $('div.popup-calendar a', _openCal).unbind(); |
---|
| 217 | $('div.popup-calendar', _openCal).empty(); |
---|
| 218 | $('div.popup-calendar', _openCal).css({'display':'none'}); |
---|
| 219 | |
---|
| 220 | /* |
---|
| 221 | if ($.browser.msie) { |
---|
| 222 | _openCal.unbind('keypress', _handleKeys); |
---|
| 223 | } else { |
---|
| 224 | $(window).unbind('keypress', _handleKeys); |
---|
| 225 | } |
---|
| 226 | */ |
---|
| 227 | $(document).unbind('mousedown', _checkMouse); |
---|
| 228 | delete _openCal; |
---|
| 229 | _openCal = null; |
---|
| 230 | }; |
---|
| 231 | var _handleKeys = function(e) |
---|
| 232 | { |
---|
| 233 | var key = e.keyCode ? e.keyCode : (e.which ? e.which: 0); |
---|
| 234 | //console.log('KEY!! ' + key); |
---|
| 235 | if (key == 27) { |
---|
| 236 | _closeDatePicker(); |
---|
| 237 | } |
---|
| 238 | return false; |
---|
| 239 | }; |
---|
| 240 | var _checkMouse = function(e) |
---|
| 241 | { |
---|
| 242 | var target = $.browser.msie ? window.event.srcElement : e.target; |
---|
| 243 | var cp = $(target).findClosestParent('div.popup-calendar'); |
---|
| 244 | if (cp.get(0).className != 'date-picker-holder') { |
---|
| 245 | _closeDatePicker(); |
---|
| 246 | } |
---|
| 247 | }; |
---|
| 248 | |
---|
| 249 | return { |
---|
| 250 | show: function() |
---|
| 251 | { |
---|
| 252 | if (_openCal) { |
---|
| 253 | _closeDatePicker(); |
---|
| 254 | } |
---|
| 255 | this.blur(); |
---|
| 256 | var input = $('input', $(this).findClosestParent('input'))[0]; |
---|
| 257 | _firstDate = input._startDate; |
---|
| 258 | _lastDate = input._endDate; |
---|
| 259 | _openCal = $(this).findClosestParent('div.popup-calendar'); |
---|
| 260 | var d = $(input).val(); |
---|
| 261 | if (d != '') { |
---|
| 262 | if (_dateToStr(_strToDate(d)) == d) { |
---|
| 263 | _selectedDate = d; |
---|
| 264 | _draw(_getCalendarDiv(_strToDate(d))); |
---|
| 265 | } else { |
---|
| 266 | // invalid date in the input field - just default to this month |
---|
| 267 | _selectedDate = false; |
---|
| 268 | _draw(_getCalendarDiv()); |
---|
| 269 | } |
---|
| 270 | } else { |
---|
| 271 | _selectedDate = false; |
---|
| 272 | _draw(_getCalendarDiv()); |
---|
| 273 | } |
---|
| 274 | /* |
---|
| 275 | if ($.browser == "msie") { |
---|
| 276 | _openCal.bind('keypress', _handleKeys); |
---|
| 277 | } else { |
---|
| 278 | $(window).bind('keypress', _handleKeys); |
---|
| 279 | } |
---|
| 280 | */ |
---|
| 281 | $(document).bind('mousedown', _checkMouse); |
---|
| 282 | }, |
---|
| 283 | changeMonth: function(d, e) |
---|
| 284 | { |
---|
| 285 | |
---|
| 286 | _draw(_getCalendarDiv(d)); |
---|
| 287 | }, |
---|
| 288 | selectDate: function(d, ele) |
---|
| 289 | { |
---|
| 290 | selectedDate = d; |
---|
| 291 | $('input', $(ele).findClosestParent('input')).val(d); |
---|
| 292 | _closeDatePicker(ele); |
---|
| 293 | }, |
---|
| 294 | closeCalendar: function() |
---|
| 295 | { |
---|
| 296 | _closeDatePicker(this); |
---|
| 297 | }, |
---|
| 298 | setInited: function(i) |
---|
| 299 | { |
---|
| 300 | i._inited = true; |
---|
| 301 | }, |
---|
| 302 | isInited: function(i) |
---|
| 303 | { |
---|
| 304 | return i._inited != undefined; |
---|
| 305 | }, |
---|
| 306 | setDateFormat: function(format) |
---|
| 307 | { |
---|
| 308 | // set's the format that selected dates are returned in. |
---|
| 309 | // options are 'dd/mm/yyyy' (european), 'mm/dd/yyyy' (americian) and 'yyyy-mm-dd' (unicode) |
---|
| 310 | dateFormat = format; |
---|
| 311 | }, |
---|
| 312 | /** |
---|
| 313 | * Function: setLanguageStrings |
---|
| 314 | * |
---|
| 315 | * Allows you to localise the calendar by passing in relevant text for the english strings in the plugin. |
---|
| 316 | * |
---|
| 317 | * Arguments: |
---|
| 318 | * days - Array, e.g. ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] |
---|
| 319 | * months - Array, e.g. ['January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; |
---|
| 320 | * navLinks - Object, e.g. {p:'Prev', n:'Next', c:'Close'} |
---|
| 321 | **/ |
---|
| 322 | setLanguageStrings: function(aDays, aMonths, aNavLinks) |
---|
| 323 | { |
---|
| 324 | days = aDays; |
---|
| 325 | months = aMonths; |
---|
| 326 | navLinks = aNavLinks; |
---|
| 327 | }, |
---|
| 328 | /** |
---|
| 329 | * Function: setDateWindow |
---|
| 330 | * |
---|
| 331 | * Used internally to set the start and end dates for a given date select |
---|
| 332 | * |
---|
| 333 | * Arguments: |
---|
| 334 | * i - The id of the INPUT element this date window is for |
---|
| 335 | * w - The date window - an object containing startDate and endDate properties |
---|
| 336 | * each in the current format as set in a call to setDateFormat (or in the |
---|
| 337 | * default format dd/mm/yyyy if setDateFormat hasn't been called). |
---|
| 338 | * e.g. {startDate:'01/03/2006', endDate:'11/04/2006} |
---|
| 339 | **/ |
---|
| 340 | setDateWindow: function(i, w) |
---|
| 341 | { |
---|
| 342 | if (w == undefined) w = {}; |
---|
| 343 | if (w.startDate == undefined) { |
---|
| 344 | i._startDate = new Date(); |
---|
| 345 | } else { |
---|
| 346 | i._startDate = _strToDate(w.startDate); |
---|
| 347 | } |
---|
| 348 | if (w.endDate == undefined) { |
---|
| 349 | i._endDate = new Date(); |
---|
| 350 | i._endDate.setFullYear(i._endDate.getFullYear()+5); |
---|
| 351 | } else { |
---|
| 352 | i._endDate = _strToDate(w.endDate); |
---|
| 353 | }; |
---|
| 354 | } |
---|
| 355 | }; |
---|
| 356 | }(); |
---|
| 357 | $.fn.findClosestParent = function(s) |
---|
| 358 | { |
---|
| 359 | var ele = this; |
---|
| 360 | while (true) { |
---|
| 361 | if ($(s, ele).length > 0) { |
---|
| 362 | return (ele); |
---|
| 363 | } |
---|
| 364 | ele = ele.parent(); |
---|
| 365 | if(ele[0].length == 0) { |
---|
| 366 | return false; |
---|
| 367 | } |
---|
| 368 | } |
---|
| 369 | }; |
---|
| 370 | $.fn.datePicker = function(a) |
---|
| 371 | { |
---|
| 372 | this.each(function() { |
---|
| 373 | if(this.nodeName.toLowerCase() != 'input') return; |
---|
| 374 | $.datePicker.setDateWindow(this, a); |
---|
| 375 | if (!$.datePicker.isInited(this)) { |
---|
| 376 | var calBut = $.A({href:'javascript:;', className:'date-picker', title:'Choose date'}, $.SPAN({}, 'Choose date')); |
---|
| 377 | $(calBut).click($.datePicker.show); |
---|
| 378 | $(this).wrap( |
---|
| 379 | '<div class="date-picker-holder"></div>' |
---|
| 380 | ).before( |
---|
| 381 | $.DIV({className:'popup-calendar'}) |
---|
| 382 | ).after( |
---|
| 383 | calBut |
---|
| 384 | ); |
---|
| 385 | $.datePicker.setInited(this); |
---|
| 386 | } |
---|
| 387 | }); |
---|
| 388 | |
---|
| 389 | }; |
---|
| 390 | /* |
---|
| 391 | <!-- Generated calendar HTML looks like this - style with CSS --> |
---|
| 392 | <div class="popup-calendar"> |
---|
| 393 | <div class="link-close"><a href="#">Close</a></div> |
---|
| 394 | <h3>July 2006</h3> |
---|
| 395 | <table cellspacing="2"> |
---|
| 396 | <thead> |
---|
| 397 | <tr> |
---|
| 398 | <th scope="col" abbr="Monday" title="Monday">M</th> |
---|
| 399 | <th scope="col" abbr="Tuesday" title="Tuesday">T</th> |
---|
| 400 | <th scope="col" abbr="Wednesday" title="Wednesday">W</th> |
---|
| 401 | <th scope="col" abbr="Thursday" title="Thursday">T</th> |
---|
| 402 | <th scope="col" abbr="Friday" title="Friday">F</th> |
---|
| 403 | <th scope="col" abbr="Saturday" title="Saturday">S</th> |
---|
| 404 | <th scope="col" abbr="Sunday" title="Sunday">S</th> |
---|
| 405 | </tr> |
---|
| 406 | </thead> |
---|
| 407 | <tbody> |
---|
| 408 | <tr> |
---|
| 409 | <td> </td> |
---|
| 410 | <td> </td> |
---|
| 411 | <td> </td> |
---|
| 412 | <td class="inactive">1</td> |
---|
| 413 | <td class="inactive">2</td> |
---|
| 414 | <td class="inactive">3</td> |
---|
| 415 | <td class="inactive">4</td> |
---|
| 416 | </tr> |
---|
| 417 | <tr> |
---|
| 418 | <td class="inactive">5</td> |
---|
| 419 | <td class="inactive">6</td> |
---|
| 420 | <td class="inactive">7</td> |
---|
| 421 | <td class="today"><a href="#">8</a></td> |
---|
| 422 | <td><a href="#">9</a></td> |
---|
| 423 | <td><a href="#">10</a></td> |
---|
| 424 | <td><a href="#">11</a></td> |
---|
| 425 | </tr> |
---|
| 426 | <tr> |
---|
| 427 | <td><a href="#">12</a></td> |
---|
| 428 | <td><a href="#">13</a></td> |
---|
| 429 | <td><a href="#">14</a></td> |
---|
| 430 | <td><a href="#">15</a></td> |
---|
| 431 | <td><a href="#">16</a></td> |
---|
| 432 | <td><a href="#">17</a></td> |
---|
| 433 | <td><a href="#" class="selected">18</a></td> |
---|
| 434 | </tr> |
---|
| 435 | <tr> |
---|
| 436 | <td><a href="#">19</a></td> |
---|
| 437 | <td><a href="#">20</a></td> |
---|
| 438 | <td><a href="#">21</a></td> |
---|
| 439 | <td><a href="#">22</a></td> |
---|
| 440 | <td><a href="#">23</a></td> |
---|
| 441 | <td><a href="#">24</a></td> |
---|
| 442 | <td><a href="#">25</a></td> |
---|
| 443 | </tr> |
---|
| 444 | <tr> |
---|
| 445 | <td><a href="#">26</a></td> |
---|
| 446 | <td><a href="#">27</a></td> |
---|
| 447 | <td><a href="#">28</a></td> |
---|
| 448 | <td><a href="#">29</a></td> |
---|
| 449 | <td><a href="#">30</a></td> |
---|
| 450 | <td> </td> |
---|
| 451 | <td> </td> |
---|
| 452 | </tr> |
---|
| 453 | </tbody> |
---|
| 454 | </table> |
---|
| 455 | <div class="link-prev"><a href="#">Prev</a></div> |
---|
| 456 | <div class="link-next"><a href="#">Next</a></div> |
---|
| 457 | </div> |
---|
| 458 | */ |
---|