keithsalisbury-subtrac 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +7 -0
  3. data/Rakefile +56 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/subtrac +40 -0
  6. data/lib/subtrac.rb +245 -0
  7. data/lib/subtrac/common/clients/index.wsgi +89 -0
  8. data/lib/subtrac/common/favicon.ico +0 -0
  9. data/lib/subtrac/common/images/trac/banner_bg.jpg +0 -0
  10. data/lib/subtrac/common/images/trac/bar_bg.gif +0 -0
  11. data/lib/subtrac/common/images/trac/footer_back.png +0 -0
  12. data/lib/subtrac/common/images/trac/main_bg.gif +0 -0
  13. data/lib/subtrac/common/images/trac/saint_logo_small.png +0 -0
  14. data/lib/subtrac/common/static/404.html +14 -0
  15. data/lib/subtrac/common/styles/trac.css +222 -0
  16. data/lib/subtrac/common/trac.ini +178 -0
  17. data/lib/subtrac/config/config.yml +54 -0
  18. data/lib/subtrac/passwords +1 -0
  19. data/lib/subtrac/shared/trac.ini +178 -0
  20. data/lib/subtrac/templates/location.erb +16 -0
  21. data/lib/subtrac/templates/projects/blank/svn/branches/README +0 -0
  22. data/lib/subtrac/templates/projects/blank/svn/tags/README +0 -0
  23. data/lib/subtrac/templates/projects/blank/svn/trunk/README +0 -0
  24. data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +57 -0
  25. data/lib/subtrac/templates/projects/new/svn/trunk/trac/wiki/WikiStart +46 -0
  26. data/lib/subtrac/templates/projects/new/trac/wiki/WikiStart +23 -0
  27. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/index/index.html +22 -0
  28. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/layout.html +56 -0
  29. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/site.html +27 -0
  30. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/theme.html +86 -0
  31. data/lib/subtrac/templates/projects/trac_theme/trac/wiki/WikiStart +4 -0
  32. data/lib/subtrac/templates/trac.erb +25 -0
  33. data/lib/subtrac/templates/vhost.erb +35 -0
  34. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
  35. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +419 -0
  36. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +3 -0
  37. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +20 -0
  38. data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
  39. data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +28 -0
  40. data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +168 -0
  41. data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +137 -0
  42. data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +91 -0
  43. data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +199 -0
  44. data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +105 -0
  45. data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +287 -0
  46. data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +71 -0
  47. data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +4 -0
  48. data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +135 -0
  49. data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +70 -0
  50. data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +142 -0
  51. data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +231 -0
  52. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +27 -0
  53. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +152 -0
  54. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +160 -0
  55. data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +124 -0
  56. data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +134 -0
  57. data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +132 -0
  58. data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +97 -0
  59. data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +161 -0
  60. data/lib/subtrac/trac-plugins/clientsplugin/setup.py +43 -0
  61. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +4 -0
  62. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +273 -0
  63. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +44 -0
  64. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +36 -0
  65. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +32 -0
  66. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +409 -0
  67. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +30 -0
  68. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +53 -0
  69. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +181 -0
  70. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +66 -0
  71. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +47 -0
  72. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +93 -0
  73. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +86 -0
  74. data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +20 -0
  75. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +23 -0
  76. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +128 -0
  77. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +40 -0
  78. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +285 -0
  79. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +173 -0
  80. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
  81. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +164 -0
  82. data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +69 -0
  83. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +1 -0
  84. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +292 -0
  85. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +172 -0
  86. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +178 -0
  87. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +25 -0
  88. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +6 -0
  89. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +356 -0
  90. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +100 -0
  91. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +84 -0
  92. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +1149 -0
  93. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +119 -0
  94. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +73 -0
  95. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +165 -0
  96. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +28 -0
  97. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +221 -0
  98. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +675 -0
  99. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +150 -0
  100. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +25 -0
  101. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +131 -0
  102. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +84 -0
  103. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +104 -0
  104. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +194 -0
  105. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +62 -0
  106. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +28 -0
  107. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +127 -0
  108. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +129 -0
  109. data/lib/subtrac/trac-plugins/worklogplugin/setup.py +29 -0
  110. data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +1 -0
  111. data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +187 -0
  112. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +40 -0
  113. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +67 -0
  114. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +12 -0
  115. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +7 -0
  116. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +40 -0
  117. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +208 -0
  118. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +1439 -0
  119. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.png +0 -0
  120. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.xcf +0 -0
  121. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/worklogplugin.css +80 -0
  122. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstart.png +0 -0
  123. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstop.png +0 -0
  124. data/lib/subtrac/trac-plugins/worklogplugin/worklog/manager.py +336 -0
  125. data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +598 -0
  126. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +45 -0
  127. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +70 -0
  128. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +40 -0
  129. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +59 -0
  130. data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +33 -0
  131. data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +153 -0
  132. data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +96 -0
  133. data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +29 -0
  134. data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +31 -0
  135. data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +47 -0
  136. data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +174 -0
  137. data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +73 -0
  138. data/lib/subtrac/version.rb +4 -0
  139. metadata +191 -0
@@ -0,0 +1,1439 @@
1
+ /* jQuery UI Date Picker v3.4.3 (previously jQuery Calendar)
2
+ Written by Marc Grabanski (m@marcgrabanski.com) and Keith Wood (kbwood@virginbroadband.com.au).
3
+
4
+ Copyright (c) 2007 Marc Grabanski (http://marcgrabanski.com/code/ui-datepicker)
5
+ Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ and GPL (GPL-LICENSE.txt) licenses.
7
+ Date: 09-03-2007 */
8
+
9
+ ;(function($) { // hide the namespace
10
+
11
+ /* Date picker manager.
12
+ Use the singleton instance of this class, $.datepicker, to interact with the date picker.
13
+ Settings for (groups of) date pickers are maintained in an instance object
14
+ (DatepickerInstance), allowing multiple different settings on the same page. */
15
+
16
+ function Datepicker() {
17
+ this.debug = false; // Change this to true to start debugging
18
+ this._nextId = 0; // Next ID for a date picker instance
19
+ this._inst = []; // List of instances indexed by ID
20
+ this._curInst = null; // The current instance in use
21
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
22
+ this._datepickerShowing = false; // True if the popup picker is showing , false if not
23
+ this._inDialog = false; // True if showing within a "dialog", false if not
24
+ this.regional = []; // Available regional settings, indexed by language code
25
+ this.regional[''] = { // Default regional settings
26
+ clearText: 'Clear', // Display text for clear link
27
+ clearStatus: 'Erase the current date', // Status text for clear link
28
+ closeText: 'Close', // Display text for close link
29
+ closeStatus: 'Close without change', // Status text for close link
30
+ prevText: '<Prev', // Display text for previous month link
31
+ prevStatus: 'Show the previous month', // Status text for previous month link
32
+ nextText: 'Next>', // Display text for next month link
33
+ nextStatus: 'Show the next month', // Status text for next month link
34
+ currentText: 'Today', // Display text for current month link
35
+ currentStatus: 'Show the current month', // Status text for current month link
36
+ monthNames: ['January','February','March','April','May','June',
37
+ 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
38
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
39
+ monthStatus: 'Show a different month', // Status text for selecting a month
40
+ yearStatus: 'Show a different year', // Status text for selecting a year
41
+ weekHeader: 'Wk', // Header for the week of the year column
42
+ weekStatus: 'Week of the year', // Status text for the week of the year column
43
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
44
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
45
+ dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
46
+ dayStatus: 'Set DD as first week day', // Status text for the day of the week selection
47
+ dateStatus: 'Select DD, M d', // Status text for the date selection
48
+ dateFormat: 'mm/dd/yy', // See format options on parseDate
49
+ firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
50
+ initStatus: 'Select a date', // Initial Status text on opening
51
+ isRTL: false // True if right-to-left language, false if left-to-right
52
+ };
53
+ this._defaults = { // Global defaults for all the date picker instances
54
+ showOn: 'focus', // 'focus' for popup on focus,
55
+ // 'button' for trigger button, or 'both' for either
56
+ showAnim: 'show', // Name of jQuery animation for popup
57
+ defaultDate: null, // Used when field is blank: actual date,
58
+ // +/-number for offset from today, null for today
59
+ appendText: '', // Display text following the input box, e.g. showing the format
60
+ buttonText: '...', // Text for trigger button
61
+ buttonImage: '', // URL for trigger button image
62
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
63
+ closeAtTop: true, // True to have the clear/close at the top,
64
+ // false to have them at the bottom
65
+ mandatory: false, // True to hide the Clear link, false to include it
66
+ hideIfNoPrevNext: false, // True to hide next/previous month links
67
+ // if not applicable, false to just disable them
68
+ changeMonth: true, // True if month can be selected directly, false if only prev/next
69
+ changeYear: true, // True if year can be selected directly, false if only prev/next
70
+ yearRange: '-10:+10', // Range of years to display in drop-down,
71
+ // either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
72
+ changeFirstDay: true, // True to click on day name to change, false to remain as set
73
+ showOtherMonths: false, // True to show dates in other months, false to leave blank
74
+ showWeeks: false, // True to show week of the year, false to omit
75
+ calculateWeek: this.iso8601Week, // How to calculate the week of the year,
76
+ // takes a Date and returns the number of the week for it
77
+ shortYearCutoff: '+10', // Short year values < this are in the current century,
78
+ // > this are in the previous century,
79
+ // string value starting with '+' for current year + value
80
+ showStatus: false, // True to show status bar at bottom, false to not show it
81
+ statusForDate: this.dateStatus, // Function to provide status text for a date -
82
+ // takes date and instance as parameters, returns display text
83
+ minDate: null, // The earliest selectable date, or null for no limit
84
+ maxDate: null, // The latest selectable date, or null for no limit
85
+ speed: 'normal', // Speed of display/closure
86
+ beforeShowDay: null, // Function that takes a date and returns an array with
87
+ // [0] = true if selectable, false if not,
88
+ // [1] = custom CSS class name(s) or '', e.g. $.datepicker.noWeekends
89
+ beforeShow: null, // Function that takes an input field and
90
+ // returns a set of custom settings for the date picker
91
+ onSelect: null, // Define a callback function when a date is selected
92
+ onClose: null, // Define a callback function when the datepicker is closed
93
+ numberOfMonths: 1, // Number of months to show at a time
94
+ stepMonths: 1, // Number of months to step back/forward
95
+ rangeSelect: false, // Allows for selecting a date range on one date picker
96
+ rangeSeparator: ' - ' // Text between two dates in a range
97
+ };
98
+ $.extend(this._defaults, this.regional['']);
99
+ this._datepickerDiv = $('<div id="datepicker_div">');
100
+ }
101
+
102
+ $.extend(Datepicker.prototype, {
103
+ /* Class name added to elements to indicate already configured with a date picker. */
104
+ markerClassName: 'hasDatepicker',
105
+
106
+ /* Debug logging (if enabled). */
107
+ log: function () {
108
+ if (this.debug)
109
+ console.log.apply('', arguments);
110
+ },
111
+
112
+ /* Register a new date picker instance - with custom settings. */
113
+ _register: function(inst) {
114
+ var id = this._nextId++;
115
+ this._inst[id] = inst;
116
+ return id;
117
+ },
118
+
119
+ /* Retrieve a particular date picker instance based on its ID. */
120
+ _getInst: function(id) {
121
+ return this._inst[id] || id;
122
+ },
123
+
124
+ /* Override the default settings for all instances of the date picker.
125
+ @param settings object - the new settings to use as defaults (anonymous object)
126
+ @return the manager object */
127
+ setDefaults: function(settings) {
128
+ extendRemove(this._defaults, settings || {});
129
+ return this;
130
+ },
131
+
132
+ /* Attach the date picker to a jQuery selection.
133
+ @param target element - the target input field or division or span
134
+ @param settings object - the new settings to use for this date picker instance (anonymous) */
135
+ _attachDatepicker: function(target, settings) {
136
+ // check for settings on the control itself - in namespace 'date:'
137
+ var inlineSettings = null;
138
+ for (attrName in this._defaults) {
139
+ var attrValue = target.getAttribute('date:' + attrName);
140
+ if (attrValue) {
141
+ inlineSettings = inlineSettings || {};
142
+ try {
143
+ inlineSettings[attrName] = eval(attrValue);
144
+ } catch (err) {
145
+ inlineSettings[attrName] = attrValue;
146
+ }
147
+ }
148
+ }
149
+ var nodeName = target.nodeName.toLowerCase();
150
+ var instSettings = (inlineSettings ?
151
+ $.extend(settings || {}, inlineSettings || {}) : settings);
152
+ if (nodeName == 'input') {
153
+ var inst = (inst && !inlineSettings ? inst :
154
+ new DatepickerInstance(instSettings, false));
155
+ this._connectDatepicker(target, inst);
156
+ } else if (nodeName == 'div' || nodeName == 'span') {
157
+ var inst = new DatepickerInstance(instSettings, true);
158
+ this._inlineDatepicker(target, inst);
159
+ }
160
+ },
161
+
162
+ /* Detach a datepicker from its control.
163
+ @param target element - the target input field or division or span */
164
+ _destroyDatepicker: function(target) {
165
+ var nodeName = target.nodeName.toLowerCase();
166
+ var calId = target._calId;
167
+ target._calId = null;
168
+ var $target = $(target);
169
+ if (nodeName == 'input') {
170
+ $target.siblings('.datepicker_append').replaceWith('').end()
171
+ .siblings('.datepicker_trigger').replaceWith('').end()
172
+ .removeClass(this.markerClassName)
173
+ .unbind('focus', this._showDatepicker)
174
+ .unbind('keydown', this._doKeyDown)
175
+ .unbind('keypress', this._doKeyPress);
176
+ var wrapper = $target.parents('.datepicker_wrap');
177
+ if (wrapper)
178
+ wrapper.replaceWith(wrapper.html());
179
+ } else if (nodeName == 'div' || nodeName == 'span')
180
+ $target.removeClass(this.markerClassName).empty();
181
+ if ($('input[_calId=' + calId + ']').length == 0)
182
+ // clean up if last for this ID
183
+ this._inst[calId] = null;
184
+ },
185
+
186
+ /* Enable the date picker to a jQuery selection.
187
+ @param target element - the target input field or division or span */
188
+ _enableDatepicker: function(target) {
189
+ target.disabled = false;
190
+ $(target).siblings('button.datepicker_trigger').each(function() { this.disabled = false; }).end()
191
+ .siblings('img.datepicker_trigger').css({opacity: '1.0', cursor: ''});
192
+ this._disabledInputs = $.map(this._disabledInputs,
193
+ function(value) { return (value == target ? null : value); }); // delete entry
194
+ },
195
+
196
+ /* Disable the date picker to a jQuery selection.
197
+ @param target element - the target input field or division or span */
198
+ _disableDatepicker: function(target) {
199
+ target.disabled = true;
200
+ $(target).siblings('button.datepicker_trigger').each(function() { this.disabled = true; }).end()
201
+ .siblings('img.datepicker_trigger').css({opacity: '0.5', cursor: 'default'});
202
+ this._disabledInputs = $.map($.datepicker._disabledInputs,
203
+ function(value) { return (value == target ? null : value); }); // delete entry
204
+ this._disabledInputs[$.datepicker._disabledInputs.length] = target;
205
+ },
206
+
207
+ /* Is the first field in a jQuery collection disabled as a datepicker?
208
+ @param target element - the target input field or division or span
209
+ @return boolean - true if disabled, false if enabled */
210
+ _isDisabledDatepicker: function(target) {
211
+ if (!target)
212
+ return false;
213
+ for (var i = 0; i < this._disabledInputs.length; i++) {
214
+ if (this._disabledInputs[i] == target)
215
+ return true;
216
+ }
217
+ return false;
218
+ },
219
+
220
+ /* Update the settings for a date picker attached to an input field or division.
221
+ @param target element - the target input field or division or span
222
+ @param name string - the name of the setting to change or
223
+ object - the new settings to update
224
+ @param value any - the new value for the setting (omit if above is an object) */
225
+ _changeDatepicker: function(target, name, value) {
226
+ var settings = name || {};
227
+ if (typeof name == 'string') {
228
+ settings = {};
229
+ settings[name] = value;
230
+ }
231
+ if (inst = this._getInst(target._calId)) {
232
+ extendRemove(inst._settings, settings);
233
+ this._updateDatepicker(inst);
234
+ }
235
+ },
236
+
237
+ /* Set the dates for a jQuery selection.
238
+ @param target element - the target input field or division or span
239
+ @param date Date - the new date
240
+ @param endDate Date - the new end date for a range (optional) */
241
+ _setDateDatepicker: function(target, date, endDate) {
242
+ if (inst = this._getInst(target._calId)) {
243
+ inst._setDate(date, endDate);
244
+ this._updateDatepicker(inst);
245
+ }
246
+ },
247
+
248
+ /* Get the date(s) for the first entry in a jQuery selection.
249
+ @param target element - the target input field or division or span
250
+ @return Date - the current date or
251
+ Date[2] - the current dates for a range */
252
+ _getDateDatepicker: function(target) {
253
+ var inst = this._getInst(target._calId);
254
+ return (inst ? inst._getDate() : null);
255
+ },
256
+
257
+ /* Handle keystrokes. */
258
+ _doKeyDown: function(e) {
259
+ var inst = $.datepicker._getInst(this._calId);
260
+ if ($.datepicker._datepickerShowing)
261
+ switch (e.keyCode) {
262
+ case 9: $.datepicker._hideDatepicker(null, '');
263
+ break; // hide on tab out
264
+ case 13: $.datepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear,
265
+ $('td.datepicker_daysCellOver', inst._datepickerDiv)[0]);
266
+ return false; // don't submit the form
267
+ break; // select the value on enter
268
+ case 27: $.datepicker._hideDatepicker(null, inst._get('speed'));
269
+ break; // hide on escape
270
+ case 33: $.datepicker._adjustDate(inst,
271
+ (e.ctrlKey ? -1 : -inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M'));
272
+ break; // previous month/year on page up/+ ctrl
273
+ case 34: $.datepicker._adjustDate(inst,
274
+ (e.ctrlKey ? +1 : +inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M'));
275
+ break; // next month/year on page down/+ ctrl
276
+ case 35: if (e.ctrlKey) $.datepicker._clearDate(inst);
277
+ break; // clear on ctrl+end
278
+ case 36: if (e.ctrlKey) $.datepicker._gotoToday(inst);
279
+ break; // current on ctrl+home
280
+ case 37: if (e.ctrlKey) $.datepicker._adjustDate(inst, -1, 'D');
281
+ break; // -1 day on ctrl+left
282
+ case 38: if (e.ctrlKey) $.datepicker._adjustDate(inst, -7, 'D');
283
+ break; // -1 week on ctrl+up
284
+ case 39: if (e.ctrlKey) $.datepicker._adjustDate(inst, +1, 'D');
285
+ break; // +1 day on ctrl+right
286
+ case 40: if (e.ctrlKey) $.datepicker._adjustDate(inst, +7, 'D');
287
+ break; // +1 week on ctrl+down
288
+ }
289
+ else if (e.keyCode == 36 && e.ctrlKey) // display the date picker on ctrl+home
290
+ $.datepicker._showDatepicker(this);
291
+ },
292
+
293
+ /* Filter entered characters - based on date format. */
294
+ _doKeyPress: function(e) {
295
+ var inst = $.datepicker._getInst(this._calId);
296
+ var chars = $.datepicker._possibleChars(inst._get('dateFormat'));
297
+ var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode);
298
+ return e.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
299
+ },
300
+
301
+ /* Attach the date picker to an input field. */
302
+ _connectDatepicker: function(target, inst) {
303
+ var input = $(target);
304
+ if (input.is('.' + this.markerClassName))
305
+ return;
306
+ var appendText = inst._get('appendText');
307
+ var isRTL = inst._get('isRTL');
308
+ if (appendText) {
309
+ if (isRTL)
310
+ input.before('<span class="datepicker_append">' + appendText);
311
+ else
312
+ input.after('<span class="datepicker_append">' + appendText);
313
+ }
314
+ var showOn = inst._get('showOn');
315
+ if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
316
+ input.focus(this._showDatepicker);
317
+ if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
318
+ input.wrap('<span class="datepicker_wrap">');
319
+ var buttonText = inst._get('buttonText');
320
+ var buttonImage = inst._get('buttonImage');
321
+ var trigger = $(inst._get('buttonImageOnly') ?
322
+ $('<img>').addClass('datepicker_trigger').attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
323
+ $('<button>').addClass('datepicker_trigger').attr({ type: 'button' }).html(buttonImage != '' ?
324
+ $('<img>').attr({ src:buttonImage, alt:buttonText, title:buttonText }) : buttonText));
325
+ if (isRTL)
326
+ input.before(trigger);
327
+ else
328
+ input.after(trigger);
329
+ trigger.click(function() {
330
+ if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target)
331
+ $.datepicker._hideDatepicker();
332
+ else
333
+ $.datepicker._showDatepicker(target);
334
+ });
335
+ }
336
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress)
337
+ .bind("setData.datepicker", function(event, key, value) {
338
+ inst._settings[key] = value;
339
+ }).bind("getData.datepicker", function(event, key) {
340
+ return inst._get(key);
341
+ });
342
+ input[0]._calId = inst._id;
343
+ },
344
+
345
+ /* Attach an inline date picker to a div. */
346
+ _inlineDatepicker: function(target, inst) {
347
+ var input = $(target);
348
+ if (input.is('.' + this.markerClassName))
349
+ return;
350
+ input.addClass(this.markerClassName).append(inst._datepickerDiv)
351
+ .bind("setData.datepicker", function(event, key, value){
352
+ inst._settings[key] = value;
353
+ }).bind("getData.datepicker", function(event, key){
354
+ return inst._get(key);
355
+ });
356
+ input[0]._calId = inst._id;
357
+ this._updateDatepicker(inst);
358
+ },
359
+
360
+ /* Tidy up after displaying the date picker. */
361
+ _inlineShow: function(inst) {
362
+ var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers
363
+ inst._datepickerDiv.width(numMonths[1] * $('.datepicker', inst._datepickerDiv[0]).width());
364
+ },
365
+
366
+ /* Pop-up the date picker in a "dialog" box.
367
+ @param input element - ignored
368
+ @param dateText string - the initial date to display (in the current format)
369
+ @param onSelect function - the function(dateText) to call when a date is selected
370
+ @param settings object - update the dialog date picker instance's settings (anonymous object)
371
+ @param pos int[2] - coordinates for the dialog's position within the screen or
372
+ event - with x/y coordinates or
373
+ leave empty for default (screen centre)
374
+ @return the manager object */
375
+ _dialogDatepicker: function(input, dateText, onSelect, settings, pos) {
376
+ var inst = this._dialogInst; // internal instance
377
+ if (!inst) {
378
+ inst = this._dialogInst = new DatepickerInstance({}, false);
379
+ this._dialogInput = $('<input type="text" size="1" style="position: absolute; top: -100px;"/>');
380
+ this._dialogInput.keydown(this._doKeyDown);
381
+ $('body').append(this._dialogInput);
382
+ this._dialogInput[0]._calId = inst._id;
383
+ }
384
+ extendRemove(inst._settings, settings || {});
385
+ this._dialogInput.val(dateText);
386
+
387
+ this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
388
+ if (!this._pos) {
389
+ var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
390
+ var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
391
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
392
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
393
+ this._pos = // should use actual width/height below
394
+ [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
395
+ }
396
+
397
+ // move input on screen for focus, but hidden behind dialog
398
+ this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px');
399
+ inst._settings.onSelect = onSelect;
400
+ this._inDialog = true;
401
+ this._datepickerDiv.addClass('datepicker_dialog');
402
+ this._showDatepicker(this._dialogInput[0]);
403
+ if ($.blockUI)
404
+ $.blockUI(this._datepickerDiv);
405
+ return this;
406
+ },
407
+
408
+ /* Pop-up the date picker for a given input field.
409
+ @param input element - the input field attached to the date picker or
410
+ event - if triggered by focus */
411
+ _showDatepicker: function(input) {
412
+ input = input.target || input;
413
+ if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
414
+ input = $('input', input.parentNode)[0];
415
+ if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
416
+ return;
417
+ var inst = $.datepicker._getInst(input._calId);
418
+ var beforeShow = inst._get('beforeShow');
419
+ extendRemove(inst._settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
420
+ $.datepicker._hideDatepicker(null, '');
421
+ $.datepicker._lastInput = input;
422
+ inst._setDateFromField(input);
423
+ if ($.datepicker._inDialog) // hide cursor
424
+ input.value = '';
425
+ if (!$.datepicker._pos) { // position below input
426
+ $.datepicker._pos = $.datepicker._findPos(input);
427
+ $.datepicker._pos[1] += input.offsetHeight; // add the height
428
+ }
429
+ var isFixed = false;
430
+ $(input).parents().each(function() {
431
+ isFixed |= $(this).css('position') == 'fixed';
432
+ });
433
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
434
+ $.datepicker._pos[0] -= document.documentElement.scrollLeft;
435
+ $.datepicker._pos[1] -= document.documentElement.scrollTop;
436
+ }
437
+ inst._datepickerDiv.css('position', ($.datepicker._inDialog && $.blockUI ?
438
+ 'static' : (isFixed ? 'fixed' : 'absolute')))
439
+ .css({ left: $.datepicker._pos[0] + 'px', top: $.datepicker._pos[1] + 'px' });
440
+ $.datepicker._pos = null;
441
+ inst._rangeStart = null;
442
+ $.datepicker._updateDatepicker(inst);
443
+ if (!inst._inline) {
444
+ var speed = inst._get('speed');
445
+ var postProcess = function() {
446
+ $.datepicker._datepickerShowing = true;
447
+ $.datepicker._afterShow(inst);
448
+ };
449
+ var showAnim = inst._get('showAnim') || 'show';
450
+ inst._datepickerDiv[showAnim](speed, postProcess);
451
+ if (speed == '')
452
+ postProcess();
453
+ if (inst._input[0].type != 'hidden')
454
+ inst._input[0].focus();
455
+ $.datepicker._curInst = inst;
456
+ }
457
+ },
458
+
459
+ /* Generate the date picker content. */
460
+ _updateDatepicker: function(inst) {
461
+ inst._datepickerDiv.empty().append(inst._generateDatepicker());
462
+ var numMonths = inst._getNumberOfMonths();
463
+ if (numMonths[0] != 1 || numMonths[1] != 1)
464
+ inst._datepickerDiv.addClass('datepicker_multi');
465
+ else
466
+ inst._datepickerDiv.removeClass('datepicker_multi');
467
+
468
+ if (inst._get('isRTL'))
469
+ inst._datepickerDiv.addClass('datepicker_rtl');
470
+ else
471
+ inst._datepickerDiv.removeClass('datepicker_rtl');
472
+
473
+ if (inst._input && inst._input[0].type != 'hidden')
474
+ inst._input[0].focus();
475
+ },
476
+
477
+ /* Tidy up after displaying the date picker. */
478
+ _afterShow: function(inst) {
479
+ var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers
480
+ inst._datepickerDiv.width(numMonths[1] * $('.datepicker', inst._datepickerDiv[0])[0].offsetWidth);
481
+ if ($.browser.msie && parseInt($.browser.version) < 7) { // fix IE < 7 select problems
482
+ $('#datepicker_cover').css({width: inst._datepickerDiv.width() + 4,
483
+ height: inst._datepickerDiv.height() + 4});
484
+ }
485
+ // re-position on screen if necessary
486
+ var isFixed = inst._datepickerDiv.css('position') == 'fixed';
487
+ var pos = inst._input ? $.datepicker._findPos(inst._input[0]) : null;
488
+ var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
489
+ var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
490
+ var scrollX = (isFixed ? 0 : document.documentElement.scrollLeft || document.body.scrollLeft);
491
+ var scrollY = (isFixed ? 0 : document.documentElement.scrollTop || document.body.scrollTop);
492
+ // reposition date picker horizontally if outside the browser window
493
+ if ((inst._datepickerDiv.offset().left + inst._datepickerDiv.width() -
494
+ (isFixed && $.browser.msie ? document.documentElement.scrollLeft : 0)) >
495
+ (browserWidth + scrollX)) {
496
+ inst._datepickerDiv.css('left', Math.max(scrollX,
497
+ pos[0] + (inst._input ? $(inst._input[0]).width() : null) - inst._datepickerDiv.width() -
498
+ (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0)) + 'px');
499
+ }
500
+ // reposition date picker vertically if outside the browser window
501
+ if ((inst._datepickerDiv.offset().top + inst._datepickerDiv.height() -
502
+ (isFixed && $.browser.msie ? document.documentElement.scrollTop : 0)) >
503
+ (browserHeight + scrollY) ) {
504
+ inst._datepickerDiv.css('top', Math.max(scrollY,
505
+ pos[1] - (this._inDialog ? 0 : inst._datepickerDiv.height()) -
506
+ (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0)) + 'px');
507
+ }
508
+ },
509
+
510
+ /* Find an object's position on the screen. */
511
+ _findPos: function(obj) {
512
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
513
+ obj = obj.nextSibling;
514
+ }
515
+ var position = $(obj).offset();
516
+ return [position.left, position.top];
517
+ },
518
+
519
+ /* Hide the date picker from view.
520
+ @param input element - the input field attached to the date picker
521
+ @param speed string - the speed at which to close the date picker */
522
+ _hideDatepicker: function(input, speed) {
523
+ var inst = this._curInst;
524
+ if (!inst)
525
+ return;
526
+ var rangeSelect = inst._get('rangeSelect');
527
+ if (rangeSelect && this._stayOpen) {
528
+ this._selectDate(inst, inst._formatDate(
529
+ inst._currentDay, inst._currentMonth, inst._currentYear));
530
+ }
531
+ this._stayOpen = false;
532
+ if (this._datepickerShowing) {
533
+ speed = (speed != null ? speed : inst._get('speed'));
534
+ var showAnim = inst._get('showAnim');
535
+ inst._datepickerDiv[(showAnim == 'slideDown' ? 'slideUp' :
536
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))](speed, function() {
537
+ $.datepicker._tidyDialog(inst);
538
+ });
539
+ if (speed == '')
540
+ this._tidyDialog(inst);
541
+ var onClose = inst._get('onClose');
542
+ if (onClose) {
543
+ onClose.apply((inst._input ? inst._input[0] : null),
544
+ [inst._getDate(), inst]); // trigger custom callback
545
+ }
546
+ this._datepickerShowing = false;
547
+ this._lastInput = null;
548
+ inst._settings.prompt = null;
549
+ if (this._inDialog) {
550
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
551
+ if ($.blockUI) {
552
+ $.unblockUI();
553
+ $('body').append(this._datepickerDiv);
554
+ }
555
+ }
556
+ this._inDialog = false;
557
+ }
558
+ this._curInst = null;
559
+ },
560
+
561
+ /* Tidy up after a dialog display. */
562
+ _tidyDialog: function(inst) {
563
+ inst._datepickerDiv.removeClass('datepicker_dialog').unbind('.datepicker');
564
+ $('.datepicker_prompt', inst._datepickerDiv).remove();
565
+ },
566
+
567
+ /* Close date picker if clicked elsewhere. */
568
+ _checkExternalClick: function(event) {
569
+ if (!$.datepicker._curInst)
570
+ return;
571
+ var $target = $(event.target);
572
+ if (($target.parents("#datepicker_div").length == 0) &&
573
+ ($target.attr('class') != 'datepicker_trigger') &&
574
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI)) {
575
+ $.datepicker._hideDatepicker(null, '');
576
+ }
577
+ },
578
+
579
+ /* Adjust one of the date sub-fields. */
580
+ _adjustDate: function(id, offset, period) {
581
+ var inst = this._getInst(id);
582
+ inst._adjustDate(offset, period);
583
+ this._updateDatepicker(inst);
584
+ },
585
+
586
+ /* Action for current link. */
587
+ _gotoToday: function(id) {
588
+ var date = new Date();
589
+ var inst = this._getInst(id);
590
+ inst._selectedDay = date.getDate();
591
+ inst._drawMonth = inst._selectedMonth = date.getMonth();
592
+ inst._drawYear = inst._selectedYear = date.getFullYear();
593
+ this._adjustDate(inst);
594
+ },
595
+
596
+ /* Action for selecting a new month/year. */
597
+ _selectMonthYear: function(id, select, period) {
598
+ var inst = this._getInst(id);
599
+ inst._selectingMonthYear = false;
600
+ inst[period == 'M' ? '_drawMonth' : '_drawYear'] =
601
+ select.options[select.selectedIndex].value - 0;
602
+ this._adjustDate(inst);
603
+ },
604
+
605
+ /* Restore input focus after not changing month/year. */
606
+ _clickMonthYear: function(id) {
607
+ var inst = this._getInst(id);
608
+ if (inst._input && inst._selectingMonthYear && !$.browser.msie)
609
+ inst._input[0].focus();
610
+ inst._selectingMonthYear = !inst._selectingMonthYear;
611
+ },
612
+
613
+ /* Action for changing the first week day. */
614
+ _changeFirstDay: function(id, day) {
615
+ var inst = this._getInst(id);
616
+ inst._settings.firstDay = day;
617
+ this._updateDatepicker(inst);
618
+ },
619
+
620
+ /* Action for selecting a day. */
621
+ _selectDay: function(id, month, year, td) {
622
+ if ($(td).is('.datepicker_unselectable'))
623
+ return;
624
+ var inst = this._getInst(id);
625
+ var rangeSelect = inst._get('rangeSelect');
626
+ if (rangeSelect) {
627
+ if (!this._stayOpen) {
628
+ $('.datepicker td').removeClass('datepicker_currentDay');
629
+ $(td).addClass('datepicker_currentDay');
630
+ }
631
+ this._stayOpen = !this._stayOpen;
632
+ }
633
+ inst._selectedDay = inst._currentDay = $('a', td).html();
634
+ inst._selectedMonth = inst._currentMonth = month;
635
+ inst._selectedYear = inst._currentYear = year;
636
+ this._selectDate(id, inst._formatDate(
637
+ inst._currentDay, inst._currentMonth, inst._currentYear));
638
+ if (this._stayOpen) {
639
+ inst._endDay = inst._endMonth = inst._endYear = null;
640
+ inst._rangeStart = new Date(inst._currentYear, inst._currentMonth, inst._currentDay);
641
+ this._updateDatepicker(inst);
642
+ }
643
+ else if (rangeSelect) {
644
+ inst._endDay = inst._currentDay;
645
+ inst._endMonth = inst._currentMonth;
646
+ inst._endYear = inst._currentYear;
647
+ inst._selectedDay = inst._currentDay = inst._rangeStart.getDate();
648
+ inst._selectedMonth = inst._currentMonth = inst._rangeStart.getMonth();
649
+ inst._selectedYear = inst._currentYear = inst._rangeStart.getFullYear();
650
+ inst._rangeStart = null;
651
+ if (inst._inline)
652
+ this._updateDatepicker(inst);
653
+ }
654
+ },
655
+
656
+ /* Erase the input field and hide the date picker. */
657
+ _clearDate: function(id) {
658
+ var inst = this._getInst(id);
659
+ if (inst._get('mandatory'))
660
+ return;
661
+ this._stayOpen = false;
662
+ inst._endDay = inst._endMonth = inst._endYear = inst._rangeStart = null;
663
+ this._selectDate(inst, '');
664
+ },
665
+
666
+ /* Update the input field with the selected date. */
667
+ _selectDate: function(id, dateStr) {
668
+ var inst = this._getInst(id);
669
+ dateStr = (dateStr != null ? dateStr : inst._formatDate());
670
+ if (inst._rangeStart)
671
+ dateStr = inst._formatDate(inst._rangeStart) + inst._get('rangeSeparator') + dateStr;
672
+ if (inst._input)
673
+ inst._input.val(dateStr);
674
+ var onSelect = inst._get('onSelect');
675
+ if (onSelect)
676
+ onSelect.apply((inst._input ? inst._input[0] : null), [dateStr, inst]); // trigger custom callback
677
+ else if (inst._input)
678
+ inst._input.trigger('change'); // fire the change event
679
+ if (inst._inline)
680
+ this._updateDatepicker(inst);
681
+ else if (!this._stayOpen) {
682
+ this._hideDatepicker(null, inst._get('speed'));
683
+ this._lastInput = inst._input[0];
684
+ if (typeof(inst._input[0]) != 'object')
685
+ inst._input[0].focus(); // restore focus
686
+ this._lastInput = null;
687
+ }
688
+ },
689
+
690
+ /* Set as beforeShowDay function to prevent selection of weekends.
691
+ @param date Date - the date to customise
692
+ @return [boolean, string] - is this date selectable?, what is its CSS class? */
693
+ noWeekends: function(date) {
694
+ var day = date.getDay();
695
+ return [(day > 0 && day < 6), ''];
696
+ },
697
+
698
+ /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
699
+ @param date Date - the date to get the week for
700
+ @return number - the number of the week within the year that contains this date */
701
+ iso8601Week: function(date) {
702
+ var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), (date.getTimezoneOffset() / -60));
703
+ var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan
704
+ var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7
705
+ firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday
706
+ if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary
707
+ checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year
708
+ return $.datepicker.iso8601Week(checkDate);
709
+ } else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year
710
+ firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7;
711
+ if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary
712
+ checkDate.setDate(checkDate.getDate() + 3); // Generate for next year
713
+ return $.datepicker.iso8601Week(checkDate);
714
+ }
715
+ }
716
+ return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date
717
+ },
718
+
719
+ /* Provide status text for a particular date.
720
+ @param date the date to get the status for
721
+ @param inst the current datepicker instance
722
+ @return the status display text for this date */
723
+ dateStatus: function(date, inst) {
724
+ return $.datepicker.formatDate(inst._get('dateStatus'), date, inst._getFormatConfig());
725
+ },
726
+
727
+ /* Parse a string value into a date object.
728
+ The format can be combinations of the following:
729
+ d - day of month (no leading zero)
730
+ dd - day of month (two digit)
731
+ D - day name short
732
+ DD - day name long
733
+ m - month of year (no leading zero)
734
+ mm - month of year (two digit)
735
+ M - month name short
736
+ MM - month name long
737
+ y - year (two digit)
738
+ yy - year (four digit)
739
+ '...' - literal text
740
+ '' - single quote
741
+
742
+ @param format String - the expected format of the date
743
+ @param value String - the date in the above format
744
+ @param settings Object - attributes include:
745
+ shortYearCutoff Number - the cutoff year for determining the century (optional)
746
+ dayNamesShort String[7] - abbreviated names of the days from Sunday (optional)
747
+ dayNames String[7] - names of the days from Sunday (optional)
748
+ monthNamesShort String[12] - abbreviated names of the months (optional)
749
+ monthNames String[12] - names of the months (optional)
750
+ @return Date - the extracted date value or null if value is blank */
751
+ parseDate: function (format, value, settings) {
752
+ if (format == null || value == null)
753
+ throw 'Invalid arguments';
754
+ value = (typeof value == 'object' ? value.toString() : value + '');
755
+ if (value == '')
756
+ return null;
757
+ var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
758
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
759
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
760
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
761
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
762
+ var year = -1;
763
+ var month = -1;
764
+ var day = -1;
765
+ var literal = false;
766
+ // Check whether a format character is doubled
767
+ var lookAhead = function(match) {
768
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
769
+ if (matches)
770
+ iFormat++;
771
+ return matches;
772
+ };
773
+ // Extract a number from the string value
774
+ var getNumber = function(match) {
775
+ lookAhead(match);
776
+ var size = (match == 'y' ? 4 : 2);
777
+ var num = 0;
778
+ while (size > 0 && iValue < value.length &&
779
+ value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') {
780
+ num = num * 10 + (value.charAt(iValue++) - 0);
781
+ size--;
782
+ }
783
+ if (size == (match == 'y' ? 4 : 2))
784
+ throw 'Missing number at position ' + iValue;
785
+ return num;
786
+ };
787
+ // Extract a name from the string value and convert to an index
788
+ var getName = function(match, shortNames, longNames) {
789
+ var names = (lookAhead(match) ? longNames : shortNames);
790
+ var size = 0;
791
+ for (var j = 0; j < names.length; j++)
792
+ size = Math.max(size, names[j].length);
793
+ var name = '';
794
+ var iInit = iValue;
795
+ while (size > 0 && iValue < value.length) {
796
+ name += value.charAt(iValue++);
797
+ for (var i = 0; i < names.length; i++)
798
+ if (name == names[i])
799
+ return i + 1;
800
+ size--;
801
+ }
802
+ throw 'Unknown name at position ' + iInit;
803
+ };
804
+ // Confirm that a literal character matches the string value
805
+ var checkLiteral = function() {
806
+ if (value.charAt(iValue) != format.charAt(iFormat))
807
+ throw 'Unexpected literal at position ' + iValue;
808
+ iValue++;
809
+ };
810
+ var iValue = 0;
811
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
812
+ if (literal)
813
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
814
+ literal = false;
815
+ else
816
+ checkLiteral();
817
+ else
818
+ switch (format.charAt(iFormat)) {
819
+ case 'd':
820
+ day = getNumber('d');
821
+ break;
822
+ case 'D':
823
+ getName('D', dayNamesShort, dayNames);
824
+ break;
825
+ case 'm':
826
+ month = getNumber('m');
827
+ break;
828
+ case 'M':
829
+ month = getName('M', monthNamesShort, monthNames);
830
+ break;
831
+ case 'y':
832
+ year = getNumber('y');
833
+ break;
834
+ case "'":
835
+ if (lookAhead("'"))
836
+ checkLiteral();
837
+ else
838
+ literal = true;
839
+ break;
840
+ default:
841
+ checkLiteral();
842
+ }
843
+ }
844
+ if (year < 100) {
845
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
846
+ (year <= shortYearCutoff ? 0 : -100);
847
+ }
848
+ var date = new Date(year, month - 1, day);
849
+ if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) {
850
+ throw 'Invalid date'; // E.g. 31/02/*
851
+ }
852
+ return date;
853
+ },
854
+
855
+ /* Format a date object into a string value.
856
+ The format can be combinations of the following:
857
+ d - day of month (no leading zero)
858
+ dd - day of month (two digit)
859
+ D - day name short
860
+ DD - day name long
861
+ m - month of year (no leading zero)
862
+ mm - month of year (two digit)
863
+ M - month name short
864
+ MM - month name long
865
+ y - year (two digit)
866
+ yy - year (four digit)
867
+ '...' - literal text
868
+ '' - single quote
869
+
870
+ @param format String - the desired format of the date
871
+ @param date Date - the date value to format
872
+ @param settings Object - attributes include:
873
+ dayNamesShort String[7] - abbreviated names of the days from Sunday (optional)
874
+ dayNames String[7] - names of the days from Sunday (optional)
875
+ monthNamesShort String[12] - abbreviated names of the months (optional)
876
+ monthNames String[12] - names of the months (optional)
877
+ @return String - the date in the above format */
878
+ formatDate: function (format, date, settings) {
879
+ if (!date)
880
+ return '';
881
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
882
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
883
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
884
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
885
+ // Check whether a format character is doubled
886
+ var lookAhead = function(match) {
887
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
888
+ if (matches)
889
+ iFormat++;
890
+ return matches;
891
+ };
892
+ // Format a number, with leading zero if necessary
893
+ var formatNumber = function(match, value) {
894
+ return (lookAhead(match) && value < 10 ? '0' : '') + value;
895
+ };
896
+ // Format a name, short or long as requested
897
+ var formatName = function(match, value, shortNames, longNames) {
898
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
899
+ };
900
+ var output = '';
901
+ var literal = false;
902
+ if (date) {
903
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
904
+ if (literal)
905
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
906
+ literal = false;
907
+ else
908
+ output += format.charAt(iFormat);
909
+ else
910
+ switch (format.charAt(iFormat)) {
911
+ case 'd':
912
+ output += formatNumber('d', date.getDate());
913
+ break;
914
+ case 'D':
915
+ output += formatName('D', date.getDay(), dayNamesShort, dayNames);
916
+ break;
917
+ case 'm':
918
+ output += formatNumber('m', date.getMonth() + 1);
919
+ break;
920
+ case 'M':
921
+ output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
922
+ break;
923
+ case 'y':
924
+ output += (lookAhead('y') ? date.getFullYear() :
925
+ (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
926
+ break;
927
+ case "'":
928
+ if (lookAhead("'"))
929
+ output += "'";
930
+ else
931
+ literal = true;
932
+ break;
933
+ default:
934
+ output += format.charAt(iFormat);
935
+ }
936
+ }
937
+ }
938
+ return output;
939
+ },
940
+
941
+ /* Extract all possible characters from the date format. */
942
+ _possibleChars: function (format) {
943
+ var chars = '';
944
+ var literal = false;
945
+ for (var iFormat = 0; iFormat < format.length; iFormat++)
946
+ if (literal)
947
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
948
+ literal = false;
949
+ else
950
+ chars += format.charAt(iFormat);
951
+ else
952
+ switch (format.charAt(iFormat)) {
953
+ case 'd' || 'm' || 'y':
954
+ chars += '0123456789';
955
+ break;
956
+ case 'D' || 'M':
957
+ return null; // Accept anything
958
+ case "'":
959
+ if (lookAhead("'"))
960
+ chars += "'";
961
+ else
962
+ literal = true;
963
+ break;
964
+ default:
965
+ chars += format.charAt(iFormat);
966
+ }
967
+ return chars;
968
+ }
969
+ });
970
+
971
+ /* Individualised settings for date picker functionality applied to one or more related inputs.
972
+ Instances are managed and manipulated through the Datepicker manager. */
973
+ function DatepickerInstance(settings, inline) {
974
+ this._id = $.datepicker._register(this);
975
+ this._selectedDay = 0; // Current date for selection
976
+ this._selectedMonth = 0; // 0-11
977
+ this._selectedYear = 0; // 4-digit year
978
+ this._drawMonth = 0; // Current month at start of datepicker
979
+ this._drawYear = 0;
980
+ this._input = null; // The attached input field
981
+ this._inline = inline; // True if showing inline, false if used in a popup
982
+ this._datepickerDiv = (!inline ? $.datepicker._datepickerDiv :
983
+ $('<div id="datepicker_div_' + this._id + '" class="datepicker_inline">'));
984
+ // customise the date picker object - uses manager defaults if not overridden
985
+ this._settings = extendRemove(settings || {}); // clone
986
+ if (inline)
987
+ this._setDate(this._getDefaultDate());
988
+ }
989
+
990
+ $.extend(DatepickerInstance.prototype, {
991
+ /* Get a setting value, defaulting if necessary. */
992
+ _get: function(name) {
993
+ return this._settings[name] || $.datepicker._defaults[name];
994
+ },
995
+
996
+ /* Parse existing date and initialise date picker. */
997
+ _setDateFromField: function(input) {
998
+ this._input = $(input);
999
+ var dateFormat = this._get('dateFormat');
1000
+ var dates = this._input ? this._input.val().split(this._get('rangeSeparator')) : null;
1001
+ this._endDay = this._endMonth = this._endYear = null;
1002
+ var date = defaultDate = this._getDefaultDate();
1003
+ if (dates.length > 0) {
1004
+ var settings = this._getFormatConfig();
1005
+ if (dates.length > 1) {
1006
+ date = $.datepicker.parseDate(dateFormat, dates[1], settings) || defaultDate;
1007
+ this._endDay = date.getDate();
1008
+ this._endMonth = date.getMonth();
1009
+ this._endYear = date.getFullYear();
1010
+ }
1011
+ try {
1012
+ date = $.datepicker.parseDate(dateFormat, dates[0], settings) || defaultDate;
1013
+ } catch (e) {
1014
+ $.datepicker.log(e);
1015
+ date = defaultDate;
1016
+ }
1017
+ }
1018
+ this._selectedDay = date.getDate();
1019
+ this._drawMonth = this._selectedMonth = date.getMonth();
1020
+ this._drawYear = this._selectedYear = date.getFullYear();
1021
+ this._currentDay = (dates[0] ? date.getDate() : 0);
1022
+ this._currentMonth = (dates[0] ? date.getMonth() : 0);
1023
+ this._currentYear = (dates[0] ? date.getFullYear() : 0);
1024
+ this._adjustDate();
1025
+ },
1026
+
1027
+ /* Retrieve the default date shown on opening. */
1028
+ _getDefaultDate: function() {
1029
+ var date = this._determineDate('defaultDate', new Date());
1030
+ var minDate = this._getMinMaxDate('min', true);
1031
+ var maxDate = this._getMinMaxDate('max');
1032
+ date = (minDate && date < minDate ? minDate : date);
1033
+ date = (maxDate && date > maxDate ? maxDate : date);
1034
+ return date;
1035
+ },
1036
+
1037
+ /* A date may be specified as an exact value or a relative one. */
1038
+ _determineDate: function(name, defaultDate) {
1039
+ var offsetNumeric = function(offset) {
1040
+ var date = new Date();
1041
+ date.setDate(date.getDate() + offset);
1042
+ return date;
1043
+ };
1044
+ var offsetString = function(offset, getDaysInMonth) {
1045
+ var date = new Date();
1046
+ var matches = /^([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?$/.exec(offset);
1047
+ if (matches) {
1048
+ var year = date.getFullYear();
1049
+ var month = date.getMonth();
1050
+ var day = date.getDate();
1051
+ switch (matches[2] || 'd') {
1052
+ case 'd' : case 'D' :
1053
+ day += (matches[1] - 0); break;
1054
+ case 'w' : case 'W' :
1055
+ day += (matches[1] * 7); break;
1056
+ case 'm' : case 'M' :
1057
+ month += (matches[1] - 0);
1058
+ day = Math.min(day, getDaysInMonth(year, month));
1059
+ break;
1060
+ case 'y': case 'Y' :
1061
+ year += (matches[1] - 0);
1062
+ day = Math.min(day, getDaysInMonth(year, month));
1063
+ break;
1064
+ }
1065
+ date = new Date(year, month, day);
1066
+ }
1067
+ return date;
1068
+ };
1069
+ var date = this._get(name);
1070
+ return (date == null ? defaultDate :
1071
+ (typeof date == 'string' ? offsetString(date, this._getDaysInMonth) :
1072
+ (typeof date == 'number' ? offsetNumeric(date) : date)));
1073
+ },
1074
+
1075
+ /* Set the date(s) directly. */
1076
+ _setDate: function(date, endDate) {
1077
+ this._selectedDay = this._currentDay = date.getDate();
1078
+ this._drawMonth = this._selectedMonth = this._currentMonth = date.getMonth();
1079
+ this._drawYear = this._selectedYear = this._currentYear = date.getFullYear();
1080
+ if (this._get('rangeSelect')) {
1081
+ if (endDate) {
1082
+ this._endDay = endDate.getDate();
1083
+ this._endMonth = endDate.getMonth();
1084
+ this._endYear = endDate.getFullYear();
1085
+ } else {
1086
+ this._endDay = this._currentDay;
1087
+ this._endMonth = this._currentMonth;
1088
+ this._endYear = this._currentYear;
1089
+ }
1090
+ }
1091
+ this._adjustDate();
1092
+ },
1093
+
1094
+ /* Retrieve the date(s) directly. */
1095
+ _getDate: function() {
1096
+ var startDate = (!this._currentYear || (this._input && this._input.val() == '') ? null :
1097
+ new Date(this._currentYear, this._currentMonth, this._currentDay));
1098
+ if (this._get('rangeSelect')) {
1099
+ return [startDate, (!this._endYear ? null :
1100
+ new Date(this._endYear, this._endMonth, this._endDay))];
1101
+ } else
1102
+ return startDate;
1103
+ },
1104
+
1105
+ /* Generate the HTML for the current state of the date picker. */
1106
+ _generateDatepicker: function() {
1107
+ var today = new Date();
1108
+ today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time
1109
+ var showStatus = this._get('showStatus');
1110
+ var isRTL = this._get('isRTL');
1111
+ // build the date picker HTML
1112
+ var clear = (this._get('mandatory') ? '' :
1113
+ '<div class="datepicker_clear"><a onclick="jQuery.datepicker._clearDate(' + this._id + ');"' +
1114
+ (showStatus ? this._addStatus(this._get('clearStatus') || '&#xa0;') : '') + '>' +
1115
+ this._get('clearText') + '</a></div>');
1116
+ var controls = '<div class="datepicker_control">' + (isRTL ? '' : clear) +
1117
+ '<div class="datepicker_close"><a onclick="jQuery.datepicker._hideDatepicker();"' +
1118
+ (showStatus ? this._addStatus(this._get('closeStatus') || '&#xa0;') : '') + '>' +
1119
+ this._get('closeText') + '</a></div>' + (isRTL ? clear : '') + '</div>';
1120
+ var prompt = this._get('prompt');
1121
+ var closeAtTop = this._get('closeAtTop');
1122
+ var hideIfNoPrevNext = this._get('hideIfNoPrevNext');
1123
+ var numMonths = this._getNumberOfMonths();
1124
+ var stepMonths = this._get('stepMonths');
1125
+ var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
1126
+ var minDate = this._getMinMaxDate('min', true);
1127
+ var maxDate = this._getMinMaxDate('max');
1128
+ var drawMonth = this._drawMonth;
1129
+ var drawYear = this._drawYear;
1130
+ if (maxDate) {
1131
+ var maxDraw = new Date(maxDate.getFullYear(),
1132
+ maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate());
1133
+ maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1134
+ while (new Date(drawYear, drawMonth, 1) > maxDraw) {
1135
+ drawMonth--;
1136
+ if (drawMonth < 0) {
1137
+ drawMonth = 11;
1138
+ drawYear--;
1139
+ }
1140
+ }
1141
+ }
1142
+ // controls and links
1143
+ var prev = '<div class="datepicker_prev">' + (this._canAdjustMonth(-1, drawYear, drawMonth) ?
1144
+ '<a onclick="jQuery.datepicker._adjustDate(' + this._id + ', -' + stepMonths + ', \'M\');"' +
1145
+ (showStatus ? this._addStatus(this._get('prevStatus') || '&#xa0;') : '') + '>' +
1146
+ this._get('prevText') + '</a>' :
1147
+ (hideIfNoPrevNext ? '' : '<label>' + this._get('prevText') + '</label>')) + '</div>';
1148
+ var next = '<div class="datepicker_next">' + (this._canAdjustMonth(+1, drawYear, drawMonth) ?
1149
+ '<a onclick="jQuery.datepicker._adjustDate(' + this._id + ', +' + stepMonths + ', \'M\');"' +
1150
+ (showStatus ? this._addStatus(this._get('nextStatus') || '&#xa0;') : '') + '>' +
1151
+ this._get('nextText') + '</a>' :
1152
+ (hideIfNoPrevNext ? '>' : '<label>' + this._get('nextText') + '</label>')) + '</div>';
1153
+ var html = (prompt ? '<div class="datepicker_prompt">' + prompt + '</div>' : '') +
1154
+ (closeAtTop && !this._inline ? controls : '') +
1155
+ '<div class="datepicker_links">' + (isRTL ? next : prev) +
1156
+ (this._isInRange(today) ? '<div class="datepicker_current">' +
1157
+ '<a onclick="jQuery.datepicker._gotoToday(' + this._id + ');"' +
1158
+ (showStatus ? this._addStatus(this._get('currentStatus') || '&#xa0;') : '') + '>' +
1159
+ this._get('currentText') + '</a></div>' : '') + (isRTL ? prev : next) + '</div>';
1160
+ var showWeeks = this._get('showWeeks');
1161
+ for (var row = 0; row < numMonths[0]; row++)
1162
+ for (var col = 0; col < numMonths[1]; col++) {
1163
+ var selectedDate = new Date(drawYear, drawMonth, this._selectedDay);
1164
+ html += '<div class="datepicker_oneMonth' + (col == 0 ? ' datepicker_newRow' : '') + '">' +
1165
+ this._generateMonthYearHeader(drawMonth, drawYear, minDate, maxDate,
1166
+ selectedDate, row > 0 || col > 0) + // draw month headers
1167
+ '<table class="datepicker" cellpadding="0" cellspacing="0"><thead>' +
1168
+ '<tr class="datepicker_titleRow">' +
1169
+ (showWeeks ? '<td>' + this._get('weekHeader') + '</td>' : '');
1170
+ var firstDay = this._get('firstDay');
1171
+ var changeFirstDay = this._get('changeFirstDay');
1172
+ var dayNames = this._get('dayNames');
1173
+ var dayNamesShort = this._get('dayNamesShort');
1174
+ var dayNamesMin = this._get('dayNamesMin');
1175
+ for (var dow = 0; dow < 7; dow++) { // days of the week
1176
+ var day = (dow + firstDay) % 7;
1177
+ var status = this._get('dayStatus') || '&#xa0;';
1178
+ status = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) :
1179
+ status.replace(/D/, dayNamesShort[day]));
1180
+ html += '<td' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="datepicker_weekEndCell"' : '') + '>' +
1181
+ (!changeFirstDay ? '<span' :
1182
+ '<a onclick="jQuery.datepicker._changeFirstDay(' + this._id + ', ' + day + ');"') +
1183
+ (showStatus ? this._addStatus(status) : '') + ' title="' + dayNames[day] + '">' +
1184
+ dayNamesMin[day] + (changeFirstDay ? '</a>' : '</span>') + '</td>';
1185
+ }
1186
+ html += '</tr></thead><tbody>';
1187
+ var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1188
+ if (drawYear == this._selectedYear && drawMonth == this._selectedMonth) {
1189
+ this._selectedDay = Math.min(this._selectedDay, daysInMonth);
1190
+ }
1191
+ var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1192
+ var currentDate = (!this._currentDay ? new Date(9999, 9, 9) :
1193
+ new Date(this._currentYear, this._currentMonth, this._currentDay));
1194
+ var endDate = this._endDay ? new Date(this._endYear, this._endMonth, this._endDay) : currentDate;
1195
+ var printDate = new Date(drawYear, drawMonth, 1 - leadDays);
1196
+ var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
1197
+ var beforeShowDay = this._get('beforeShowDay');
1198
+ var showOtherMonths = this._get('showOtherMonths');
1199
+ var calculateWeek = this._get('calculateWeek') || $.datepicker.iso8601Week;
1200
+ var dateStatus = this._get('statusForDate') || $.datepicker.dateStatus;
1201
+ for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1202
+ html += '<tr class="datepicker_daysRow">' +
1203
+ (showWeeks ? '<td class="datepicker_weekCol">' + calculateWeek(printDate) + '</td>' : '');
1204
+ for (var dow = 0; dow < 7; dow++) { // create date picker days
1205
+ var daySettings = (beforeShowDay ?
1206
+ beforeShowDay.apply((this._input ? this._input[0] : null), [printDate]) : [true, '']);
1207
+ var otherMonth = (printDate.getMonth() != drawMonth);
1208
+ var unselectable = otherMonth || !daySettings[0] ||
1209
+ (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1210
+ html += '<td class="datepicker_daysCell' +
1211
+ ((dow + firstDay + 6) % 7 >= 5 ? ' datepicker_weekEndCell' : '') + // highlight weekends
1212
+ (otherMonth ? ' datepicker_otherMonth' : '') + // highlight days from other months
1213
+ (printDate.getTime() == selectedDate.getTime() && drawMonth == this._selectedMonth ?
1214
+ ' datepicker_daysCellOver' : '') + // highlight selected day
1215
+ (unselectable ? ' datepicker_unselectable' : '') + // highlight unselectable days
1216
+ (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
1217
+ (printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range
1218
+ ' datepicker_currentDay' : '') + // highlight selected day
1219
+ (printDate.getTime() == today.getTime() ? ' datepicker_today' : '')) + '"' + // highlight today (if different)
1220
+ (unselectable ? '' : ' onmouseover="jQuery(this).addClass(\'datepicker_daysCellOver\');' +
1221
+ (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datepicker_status_' +
1222
+ this._id + '\').html(\'' + (dateStatus.apply((this._input ? this._input[0] : null),
1223
+ [printDate, this]) || '&#xa0;') +'\');') + '"' +
1224
+ ' onmouseout="jQuery(this).removeClass(\'datepicker_daysCellOver\');' +
1225
+ (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datepicker_status_' +
1226
+ this._id + '\').html(\'&#xa0;\');') + '" onclick="jQuery.datepicker._selectDay(' +
1227
+ this._id + ',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions
1228
+ (otherMonth ? (showOtherMonths ? printDate.getDate() : '&#xa0;') : // display for other months
1229
+ (unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month
1230
+ printDate.setDate(printDate.getDate() + 1);
1231
+ }
1232
+ html += '</tr>';
1233
+ }
1234
+ drawMonth++;
1235
+ if (drawMonth > 11) {
1236
+ drawMonth = 0;
1237
+ drawYear++;
1238
+ }
1239
+ html += '</tbody></table></div>';
1240
+ }
1241
+ html += (showStatus ? '<div id="datepicker_status_' + this._id +
1242
+ '" class="datepicker_status">' + (this._get('initStatus') || '&#xa0;') + '</div>' : '') +
1243
+ (!closeAtTop && !this._inline ? controls : '') +
1244
+ '<div style="clear: both;"></div>' +
1245
+ ($.browser.msie && parseInt($.browser.version) < 7 && !this._inline ?
1246
+ '<iframe src="javascript:false;" class="datepicker_cover"></iframe>' : '');
1247
+ return html;
1248
+ },
1249
+
1250
+ /* Generate the month and year header. */
1251
+ _generateMonthYearHeader: function(drawMonth, drawYear, minDate, maxDate, selectedDate, secondary) {
1252
+ minDate = (this._rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate);
1253
+ var showStatus = this._get('showStatus');
1254
+ var html = '<div class="datepicker_header">';
1255
+ // month selection
1256
+ var monthNames = this._get('monthNames');
1257
+ if (secondary || !this._get('changeMonth'))
1258
+ html += monthNames[drawMonth] + '&#xa0;';
1259
+
1260
+ else {
1261
+ var inMinYear = (minDate && minDate.getFullYear() == drawYear);
1262
+ var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
1263
+ html += '<select class="datepicker_newMonth" ' +
1264
+ 'onchange="jQuery.datepicker._selectMonthYear(' + this._id + ', this, \'M\');" ' +
1265
+ 'onclick="jQuery.datepicker._clickMonthYear(' + this._id + ');"' +
1266
+ (showStatus ? this._addStatus(this._get('monthStatus') || '&#xa0;') : '') + '>';
1267
+ for (var month = 0; month < 12; month++) {
1268
+ if ((!inMinYear || month >= minDate.getMonth()) &&
1269
+ (!inMaxYear || month <= maxDate.getMonth())) {
1270
+ html += '<option value="' + month + '"' +
1271
+ (month == drawMonth ? ' selected="selected"' : '') +
1272
+ '>' + monthNames[month] + '</option>';
1273
+ }
1274
+ }
1275
+ html += '</select>';
1276
+ }
1277
+ // year selection
1278
+ if (secondary || !this._get('changeYear'))
1279
+ html += drawYear;
1280
+ else {
1281
+ // determine range of years to display
1282
+ var years = this._get('yearRange').split(':');
1283
+ var year = 0;
1284
+ var endYear = 0;
1285
+ if (years.length != 2) {
1286
+ year = drawYear - 10;
1287
+ endYear = drawYear + 10;
1288
+ } else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
1289
+ year = drawYear + parseInt(years[0], 10);
1290
+ endYear = drawYear + parseInt(years[1], 10);
1291
+ } else {
1292
+ year = parseInt(years[0], 10);
1293
+ endYear = parseInt(years[1], 10);
1294
+ }
1295
+ year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1296
+ endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1297
+ html += '<select class="datepicker_newYear" ' +
1298
+ 'onchange="jQuery.datepicker._selectMonthYear(' + this._id + ', this, \'Y\');" ' +
1299
+ 'onclick="jQuery.datepicker._clickMonthYear(' + this._id + ');"' +
1300
+ (showStatus ? this._addStatus(this._get('yearStatus') || '&#xa0;') : '') + '>';
1301
+ for (; year <= endYear; year++) {
1302
+ html += '<option value="' + year + '"' +
1303
+ (year == drawYear ? ' selected="selected"' : '') +
1304
+ '>' + year + '</option>';
1305
+ }
1306
+ html += '</select>';
1307
+ }
1308
+ html += '</div>'; // Close datepicker_header
1309
+ return html;
1310
+ },
1311
+
1312
+ /* Provide code to set and clear the status panel. */
1313
+ _addStatus: function(text) {
1314
+ return ' onmouseover="jQuery(\'#datepicker_status_' + this._id + '\').html(\'' + text + '\');" ' +
1315
+ 'onmouseout="jQuery(\'#datepicker_status_' + this._id + '\').html(\'&#xa0;\');"';
1316
+ },
1317
+
1318
+ /* Adjust one of the date sub-fields. */
1319
+ _adjustDate: function(offset, period) {
1320
+ var year = this._drawYear + (period == 'Y' ? offset : 0);
1321
+ var month = this._drawMonth + (period == 'M' ? offset : 0);
1322
+ var day = Math.min(this._selectedDay, this._getDaysInMonth(year, month)) +
1323
+ (period == 'D' ? offset : 0);
1324
+ var date = new Date(year, month, day);
1325
+ // ensure it is within the bounds set
1326
+ var minDate = this._getMinMaxDate('min', true);
1327
+ var maxDate = this._getMinMaxDate('max');
1328
+ date = (minDate && date < minDate ? minDate : date);
1329
+ date = (maxDate && date > maxDate ? maxDate : date);
1330
+ this._selectedDay = date.getDate();
1331
+ this._drawMonth = this._selectedMonth = date.getMonth();
1332
+ this._drawYear = this._selectedYear = date.getFullYear();
1333
+ },
1334
+
1335
+ /* Determine the number of months to show. */
1336
+ _getNumberOfMonths: function() {
1337
+ var numMonths = this._get('numberOfMonths');
1338
+ return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
1339
+ },
1340
+
1341
+ /* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */
1342
+ _getMinMaxDate: function(minMax, checkRange) {
1343
+ var date = this._determineDate(minMax + 'Date', null);
1344
+ if (date) {
1345
+ date.setHours(0);
1346
+ date.setMinutes(0);
1347
+ date.setSeconds(0);
1348
+ date.setMilliseconds(0);
1349
+ }
1350
+ return date || (checkRange ? this._rangeStart : null);
1351
+ },
1352
+
1353
+ /* Find the number of days in a given month. */
1354
+ _getDaysInMonth: function(year, month) {
1355
+ return 32 - new Date(year, month, 32).getDate();
1356
+ },
1357
+
1358
+ /* Find the day of the week of the first of a month. */
1359
+ _getFirstDayOfMonth: function(year, month) {
1360
+ return new Date(year, month, 1).getDay();
1361
+ },
1362
+
1363
+ /* Determines if we should allow a "next/prev" month display change. */
1364
+ _canAdjustMonth: function(offset, curYear, curMonth) {
1365
+ var numMonths = this._getNumberOfMonths();
1366
+ var date = new Date(curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1);
1367
+ if (offset < 0)
1368
+ date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1369
+ return this._isInRange(date);
1370
+ },
1371
+
1372
+ /* Is the given date in the accepted range? */
1373
+ _isInRange: function(date) {
1374
+ // during range selection, use minimum of selected date and range start
1375
+ var newMinDate = (!this._rangeStart ? null :
1376
+ new Date(this._selectedYear, this._selectedMonth, this._selectedDay));
1377
+ newMinDate = (newMinDate && this._rangeStart < newMinDate ? this._rangeStart : newMinDate);
1378
+ var minDate = newMinDate || this._getMinMaxDate('min');
1379
+ var maxDate = this._getMinMaxDate('max');
1380
+ return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
1381
+ },
1382
+
1383
+ /* Provide the configuration settings for formatting/parsing. */
1384
+ _getFormatConfig: function() {
1385
+ var shortYearCutoff = this._get('shortYearCutoff');
1386
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
1387
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1388
+ return {shortYearCutoff: shortYearCutoff,
1389
+ dayNamesShort: this._get('dayNamesShort'), dayNames: this._get('dayNames'),
1390
+ monthNamesShort: this._get('monthNamesShort'), monthNames: this._get('monthNames')};
1391
+ },
1392
+
1393
+ /* Format the given date for display. */
1394
+ _formatDate: function(day, month, year) {
1395
+ if (!day) {
1396
+ this._currentDay = this._selectedDay;
1397
+ this._currentMonth = this._selectedMonth;
1398
+ this._currentYear = this._selectedYear;
1399
+ }
1400
+ var date = (day ? (typeof day == 'object' ? day : new Date(year, month, day)) :
1401
+ new Date(this._currentYear, this._currentMonth, this._currentDay));
1402
+ return $.datepicker.formatDate(this._get('dateFormat'), date, this._getFormatConfig());
1403
+ }
1404
+ });
1405
+
1406
+ /* jQuery extend now ignores nulls! */
1407
+ function extendRemove(target, props) {
1408
+ $.extend(target, props);
1409
+ for (var name in props)
1410
+ if (props[name] == null)
1411
+ target[name] = null;
1412
+ return target;
1413
+ };
1414
+
1415
+ /* Invoke the datepicker functionality.
1416
+ @param options String - a command, optionally followed by additional parameters or
1417
+ Object - settings for attaching new datepicker functionality
1418
+ @return jQuery object */
1419
+ $.fn.datepicker = function(options){
1420
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
1421
+ if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate')) {
1422
+ return $.datepicker['_' + options + 'Datepicker'].apply($.datepicker, [this[0]].concat(otherArgs));
1423
+ }
1424
+ return this.each(function() {
1425
+ typeof options == 'string' ?
1426
+ $.datepicker['_' + options + 'Datepicker'].apply($.datepicker, [this].concat(otherArgs)) :
1427
+ $.datepicker._attachDatepicker(this, options);
1428
+ });
1429
+ };
1430
+
1431
+ /* Initialise the date picker. */
1432
+ $(document).ready(function() {
1433
+ $(document.body).append($.datepicker._datepickerDiv)
1434
+ .mousedown($.datepicker._checkExternalClick);
1435
+ });
1436
+
1437
+ $.datepicker = new Datepicker(); // singleton instance
1438
+
1439
+ })(jQuery);