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