1 2 3(function($) { // Hide scope, no $ conflict 4 'use strict'; 5 6 var pluginName = 'datepick'; 7 8 $.JQPlugin.createPlugin({ 9 10 /** The name of the plugin. 11 @default 'datepick' */ 12 name: pluginName, 13 14 /** Default template for generating a datepicker. 15 Insert anywhere: '{l10n:name}' to insert localised value for name, 16 '{link:name}' to insert a link trigger for command name, 17 '{button:name}' to insert a button trigger for command name, 18 '{popup:start}...{popup:end}' to mark a section for inclusion in a popup datepicker only, 19 '{inline:start}...{inline:end}' to mark a section for inclusion in an inline datepicker only. 20 @property {string} picker Overall structure: '{months}' to insert calendar months. 21 @property {string} monthRow One row of months: '{months}' to insert calendar months. 22 @property {string} month A single month: '{monthHeader<em>:dateFormat</em>}' to insert the month header - 23 <em>dateFormat</em> is optional and defaults to 'MM yyyy', 24 '{weekHeader}' to insert a week header, '{weeks}' to insert the month's weeks. 25 @property {string} weekHeader A week header: '{days}' to insert individual day names. 26 @property {string} dayHeader Individual day header: '{day}' to insert day name. 27 @property {string} week One week of the month: '{days}' to insert the week's days, 28 '{weekOfYear}' to insert week of year. 29 @property {string} day An individual day: '{day}' to insert day value. 30 @property {string} monthSelector jQuery selector, relative to picker, for a single month. 31 @property {string} daySelector jQuery selector, relative to picker, for individual days. 32 @property {string} rtlClass Class for right-to-left (RTL) languages. 33 @property {string} multiClass Class for multi-month datepickers. 34 @property {string} defaultClass Class for selectable dates. 35 @property {string} selectedClass Class for currently selected dates. 36 @property {string} highlightedClass Class for highlighted dates. 37 @property {string} todayClass Class for today. 38 @property {string} otherMonthClass Class for days from other months. 39 @property {string} weekendClass Class for days on weekends. 40 @property {string} commandClass Class prefix for commands. 41 @property {string} commandButtonClass Extra class(es) for commands that are buttons. 42 @property {string} commandLinkClass Extra class(es) for commands that are links. 43 @property {string} disabledClass Class for disabled commands. */ 44 defaultRenderer: { 45 picker: '<div class="datepick">' + 46 '<div class="datepick-nav">{link:prev}{link:today}{link:next}</div>{months}' + 47 '{popup:start}<div class="datepick-ctrl">{link:clear}{link:close}</div>{popup:end}' + 48 '<div class="datepick-clear-fix"></div></div>', 49 monthRow: '<div class="datepick-month-row">{months}</div>', 50 month: '<div class="datepick-month"><div class="datepick-month-header">{monthHeader}</div>' + 51 '<table><thead>{weekHeader}</thead><tbody>{weeks}</tbody></table></div>', 52 weekHeader: '<tr>{days}</tr>', 53 dayHeader: '<th>{day}</th>', 54 week: '<tr>{days}</tr>', 55 day: '<td>{day}</td>', 56 monthSelector: '.datepick-month', 57 daySelector: 'td', 58 rtlClass: 'datepick-rtl', 59 multiClass: 'datepick-multi', 60 defaultClass: '', 61 selectedClass: 'datepick-selected', 62 highlightedClass: 'datepick-highlight', 63 todayClass: 'datepick-today', 64 otherMonthClass: 'datepick-other-month', 65 weekendClass: 'datepick-weekend', 66 commandClass: 'datepick-cmd', 67 commandButtonClass: '', 68 commandLinkClass: '', 69 disabledClass: 'datepick-disabled' 70 }, 71 72 /** Command actions that may be added to a layout by name. 73 <ul> 74 <li>prev - Show the previous month (based on <code>monthsToStep</code> option) - <em>PageUp</em></li> 75 <li>prevJump - Show the previous year (based on <code>monthsToJump</code> option) - <em>Ctrl+PageUp</em></li> 76 <li>next - Show the next month (based on <code>monthsToStep</code> option) - <em>PageDown</em></li> 77 <li>nextJump - Show the next year (based on <code>monthsToJump</code> option) - <em>Ctrl+PageDown</em></li> 78 <li>current - Show the currently selected month or today's if none selected - <em>Ctrl+Home</em></li> 79 <li>today - Show today's month - <em>Ctrl+Home</em></li> 80 <li>clear - Erase the date and close the datepicker popup - <em>Ctrl+End</em></li> 81 <li>close - Close the datepicker popup - <em>Esc</em></li> 82 <li>prevWeek - Move the cursor to the previous week - <em>Ctrl+Up</em></li> 83 <li>prevDay - Move the cursor to the previous day - <em>Ctrl+Left</em></li> 84 <li>nextDay - Move the cursor to the next day - <em>Ctrl+Right</em></li> 85 <li>nextWeek - Move the cursor to the next week - <em>Ctrl+Down</em></li> 86 </ul> 87 The command name is the key name and is used to add the command to a layout 88 with '{button:name}' or '{link:name}'. Each has the following attributes: 89 @property {string} text The field in the regional settings for the displayed text. 90 @property {string} status The field in the regional settings for the status text. 91 @property {object} keystroke The keystroke to trigger the action. 92 @property {number} keystroke.keyCode The code for the keystroke. 93 @property {boolean} keystroke.ctrlKey <code>true</code> if <em>Ctrl</em> is required, 94 @property {boolean} keystroke.altKey <code>true</code> if <em>Alt</em> is required, 95 @property {boolean} keystroke.shiftKey <code>true</code> if <em>Shift</em> is required. 96 @property {DatepickCommandEnabled} enabled The function that indicates the command is enabled. 97 @property {DatepickCommandDate} date The function to get the date associated with this action. 98 @property {DatepickCommandAction} action The function that implements the action. */ 99 commands: { 100 prev: {text: 'prevText', status: 'prevStatus', // Previous month 101 keystroke: {keyCode: 33}, // Page up 102 enabled: function(inst) { 103 var minDate = inst.curMinDate(); 104 return (!minDate || plugin.add(plugin.day(
105 plugin._applyMonthsOffset(plugin.add(plugin.newDate(inst.drawDate), 106 1 - inst.options.monthsToStep, 'm'), inst), 1), -1, 'd'). 107 getTime() >= minDate.getTime()); 108 }, 109 date: function(inst) { 110 return plugin.day(plugin._applyMonthsOffset(plugin.add( 111 plugin.newDate(inst.drawDate), -inst.options.monthsToStep, 'm'), inst), 1); 112 }, 113 action: function(inst) { 114 plugin.changeMonth(this, -inst.options.monthsToStep); 115 } 116 }, 117 prevJump: {text: 'prevJumpText', status: 'prevJumpStatus', // Previous year 118 keystroke: {keyCode: 33, ctrlKey: true}, // Ctrl + Page up 119 enabled: function(inst) { 120 var minDate = inst.curMinDate(); 121 return (!minDate || plugin.add(plugin.day( 122 plugin._applyMonthsOffset(plugin.add(plugin.newDate(inst.drawDate), 123 1 - inst.options.monthsToJump, 'm'), inst), 1), -1, 'd'). 124 getTime() >= minDate.getTime()); 125 }, 126 date: function(inst) { 127 return plugin.day(plugin._applyMonthsOffset(plugin.add( 128 plugin.newDate(inst.drawDate), -inst.options.monthsToJump, 'm'), inst), 1); 129 }, 130 action: function(inst) { 131 plugin.changeMonth(this, -inst.options.monthsToJump); 132 } 133 }, 134 next: {text: 'nextText', status: 'nextStatus', // Next month 135 keystroke: {keyCode: 34}, // Page down 136 enabled: function(inst) { 137 var maxDate = inst.get('maxDate'); 138 return (!maxDate || plugin.day(plugin._applyMonthsOffset(plugin.add( 139 plugin.newDate(inst.drawDate), inst.options.monthsToStep, 'm'), inst), 1). 140 getTime() <= maxDate.getTime()); 141 }, 142 date: function(inst) { 143 return plugin.day(plugin._applyMonthsOffset(plugin.add( 144 plugin.newDate(inst.drawDate), inst.options.monthsToStep, 'm'), inst), 1); 145 }, 146 action: function(inst) { 147 plugin.changeMonth(this, inst.options.monthsToStep); 148 } 149 }, 150 nextJump: {text: 'nextJumpText', status: 'nextJumpStatus', // Next year 151 keystroke: {keyCode: 34, ctrlKey: true}, // Ctrl + Page down 152 enabled: function(inst) { 153 var maxDate = inst.get('maxDate'); 154 return (!maxDate || plugin.day(plugin._applyMonthsOffset(plugin.add( 155 plugin.newDate(inst.drawDate), inst.options.monthsToJump, 'm'), inst), 1). 156 getTime() <= maxDate.getTime()); 157 }, 158 date: function(inst) { 159 return plugin.day(plugin._applyMonthsOffset(plugin.add( 160 plugin.newDate(inst.drawDate), inst.options.monthsToJump, 'm'), inst), 1); 161 }, 162 action: function(inst) { 163 plugin.changeMonth(this, inst.options.monthsToJump); 164 } 165 }, 166 current: {text: 'currentText', status: 'currentStatus', // Current month 167 keystroke: {keyCode: 36, ctrlKey: true}, // Ctrl + Home 168 enabled: function(inst) { 169 var minDate = inst.curMinDate(); 170 var maxDate = inst.get('maxDate'); 171 var curDate = inst.selectedDates[0] || plugin.today(); 172 return (!minDate || curDate.getTime() >= minDate.getTime()) && 173 (!maxDate || curDate.getTime() <= maxDate.getTime()); 174 }, 175 date: function(inst) { 176 return inst.selectedDates[0] || plugin.today(); 177 }, 178 action: function(inst) { 179 var curDate = inst.selectedDates[0] || plugin.today(); 180 plugin.showMonth(this, curDate.getFullYear(), curDate.getMonth() + 1); 181 } 182 }, 183 today: {text: 'todayText', status: 'todayStatus', // Today's month 184 keystroke: {keyCode: 36, ctrlKey: true}, // Ctrl + Home 185 enabled: function(inst) { 186 var minDate = inst.curMinDate(); 187 var maxDate = inst.get('maxDate'); 188 return (!minDate || plugin.today().getTime() >= minDate.getTime()) && 189 (!maxDate || plugin.today().getTime() <= maxDate.getTime()); 190 }, 191 date: function() { return plugin.today(); }, 192 action: function() { plugin.showMonth(this); } 193 }, 194 clear: {text: 'clearText', status: 'clearStatus', // Clear the datepicker 195 keystroke: {keyCode: 35, ctrlKey: true}, // Ctrl + End 196 enabled: function() { return true; }, 197 date: function() { return null; }, 198 action: function() { plugin.clear(this); } 199 }, 200 close: {text: 'closeText', status: 'closeStatus', // Close the datepicker 201 keystroke: {keyCode: 27}, // Escape 202 enabled: function() { return true; }, 203 date: function() { return null; }, 204 action: function() { plugin.hide(this); } 205 }, 206 prevWeek: {text: 'prevWeekText', status: 'prevWeekStatus', // Previous week 207 keystroke: {keyCode: 38, ctrlKey: true}, // Ctrl + Up 208 enabled: function(inst) { 209 var minDate = inst.curMinDate();
210 return (!minDate || plugin.add(plugin.newDate(inst.drawDate), -7, 'd'). 211 getTime() >= minDate.getTime()); 212 }, 213 date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), -7, 'd'); }, 214 action: function() { plugin.changeDay(this, -7); } 215 }, 216 prevDay: {text: 'prevDayText', status: 'prevDayStatus', // Previous day 217 keystroke: {keyCode: 37, ctrlKey: true}, // Ctrl + Left 218 enabled: function(inst) { 219 var minDate = inst.curMinDate(); 220 return (!minDate || plugin.add(plugin.newDate(inst.drawDate), -1, 'd'). 221 getTime() >= minDate.getTime()); 222 }, 223 date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), -1, 'd'); }, 224 action: function() { plugin.changeDay(this, -1); } 225 }, 226 nextDay: {text: 'nextDayText', status: 'nextDayStatus', // Next day 227 keystroke: {keyCode: 39, ctrlKey: true}, // Ctrl + Right 228 enabled: function(inst) { 229 var maxDate = inst.get('maxDate'); 230 return (!maxDate || plugin.add(plugin.newDate(inst.drawDate), 1, 'd'). 231 getTime() <= maxDate.getTime()); 232 }, 233 date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), 1, 'd'); }, 234 action: function() { plugin.changeDay(this, 1); } 235 }, 236 nextWeek: {text: 'nextWeekText', status: 'nextWeekStatus', // Next week 237 keystroke: {keyCode: 40, ctrlKey: true}, // Ctrl + Down 238 enabled: function(inst) { 239 var maxDate = inst.get('maxDate'); 240 return (!maxDate || plugin.add(plugin.newDate(inst.drawDate), 7, 'd'). 241 getTime() <= maxDate.getTime()); 242 }, 243 date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), 7, 'd'); }, 244 action: function() { plugin.changeDay(this, 7); } 245 } 246 }, 247 248 /** Determine whether a {@linkcode module:Datepick~commands|command} is enabled. 249 @callback DatepickCommandEnabled 250 @global 251 @param {object} inst The current instance settings. 252 @return {boolean} <code>true</code> if this command is enabled, <code>false</code> if not. 253 @example enabled: function(inst) { 254 return !!inst.curMinDate(); 255} */ 256 257 /** Calculate the representative date for a {@linkcode module:Datepick~commands|command}. 258 @callback DatepickCommandDate 259 @global 260 @param {object} inst The current instance settings. 261 @return {Date} A date appropriate for this command. 262 @example date: function(inst) { 263 return inst.curMinDate(); 264} */ 265 266 /** Perform the action for a {@linkcode module:Datepick~commands|command}. 267 @callback DatepickCommandAction 268 @global 269 @param {object} inst The current instance settings. 270 @example action: function(inst) { 271 $.datepick.setDate(inst.elem, inst.curMinDate()); 272} */ 273 274 /** Calculate the week of the year for a date. 275 Use it with the {@linkcode module:Datepick~defaultOptions|calculateWeek} option. 276 @callback DatepickCalculateWeek 277 @global 278 @param {Date} date The date to evaluate. 279 @return {number} The week of the year. 280 @example calculateWeek: function(date) { 281 return Math.floor(($.datepick.dayOfYear(date) - 1) / 7) + 1; 282} */ 283 284 /** Determine where the first month shows in a multi-month calendar. 285 Use it with the {@linkcode module:Datepick~defaultOptions|monthsOffset} option. 286 @callback DatepickMonthsOffset 287 @global 288 @param {Date} date The first date to be shown. 289 @return {number} The offset within the calendar for the first month - first position is 0. 290 @example monthsToShow: 3, 291monthsToStep: 3, 292monthsOffset: function(date) { // Always start on the quarter 293 return date.getMonth() % 3; 294} */ 295 296 /** Provide information about an individual date shown in the calendar. 297 Use it with the {@linkcode module:Datepick~defaultOptions|onDate} option. 298 @callback DatepickOnDate 299 @global 300 @param {Date} date The date to evaluate. 301 @return {object} Information about that date, with the properties above. 302 @property selectable {boolean} <code>true</code> if this date can be selected. 303 @property dateClass {string} Class(es) to be applied to the date. 304 @property content {string} The date cell content. 305 @property tooltip {string} A popup tooltip for the date. 306 @example onDate: function(date) { 307 return {selectable: date.getDay() > 0 && date.getDay() < 5, 308 dateClass: date.getDay() == 4 ? 'last-day' : ''}; 309} */ 310 311 /** Update the datepicker display. 312 Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. 313 @callback DatepickOnShow 314 @global 315 @param {jQuery} picker The datepicker <code>div</code> to be shown. 316 @param {object} inst The current instance settings. 317 @example onShow: function(picker, inst) { 318 picker.append('<button type="button">Hi</button>
318'). 319 find('button:last').click(function() { 320 alert('Hi!'); 321 }); 322} */ 323 324 /** React to navigating through the months/years. 325 Use it with the {@linkcode module:Datepick~defaultOptions|onChangeMonthYear} option. 326 @callback DatepickOnChangeMonthYear 327 @global 328 @param {number} year The new year. 329 @param {number} month The new month (1 to 12). 330 @example onChangeMonthYear: function(year, month) { 331 alert('Now in ' + month + '/' + year); 332} */ 333 334 /** Datepicker on select callback. 335 Triggered when a date is selected. 336 Use it with the {@linkcode module:Datepick~defaultOptions|onSelect} option. 337 @callback DatepickOnSelect 338 @global 339 @param {Date[]} dates The selected date(s). 340 @example onSelect: function(dates) { 341 alert('Selected ' + dates); 342} */ 343 344 /** Datepicker on close callback. 345 Triggered when a popup calendar is closed. 346 Use it with the {@linkcode module:Datepick~defaultOptions|onClose} option. 347 @callback DatepickOnClose 348 @global 349 @param {Date[]} dates The selected date(s). 350 @example onClose: function(dates) { 351 alert('Selected ' + dates); 352} */ 353 354 /** Default settings for the plugin. 355 @property {string} [pickerClass=''] CSS class to add to this instance of the datepicker. 356 @property {boolean} [showOnFocus=true] <code>true</code> for popup on focus, <code>false</code> for not. 357 @property {string|Element|jQuery} [showTrigger=null] Element to be cloned for a trigger, 358 <code>null</code> for none. 359 @property {string} [showAnim='show'] Name of jQuery animation for popup, '' for no animation. 360 @property {object} [showOptions=null] Options for enhanced animations. 361 @property {string|number} [showSpeed='normal'] Duration of display/closure, named value or milliseconds. 362 @property {string|Element|jQuery} [popupContainer=null] The element to which a popup calendar is added, 363 <code>null</code> for body. 364 @property {string} [alignment='bottom'] Alignment of popup - with nominated corner of input: 365 'top' or 'bottom' aligns depending on language direction, 366 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'. 367 @property {boolean} [fixedWeeks=false] <code>true</code> to always show 6 weeks, 368 <code>false</code> to only show as many as are needed. 369 @property {number} [firstDay=0] First day of the week, 0 = Sunday, 1 = Monday, etc. 370 @property {DatepickCalculateWeek} [calculateWeek=this.iso8601Week] Calculate week of the year from a date, 371 <code>null</code> for ISO8601. 372 @property {number|number[]} [monthsToShow=1] How many months to show, cols or [rows, cols]. 373 @property {number|DatepickMonthsOffset} [monthsOffset=0] How many months to offset the primary month by; 374 may be a function that takes the date and returns the offset. 375 @property {number} [monthsToStep=1] How many months to move when prev/next clicked. 376 @property {number} [monthsToJump=12] How many months to move when large prev/next clicked. 377 @property {boolean} [useMouseWheel=true] <code>true</code> to use mousewheel if available, 378 <code>false</code> to never use it. 379 @property {boolean} [changeMonth=true] <code>true</code> to change month/year via drop-down, 380 <code>false</code> for navigation only. 381 @property {string} [yearRange='c-10:c+10'] Range of years to show in drop-down: 'any' for direct text entry 382 or 'start:end', where start/end are '±nn' for relative to today 383 or 'c±nn' for relative to the currently selected date 384 or 'nnnn' for an absolute year. 385 @property {string|number} [shortYearCutoff='+10'] Cutoff for two-digit year in the current century. 386 If expressed as a string it is offset from the current year. 387 If expressed as a number it is used directly. 388 Use -1 to always use 1900 as the base year, or use 100 to disable the functionality. 389 Any short year ('yy') entered is transformed into a full year in the current century 390 if less than or equal to this cutoff value, and the previous century otherwise. 391 @property {boolean} [showOtherMonths=false] <code>true</code> to show dates from other months, 392 <code>false</code> to not show them. 393 @property {boolean} [selectOtherMonths=false] <code>true</code> to allow selection of dates 394 from other months too. 395 @property {string|number|Date} [defaultDate=null] Date to show if no other selected. 396 If expressed as a string it is parsed using the current 397 {@linkcode module:Datepick~regionalOptions|dateFormat}. 398 If expressed as a number it is offset that number of days from today. 399 If expressed as a <code>Date</code> it is used directly. 400 @property {boolean} [selectDefaultDate=false] <code>true</code> to pre-select the default date 401 if no other is chosen. 402 @property {string|number|Date} [minDate=null] The minimum selectable date. 403 See the allowed values in <code>defaultDate</code> above. 404 @property {string|number|Date} [maxDate=null] The maximum selectable date. 405 See the allowed values in <code>defaultDate</code> above. 406 @property {string} [dateFormat='mm/dd/yyyy'] Format for dates. 407 See {@linkcode module:Datepick~formatDate|formatDate} for allowed formats. 408 @property {boolean} [autoSize=false] <code>true</code> to size the input field according to 409 the {@linkcode module:Datepick~regionalOptions|dateFormat}. 410 @property {boolean} [rangeSelect=false] Allows for selecting a date range on one date picker. 411 @property {string} [rangeSeparator=' - '] Text between two dates in a range when displayed. 412 @property {number} [multiSelect=0] Maximum number of selectable dates for multiple independent dates, 413 zero for single select. If specified, 414 <code>multiSelect</code> takes precedence over <code>rangeSelect</code>. 415 @property {string} [multiSeparator=','] Text between multiple dates. 416 @property {DatepickOnDate} [onDate=null] Callback as each date is added to the display calendar. 417 This allows you to customise the behaviour and presentation of each date. 418 @property {DatepickOnShow} [onShow=null] Callback just before a datepicker is shown.
419 This allows you to customise the datepicker before display. 420 @property {DatepickOnChangeMonthYear} [onChangeMonthYear=null] Callback when a new month/year is selected. 421 This allows you to perform other actions when the calendar changes. 422 @property {DatepickOnSelect} [onSelect=null] Callback when a date is selected. 423 @property {DatepickOnClose} [onClose=null] Callback when a datepicker is closed. 424 @property {string|Element|jQuery} [altField=null] Alternate field to update in synch with the datepicker. 425 @property {string} [altFormat=null] Date format for alternate field, defaults to 426 {@linkcode module:Datepick~regionalOptions|dateFormat}. 427 This allows you to display one (human-friendly) format, 428 while automatically maintaining another (computer-friendly) format. 429 @property {boolean} [constrainInput=true] <code>true</code> to constrain typed input to 430 {@linkcode module:Datepick~regionalOptions|dateFormat} allowed characters. 431 @property {boolean} [commandsAsDateFormat=false] <code>true</code> to apply 432 {@linkcode module:Datepick~formatDate|formatDate} to the command texts. 433 @property {object} [commands=this.commands] Command actions that may be added to a layout by name. */ 434 defaultOptions: { 435 pickerClass: '', 436 showOnFocus: true, 437 showTrigger: null, 438 showAnim: 'show', 439 showOptions: {}, 440 showSpeed: 'normal', 441 popupContainer: null, 442 alignment: 'bottom', 443 fixedWeeks: false, 444 firstDay: 0, 445 calculateWeek: null, // this.iso8601Week, 446 monthsToShow: 1, 447 monthsOffset: 0, 448 monthsToStep: 1, 449 monthsToJump: 12, 450 useMouseWheel: true, 451 changeMonth: true, 452 yearRange: 'c-10:c+10', 453 shortYearCutoff: '+10', 454 showOtherMonths: false, 455 selectOtherMonths: false, 456 defaultDate: null, 457 selectDefaultDate: false, 458 minDate: null, 459 maxDate: null, 460 dateFormat: 'mm/dd/yyyy', 461 autoSize: false, 462 rangeSelect: false, 463 rangeSeparator: ' - ', 464 multiSelect: 0, 465 multiSeparator: ',', 466 onDate: null, 467 onShow: null, 468 onChangeMonthYear: null, 469 onSelect: null, 470 onClose: null, 471 altField: null, 472 altFormat: null, 473 constrainInput: true, 474 commandsAsDateFormat: false, 475 commands: {} // this.commands 476 }, 477 478 /** Localisations for the plugin. 479 Entries are objects indexed by the language code ('' being the default US/English). 480 Each object has the following attributes. 481 @property {string[]} [monthNames=['January','February',...,'November','December']] 482 The long names of the months. 483 @property {string[]} [monthNamesShort=['Jan','Feb',...,'Nov','Dec']] 484 The short names of the months. 485 @property {string[]} [dayNames=['Sunday','Monday',...,'Friday','Saturday']] 486 The long names of the days of the week. 487 @property {string[]} [dayNamesShort=['Sun','Mon','Tue','Wed','Thu','Fri','Sat']] 488 The short names of the days of the week. 489 @property {string[]} [dayNamesMin=['Su','Mo','Tu','We','Th','Fr','Sa']] 490 The minimal names of the days of the week. 491 @property {string} [dateFormat='mm/dd/yyyy'] See options on {@linkcode module:Datepick~formatDate|formatDate}. 492 @property {number} [firstDay=0] The first day of the week, Sun = 0, Mon = 1, etc. 493 @property {string} [renderer=this.defaultRenderer] The rendering templates. 494 @property {string} [prevText='<Prev'] Text for the previous month command. 495 @property {string} [prevStatus='Show the previous month'] Status text for the previous month command. 496 @property {string} [prevJumpText='<<'] Text for the previous year command. 497 @property {string} [prevJumpStatus='Show the previous year'] Status text for the previous year command. 498 @property {string} [nextText='Next>'] Text for the next month command. 499 @property {string} [nextStatus='Show the next month'] Status text for the next month command. 500 @property {string} [nextJumpText='>>'] Text for the next year command. 501 @property {string} [nextJumpStatus='Show the next year'] Status text for the next year command. 502 @property {string} [currentText='Current'] Text for the current month command. 503 @property {string} [currentStatus='Show the current month'] Status text for the current month command. 504 @property {string} [todayText='Today'] Text for the today's month command. 505 @property {string} [todayStatus='Show today\'s month'] Status text for the today's month command. 506 @property {string} [clearText='Clear'] Text for the clear command. 507 @property {string} [clearStatus='Clear all the dates'] Status text for the clear command. 508 @property {string} [closeText='Close'] Text for the close command. 509 @property {string} [closeStatus='Close the datepicker'] Status text for the close command. 510 @property {string} [yearStatus='Change the year'] Status text for year selection. 511 @property {string} [earlierText='  â²'] Text for earlier years. 512 @property {string} [laterText='  â¼'] Text for later years. 513 @property {string} [monthStatus='Change the month'] Status text for month selection. 514 @property {string}
514 [weekText='Wk'] Text for week of the year column header. 515 @property {string} [weekStatus='Week of the year'] Status text for week of the year column header. 516 @property {string} [dayStatus='Select DD, M d, yyyy'] Status text for selectable days. 517 @property {string} [defaultStatus='Select a date'] Status text shown by default. 518 @property {boolean} [isRTL=false] <code>true</code> if language is written right-to-left. */ 519 regionalOptions: { // Available regional settings, indexed by language/country code 520 '': { // Default regional settings - English/US 521 monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 522 'July', 'August', 'September', 'October', 'November', 'December'], 523 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 524 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 525 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 526 dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], 527 dateFormat: 'mm/dd/yyyy', 528 firstDay: 0, 529 renderer: {}, // this.defaultRenderer 530 prevText: '<Prev', 531 prevStatus: 'Show the previous month', 532 prevJumpText: '<<', 533 prevJumpStatus: 'Show the previous year', 534 nextText: 'Next>', 535 nextStatus: 'Show the next month', 536 nextJumpText: '>>', 537 nextJumpStatus: 'Show the next year', 538 currentText: 'Current', 539 currentStatus: 'Show the current month', 540 todayText: 'Today', 541 todayStatus: 'Show today\'s month', 542 clearText: 'Clear', 543 clearStatus: 'Clear all the dates', 544 closeText: 'Close', 545 closeStatus: 'Close the datepicker', 546 yearStatus: 'Change the year', 547 earlierText: '  â²', 548 laterText: '  â¼', 549 monthStatus: 'Change the month', 550 weekText: 'Wk', 551 weekStatus: 'Week of the year', 552 dayStatus: 'Select DD, M d, yyyy', 553 defaultStatus: 'Select a date', 554 isRTL: false 555 } 556 }, 557 558 _disabled: [], 559 560 _popupClass: pluginName + '-popup', // Marker for popup division 561 _triggerClass: pluginName + '-trigger', // Marker for trigger element 562 _disableClass: pluginName + '-disable', // Marker for disabled element 563 _monthYearClass: pluginName + '-month-year', // Marker for month/year inputs 564 _curMonthClass: pluginName + '-month-', // Marker for current month/year 565 _anyYearClass: pluginName + '-any-year', // Marker for year direct input 566 _curDoWClass: pluginName + '-dow-', // Marker for day of week 567 568 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + 569 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), 570 _msPerDay: 24 * 60 * 60 * 1000, 571 572 /** The {@linkcode module:Datepick~formatDate|date format} for use with Atom (RFC 3339/ISO 8601): yyyy-mm-dd. */ 573 ATOM: 'yyyy-mm-dd', 574 /** The {@linkcode module:Datepick~formatDate|date format} for use with cookies: D, dd M yyyy. */ 575 COOKIE: 'D, dd M yyyy', 576 /** The {@linkcode module:Datepick~formatDate|date format} for full display: DD, MM d, yyyy. */ 577 FULL: 'DD, MM d, yyyy', 578 /** The {@linkcode module:Datepick~formatDate|date format} for use with ISO 8601: yyyy-mm-dd. */ 579 ISO_8601: 'yyyy-mm-dd', 580 /** The {@linkcode module:Datepick~formatDate|date format} for Julian dates: J. */ 581 JULIAN: 'J', 582 /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 822: D, d M yy. */ 583 RFC_822: 'D, d M yy', 584 /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 850: DD, dd-M-yy. */ 585 RFC_850: 'DD, dd-M-yy', 586 /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 1036: D, d M yy. */ 587 RFC_1036: 'D, d M yy', 588 /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 1123: D, d M yyyy. */ 589 RFC_1123: 'D, d M yyyy', 590 /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 2822: D, d M yyyy. */ 591 RFC_2822: 'D, d M yyyy', 592 /** The {@linkcode module:Datepick~formatDate|date format} for use with RSS (RFC 822): D, d M yy. */ 593 RSS: 'D, d M yy', 594 /** The {@linkcode module:Datepick~formatDate|date format} for Windows ticks: !. */ 595 TICKS: '!', 596 /** The {@linkcode module:Datepick~formatDate|date format} for Unix timestamp: @. */ 597 TIMESTAMP: '@', 598 /** The {@linkcode module:Datepick~formatDate|date format} for use with W3C (ISO 8601): yyyy-mm-dd. */ 599 W3C: 'yyyy-mm-dd', 600 601 /** Format a date object into a string value. 602 The format can be combinations of the following: 603 <ul> 604 <li>d - day of month (no leading zero)</li> 605 <li>dd - day of month (two digit)</li> 606 <li>o - day of year (no leading zeros)</li> 607 <li>oo - day of year (three digit)</li> 608 <li>D - day name short</li> 609 <li>DD - day name long</li> 610 <li>w - week of year (no leading zero)</li> 611 <li>ww - week of year (two digit)</li> 612 <li>m - month of year (no leading zero)</li> 613 <li>mm - month of year (two digit)</li> 614 <li>M - month name short</li> 615 <li>MM - month name long</li> 616 <li>yy - year (two digit)</li> 617 <li>yyyy - year (four digit)</li> 618 <li>@ - Unix timestamp (s since 01/01/1970)</li> 619 <li>! - Windows ticks (100ns since 01/01/0001)</li> 620 <li>'...' - literal text</li> 621 <li>'' - single quote</li> 622 </ul> 623 @param {string} [format=defaultOptions.dateFormat] The desired format of the date. 624 @param {Date} date The date value to format. 625 @param {object} [settings] With these properties: 626 @param {string[]} [settings.dayNames] Names of the days from Sunday. 627 @param {string[]} [settings.dayNamesShort] Abbreviated names of the days from Sunday. 628 @param {string[]} [settings.monthNames] Names of the months. 629 @param {string[]} [settings.monthNamesShort] Abbreviated names of the months. 630 @param {DatepickCalculateWeek} [settings.calculateWeek] Function that determines week of the year. 631 @return {string} The date in the above format. 632 @example var display = $.datepick.formatDate('yyyy-mm-dd', new Date(2014, 12-1, 25)) */ 633 formatDate: function(format, date, settings) { 634 if (typeof format !== 'string') { 635 settings = date; 636 date = format; 637 format = ''; 638 } 639 if (!date) { 640 return ''; 641 } 642 format = format || this.defaultOptions.dateFormat; 643 settings = settings || {}; 644 var dayNamesShort = settings.dayNamesShort || this.defaultOptions.dayNamesShort; 645 var dayNames = settings.dayNames || this.defaultOptions.dayNames; 646 var monthNamesShort = settings.monthNamesShort || this.defaultOptions.monthNamesShort; 647 var monthNames = settings.monthNames || this.defaultOptions.monthNames; 648 var calculateWeek = settings.calculateWeek || this.defaultOptions.calculateWeek; 649 // Check whether a format character is doubled 650 var doubled = function(match, step) { 651 var matches = 1;
652 while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) { 653 matches++; 654 } 655 iFormat += matches - 1; 656 return Math.floor(matches / (step || 1)) > 1; 657 }; 658 // Format a number, with leading zeroes if necessary 659 var formatNumber = function(match, value, len, step) { 660 var num = '' + value; 661 if (doubled(match, step)) { 662 while (num.length < len) { 663 num = '0' + num; 664 } 665 } 666 return num; 667 }; 668 // Format a name, short or long as requested 669 var formatName = function(match, value, shortNames, longNames) { 670 return (doubled(match) ? longNames[value] : shortNames[value]); 671 }; 672 var output = ''; 673 var literal = false; 674 for (var iFormat = 0; iFormat < format.length; iFormat++) { 675 if (literal) { 676 if (format.charAt(iFormat) === '\'' && !doubled('\'')) { 677 literal = false; 678 } 679 else { 680 output += format.charAt(iFormat); 681 } 682 } 683 else { 684 switch (format.charAt(iFormat)) { 685 case 'd': 686 output += formatNumber('d', date.getDate(), 2); 687 break; 688 case 'D': 689 output += formatName('D', date.getDay(), dayNamesShort, dayNames); 690 break; 691 case 'o': 692 output += formatNumber('o', this.dayOfYear(date), 3); 693 break; 694 case 'w': 695 output += formatNumber('w', calculateWeek(date), 2); 696 break; 697 case 'm': 698 output += formatNumber('m', date.getMonth() + 1, 2); 699 break; 700 case 'M': 701 output += formatName('M', date.getMonth(), monthNamesShort, monthNames); 702 break; 703 case 'y': 704 output += (doubled('y', 2) ? date.getFullYear() : 705 (date.getFullYear() % 100 < 10 ? '0' : '') + date.getFullYear() % 100); 706 break; 707 case '@': 708 output += Math.floor(date.getTime() / 1000); 709 break; 710 case '!': 711 output += date.getTime() * 10000 + this._ticksTo1970; 712 break; 713 case '\'': 714 if (doubled('\'')) { 715 output += '\''; 716 } 717 else { 718 literal = true; 719 } 720 break; 721 default: 722 output += format.charAt(iFormat); 723 } 724 } 725 } 726 return output; 727 }, 728 729 /** Parse a string value into a date object. 730 See {@linkcode module:Datepick~formatDate|formatDate} for the possible formats, plus: 731 <ul> 732 <li>* - ignore rest of string</li> 733 </ul> 734 @param {string} format The expected format of the date ('' for default datepicker format). 735 @param {string} value The date in the above format. 736 @param {object} [settings] With these properties: 737 @param {number} [settings.shortYearCutoff] The cutoff year for determining the century. 738 @param {string[]} [settings.dayNames] The names of the days from Sunday. 739 @param {string[]} [settings.dayNamesShort] The abbreviated names of the days from Sunday. 740 @param {string[]} [settings.monthNames] The Names of the months. 741 @param {string[]} [settings.monthNamesShort] The abbreviated names of the months. 742 @return {Date} The extracted date value or <code>null</code> if value is blank. 743 @throws Errors if the format and/or value are missing, if the value doesn't match the format, 744 or if the date is invalid. 745 @example var date = $.datepick.parseDate('dd/mm/yyyy', '25/12/2014') */ 746 parseDate: function(format, value, settings) { 747 if (typeof value === 'undefined' || value === null) { 748 throw 'Invalid arguments'; 749 } 750 value = (typeof value === 'object' ? value.toString() : value + ''); 751 if (value === '') { 752 return null; 753 } 754 format = format || this.defaultOptions.dateFormat; 755 settings = settings || {}; 756 var shortYearCutoff = settings.shortYearCutoff || this.defaultOptions.shortYearCutoff; 757 shortYearCutoff = (typeof shortYearCutoff !== 'string' ? shortYearCutoff : 758 this.today().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); 759 var dayNamesShort = settings.dayNamesShort || this.defaultOptions.dayNamesShort; 760 var dayNames = settings.dayNames || this.defaultOptions.dayNames; 761 var monthNamesShort = settings.monthNamesShort || this.defaultOptions.monthNamesShort; 762 var monthNames = settings.monthNames || this.defaultOptions.monthNames; 763 var year = -1;
764 var month = -1; 765 var day = -1; 766 var doy = -1; 767 var shortYear = false; 768 var literal = false; 769 var date = null; 770 // Check whether a format character is doubled 771 var doubled = function(match, step) { 772 var matches = 1; 773 while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) { 774 matches++; 775 } 776 iFormat += matches - 1; 777 return Math.floor(matches / (step || 1)) > 1; 778 }; 779 // Extract a number from the string value 780 var getNumber = function(match, step) { 781 var isDoubled = doubled(match, step); 782 var size = [2, 3, isDoubled ? 4 : 2, 11, 20]['oy@!'.indexOf(match) + 1]; 783 var digits = new RegExp('^-?\\d{1,' + size + '}'); 784 var num = value.substring(iValue).match(digits); 785 if (!num) { 786 throw 'Missing number at position {0}'.replace(/\{0\}/, iValue); 787 } 788 iValue += num[0].length; 789 return parseInt(num[0], 10); 790 }; 791 // Extract a name from the string value and convert to an index 792 var getName = function(match, shortNames, longNames, step) { 793 var names = (doubled(match, step) ? longNames : shortNames); 794 for (var i = 0; i < names.length; i++) { 795 if (value.substr(iValue, names[i].length).toLowerCase() === names[i].toLowerCase()) { 796 iValue += names[i].length; 797 return i + 1; 798 } 799 } 800 throw 'Unknown name at position {0}'.replace(/\{0\}/, iValue); 801 }; 802 // Confirm that a literal character matches the string value 803 var checkLiteral = function() { 804 if (value.charAt(iValue) !== format.charAt(iFormat)) { 805 throw 'Unexpected literal at position {0}'.replace(/\{0\}/, iValue); 806 } 807 iValue++; 808 }; 809 var iValue = 0; 810 for (var iFormat = 0; iFormat < format.length; iFormat++) { 811 if (literal) { 812 if (format.charAt(iFormat) === '\'' && !doubled('\'')) { 813 literal = false; 814 } 815 else { 816 checkLiteral(); 817 } 818 } 819 else { 820 switch (format.charAt(iFormat)) { 821 case 'd': 822 day = getNumber('d'); 823 break; 824 case 'D': 825 getName('D', dayNamesShort, dayNames); 826 break; 827 case 'o': 828 doy = getNumber('o'); 829 break; 830 case 'w': 831 getNumber('w'); 832 break; 833 case 'm': 834 month = getNumber('m'); 835 break; 836 case 'M': 837 month = getName('M', monthNamesShort, monthNames); 838 break; 839 case 'y': 840 var iSave = iFormat; 841 shortYear = !doubled('y', 2); 842 iFormat = iSave; 843 year = getNumber('y', 2); 844 break; 845 case '@': 846 date = this._normaliseDate(new Date(getNumber('@') * 1000)); 847 year = date.getFullYear(); 848 month = date.getMonth() + 1; 849 day = date.getDate(); 850 break; 851 case '!': 852 date = this._normaliseDate( 853 new Date((getNumber('!') - this._ticksTo1970) / 10000)); 854 year = date.getFullYear(); 855 month = date.getMonth() + 1; 856 day = date.getDate(); 857 break; 858 case '*': 859 iValue = value.length; 860 break; 861 case '\'': 862 if (doubled('\'')) { 863 checkLiteral(); 864 } 865 else { 866 literal = true; 867 } 868 break; 869 default: 870 checkLiteral(); 871 } 872 } 873 } 874 if (iValue < value.length) { 875 throw 'Additional text found at end'; 876 } 877 if (year === -1) { 878 year = this.today().getFullYear(); 879 } 880 else if (year < 100 && shortYear) { 881 year += (shortYearCutoff === -1 ? 1900 : this.today().getFullYear() - 882 this.today().getFullYear() % 100 - (year <= shortYearCutoff ? 0 : 100)); 883 } 884 if (doy > -1) { 885 month = 1; 886 day = doy; 887 for (var dim = this.daysInMonth(year, month); day > dim; 888 dim = this.daysInMonth(year, month)) { 889 month++; 890 day -= dim; 891 } 892 } 893 date = this.newDate(year, month, day); 894 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { 895 throw 'Invalid date'; 896 } 897 return date; 898 }, 899 900 /** A date may be specified as an exact value or a relative one. 901 @param {Date|number|string} dateSpec The date as a <code>Date</code>, 902 or as a string in the current {@linkcode module:Datepick~regionalOptions|dateFormat}, 903 or as a numeric offset - in days from today, 904 or as a string of amounts and periods, e.g. '+1m +2w', 905 using 'd' for days, 'w' for weeks, 'm' for months, and 'y' for years. 906 @param {Date} [defaultDate] The date to use if no other supplied, may be <code>null</code>. 907 @param {Date} [currentDate] The current date as a possible basis for relative dates, 908 if <code>null</code> today is used. 909 @param {string} [dateFormat] The expected date format - see {@linkcode module:Datepick~formatDate|formatDate}. 910 @param {object} [settings] With these properties: 911 @param {number} [settings.shortYearCutoff] The cutoff year for determining the century. 912 @param {string[]} [settings.dayNamesShort] Abbreviated names of the days from Sunday. 913 @param {string[]} [settings.dayNames] Names of the days from Sunday. 914 @param {string[]} [settings.monthNamesShort] Abbreviated names of the months. 915 @param {string[]} [settings.monthNames] Names of the months. 916 @return {Date} The decoded date. 917 @example var date = $.datepick.determineDate('+1m +2w', new Date()) */ 918 determineDate: function(dateSpec, defaultDate, currentDate, dateFormat, settings) { 919 if (currentDate && typeof currentDate !== 'object') { 920 settings = dateFormat; 921 dateFormat = currentDate; 922 currentDate = null; 923 } 924 if (typeof dateFormat !== 'string') { 925 settings = dateFormat; 926 dateFormat = ''; 927 } 928 var offsetString = function(offset) { 929 try { 930 return plugin.parseDate(dateFormat, offset, settings); 931 } 932 catch (e) { 933 // Ignore 934 } 935 offset = offset.toLowerCase();
936 var date = (offset.match(/^c/) && currentDate ? plugin.newDate(currentDate) : null) || 937 plugin.today(); 938 var pattern = /([+-]?[0-9]+)\s*(d|w|m|y)?/g; 939 var matches = null; 940 while ((matches = pattern.exec(offset))) { 941 date = plugin.add(date, parseInt(matches[1], 10), matches[2] || 'd'); 942 } 943 return date; 944 }; 945 defaultDate = (defaultDate ? plugin.newDate(defaultDate) : null); 946 dateSpec = (typeof dateSpec === 'undefined' ? defaultDate : 947 (typeof dateSpec === 'string' ? offsetString(dateSpec) : (typeof dateSpec === 'number' ? 948 (isNaN(dateSpec) || dateSpec === Infinity || dateSpec === -Infinity ? defaultDate : 949 plugin.add(plugin.today(), dateSpec, 'd')) : plugin.newDate(dateSpec)))); 950 return dateSpec; 951 }, 952 953 /** Find the number of days in a given month. 954 @param {Date|number} year The date to get days for or the full year. 955 @param {number} [month] The month (1 to 12), if the year is a number. 956 @return {number} The number of days in this month. 957 @example var days = $.datepick.daysInMonth(2014, 12) 958var days = $.datepick.daysInMonth(new Date(2014, 12-1, 25)) */ 959 daysInMonth: function(year, month) { 960 month = (year.getFullYear ? year.getMonth() + 1 : month); 961 year = (year.getFullYear ? year.getFullYear() : year); 962 return this.newDate(year, month + 1, 0).getDate(); 963 }, 964 965 /** Calculate the day of the year for a date. 966 @param {Date|number} year The date to get the day-of-year for or the full year. 967 @param {number} [month] The month (1-12), if the year is a number. 968 @param {number} [day] The day, if the year is a number. 969 @return {number} The day of the year. 970 @example var doy = $.datepick.dayOfYear(2014, 12, 25) 971var doy = $.datepick.dayOfYear(new Date(2014, 12-1, 25)) */ 972 dayOfYear: function(year, month, day) { 973 var date = (year.getFullYear ? year : plugin.newDate(year, month, day)); 974 var newYear = plugin.newDate(date.getFullYear(), 1, 1); 975 return Math.floor((date.getTime() - newYear.getTime()) / plugin._msPerDay) + 1; 976 }, 977 978 /** Set as <code>calculateWeek</code> to determine the week of the year based on the ISO 8601 definition. 979 @param {Date|number} year The date to get the week for or the full year. 980 @param {number} [month] The month (1-12), if the year is a number. 981 @param {number} [day] The day, if the year is a number. 982 @return {number} The number of the week within the year that contains this date. 983 @example var week = $.datepick.iso8601Week(2014, 12, 25) 984var week = $.datepick.iso8601Week(new Date(2014, 12-1, 25)) */ 985 iso8601Week: function(year, month, day) { 986 var checkDate = (year.getFullYear ? 987 new Date(year.getTime()) : plugin.newDate(year, month, day)); 988 // Find Thursday of this week starting on Monday 989 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); 990 var time = checkDate.getTime(); 991 checkDate.setMonth(0, 1); // Compare with Jan 1 992 return Math.floor(Math.round((time - checkDate) / plugin._msPerDay) / 7) + 1; 993 }, 994 995 /** Return today's date. 996 @return {Date} Today. 997 @example var today = $.datepick.today() */ 998 today: function() { 999 return this._normaliseDate(new Date()); 1000 }, 1001 1002 /** Return a new date. 1003 @param {Date|number} year The date to clone or the year. 1004 @param {number} [month] The month (1-12), if the year is a number. 1005 @param {number} [day] The day, if the year is a number. 1006 @return {Date} The date. 1007 @example $.datepick.newDate(oldDate) 1008$.datepick.newDate(2014, 12, 25) */ 1009 newDate: function(year, month, day) { 1010 return (!year ? null : (year.getFullYear ? this._normaliseDate(new Date(year.getTime())) : 1011 new Date(year, month - 1, day, 12))); 1012 }, 1013 1014 /** Standardise a date into a common format - time portion is 12 noon. 1015 @private 1016 @param {Date} date The date to standardise. 1017 @return {Date} The normalised date. */ 1018 _normaliseDate: function(date) { 1019 if (date) { 1020 date.setHours(12, 0, 0, 0); 1021 } 1022 return date; 1023 }, 1024 1025 /** Set the year for a date. 1026 @param {Date} date The original date. 1027 @param {number} year The new year. 1028 @return {Date} The updated date. 1029 @example $.datepick.year(date, 2014) */ 1030 year: function(date, year) { 1031 date.setFullYear(year); 1032 return this._normaliseDate(date); 1033 }, 1034 1035 /** Set the month for a date. 1036 @param {Date} date The original date. 1037 @param {number} month The new month (1-12). 1038 @return {Date} The updated date. 1039 @example $.datepick.month(date, 12) */ 1040 month: function(date, month) { 1041 date.setMonth(month - 1); 1042 return this._normaliseDate(date); 1043 }, 1044 1045 /** Set the day for a date. 1046 @param {Date} date The original date. 1047 @param {number} day The new day of the month. 1048 @return {Date} The updated date. 1049 @example $.datepick.day(date, 25) */ 1050 day: function(date, day) { 1051 date.setDate(day); 1052 return this._normaliseDate(date); 1053 }, 1054 1055 /** Add a number of periods to a date. 1056 @param {Date} date The original date. 1057 @param {number} amount The number of periods. 1058 @param {string} period The type of period 'd' for days, 'w' for weeks, 'm' for months, 'y' for years. 1059 @return {Date} The updated date. 1060 @example $.datepick.add(date, 10, 'd') */
1061 add: function(date, amount, period) { 1062 if (period === 'd' || period === 'w') { 1063 this._normaliseDate(date); 1064 date.setDate(date.getDate() + amount * (period === 'w' ? 7 : 1)); 1065 } 1066 else { 1067 var year = date.getFullYear() + (period === 'y' ? amount : 0); 1068 var month = date.getMonth() + (period === 'm' ? amount : 0); 1069 date.setTime(plugin.newDate(year, month + 1, 1070 Math.min(date.getDate(), this.daysInMonth(year, month + 1))).getTime()); 1071 } 1072 return date; 1073 }, 1074 1075 /** Apply the months offset value to a date. 1076 @private 1077 @param {Date} date The original date. 1078 @param {object} inst The current instance settings. 1079 @return {Date} The updated date. */ 1080 _applyMonthsOffset: function(date, inst) { 1081 var monthsOffset = inst.options.monthsOffset; 1082 if ($.isFunction(monthsOffset)) { 1083 monthsOffset = monthsOffset.apply(inst.elem[0], [date]); 1084 } 1085 return plugin.add(date, -monthsOffset, 'm'); 1086 }, 1087 1088 _init: function() { 1089 this.defaultOptions.commands = this.commands; 1090 this.defaultOptions.calculateWeek = this.iso8601Week; 1091 this.regionalOptions[''].renderer = this.defaultRenderer; 1092 this._super(); 1093 }, 1094 1095 _instSettings: function(elem) { 1096 return {selectedDates: [], drawDate: null, pickingRange: false, 1097 inline: ($.inArray(elem[0].nodeName.toLowerCase(), ['div', 'span']) > -1), 1098 get: function(name) { // Get a setting value, computing if necessary 1099 if ($.inArray(name, ['defaultDate', 'minDate', 'maxDate']) > -1) { // Decode date settings 1100 return plugin.determineDate(this.options[name], null, 1101 this.selectedDates[0], this.options.dateFormat, this.getConfig()); 1102 } 1103 return this.options[name]; 1104 }, 1105 curMinDate: function() { 1106 return (this.pickingRange ? this.selectedDates[0] : this.get('minDate')); 1107 }, 1108 getConfig: function() { 1109 return {dayNamesShort: this.options.dayNamesShort, dayNames: this.options.dayNames, 1110 monthNamesShort: this.options.monthNamesShort, monthNames: this.options.monthNames, 1111 calculateWeek: this.options.calculateWeek, 1112 shortYearCutoff: this.options.shortYearCutoff}; 1113 } 1114 }; 1115 }, 1116 1117 _postAttach: function(elem, inst) { 1118 if (inst.inline) { 1119 inst.drawDate = plugin._checkMinMax(plugin.newDate(inst.selectedDates[0] || 1120 inst.get('defaultDate') || plugin.today()), inst); 1121 inst.prevDate = plugin.newDate(inst.drawDate); 1122 this._update(elem[0]); 1123 if ($.fn.mousewheel) { 1124 elem.mousewheel(this._doMouseWheel); 1125 } 1126 } 1127 else { 1128 this._attachments(elem, inst); 1129 elem.on('keydown.' + inst.name, this._keyDown).on('keypress.' + inst.name, this._keyPress). 1130 on('keyup.' + inst.name, this._keyUp); 1131 if (elem.attr('disabled')) { 1132 this.disable(elem[0]); 1133 } 1134 } 1135 }, 1136 1137 _optionsChanged: function(elem, inst, options) { 1138 if (options.calendar && options.calendar !== inst.options.calendar) { 1139 var discardDate = function(name) { 1140 return (typeof inst.options[name] === 'object' ? null : inst.options[name]); 1141 }; 1142 options = $.extend({defaultDate: discardDate('defaultDate'), 1143 minDate: discardDate('minDate'), maxDate: discardDate('maxDate')}, options); 1144 inst.selectedDates = []; 1145 inst.drawDate = null; 1146 } 1147 var dates = inst.selectedDates; 1148 $.extend(inst.options, options); 1149 this.setDate(elem[0], dates, null, false, true); 1150 inst.pickingRange = false; 1151 inst.drawDate = plugin.newDate(this._checkMinMax( 1152 (inst.options.defaultDate ? inst.get('defaultDate') : inst.drawDate) || 1153 inst.get('defaultDate') || plugin.today(), inst)); 1154 if (!inst.inline) { 1155 this._attachments(elem, inst); 1156 } 1157 if (inst.inline || inst.div) { 1158 this._update(elem[0]); 1159 } 1160 }, 1161 1162 /** Attach events and trigger, if necessary. 1163 @private 1164 @param {jQuery} elem The control to affect. 1165 @param {object} inst The current instance settings. */ 1166 _attachments: function(elem, inst) { 1167 elem.off('focus.' + inst.name); 1168 if (inst.options.showOnFocus) { 1169 elem.on('focus.' + inst.name, this.show); 1170 } 1171 if (inst.trigger) { 1172 inst.trigger.remove(); 1173 } 1174 var trigger = inst.options.showTrigger; 1175 inst.trigger = (!trigger ? $([]) : 1176 $(trigger).clone().removeAttr('id').addClass(this._triggerClass) 1177 [inst.options.isRTL ? 'insertBefore' : 'insertAfter'](elem). 1178 click(function() { 1179 if (!plugin.isDisabled(elem[0])) { 1180 plugin[plugin.curInst === inst ? 'hide' : 'show'](elem[0]); 1181 } 1182 })); 1183 this._autoSize(elem, inst); 1184 var dates = this._extractDates(inst, elem.val()); 1185 if (dates) { 1186 this.setDate(elem[0], dates, null, true); 1187 } 1188 var defaultDate = inst.get('defaultDate'); 1189 if (inst.options.selectDefaultDate && defaultDate && inst.selectedDates.length === 0) {
1190 this.setDate(elem[0], plugin.newDate(defaultDate || plugin.today())); 1191 } 1192 }, 1193 1194 /** Apply the maximum length for the date format. 1195 @private 1196 @param {jQuery} elem The control to affect. 1197 @param {object} inst The current instance settings. */ 1198 _autoSize: function(elem, inst) { 1199 if (inst.options.autoSize && !inst.inline) { 1200 var date = plugin.newDate(2009, 10, 20); // Ensure double digits 1201 var dateFormat = inst.options.dateFormat; 1202 if (dateFormat.match(/[DM]/)) { 1203 var findMax = function(names) { 1204 var max = 0; 1205 var maxI = 0; 1206 for (var i = 0; i < names.length; i++) { 1207 if (names[i].length > max) { 1208 max = names[i].length; 1209 maxI = i; 1210 } 1211 } 1212 return maxI; 1213 }; 1214 date.setMonth(findMax(inst.options[dateFormat.match(/MM/) ? // Longest month 1215 'monthNames' : 'monthNamesShort'])); 1216 date.setDate(findMax(inst.options[dateFormat.match(/DD/) ? // Longest day 1217 'dayNames' : 'dayNamesShort']) + 20 - date.getDay()); 1218 } 1219 inst.elem.attr('size', plugin.formatDate(dateFormat, date, inst.getConfig()).length); 1220 } 1221 }, 1222 1223 _preDestroy: function(elem, inst) { 1224 if (inst.trigger) { 1225 inst.trigger.remove(); 1226 } 1227 elem.empty().off('.' + inst.name); 1228 if (inst.inline && $.fn.mousewheel) { 1229 elem.unmousewheel(); 1230 } 1231 if (!inst.inline && inst.options.autoSize) { 1232 elem.removeAttr('size'); 1233 } 1234 }, 1235 1236 /** Apply multiple event functions. 1237 @param {function} fns The functions to apply. 1238 @example onShow: $.datepick.multipleEvents(fn1, fn2, ...) */ 1239 multipleEvents: function() { 1240 var funcs = arguments; 1241 return function() { 1242 for (var i = 0; i < funcs.length; i++) { 1243 funcs[i].apply(this, arguments); 1244 } 1245 }; 1246 }, 1247 1248 /** Enable the control. 1249 @param {Element} elem The control to affect. 1250 @example $(selector).datepick('enable') */ 1251 enable: function(elem) { 1252 elem = $(elem); 1253 if (!elem.hasClass(this._getMarker())) { 1254 return; 1255 } 1256 var inst = this._getInst(elem); 1257 if (inst.inline) { 1258 elem.children('.' + this._disableClass).remove().end(). 1259 find('button,select').prop('disabled', false).end(). 1260 find('a').attr('href', '#'); 1261 } 1262 else { 1263 elem.prop('disabled', false); 1264 inst.trigger.filter('button.' + this._triggerClass).prop('disabled', false).end(). 1265 filter('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''}); 1266 } 1267 this._disabled = $.map(this._disabled, 1268 function(value) { return (value === elem[0] ? null : value); }); // Delete entry 1269 }, 1270 1271 /** Disable the control. 1272 @param {Element} elem The control to affect. 1273 @example $(selector).datepick('disable') */ 1274 disable: function(elem) { 1275 elem = $(elem); 1276 if (!elem.hasClass(this._getMarker())) { 1277 return; 1278 } 1279 var inst = this._getInst(elem); 1280 if (inst.inline) { 1281 var inline = elem.children(':last'); 1282 var offset = inline.offset(); 1283 var relOffset = {left: 0, top: 0}; 1284 inline.parents().each(function() { 1285 if ($(this).css('position') === 'relative') { 1286 relOffset = $(this).offset(); 1287 return false; 1288 } 1289 }); 1290 var zIndex = elem.css('zIndex'); 1291 zIndex = (zIndex === 'auto' ? 0 : parseInt(zIndex, 10)) + 1; 1292 elem.prepend('<div class="' + this._disableClass + '" style="' + 1293 'width: ' + inline.outerWidth() + 'px; height: ' + inline.outerHeight() + 1294 'px; left: ' + (offset.left - relOffset.left) + 'px; top: ' + 1295 (offset.top - relOffset.top) + 'px; z-index: ' + zIndex + '"></div>'). 1296 find('button,select').prop('disabled', true).end(). 1297 find('a').removeAttr('href'); 1298 } 1299 else { 1300 elem.prop('disabled', true); 1301 inst.trigger.filter('button.' + this._triggerClass).prop('disabled', true).end(). 1302 filter('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'}); 1303 } 1304 this._disabled = $.map(this._disabled, 1305 function(value) { return (value === elem[0] ? null : value); }); // Delete entry 1306 this._disabled.push(elem[0]); 1307 }, 1308 1309 /** Is the first field in a jQuery collection disabled as a datepicker? 1310 @param {Element} elem The control to examine. 1311 @return {boolean} <code>true</code> if disabled, <code>false</code> if enabled. 1312 @example if ($(selector).datepick('isDisabled')) {...} */ 1313 isDisabled: function(elem) { 1314 return (elem && $.inArray(elem, this._disabled) > -1); 1315 }, 1316 1317 /** Show a popup datepicker. 1318 @param {Element|Event} elem The control to use or a focus event (internal). 1319 @example $(selector).datepick('show') */ 1320 show: function(elem) { 1321 elem = $(elem.target || elem); 1322 var inst = plugin._getInst(elem); 1323 if (plugin.curInst === inst) { 1324 return; 1325 } 1326 if (plugin.curInst) { 1327 plugin.hide(plugin.curInst, true); 1328 } 1329 if (!$.isEmptyObject(inst)) { 1330 // Retrieve existing date(s) 1331 inst.lastVal = null; 1332 inst.selectedDates = plugin._extractDates(inst, elem.val()); 1333 inst.pickingRange = false;
1334 inst.drawDate = plugin._checkMinMax(plugin.newDate(inst.selectedDates[0] || 1335 inst.get('defaultDate') || plugin.today()), inst); 1336 inst.prevDate = plugin.newDate(inst.drawDate); 1337 plugin.curInst = inst; 1338 // Generate content 1339 plugin._update(elem[0], true); 1340 // Adjust position before showing 1341 var offset = plugin._checkOffset(inst); 1342 inst.div.css({left: offset.left, top: offset.top}); 1343 // And display 1344 var showAnim = inst.options.showAnim; 1345 var showSpeed = inst.options.showSpeed; 1346 showSpeed = (showSpeed === 'normal' && $.ui && 1347 parseInt($.ui.version.substring(2)) >= 8 ? '_default' : showSpeed); 1348 if ($.effects && ($.effects[showAnim] || ($.effects.effect && $.effects.effect[showAnim]))) { 1349 var data = inst.div.data(); // Update old effects data 1350 for (var key in data) { 1351 if (key.match(/^ec\.storage\./)) { 1352 data[key] = inst._mainDiv.css(key.replace(/ec\.storage\./, '')); 1353 } 1354 } 1355 inst.div.data(data).show(showAnim, inst.options.showOptions, showSpeed); 1356 } 1357 else { 1358 inst.div[showAnim || 'show'](showAnim ? showSpeed : 0); 1359 } 1360 } 1361 }, 1362 1363 /** Extract possible dates from a string. 1364 @private 1365 @param {object} inst The current instance settings. 1366 @param {string} text The text to extract from. 1367 @return {Date[]} The extracted dates. */ 1368 _extractDates: function(inst, datesText) { 1369 if (datesText === inst.lastVal) { 1370 return; 1371 } 1372 inst.lastVal = datesText; 1373 datesText = datesText.split(inst.options.multiSelect ? inst.options.multiSeparator : 1374 (inst.options.rangeSelect ? inst.options.rangeSeparator : '\x00')); 1375 var dates = []; 1376 for (var i = 0; i < datesText.length; i++) { 1377 try { 1378 var date = plugin.parseDate(inst.options.dateFormat, datesText[i], inst.getConfig()); 1379 if (date) { 1380 var found = false; 1381 for (var j = 0; j < dates.length; j++) { 1382 if (dates[j].getTime() === date.getTime()) { 1383 found = true; 1384 break; 1385 } 1386 } 1387 if (!found) { 1388 dates.push(date); 1389 } 1390 } 1391 } 1392 catch (e) { 1393 // Ignore 1394 } 1395 } 1396 dates.splice(inst.options.multiSelect || (inst.options.rangeSelect ? 2 : 1), dates.length); 1397 if (inst.options.rangeSelect && dates.length === 1) { 1398 dates[1] = dates[0]; 1399 } 1400 return dates; 1401 }, 1402 1403 /** Update the datepicker display. 1404 @private 1405 @param {Event|Element} elem A focus event or the control to use. 1406 @param {boolean} hidden <code>true</code> to initially hide the datepicker. */ 1407 _update: function(elem, hidden) { 1408 elem = $(elem.target || elem); 1409 var inst = plugin._getInst(elem); 1410 if (!$.isEmptyObject(inst)) { 1411 if (inst.inline || plugin.curInst === inst) { 1412 if ($.isFunction(inst.options.onChangeMonthYear) && (!inst.prevDate || 1413 inst.prevDate.getFullYear() !== inst.drawDate.getFullYear() || 1414 inst.prevDate.getMonth() !== inst.drawDate.getMonth())) { 1415 inst.options.onChangeMonthYear.apply(elem[0], 1416 [inst.drawDate.getFullYear(), inst.drawDate.getMonth() + 1]); 1417 } 1418 } 1419 if (inst.inline) { 1420 var index = $('a, :input', elem).index($(':focus', elem)); 1421 elem.html(this._generateContent(elem[0], inst)); 1422 var focus = elem.find('a, :input'); 1423 focus.eq(Math.max(Math.min(index, focus.length - 1), 0)).focus(); 1424 } 1425 else if (plugin.curInst === inst) { 1426 if (!inst.div) { 1427 inst.div = $('<div></div>').addClass(this._popupClass). 1428 css({display: (hidden ? 'none' : 'static'), position: 'absolute', 1429 left: elem.offset().left, top: elem.offset().top + elem.outerHeight()}). 1430 appendTo($(inst.options.popupContainer || 'body')); 1431 if ($.fn.mousewheel) { 1432 inst.div.mousewheel(this._doMouseWheel); 1433 } 1434 } 1435 inst.div.html(this._generateContent(elem[0], inst)); 1436 elem.focus(); 1437 } 1438 } 1439 }, 1440 1441 /** Update the input field and any alternate field with the current dates. 1442 @private 1443 @param {Element} elem The control to use. 1444 @param {boolean} keyUp <code>true</code> if coming from <code>keyUp</code> processing (internal). */ 1445 _updateInput: function(elem, keyUp) { 1446 var inst = this._getInst(elem); 1447 if (!$.isEmptyObject(inst)) { 1448 var value = ''; 1449 var altValue = ''; 1450 var sep = (inst.options.multiSelect ? inst.options.multiSeparator : 1451 inst.options.rangeSeparator); 1452 var altFormat = inst.options.altFormat || inst.options.dateFormat; 1453 for (var i = 0; i < inst.selectedDates.length; i++) { 1454 value += (keyUp ? '' : (i > 0 ? sep : '') + plugin.formatDate( 1455 inst.options.dateFormat, inst.selectedDates[i], inst.getConfig())); 1456 altValue += (i > 0 ? sep : '') + plugin.formatDate( 1457 altFormat, inst.selectedDates[i], inst.getConfig()); 1458 } 1459 if (!inst.inline && !keyUp) { 1460 $(elem).val(value); 1461 } 1462 $(inst.options.altField).val(altValue); 1463 if ($.isFunction(inst.options.onSelect) && !keyUp && !inst.inSelect) { 1464 inst.inSelect = true; // Prevent endless loops 1465 inst.options.onSelect.apply(elem, [inst.selectedDates]); 1466 inst.inSelect = false;
1467 } 1468 } 1469 }, 1470 1471 /** Retrieve the size of left and top borders for an element. 1472 @private 1473 @param {jQuery} elem The element of interest. 1474 @return {number[]} The left and top borders. */ 1475 _getBorders: function(elem) { 1476 var convert = function(value) { 1477 return {thin: 1, medium: 3, thick: 5}[value] || value; 1478 }; 1479 return [parseFloat(convert(elem.css('border-left-width'))), 1480 parseFloat(convert(elem.css('border-top-width')))]; 1481 }, 1482 1483 /** Check positioning to remain on the screen. 1484 @private 1485 @param {object} inst The current instance settings. 1486 @return {object} The updated offset for the datepicker. */ 1487 _checkOffset: function(inst) { 1488 var base = (inst.elem.is(':hidden') && inst.trigger ? inst.trigger : inst.elem); 1489 var offset = base.offset(); 1490 var browserWidth = $(window).width(); 1491 var browserHeight = $(window).height(); 1492 if (browserWidth === 0) { 1493 return offset; 1494 } 1495 var isFixed = false; 1496 $(inst.elem).parents().each(function() { 1497 isFixed = isFixed || ($(this).css('position') === 'fixed'); 1498 return !isFixed; 1499 }); 1500 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; 1501 var scrollY = document.documentElement.scrollTop || document.body.scrollTop; 1502 var above = offset.top - (isFixed ? scrollY : 0) - inst.div.outerHeight(); 1503 var below = offset.top - (isFixed ? scrollY : 0) + base.outerHeight(); 1504 var alignL = offset.left - (isFixed ? scrollX : 0); 1505 var alignR = offset.left - (isFixed ? scrollX : 0) + base.outerWidth() - inst.div.outerWidth(); 1506 var tooWide = (offset.left - scrollX + inst.div.outerWidth()) > browserWidth; 1507 var tooHigh = (offset.top - scrollY + inst.elem.outerHeight() + 1508 inst.div.outerHeight()) > browserHeight; 1509 inst.div.css('position', isFixed ? 'fixed' : 'absolute'); 1510 var alignment = inst.options.alignment; 1511 if (alignment === 'topLeft') { 1512 offset = {left: alignL, top: above}; 1513 } 1514 else if (alignment === 'topRight') { 1515 offset = {left: alignR, top: above}; 1516 } 1517 else if (alignment === 'bottomLeft') { 1518 offset = {left: alignL, top: below}; 1519 } 1520 else if (alignment === 'bottomRight') { 1521 offset = {left: alignR, top: below}; 1522 } 1523 else if (alignment === 'top') { 1524 offset = {left: (inst.options.isRTL || tooWide ? alignR : alignL), top: above}; 1525 } 1526 else { // bottom 1527 offset = {left: (inst.options.isRTL || tooWide ? alignR : alignL), 1528 top: (tooHigh ? above : below)}; 1529 } 1530 offset.left = Math.max((isFixed ? 0 : scrollX), offset.left); 1531 offset.top = Math.max((isFixed ? 0 : scrollY), offset.top); 1532 return offset; 1533 }, 1534 1535 /** Close date picker if clicked elsewhere. 1536 @private 1537 @param {MouseEvent} event The mouse click to check. */ 1538 _checkExternalClick: function(event) { 1539 if (!plugin.curInst) { 1540 return; 1541 } 1542 var elem = $(event.target); 1543 if (elem.closest('.' + plugin._popupClass + ',.' + plugin._triggerClass).length === 0 && 1544 !elem.hasClass(plugin._getMarker())) { 1545 plugin.hide(plugin.curInst); 1546 } 1547 }, 1548 1549 /** Hide a popup datepicker. 1550 @param {Element|object} elem The control to use or the current instance settings. 1551 @param {boolean} [immediate=false] <code>true</code> to close immediately without animation (internal). 1552 @example $(selector).datepick('hide') */ 1553 hide: function(elem, immediate) { 1554 if (!elem) { 1555 return; 1556 } 1557 var inst = this._getInst(elem); 1558 if ($.isEmptyObject(inst)) { 1559 inst = elem; 1560 } 1561 if (inst && inst === plugin.curInst) { 1562 var showAnim = (immediate ? '' : inst.options.showAnim); 1563 var showSpeed = inst.options.showSpeed; 1564 showSpeed = (showSpeed === 'normal' && $.ui && 1565 parseInt($.ui.version.substring(2)) >= 8 ? '_default' : showSpeed); 1566 var postProcess = function() { 1567 if (!inst.div) { 1568 return; 1569 } 1570 inst.div.remove(); 1571 inst.div = null; 1572 plugin.curInst = null; 1573 if ($.isFunction(inst.options.onClose)) { 1574 inst.options.onClose.apply(elem, [inst.selectedDates]); 1575 } 1576 }; 1577 inst.div.stop(); 1578 if ($.effects && ($.effects[showAnim] || ($.effects.effect && $.effects.effect[showAnim]))) { 1579 inst.div.hide(showAnim, inst.options.showOptions, showSpeed, postProcess); 1580 } 1581 else { 1582 var hideAnim = (showAnim === 'slideDown' ? 'slideUp' : 1583 (showAnim === 'fadeIn' ? 'fadeOut' : 'hide')); 1584 inst.div[hideAnim]((showAnim ? showSpeed : ''), postProcess); 1585 } 1586 if (!showAnim) { 1587 postProcess(); 1588 } 1589 } 1590 }, 1591 1592 /** Handle keystrokes in the datepicker. 1593 @private 1594 @param {KeyEvent} event The keystroke. 1595 @return {boolean} <code>true</code> if not handled, <code>false</code> if handled. */ 1596 _keyDown: function(event) { 1597 var elem = (event.data && event.data.elem) || event.target; 1598 var inst = plugin._getInst(elem); 1599 var handled = false;
1600 var command = null; 1601 if (inst.inline || inst.div) { 1602 if (event.keyCode === 9) { // Tab - close 1603 plugin.hide(elem); 1604 } 1605 else if (event.keyCode === 13) { // Enter - select 1606 plugin.selectDate(elem, 1607 $('a.' + inst.options.renderer.highlightedClass, inst.div)[0]); 1608 handled = true; 1609 } 1610 else { // Command keystrokes 1611 for (var key in inst.options.commands) { 1612 if (inst.options.commands.hasOwnProperty(key)) { 1613 command = inst.options.commands[key]; 1614 /* jshint -W018 */ // Dislikes !! 1615 if (command.keystroke.keyCode === event.keyCode && 1616 !!command.keystroke.ctrlKey === !!(event.ctrlKey || event.metaKey) && 1617 !!command.keystroke.altKey === event.altKey && 1618 !!command.keystroke.shiftKey === event.shiftKey) { 1619 /* jshint +W018 */ 1620 plugin.performAction(elem, key); 1621 handled = true; 1622 break; 1623 } 1624 } 1625 } 1626 } 1627 } 1628 else { // Show on 'current' keystroke 1629 command = inst.options.commands.current; 1630 /* jshint -W018 */ // Dislikes !! 1631 if (command.keystroke.keyCode === event.keyCode && 1632 !!command.keystroke.ctrlKey === !!(event.ctrlKey || event.metaKey) && 1633 !!command.keystroke.altKey === event.altKey && 1634 !!command.keystroke.shiftKey === event.shiftKey) { 1635 /* jshint +W018 */ 1636 plugin.show(elem); 1637 handled = true; 1638 } 1639 } 1640 inst.ctrlKey = ((event.keyCode < 48 && event.keyCode !== 32) || event.ctrlKey || event.metaKey); 1641 if (handled) { 1642 event.preventDefault(); 1643 event.stopPropagation(); 1644 } 1645 return !handled; 1646 }, 1647 1648 /** Filter keystrokes in the datepicker. 1649 @private 1650 @param {KeyEvent} event The keystroke. 1651 @return {boolean} <code>true</code> if allowed, <code>false</code> if not allowed. */ 1652 _keyPress: function(event) { 1653 var inst = plugin._getInst((event.data && event.data.elem) || event.target); 1654 if (!$.isEmptyObject(inst) && inst.options.constrainInput) { 1655 var ch = String.fromCharCode(event.keyCode || event.charCode); 1656 var allowedChars = plugin._allowedChars(inst); 1657 return (event.metaKey || inst.ctrlKey || ch < ' ' || 1658 !allowedChars || allowedChars.indexOf(ch) > -1); 1659 } 1660 return true; 1661 }, 1662 1663 /** Determine the set of characters allowed by the date format. 1664 @private 1665 @param {object} inst The current instance settings. 1666 @return {string} The set of allowed characters, or <code>null</code> if anything allowed. */ 1667 _allowedChars: function(inst) { 1668 var allowedChars = (inst.options.multiSelect ? inst.options.multiSeparator : 1669 (inst.options.rangeSelect ? inst.options.rangeSeparator : '')); 1670 var literal = false; 1671 var hasNum = false; 1672 var dateFormat = inst.options.dateFormat; 1673 for (var i = 0; i < dateFormat.length; i++) { 1674 var ch = dateFormat.charAt(i); 1675 if (literal) { 1676 if (ch === '\'' && dateFormat.charAt(i + 1) !== '\'') { 1677 literal = false; 1678 } 1679 else { 1680 allowedChars += ch; 1681 } 1682 } 1683 else { 1684 switch (ch) { 1685 case 'd': 1686 case 'm': 1687 case 'o': 1688 case 'w': 1689 allowedChars += (hasNum ? '' : '0123456789'); 1690 hasNum = true; 1691 break; 1692 case 'y': 1693 case '@': 1694 case '!': 1695 allowedChars += (hasNum ? '' : '0123456789') + '-'; 1696 hasNum = true; 1697 break; 1698 case 'J': 1699 allowedChars += (hasNum ? '' : '0123456789') + '-.'; 1700 hasNum = true; 1701 break; 1702 case 'D': 1703 case 'M': 1704 case 'Y': 1705 return null; // Accept anything 1706 case '\'': 1707 if (dateFormat.charAt(i + 1) === '\'') { 1708 allowedChars += '\''; 1709 } 1710 else { 1711 literal = true; 1712 } 1713 break; 1714 default: 1715 allowedChars += ch; 1716 } 1717 } 1718 } 1719 return allowedChars; 1720 }, 1721 1722 /** Synchronise datepicker with the field. 1723 @private 1724 @param {KeyEvent} event The keystroke. 1725 @return {boolean} <code>true</code> if allowed, <code>false</code> if not allowed. */ 1726 _keyUp: function(event) { 1727 var elem = (event.data && event.data.elem) || event.target; 1728 var inst = plugin._getInst(elem); 1729 if (!$.isEmptyObject(inst) && !inst.ctrlKey && inst.lastVal !== inst.elem.val()) { 1730 try { 1731 var dates = plugin._extractDates(inst, inst.elem.val()); 1732 if (dates.length > 0) { 1733 plugin.setDate(elem, dates, null, true); 1734 } 1735 } 1736 catch (e) { 1737 // Ignore 1738 } 1739 } 1740 return true; 1741 }, 1742 1743 /** Increment/decrement month/year on mouse wheel activity. 1744 @private 1745 @param {event} event The mouse wheel event. 1746 @param {number} delta The amount of change. */ 1747 _doMouseWheel: function(event, delta) { 1748 var elem = (plugin.curInst && plugin.curInst.elem[0]) || 1749 $(event.target).closest('.' + plugin._getMarker())[0]; 1750 if (plugin.isDisabled(elem)) { 1751 return; 1752 } 1753 var inst = plugin._getInst(elem); 1754 if (inst.options.useMouseWheel) { 1755 delta = (delta < 0 ? -1 : +1); 1756 plugin.changeMonth(elem, -inst.options[event.ctrlKey ? 'monthsToJump' : 'monthsToStep'] * delta); 1757 } 1758 event.preventDefault(); 1759 }, 1760 1761 /** Clear an input and close a popup datepicker. 1762 @param {Element} elem The control to use. 1763 @example $(selector).datepick('clear') */ 1764 clear: function(elem) { 1765 var inst = this._getInst(elem); 1766 if (!$.isEmptyObject(inst)) { 1767 inst.selectedDates = []; 1768 this.hide(elem); 1769 var defaultDate = inst.get('defaultDate'); 1770 if (inst.options.selectDefaultDate && defaultDate) {
1771 this.setDate(elem, plugin.newDate(defaultDate || plugin.today())); 1772 } 1773 else { 1774 this._updateInput(elem); 1775 } 1776 } 1777 }, 1778 1779 /** Retrieve the selected date(s) for a datepicker. 1780 @param {Element} elem The control to examine. 1781 @return {Date[]} The selected date(s). 1782 @example var dates = $(selector).datepick('getDate') */ 1783 getDate: function(elem) { 1784 var inst = this._getInst(elem); 1785 return (!$.isEmptyObject(inst) ? inst.selectedDates : []); 1786 }, 1787 1788 /** Set the selected date(s) for a datepicker. 1789 @param {Element} elem The control to examine. 1790 @param {Date|number|string|array} dates The selected date(s), as a <code>Date</code>, 1791 or as a string in the current {@linkcode module:Datepick~regionalOptions|dateFormat} 1792 or as a numeric offset - in days from today, 1793 or as a string of amounts and periods, e.g. '+1m +2w', 1794 using 'd' for days, 'w' for weeks, 'm' for months, and 'y' for years, 1795 or as an array of these. 1796 @param {Date|number|string} [endDate] The ending date for a range. 1797 @param {boolean} [keyUp=false] <code>true</code> if coming from <code>keyUp</code> processing (internal). 1798 @param {boolean} [setOpt=false] <code>true</code> if coming from option processing (internal). 1799 @example $(selector).datepick('setDate', new Date(2014, 12-1, 25)) 1800$(selector).datepick('setDate', '12/25/2014', '01/01/2015') 1801$(selector).datepick('setDate', [date1, date2, date3]) */ 1802 setDate: function(elem, dates, endDate, keyUp, setOpt) { 1803 var inst = this._getInst(elem); 1804 if (!$.isEmptyObject(inst)) { 1805 if (!$.isArray(dates)) { 1806 dates = [dates]; 1807 if (endDate) { 1808 dates.push(endDate); 1809 } 1810 } 1811 var minDate = inst.get('minDate'); 1812 var maxDate = inst.get('maxDate'); 1813 var curDate = inst.selectedDates[0]; 1814 inst.selectedDates = []; 1815 for (var i = 0; i < dates.length; i++) { 1816 var date = plugin.determineDate( 1817 dates[i], null, curDate, inst.options.dateFormat, inst.getConfig()); 1818 if (date) { 1819 if ((!minDate || date.getTime() >= minDate.getTime()) && 1820 (!maxDate || date.getTime() <= maxDate.getTime())) { 1821 var found = false; 1822 for (var j = 0; j < inst.selectedDates.length; j++) { 1823 if (inst.selectedDates[j].getTime() === date.getTime()) { 1824 found = true; 1825 break; 1826 } 1827 } 1828 if (!found) { 1829 inst.selectedDates.push(date); 1830 } 1831 } 1832 } 1833 } 1834 inst.selectedDates.splice(inst.options.multiSelect || 1835 (inst.options.rangeSelect ? 2 : 1), inst.selectedDates.length); 1836 if (inst.options.rangeSelect) { 1837 switch (inst.selectedDates.length) { 1838 case 1: 1839 inst.selectedDates[1] = inst.selectedDates[0]; 1840 break; 1841 case 2: 1842 inst.selectedDates[1] = 1843 (inst.selectedDates[0].getTime() > inst.selectedDates[1].getTime() ? 1844 inst.selectedDates[0] : inst.selectedDates[1]); 1845 break; 1846 } 1847 inst.pickingRange = false; 1848 } 1849 inst.prevDate = (inst.drawDate ? plugin.newDate(inst.drawDate) : null); 1850 inst.drawDate = this._checkMinMax(plugin.newDate(inst.selectedDates[0] || 1851 inst.get('defaultDate') || plugin.today()), inst); 1852 if (!setOpt) { 1853 this._update(elem); 1854 this._updateInput(elem, keyUp); 1855 } 1856 } 1857 }, 1858 1859 /** Determine whether a date is selectable for this datepicker. 1860 @private 1861 @param {Element} elem The control to check. 1862 @param {Date|string|number} date The date to check. 1863 @return {boolean} <code>true</code> if selectable, <code>false</code> if not. 1864 @example var selectable = $(selector).datepick('isSelectable', date) */ 1865 isSelectable: function(elem, date) { 1866 var inst = this._getInst(elem); 1867 if ($.isEmptyObject(inst)) { 1868 return false; 1869 } 1870 date = plugin.determineDate(date, inst.selectedDates[0] || this.today(), null, 1871 inst.options.dateFormat, inst.getConfig()); 1872 return this._isSelectable(elem, date, inst.options.onDate, 1873 inst.get('minDate'), inst.get('maxDate')); 1874 }, 1875 1876 /** Internally determine whether a date is selectable for this datepicker. 1877 @private 1878 @param {Element} elem the control to check. 1879 @param {Date} date The date to check. 1880 @param {DatepickOnDate|boolean} onDate Any {@linkcode module:Datepick~defaultOptions|onDate} callback 1881 or <code>callback.selectable</code>. 1882 @param {Date} minDate The minimum allowed date. 1883 @param {Date} maxDate The maximum allowed date. 1884 @return {boolean} <code>true</code> if selectable, <code>false</code> if not. */ 1885 _isSelectable: function(elem, date, onDate, minDate, maxDate) { 1886 var dateInfo = (typeof onDate === 'boolean' ? {selectable: onDate} : 1887 (!$.isFunction(onDate) ? {}
1887 : onDate.apply(elem, [date, true]))); 1888 return (dateInfo.selectable !== false) && 1889 (!minDate || date.getTime() >= minDate.getTime()) && 1890 (!maxDate || date.getTime() <= maxDate.getTime()); 1891 }, 1892 1893 /** Perform a {@linkcode module:Datepick~commands|named action} for a datepicker. 1894 @param {element} elem The control to affect. 1895 @param {string} action The name of the action. 1896 @example $(selector).datepick('performAction', 'prev') */ 1897 performAction: function(elem, action) { 1898 var inst = this._getInst(elem); 1899 if (!$.isEmptyObject(inst) && !this.isDisabled(elem)) { 1900 var commands = inst.options.commands; 1901 if (commands[action] && commands[action].enabled.apply(elem, [inst])) { 1902 commands[action].action.apply(elem, [inst]); 1903 } 1904 } 1905 }, 1906 1907 /** Set the currently shown month and day, defaulting to today. 1908 @param {Element} elem The control to affect. 1909 @param {number} [year] The year to show. 1910 @param {number} [month] The month to show (1-12). 1911 @param {number} [day] The day to show. 1912 @example $(selector).datepick('showMonth', 2014, 12, 25) */ 1913 showMonth: function(elem, year, month, day) { 1914 var inst = this._getInst(elem); 1915 if (!$.isEmptyObject(inst) && (typeof day !== 'undefined' || 1916 (inst.drawDate.getFullYear() !== year || inst.drawDate.getMonth() + 1 !== month))) { 1917 inst.prevDate = plugin.newDate(inst.drawDate); 1918 var show = this._checkMinMax((typeof year !== 'undefined' ? 1919 plugin.newDate(year, month, 1) : plugin.today()), inst); 1920 inst.drawDate = plugin.newDate(show.getFullYear(), show.getMonth() + 1, 1921 (typeof day !== 'undefined' ? day : Math.min(inst.drawDate.getDate(), 1922 plugin.daysInMonth(show.getFullYear(), show.getMonth() + 1)))); 1923 this._update(elem); 1924 } 1925 }, 1926 1927 /** Adjust the currently shown month. 1928 @param {Element} elem The control to affect. 1929 @param {number} offset The number of months to change by. 1930 @example $(selector).datepick('changeMonth', 2)*/ 1931 changeMonth: function(elem, offset) { 1932 var inst = this._getInst(elem); 1933 if (!$.isEmptyObject(inst)) { 1934 var date = plugin.add(plugin.newDate(inst.drawDate), offset, 'm'); 1935 this.showMonth(elem, date.getFullYear(), date.getMonth() + 1); 1936 } 1937 }, 1938 1939 /** Adjust the currently shown day. 1940 @param {Element} elem The control to affect. 1941 @param {number} offset The number of days to change by. 1942 @example $(selector).datepick('changeDay', 7)*/ 1943 changeDay: function(elem, offset) { 1944 var inst = this._getInst(elem); 1945 if (!$.isEmptyObject(inst)) { 1946 var date = plugin.add(plugin.newDate(inst.drawDate), offset, 'd'); 1947 this.showMonth(elem, date.getFullYear(), date.getMonth() + 1, date.getDate()); 1948 } 1949 }, 1950 1951 /** Restrict a date to the minimum/maximum specified. 1952 @private 1953 @param {Date} date The date to check. 1954 @param {object} inst The current instance settings. */ 1955 _checkMinMax: function(date, inst) { 1956 var minDate = inst.get('minDate'); 1957 var maxDate = inst.get('maxDate'); 1958 date = (minDate && date.getTime() < minDate.getTime() ? plugin.newDate(minDate) : date); 1959 date = (maxDate && date.getTime() > maxDate.getTime() ? plugin.newDate(maxDate) : date); 1960 return date; 1961 }, 1962 1963 /** Retrieve the date associated with an entry in the datepicker. 1964 @param {Element} elem The control to examine. 1965 @param {Element} target The selected datepicker element. 1966 @return {Date} The corresponding date, or <code>null</code>. 1967 @example var date = $(selector).datepick('retrieveDate', $('div.datepick-popup a:contains(10)')[0]) */ 1968 retrieveDate: function(elem, target) { 1969 var inst = this._getInst(elem); 1970 return ($.isEmptyObject(inst) ? null : this._normaliseDate( 1971 new Date(parseInt(target.className.replace(/^.*dp(-?\d+).*$/, '$1'), 10)))); 1972 }, 1973 1974 /** Select a date for this datepicker. 1975 @param {Element} elem The control to examine. 1976 @param {Element} target The selected datepicker element. 1977 @example $(selector).datepick('selectDate', $('div.datepick-popup a:contains(10)')[0]) */ 1978 selectDate: function(elem, target) { 1979 var inst = this._getInst(elem); 1980 if (!$.isEmptyObject(inst) && !this.isDisabled(elem)) { 1981 var date = this.retrieveDate(elem, target); 1982 if (inst.options.multiSelect) { 1983 var found = false;
1984 for (var i = 0; i < inst.selectedDates.length; i++) { 1985 if (date.getTime() === inst.selectedDates[i].getTime()) { 1986 inst.selectedDates.splice(i, 1); 1987 found = true; 1988 break; 1989 } 1990 } 1991 if (!found && inst.selectedDates.length < inst.options.multiSelect) { 1992 inst.selectedDates.push(date); 1993 } 1994 } 1995 else if (inst.options.rangeSelect) { 1996 if (inst.pickingRange) { 1997 inst.selectedDates[1] = date; 1998 } 1999 else { 2000 inst.selectedDates = [date, date]; 2001 } 2002 inst.pickingRange = !inst.pickingRange; 2003 } 2004 else { 2005 inst.selectedDates = [date]; 2006 } 2007 inst.prevDate = inst.drawDate = plugin.newDate(date); 2008 this._updateInput(elem); 2009 if (inst.inline || inst.pickingRange || inst.selectedDates.length < 2010 (inst.options.multiSelect || (inst.options.rangeSelect ? 2 : 1))) { 2011 this._update(elem); 2012 } 2013 else { 2014 this.hide(elem); 2015 } 2016 } 2017 }, 2018 2019 /** Generate the datepicker content for this control. 2020 @private 2021 @param {Element} elem The control to affect. 2022 @param {object} inst The current instance settings. 2023 @return {jQuery} The datepicker content */ 2024 _generateContent: function(elem, inst) { 2025 var monthsToShow = inst.options.monthsToShow; 2026 monthsToShow = ($.isArray(monthsToShow) ? monthsToShow : [1, monthsToShow]); 2027 inst.drawDate = this._checkMinMax( 2028 inst.drawDate || inst.get('defaultDate') || plugin.today(), inst); 2029 var drawDate = plugin._applyMonthsOffset(plugin.newDate(inst.drawDate), inst); 2030 // Generate months 2031 var monthRows = ''; 2032 for (var row = 0; row < monthsToShow[0]; row++) { 2033 var months = ''; 2034 for (var col = 0; col < monthsToShow[1]; col++) { 2035 months += this._generateMonth(elem, inst, drawDate.getFullYear(), 2036 drawDate.getMonth() + 1, inst.options.renderer, (row === 0 && col === 0)); 2037 plugin.add(drawDate, 1, 'm'); 2038 } 2039 monthRows += this._prepare(inst.options.renderer.monthRow, inst).replace(/\{months\}/, months); 2040 } 2041 var picker = this._prepare(inst.options.renderer.picker, inst).replace(/\{months\}/, monthRows). 2042 replace(/\{weekHeader\}/g, this._generateDayHeaders(inst, inst.options.renderer)); 2043 // Add commands 2044 var addCommand = function(type, open, close, name, classes) { 2045 if (picker.indexOf('{' + type + ':' + name + '}') === -1) { 2046 return; 2047 } 2048 var command = inst.options.commands[name]; 2049 var date = (inst.options.commandsAsDateFormat ? command.date.apply(elem, [inst]) : null); 2050 picker = picker.replace(new RegExp('\\{' + type + ':' + name + '\\}', 'g'), 2051 '<' + open + (command.status ? ' title="' + inst.options[command.status] + '"' : '') + 2052 ' class="' + inst.options.renderer.commandClass + ' ' + 2053 inst.options.renderer.commandClass + '-' + name + ' ' + classes + 2054 (command.enabled(inst) ? '' : ' ' + inst.options.renderer.disabledClass) + '">' + 2055 (date ? plugin.formatDate(inst.options[command.text], date, inst.getConfig()) : 2056 inst.options[command.text]) + '</' + close + '>'); 2057 }; 2058 for (var key in inst.options.commands) { 2059 if (inst.options.commands.hasOwnProperty(key)) { 2060 addCommand('button', 'button type="button"', 'button', key, 2061 inst.options.renderer.commandButtonClass); 2062 addCommand('link', 'a href="javascript:void(0)"', 'a', key, 2063 inst.options.renderer.commandLinkClass); 2064 } 2065 } 2066 picker = $(picker); 2067 if (monthsToShow[1] > 1) { 2068 var count = 0; 2069 $(inst.options.renderer.monthSelector, picker).each(function() { 2070 var nth = ++count % monthsToShow[1];
2071 $(this).addClass(nth === 1 ? 'first' : (nth === 0 ? 'last' : '')); 2072 }); 2073 } 2074 // Add datepicker behaviour 2075 var self = this; 2076 function removeHighlight() { 2077 /* jshint -W040 */ 2078 (inst.inline ? $(this).closest('.' + self._getMarker()) : inst.div). 2079 find(inst.options.renderer.daySelector + ' a'). 2080 removeClass(inst.options.renderer.highlightedClass); 2081 /* jshint +W040 */ 2082 } 2083 picker.find(inst.options.renderer.daySelector + ' a').hover( 2084 function() { 2085 removeHighlight.apply(this); 2086 $(this).addClass(inst.options.renderer.highlightedClass); 2087 }, 2088 removeHighlight). 2089 click(function() { 2090 self.selectDate(elem, this); 2091 }).end(). 2092 find('select.' + this._monthYearClass + ':not(.' + this._anyYearClass + ')'). 2093 change(function() { 2094 var monthYear = $(this).val().split('/'); 2095 self.showMonth(elem, parseInt(monthYear[1], 10), parseInt(monthYear[0], 10)); 2096 }).end(). 2097 find('select.' + this._anyYearClass).click(function() { 2098 $(this).css('visibility', 'hidden'). 2099 next('input').css({left: this.offsetLeft, top: this.offsetTop, 2100 width: this.offsetWidth, height: this.offsetHeight}).show().focus(); 2101 }).end(). 2102 find('input.' + self._monthYearClass).change(function() { 2103 try { 2104 var year = parseInt($(this).val(), 10); 2105 year = (isNaN(year) ? inst.drawDate.getFullYear() : year); 2106 self.showMonth(elem, year, inst.drawDate.getMonth() + 1, inst.drawDate.getDate()); 2107 } 2108 catch (e) { 2109 window.alert(e); 2110 } 2111 }).keydown(function(event) { 2112 if (event.keyCode === 13) { // Enter 2113 $(event.elem).change(); 2114 } 2115 else if (event.keyCode === 27) { // Escape 2116 $(event.elem).hide().prev('select').css('visibility', 'visible'); 2117 inst.elem.focus(); 2118 } 2119 }); 2120 // Add keyboard handling 2121 var data = {elem: inst.elem[0]}; 2122 picker.keydown(data, this._keyDown).keypress(data, this._keyPress).keyup(data, this._keyUp); 2123 // Add command behaviour 2124 picker.find('.' + inst.options.renderer.commandClass).click(function() { 2125 if (!$(this).hasClass(inst.options.renderer.disabledClass)) { 2126 var action = this.className.replace( 2127 new RegExp('^.*' + inst.options.renderer.commandClass + '-([^ ]+).*$'), '$1'); 2128 plugin.performAction(elem, action); 2129 } 2130 }); 2131 // Add classes 2132 if (inst.options.isRTL) { 2133 picker.addClass(inst.options.renderer.rtlClass); 2134 } 2135 if (monthsToShow[0] * monthsToShow[1] > 1) { 2136 picker.addClass(inst.options.renderer.multiClass); 2137 } 2138 if (inst.options.pickerClass) { 2139 picker.addClass(inst.options.pickerClass); 2140 } 2141 // Resize 2142 $('body').append(picker); 2143 var width = 0; 2144 picker.find(inst.options.renderer.monthSelector).each(function() { 2145 width += $(this).outerWidth(); 2146 }); 2147 picker.width(width / monthsToShow[0]); 2148 // Pre-show customisation 2149 if ($.isFunction(inst.options.onShow)) { 2150 inst.options.onShow.apply(elem, [picker, inst]); 2151 } 2152 return picker; 2153 }, 2154 2155 /** Generate the content for a single month. 2156 @private 2157 @param {Element} elem The control to affect. 2158 @param {object} inst The current instance settings. 2159 @param {number} year The year to generate. 2160 @param {number} month The month to generate. 2161 @param {object} renderer The rendering templates. 2162 @param {boolean} first <code>true</code> if first of multiple months. 2163 @return {string} The month content. */ 2164 _generateMonth: function(elem, inst, year, month, renderer, first) { 2165 var daysInMonth = plugin.daysInMonth(year, month); 2166 var monthsToShow = inst.options.monthsToShow; 2167 monthsToShow = ($.isArray(monthsToShow) ? monthsToShow : [1, monthsToShow]); 2168 var fixedWeeks = inst.options.fixedWeeks || (monthsToShow[0] * monthsToShow[1] > 1); 2169 var firstDay = inst.options.firstDay; 2170 var leadDays = (plugin.newDate(year, month, 1).getDay() - firstDay + 7) % 7; 2171 var numWeeks = (fixedWeeks ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); 2172 var selectOtherMonths = inst.options.selectOtherMonths && inst.options.showOtherMonths; 2173 var minDate = (inst.pickingRange ? inst.selectedDates[0] : inst.get('minDate')); 2174 var maxDate = inst.get('maxDate'); 2175 var showWeeks = renderer.week.indexOf('{weekOfYear}') > -1; 2176 var today = plugin.today();
2177 var drawDate = plugin.newDate(year, month, 1); 2178 plugin.add(drawDate, -leadDays - (fixedWeeks && (drawDate.getDay() === firstDay) ? 7 : 0), 'd'); 2179 var ts = drawDate.getTime(); 2180 // Generate weeks 2181 var weeks = ''; 2182 for (var week = 0; week < numWeeks; week++) { 2183 var weekOfYear = (!showWeeks ? '' : '<span class="dp' + ts + '">' + 2184 ($.isFunction(inst.options.calculateWeek) ? inst.options.calculateWeek(drawDate) : 0) + '</span>'); 2185 var days = ''; 2186 for (var day = 0; day < 7; day++) { 2187 var selected = false; 2188 if (inst.options.rangeSelect && inst.selectedDates.length > 0) { 2189 selected = (drawDate.getTime() >= inst.selectedDates[0] && 2190 drawDate.getTime() <= inst.selectedDates[1]); 2191 } 2192 else { 2193 for (var i = 0; i < inst.selectedDates.length; i++) { 2194 if (inst.selectedDates[i].getTime() === drawDate.getTime()) { 2195 selected = true; 2196 break; 2197 } 2198 } 2199 } 2200 var dateInfo = (!$.isFunction(inst.options.onDate) ? {} : 2201 inst.options.onDate.apply(elem, [drawDate, drawDate.getMonth() + 1 === month])); 2202 var selectable = (selectOtherMonths || drawDate.getMonth() + 1 === month) && 2203 this._isSelectable(elem, drawDate, dateInfo.selectable, minDate, maxDate); 2204 days += this._prepare(renderer.day, inst).replace(/\{day\}/g, 2205 (selectable ? '<a href="javascript:void(0)"' : '<span') + 2206 ' class="dp' + ts + ' ' + (dateInfo.dateClass || '') + 2207 (selected && (selectOtherMonths || drawDate.getMonth() + 1 === month) ? 2208 ' ' + renderer.selectedClass : '') + 2209 (selectable ? ' ' + renderer.defaultClass : '') + 2210 ((drawDate.getDay() || 7) < 6 ? '' : ' ' + renderer.weekendClass) + 2211 (drawDate.getMonth() + 1 === month ? '' : ' ' + renderer.otherMonthClass) + 2212 (drawDate.getTime() === today.getTime() && (drawDate.getMonth() + 1) === month ? 2213 ' ' + renderer.todayClass : '') + 2214 (drawDate.getTime() === inst.drawDate.getTime() && (drawDate.getMonth() + 1) === month ? 2215 ' ' + renderer.highlightedClass : '') + '"' + 2216 (dateInfo.title || (inst.options.dayStatus && selectable) ? ' title="' + 2217 (dateInfo.title || plugin.formatDate( 2218 inst.options.dayStatus, drawDate, inst.getConfig())) + '"' : '') + '>' + 2219 (inst.options.showOtherMonths || (drawDate.getMonth() + 1) === month ? 2220 dateInfo.content || drawDate.getDate() : ' ') + 2221 (selectable ? '</a>' : '</span>')); 2222 plugin.add(drawDate, 1, 'd'); 2223 ts = drawDate.getTime(); 2224 } 2225 weeks += this._prepare(renderer.week, inst).replace(/\{days\}/g, days). 2226 replace(/\{weekOfYear\}/g, weekOfYear); 2227 } 2228 var monthHeader = this._prepare(renderer.month, inst).match(/\{monthHeader(:[^\}]+)?\}/); 2229 monthHeader = (monthHeader[0].length <= 13 ? 'MM yyyy' : 2230 monthHeader[0].substring(13, monthHeader[0].length - 1)); 2231 monthHeader = (first ? this._generateMonthSelection( 2232 inst, year, month, minDate, maxDate, monthHeader, renderer) : 2233 plugin.formatDate(monthHeader, plugin.newDate(year, month, 1), inst.getConfig())); 2234 var weekHeader = this._prepare(renderer.weekHeader, inst). 2235 replace(/\{days\}/g, this._generateDayHeaders(inst, renderer)); 2236 return this._prepare(renderer.month, inst).replace(/\{monthHeader(:[^\}]+)?\}/g, monthHeader). 2237 replace(/\{weekHeader\}/g, weekHeader).replace(/\{weeks\}/g, weeks); 2238 }, 2239 2240 /** Generate the HTML for the day headers. 2241 @private 2242 @param {object} inst The current instance settings. 2243 @param {object} renderer The rendering templates. 2244 @return {string} A week's worth of day headers. */ 2245 _generateDayHeaders: function(inst, renderer) { 2246 var header = ''; 2247 for (var day = 0; day < 7; day++) { 2248 var dow = (day + inst.options.firstDay) % 7; 2249 header += this._prepare(renderer.dayHeader, inst).replace(/\{day\}/g, 2250 '<span class="' + this._curDoWClass + dow + '" title="' + 2251 inst.options.dayNames[dow] + '">' + inst.options.dayNamesMin[dow] + '</span>'); 2252 } 2253 return header; 2254 }, 2255 2256 /** Generate selection controls for month. 2257 @private 2258 @param {object} inst The current instance settings. 2259 @param {number} year The year to generate. 2260 @param {number} month The month to generate. 2261 @param {Date} minDate The minimum date allowed. 2262 @param {Date} maxDate The maximum date allowed. 2263 @param {string} monthHeader The month/year format. 2264 @return {string}
2264 The month selection content. */ 2265 _generateMonthSelection: function(inst, year, month, minDate, maxDate, monthHeader) { 2266 if (!inst.options.changeMonth) { 2267 return plugin.formatDate( 2268 monthHeader, plugin.newDate(year, month, 1), inst.getConfig()); 2269 } 2270 // Months 2271 var monthNames = inst.options['monthNames' + (monthHeader.match(/mm/i) ? '' : 'Short')]; 2272 var html = monthHeader.replace(/m+/i, '\\x2E').replace(/y+/i, '\\x2F'); 2273 var selector = '<select class="' + this._monthYearClass + 2274 '" title="' + inst.options.monthStatus + '">'; 2275 for (var m = 1; m <= 12; m++) { 2276 if ((!minDate || plugin.newDate(year, m, plugin.daysInMonth(year, m)). 2277 getTime() >= minDate.getTime()) && 2278 (!maxDate || plugin.newDate(year, m, 1).getTime() <= maxDate.getTime())) { 2279 selector += '<option value="' + m + '/' + year + '"' + 2280 (month === m ? ' selected="selected"' : '') + '>' + 2281 monthNames[m - 1] + '</option>'; 2282 } 2283 } 2284 selector += '</select>'; 2285 html = html.replace(/\\x2E/, selector); 2286 // Years 2287 var yearRange = inst.options.yearRange; 2288 if (yearRange === 'any') { 2289 selector = '<select class="' + this._monthYearClass + ' ' + this._anyYearClass + 2290 '" title="' + inst.options.yearStatus + '">' + 2291 '<option>' + year + '</option></select>' + 2292 '<input class="' + this._monthYearClass + ' ' + this._curMonthClass + 2293 month + '" value="' + year + '">'; 2294 } 2295 else { 2296 yearRange = yearRange.split(':'); 2297 var todayYear = plugin.today().getFullYear(); 2298 var start = (yearRange[0].match('c[+-].*') ? year + parseInt(yearRange[0].substring(1), 10) : 2299 ((yearRange[0].match('[+-].*') ? todayYear : 0) + parseInt(yearRange[0], 10))); 2300 var end = (yearRange[1].match('c[+-].*') ? year + parseInt(yearRange[1].substring(1), 10) : 2301 ((yearRange[1].match('[+-].*') ? todayYear : 0) + parseInt(yearRange[1], 10))); 2302 selector = '<select class="' + this._monthYearClass + 2303 '" title="' + inst.options.yearStatus + '">'; 2304 start = plugin.add(plugin.newDate(start + 1, 1, 1), -1, 'd'); 2305 end = plugin.newDate(end, 1, 1); 2306 var addYear = function(y, yDisplay) { 2307 if (y !== 0) { 2308 selector += '<option value="' + month + '/' + y + '"' + 2309 (year === y ? ' selected="selected"' : '') + '>' + (yDisplay || y) + '</option>'; 2310 } 2311 }; 2312 var earlierLater = null; 2313 var y = null; 2314 if (start.getTime() < end.getTime()) { 2315 start = (minDate && minDate.getTime() > start.getTime() ? minDate : start).getFullYear(); 2316 end = (maxDate && maxDate.getTime() < end.getTime() ? maxDate : end).getFullYear(); 2317 earlierLater = Math.floor((end - start) / 2); 2318 if (!minDate || minDate.getFullYear() < start) { 2319 addYear(start - earlierLater, inst.options.earlierText); 2320 } 2321 for (y = start; y <= end; y++) { 2322 addYear(y); 2323 } 2324 if (!maxDate || maxDate.getFullYear() > end) { 2325 addYear(end + earlierLater, inst.options.laterText); 2326 } 2327 } 2328 else { 2329 start = (maxDate && maxDate.getTime() < start.getTime() ? maxDate : start).getFullYear(); 2330 end = (minDate && minDate.getTime() > end.getTime() ? minDate : end).getFullYear(); 2331 earlierLater = Math.floor((start - end) / 2); 2332 if (!maxDate || maxDate.getFullYear() > start) { 2333 addYear(start + earlierLater, inst.options.earlierText); 2334 } 2335 for (y = start; y >= end; y--) { 2336 addYear(y); 2337 } 2338 if (!minDate || minDate.getFullYear() < end) { 2339 addYear(end - earlierLater, inst.options.laterText); 2340 } 2341 } 2342 selector += '</select>'; 2343 } 2344 html = html.replace(/\\x2F/, selector); 2345 return html; 2346 }, 2347 2348 /** Prepare a render template for use. 2349 Exclude popup/inline sections that are not applicable. 2350 Localise text of the form: {l10n:name}. 2351 @private 2352 @param {string} text The text to localise. 2353 @param {object} inst The current instance settings. 2354 @return {string} The localised text. */ 2355 _prepare: function(text, inst) { 2356 var replaceSection = function(type, retain) { 2357 while (true) { 2358 var start = text.indexOf('{' + type + ':start}'); 2359 if (start === -1) { 2360 return; 2361 } 2362 var end = text.substring(start).indexOf('{' + type + ':end}'); 2363 if (end > -1) { 2364 text = text.substring(0, start) + 2365 (retain ? text.substr(start + type.length + 8, end - type.length - 8) : '') + 2366 text.substring(start + end + type.length + 6); 2367 } 2368 } 2369 }; 2370 replaceSection('inline', inst.inline); 2371 replaceSection('popup', !inst.inline); 2372 var pattern = /\{l10n:([^\}]+)\}/; 2373 var matches = null; 2374 while ((matches = pattern.exec(text))) { 2375 text = text.replace(matches[0], inst.options[matches[1]]); 2376 } 2377 return text; 2378 } 2379 }); 2380 2381 var plugin = $.datepick; // Singleton instance 2382 2383 $(function() { 2384 $(document).on('mousedown.' + pluginName, plugin._checkExternalClick). 2385 on('resize.' + pluginName, function() { plugin.hide(plugin.curInst); }); 2386 }); 2387 2388})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.