arachni 0.2.4 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGELOG.md +33 -0
  2. data/README.md +2 -4
  3. data/Rakefile +15 -4
  4. data/bin/arachni +0 -0
  5. data/bin/arachni_web +0 -0
  6. data/bin/arachni_web_autostart +0 -0
  7. data/bin/arachni_xmlrpc +0 -0
  8. data/bin/arachni_xmlrpcd +0 -0
  9. data/bin/arachni_xmlrpcd_monitor +0 -0
  10. data/lib/arachni.rb +1 -1
  11. data/lib/framework.rb +36 -6
  12. data/lib/http.rb +12 -5
  13. data/lib/module/auditor.rb +482 -59
  14. data/lib/module/base.rb +17 -0
  15. data/lib/module/manager.rb +26 -2
  16. data/lib/module/trainer.rb +1 -12
  17. data/lib/module/utilities.rb +12 -0
  18. data/lib/parser/auditable.rb +8 -3
  19. data/lib/parser/elements.rb +11 -0
  20. data/lib/parser/page.rb +3 -1
  21. data/lib/parser/parser.rb +130 -18
  22. data/lib/rpc/xml/server/dispatcher.rb +21 -0
  23. data/lib/spider.rb +141 -82
  24. data/lib/ui/cli/cli.rb +2 -3
  25. data/lib/ui/web/addon_manager.rb +273 -0
  26. data/lib/ui/web/addons/autodeploy.rb +172 -0
  27. data/lib/ui/web/addons/autodeploy/lib/manager.rb +291 -0
  28. data/lib/ui/web/addons/autodeploy/views/index.erb +124 -0
  29. data/lib/ui/web/addons/sample.rb +78 -0
  30. data/lib/ui/web/addons/sample/views/index.erb +4 -0
  31. data/lib/ui/web/addons/scheduler.rb +139 -0
  32. data/lib/ui/web/addons/scheduler/views/index.erb +131 -0
  33. data/lib/ui/web/addons/scheduler/views/options.erb +93 -0
  34. data/lib/ui/web/dispatcher_manager.rb +80 -13
  35. data/lib/ui/web/instance_manager.rb +87 -0
  36. data/lib/ui/web/scheduler.rb +166 -0
  37. data/lib/ui/web/server.rb +142 -202
  38. data/lib/ui/web/server/public/js/jquery-ui-timepicker.js +985 -0
  39. data/lib/ui/web/server/public/plugins/sample/style.css +0 -0
  40. data/lib/ui/web/server/public/style.css +42 -0
  41. data/lib/ui/web/server/views/addon.erb +15 -0
  42. data/lib/ui/web/server/views/addons.erb +46 -0
  43. data/lib/ui/web/server/views/dispatchers.erb +1 -1
  44. data/lib/ui/web/server/views/instance.erb +9 -11
  45. data/lib/ui/web/server/views/layout.erb +14 -1
  46. data/lib/ui/web/server/views/welcome.erb +7 -6
  47. data/lib/ui/web/utilities.rb +134 -0
  48. data/modules/audit/code_injection_timing.rb +6 -2
  49. data/modules/audit/code_injection_timing/payloads.txt +2 -2
  50. data/modules/audit/os_cmd_injection_timing.rb +7 -3
  51. data/modules/audit/os_cmd_injection_timing/payloads.txt +1 -1
  52. data/modules/audit/sqli_blind_rdiff.rb +18 -233
  53. data/modules/audit/sqli_blind_rdiff/payloads.txt +5 -0
  54. data/modules/audit/sqli_blind_timing.rb +9 -2
  55. data/path_extractors/anchors.rb +1 -1
  56. data/path_extractors/forms.rb +1 -1
  57. data/path_extractors/frames.rb +1 -1
  58. data/path_extractors/generic.rb +1 -1
  59. data/path_extractors/links.rb +1 -1
  60. data/path_extractors/meta_refresh.rb +1 -1
  61. data/path_extractors/scripts.rb +1 -1
  62. data/path_extractors/sitemap.rb +1 -1
  63. data/plugins/proxy/server.rb +3 -2
  64. data/plugins/waf_detector.rb +0 -3
  65. metadata +37 -34
  66. data/lib/anemone/cookie_store.rb +0 -35
  67. data/lib/anemone/core.rb +0 -371
  68. data/lib/anemone/exceptions.rb +0 -5
  69. data/lib/anemone/http.rb +0 -144
  70. data/lib/anemone/page.rb +0 -338
  71. data/lib/anemone/page_store.rb +0 -160
  72. data/lib/anemone/storage.rb +0 -34
  73. data/lib/anemone/storage/base.rb +0 -75
  74. data/lib/anemone/storage/exceptions.rb +0 -15
  75. data/lib/anemone/storage/mongodb.rb +0 -89
  76. data/lib/anemone/storage/pstore.rb +0 -50
  77. data/lib/anemone/storage/redis.rb +0 -90
  78. data/lib/anemone/storage/tokyo_cabinet.rb +0 -57
  79. data/lib/anemone/tentacle.rb +0 -40
@@ -0,0 +1,985 @@
1
+ /*
2
+ * jQuery timepicker addon
3
+ * By: Trent Richardson [http://trentrichardson.com]
4
+ * Version 0.9.5
5
+ * Last Modified: 05/25/2011
6
+ *
7
+ * Copyright 2011 Trent Richardson
8
+ * Dual licensed under the MIT and GPL licenses.
9
+ * http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
10
+ * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
11
+ *
12
+ * HERES THE CSS:
13
+ * .ui-timepicker-div .ui-widget-header{ margin-bottom: 8px; }
14
+ * .ui-timepicker-div dl{ text-align: left; }
15
+ * .ui-timepicker-div dl dt{ height: 25px; }
16
+ * .ui-timepicker-div dl dd{ margin: -25px 10px 10px 65px; }
17
+ * .ui-timepicker-div td { font-size: 90%; }
18
+ */
19
+
20
+ (function($) {
21
+
22
+ $.extend($.ui, { timepicker: { version: "0.9.5" } });
23
+
24
+ /* Time picker manager.
25
+ Use the singleton instance of this class, $.timepicker, to interact with the time picker.
26
+ Settings for (groups of) time pickers are maintained in an instance object,
27
+ allowing multiple different settings on the same page. */
28
+
29
+ function Timepicker() {
30
+ this.regional = []; // Available regional settings, indexed by language code
31
+ this.regional[''] = { // Default regional settings
32
+ currentText: 'Now',
33
+ closeText: 'Done',
34
+ ampm: false,
35
+ timeFormat: 'hh:mm tt',
36
+ timeOnlyTitle: 'Choose Time',
37
+ timeText: 'Time',
38
+ hourText: 'Hour',
39
+ minuteText: 'Minute',
40
+ secondText: 'Second',
41
+ timezoneText: 'Time Zone'
42
+ };
43
+ this._defaults = { // Global defaults for all the datetime picker instances
44
+ showButtonPanel: true,
45
+ timeOnly: false,
46
+ showHour: true,
47
+ showMinute: true,
48
+ showSecond: false,
49
+ showTimezone: false,
50
+ showTime: true,
51
+ stepHour: 0.05,
52
+ stepMinute: 0.05,
53
+ stepSecond: 0.05,
54
+ hour: 0,
55
+ minute: 0,
56
+ second: 0,
57
+ timezone: '+0000',
58
+ hourMin: 0,
59
+ minuteMin: 0,
60
+ secondMin: 0,
61
+ hourMax: 23,
62
+ minuteMax: 59,
63
+ secondMax: 59,
64
+ minDateTime: null,
65
+ maxDateTime: null,
66
+ hourGrid: 0,
67
+ minuteGrid: 0,
68
+ secondGrid: 0,
69
+ alwaysSetTime: true,
70
+ separator: ' ',
71
+ altFieldTimeOnly: true,
72
+ showTimepicker: true,
73
+ timezoneList: ["-1100", "-1000", "-0900", "-0800", "-0700", "-0600",
74
+ "-0500", "-0400", "-0300", "-0200", "-0100", "+0000",
75
+ "+0100", "+0200", "+0300", "+0400", "+0500", "+0600",
76
+ "+0700", "+0800", "+0900", "+1000", "+1100", "+1200"]
77
+ };
78
+ $.extend(this._defaults, this.regional['']);
79
+ }
80
+
81
+ $.extend(Timepicker.prototype, {
82
+ $input: null,
83
+ $altInput: null,
84
+ $timeObj: null,
85
+ inst: null,
86
+ hour_slider: null,
87
+ minute_slider: null,
88
+ second_slider: null,
89
+ timezone_select: null,
90
+ hour: 0,
91
+ minute: 0,
92
+ second: 0,
93
+ timezone: '+0000',
94
+ hourMinOriginal: null,
95
+ minuteMinOriginal: null,
96
+ secondMinOriginal: null,
97
+ hourMaxOriginal: null,
98
+ minuteMaxOriginal: null,
99
+ secondMaxOriginal: null,
100
+ ampm: '',
101
+ formattedDate: '',
102
+ formattedTime: '',
103
+ formattedDateTime: '',
104
+ timezoneList: ["-1100", "-1000", "-0900", "-0800", "-0700", "-0600",
105
+ "-0500", "-0400", "-0300", "-0200", "-0100", "+0000",
106
+ "+0100", "+0200", "+0300", "+0400", "+0500", "+0600",
107
+ "+0700", "+0800", "+0900", "+1000", "+1100", "+1200"],
108
+
109
+ /* Override the default settings for all instances of the time picker.
110
+ @param settings object - the new settings to use as defaults (anonymous object)
111
+ @return the manager object */
112
+ setDefaults: function(settings) {
113
+ extendRemove(this._defaults, settings || {});
114
+ return this;
115
+ },
116
+
117
+ //########################################################################
118
+ // Create a new Timepicker instance
119
+ //########################################################################
120
+ _newInst: function($input, o) {
121
+ var tp_inst = new Timepicker(),
122
+ inlineSettings = {};
123
+
124
+ for (var attrName in this._defaults) {
125
+ var attrValue = $input.attr('time:' + attrName);
126
+ if (attrValue) {
127
+ try {
128
+ inlineSettings[attrName] = eval(attrValue);
129
+ } catch (err) {
130
+ inlineSettings[attrName] = attrValue;
131
+ }
132
+ }
133
+ }
134
+ tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, o, {
135
+ beforeShow: function(input, dp_inst) {
136
+ if ($.isFunction(o.beforeShow))
137
+ o.beforeShow(input, dp_inst, tp_inst);
138
+ },
139
+ onChangeMonthYear: function(year, month, dp_inst) {
140
+ // Update the time as well : this prevents the time from disappearing from the $input field.
141
+ tp_inst._updateDateTime(dp_inst);
142
+ if ($.isFunction(o.onChangeMonthYear))
143
+ o.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
144
+ },
145
+ onClose: function(dateText, dp_inst) {
146
+ if (tp_inst.timeDefined === true && $input.val() != '')
147
+ tp_inst._updateDateTime(dp_inst);
148
+ if ($.isFunction(o.onClose))
149
+ o.onClose.call($input[0], dateText, dp_inst, tp_inst);
150
+ },
151
+ timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
152
+ });
153
+
154
+ tp_inst.hour = tp_inst._defaults.hour;
155
+ tp_inst.minute = tp_inst._defaults.minute;
156
+ tp_inst.second = tp_inst._defaults.second;
157
+ tp_inst.ampm = '';
158
+ tp_inst.$input = $input;
159
+
160
+ if (o.altField)
161
+ tp_inst.$altInput = $(o.altField)
162
+ .css({ cursor: 'pointer' })
163
+ .focus(function(){ $input.trigger("focus"); });
164
+
165
+ // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
166
+ if(tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date)
167
+ tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
168
+ if(tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date)
169
+ tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
170
+ if(tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date)
171
+ tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
172
+ if(tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date)
173
+ tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
174
+
175
+ return tp_inst;
176
+ },
177
+
178
+ //########################################################################
179
+ // add our sliders to the calendar
180
+ //########################################################################
181
+ _addTimePicker: function(dp_inst) {
182
+ var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ?
183
+ this.$input.val() + ' ' + this.$altInput.val() :
184
+ this.$input.val();
185
+
186
+ this.timeDefined = this._parseTime(currDT);
187
+ this._limitMinMaxDateTime(dp_inst, false);
188
+ this._injectTimePicker();
189
+ },
190
+
191
+ //########################################################################
192
+ // parse the time string from input value or _setTime
193
+ //########################################################################
194
+ _parseTime: function(timeString, withDate) {
195
+ var regstr = this._defaults.timeFormat.toString()
196
+ .replace(/h{1,2}/ig, '(\\d?\\d)')
197
+ .replace(/m{1,2}/ig, '(\\d?\\d)')
198
+ .replace(/s{1,2}/ig, '(\\d?\\d)')
199
+ .replace(/t{1,2}/ig, '(am|pm|a|p)?')
200
+ .replace(/z{1}/ig, '((\\+|-)\\d\\d\\d\\d)?')
201
+ .replace(/\s/g, '\\s?') + '$',
202
+ order = this._getFormatPositions(),
203
+ treg;
204
+
205
+ if (!this.inst) this.inst = $.datepicker._getInst(this.$input[0]);
206
+
207
+ if (withDate || !this._defaults.timeOnly) {
208
+ // the time should come after x number of characters and a space.
209
+ // x = at least the length of text specified by the date format
210
+ var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
211
+ // escape special regex characters in the seperator
212
+ var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g");
213
+ regstr = '.{' + dp_dateFormat.length + ',}' + this._defaults.separator.replace(specials, "\\$&") + regstr;
214
+ }
215
+
216
+ treg = timeString.match(new RegExp(regstr, 'i'));
217
+
218
+ if (treg) {
219
+ if (order.t !== -1)
220
+ this.ampm = ((treg[order.t] === undefined || treg[order.t].length === 0) ?
221
+ '' :
222
+ (treg[order.t].charAt(0).toUpperCase() == 'A') ? 'AM' : 'PM').toUpperCase();
223
+
224
+ if (order.h !== -1) {
225
+ if (this.ampm == 'AM' && treg[order.h] == '12')
226
+ this.hour = 0; // 12am = 0 hour
227
+ else if (this.ampm == 'PM' && treg[order.h] != '12')
228
+ this.hour = (parseFloat(treg[order.h]) + 12).toFixed(0); // 12pm = 12 hour, any other pm = hour + 12
229
+ else this.hour = Number(treg[order.h]);
230
+ }
231
+
232
+ if (order.m !== -1) this.minute = Number(treg[order.m]);
233
+ if (order.s !== -1) this.second = Number(treg[order.s]);
234
+ if (order.z !== -1) this.timezone = treg[order.z];
235
+
236
+ return true;
237
+
238
+ }
239
+ return false;
240
+ },
241
+
242
+ //########################################################################
243
+ // figure out position of time elements.. cause js cant do named captures
244
+ //########################################################################
245
+ _getFormatPositions: function() {
246
+ var finds = this._defaults.timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|t{1,2}|z)/g),
247
+ orders = { h: -1, m: -1, s: -1, t: -1, z: -1 };
248
+
249
+ if (finds)
250
+ for (var i = 0; i < finds.length; i++)
251
+ if (orders[finds[i].toString().charAt(0)] == -1)
252
+ orders[finds[i].toString().charAt(0)] = i + 1;
253
+
254
+ return orders;
255
+ },
256
+
257
+ //########################################################################
258
+ // generate and inject html for timepicker into ui datepicker
259
+ //########################################################################
260
+ _injectTimePicker: function() {
261
+ var $dp = this.inst.dpDiv,
262
+ o = this._defaults,
263
+ tp_inst = this,
264
+ // Added by Peter Medeiros:
265
+ // - Figure out what the hour/minute/second max should be based on the step values.
266
+ // - Example: if stepMinute is 15, then minMax is 45.
267
+ hourMax = (o.hourMax - (o.hourMax % o.stepHour)).toFixed(0),
268
+ minMax = (o.minuteMax - (o.minuteMax % o.stepMinute)).toFixed(0),
269
+ secMax = (o.secondMax - (o.secondMax % o.stepSecond)).toFixed(0),
270
+ dp_id = this.inst.id.toString().replace(/([^A-Za-z0-9_])/g, '');
271
+
272
+ // Prevent displaying twice
273
+ //if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0) {
274
+ if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0 && o.showTimepicker) {
275
+ var noDisplay = ' style="display:none;"',
276
+ html = '<div class="ui-timepicker-div" id="ui-timepicker-div-' + dp_id + '"><dl>' +
277
+ '<dt class="ui_tpicker_time_label" id="ui_tpicker_time_label_' + dp_id + '"' +
278
+ ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' +
279
+ '<dd class="ui_tpicker_time" id="ui_tpicker_time_' + dp_id + '"' +
280
+ ((o.showTime) ? '' : noDisplay) + '></dd>' +
281
+ '<dt class="ui_tpicker_hour_label" id="ui_tpicker_hour_label_' + dp_id + '"' +
282
+ ((o.showHour) ? '' : noDisplay) + '>' + o.hourText + '</dt>',
283
+ hourGridSize = 0,
284
+ minuteGridSize = 0,
285
+ secondGridSize = 0,
286
+ size;
287
+
288
+ if (o.showHour && o.hourGrid > 0) {
289
+ html += '<dd class="ui_tpicker_hour">' +
290
+ '<div id="ui_tpicker_hour_' + dp_id + '"' + ((o.showHour) ? '' : noDisplay) + '></div>' +
291
+ '<div style="padding-left: 1px"><table><tr>';
292
+
293
+ for (var h = o.hourMin; h < hourMax; h += o.hourGrid) {
294
+ hourGridSize++;
295
+ var tmph = (o.ampm && h > 12) ? h-12 : h;
296
+ if (tmph < 10) tmph = '0' + tmph;
297
+ if (o.ampm) {
298
+ if (h == 0) tmph = 12 +'a';
299
+ else if (h < 12) tmph += 'a';
300
+ else tmph += 'p';
301
+ }
302
+ html += '<td>' + tmph + '</td>';
303
+ }
304
+
305
+ html += '</tr></table></div>' +
306
+ '</dd>';
307
+ } else html += '<dd class="ui_tpicker_hour" id="ui_tpicker_hour_' + dp_id + '"' +
308
+ ((o.showHour) ? '' : noDisplay) + '></dd>';
309
+
310
+ html += '<dt class="ui_tpicker_minute_label" id="ui_tpicker_minute_label_' + dp_id + '"' +
311
+ ((o.showMinute) ? '' : noDisplay) + '>' + o.minuteText + '</dt>';
312
+
313
+ if (o.showMinute && o.minuteGrid > 0) {
314
+ html += '<dd class="ui_tpicker_minute ui_tpicker_minute_' + o.minuteGrid + '">' +
315
+ '<div id="ui_tpicker_minute_' + dp_id + '"' +
316
+ ((o.showMinute) ? '' : noDisplay) + '></div>' +
317
+ '<div style="padding-left: 1px"><table><tr>';
318
+
319
+ for (var m = o.minuteMin; m < minMax; m += o.minuteGrid) {
320
+ minuteGridSize++;
321
+ html += '<td>' + ((m < 10) ? '0' : '') + m + '</td>';
322
+ }
323
+
324
+ html += '</tr></table></div>' +
325
+ '</dd>';
326
+ } else html += '<dd class="ui_tpicker_minute" id="ui_tpicker_minute_' + dp_id + '"' +
327
+ ((o.showMinute) ? '' : noDisplay) + '></dd>';
328
+
329
+ html += '<dt class="ui_tpicker_second_label" id="ui_tpicker_second_label_' + dp_id + '"' +
330
+ ((o.showSecond) ? '' : noDisplay) + '>' + o.secondText + '</dt>';
331
+
332
+ if (o.showSecond && o.secondGrid > 0) {
333
+ html += '<dd class="ui_tpicker_second ui_tpicker_second_' + o.secondGrid + '">' +
334
+ '<div id="ui_tpicker_second_' + dp_id + '"' +
335
+ ((o.showSecond) ? '' : noDisplay) + '></div>' +
336
+ '<div style="padding-left: 1px"><table><tr>';
337
+
338
+ for (var s = o.secondMin; s < secMax; s += o.secondGrid) {
339
+ secondGridSize++;
340
+ html += '<td>' + ((s < 10) ? '0' : '') + s + '</td>';
341
+ }
342
+
343
+ html += '</tr></table></div>' +
344
+ '</dd>';
345
+ } else html += '<dd class="ui_tpicker_second" id="ui_tpicker_second_' + dp_id + '"' +
346
+ ((o.showSecond) ? '' : noDisplay) + '></dd>';
347
+
348
+ html += '<dt class="ui_tpicker_timezone_label" id="ui_tpicker_timezone_label_' + dp_id + '"' +
349
+ ((o.showTimezone) ? '' : noDisplay) + '>' + o.timezoneText + '</dt>';
350
+ html += '<dd class="ui_tpicker_timezone" id="ui_tpicker_timezone_' + dp_id + '"' +
351
+ ((o.showTimezone) ? '' : noDisplay) + '></dd>';
352
+
353
+ html += '</dl></div>';
354
+ $tp = $(html);
355
+
356
+ // if we only want time picker...
357
+ if (o.timeOnly === true) {
358
+ $tp.prepend(
359
+ '<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' +
360
+ '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' +
361
+ '</div>');
362
+ $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
363
+ }
364
+
365
+ this.hour_slider = $tp.find('#ui_tpicker_hour_'+ dp_id).slider({
366
+ orientation: "horizontal",
367
+ value: this.hour,
368
+ min: o.hourMin,
369
+ max: hourMax,
370
+ step: o.stepHour,
371
+ slide: function(event, ui) {
372
+ tp_inst.hour_slider.slider( "option", "value", ui.value);
373
+ tp_inst._onTimeChange();
374
+ }
375
+ });
376
+
377
+ // Updated by Peter Medeiros:
378
+ // - Pass in Event and UI instance into slide function
379
+ this.minute_slider = $tp.find('#ui_tpicker_minute_'+ dp_id).slider({
380
+ orientation: "horizontal",
381
+ value: this.minute,
382
+ min: o.minuteMin,
383
+ max: minMax,
384
+ step: o.stepMinute,
385
+ slide: function(event, ui) {
386
+ // update the global minute slider instance value with the current slider value
387
+ tp_inst.minute_slider.slider( "option", "value", ui.value);
388
+ tp_inst._onTimeChange();
389
+ }
390
+ });
391
+
392
+ this.second_slider = $tp.find('#ui_tpicker_second_'+ dp_id).slider({
393
+ orientation: "horizontal",
394
+ value: this.second,
395
+ min: o.secondMin,
396
+ max: secMax,
397
+ step: o.stepSecond,
398
+ slide: function(event, ui) {
399
+ tp_inst.second_slider.slider( "option", "value", ui.value);
400
+ tp_inst._onTimeChange();
401
+ }
402
+ });
403
+
404
+
405
+ this.timezone_select = $tp.find('#ui_tpicker_timezone_'+ dp_id).append('<select></select>').find("select");
406
+ $.fn.append.apply(this.timezone_select,
407
+ $.map(o.timezoneList, function(val, idx) {
408
+ return $("<option />")
409
+ .val(typeof val == "object" ? val.value : val)
410
+ .text(typeof val == "object" ? val.label : val);
411
+ })
412
+ );
413
+ this.timezone_select.val((typeof this.timezone != "undefined" && this.timezone != null && this.timezone != "") ? this.timezone : o.timezone);
414
+ this.timezone_select.change(function() {
415
+ tp_inst._onTimeChange();
416
+ });
417
+
418
+ // Add grid functionality
419
+ if (o.showHour && o.hourGrid > 0) {
420
+ size = 100 * hourGridSize * o.hourGrid / (hourMax - o.hourMin);
421
+
422
+ $tp.find(".ui_tpicker_hour table").css({
423
+ width: size + "%",
424
+ marginLeft: (size / (-2 * hourGridSize)) + "%",
425
+ borderCollapse: 'collapse'
426
+ }).find("td").each( function(index) {
427
+ $(this).click(function() {
428
+ var h = $(this).html();
429
+ if(o.ampm) {
430
+ var ap = h.substring(2).toLowerCase(),
431
+ aph = parseInt(h.substring(0,2));
432
+ if (ap == 'a') {
433
+ if (aph == 12) h = 0;
434
+ else h = aph;
435
+ } else if (aph == 12) h = 12;
436
+ else h = aph + 12;
437
+ }
438
+ tp_inst.hour_slider.slider("option", "value", h);
439
+ tp_inst._onTimeChange();
440
+ tp_inst._onSelectHandler();
441
+ }).css({
442
+ cursor: 'pointer',
443
+ width: (100 / hourGridSize) + '%',
444
+ textAlign: 'center',
445
+ overflow: 'hidden'
446
+ });
447
+ });
448
+ }
449
+
450
+ if (o.showMinute && o.minuteGrid > 0) {
451
+ size = 100 * minuteGridSize * o.minuteGrid / (minMax - o.minuteMin);
452
+ $tp.find(".ui_tpicker_minute table").css({
453
+ width: size + "%",
454
+ marginLeft: (size / (-2 * minuteGridSize)) + "%",
455
+ borderCollapse: 'collapse'
456
+ }).find("td").each(function(index) {
457
+ $(this).click(function() {
458
+ tp_inst.minute_slider.slider("option", "value", $(this).html());
459
+ tp_inst._onTimeChange();
460
+ tp_inst._onSelectHandler();
461
+ }).css({
462
+ cursor: 'pointer',
463
+ width: (100 / minuteGridSize) + '%',
464
+ textAlign: 'center',
465
+ overflow: 'hidden'
466
+ });
467
+ });
468
+ }
469
+
470
+ if (o.showSecond && o.secondGrid > 0) {
471
+ $tp.find(".ui_tpicker_second table").css({
472
+ width: size + "%",
473
+ marginLeft: (size / (-2 * secondGridSize)) + "%",
474
+ borderCollapse: 'collapse'
475
+ }).find("td").each(function(index) {
476
+ $(this).click(function() {
477
+ tp_inst.second_slider.slider("option", "value", $(this).html());
478
+ tp_inst._onTimeChange();
479
+ tp_inst._onSelectHandler();
480
+ }).css({
481
+ cursor: 'pointer',
482
+ width: (100 / secondGridSize) + '%',
483
+ textAlign: 'center',
484
+ overflow: 'hidden'
485
+ });
486
+ });
487
+ }
488
+
489
+ var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
490
+ if ($buttonPanel.length) $buttonPanel.before($tp);
491
+ else $dp.append($tp);
492
+
493
+ this.$timeObj = $tp.find('#ui_tpicker_time_'+ dp_id);
494
+
495
+ if (this.inst !== null) {
496
+ var timeDefined = this.timeDefined;
497
+ this._onTimeChange();
498
+ this.timeDefined = timeDefined;
499
+ }
500
+
501
+ //Emulate datepicker onSelect behavior. Call on slidestop.
502
+ var onSelectDelegate = function() {
503
+ tp_inst._onSelectHandler();
504
+ };
505
+ this.hour_slider.bind('slidestop',onSelectDelegate);
506
+ this.minute_slider.bind('slidestop',onSelectDelegate);
507
+ this.second_slider.bind('slidestop',onSelectDelegate);
508
+ }
509
+ },
510
+
511
+ //########################################################################
512
+ // This function tries to limit the ability to go outside the
513
+ // min/max date range
514
+ //########################################################################
515
+ _limitMinMaxDateTime: function(dp_inst, adjustSliders){
516
+ var o = this._defaults,
517
+ dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);
518
+
519
+ if(!this._defaults.showTimepicker) return; // No time so nothing to check here
520
+
521
+ if(this._defaults.minDateTime !== null && dp_date){
522
+ var minDateTime = this._defaults.minDateTime,
523
+ minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);
524
+
525
+ if(this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null){
526
+ this.hourMinOriginal = o.hourMin;
527
+ this.minuteMinOriginal = o.minuteMin;
528
+ this.secondMinOriginal = o.secondMin;
529
+ }
530
+
531
+ if(dp_inst.settings.timeOnly || minDateTimeDate.getTime() == dp_date.getTime()) {
532
+ this._defaults.hourMin = minDateTime.getHours();
533
+ if (this.hour <= this._defaults.hourMin) {
534
+ this.hour = this._defaults.hourMin;
535
+ this._defaults.minuteMin = minDateTime.getMinutes();
536
+ if (this.minute <= this._defaults.minuteMin) {
537
+ this.minute = this._defaults.minuteMin;
538
+ this._defaults.secondMin = minDateTime.getSeconds();
539
+ } else {
540
+ if(this.second < this._defaults.secondMin) this.second = this._defaults.secondMin;
541
+ this._defaults.secondMin = this.secondMinOriginal;
542
+ }
543
+ } else {
544
+ this._defaults.minuteMin = this.minuteMinOriginal;
545
+ this._defaults.secondMin = this.secondMinOriginal;
546
+ }
547
+ }else{
548
+ this._defaults.hourMin = this.hourMinOriginal;
549
+ this._defaults.minuteMin = this.minuteMinOriginal;
550
+ this._defaults.secondMin = this.secondMinOriginal;
551
+ }
552
+ }
553
+
554
+ if(this._defaults.maxDateTime !== null && dp_date){
555
+ var maxDateTime = this._defaults.maxDateTime,
556
+ maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);
557
+
558
+ if(this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null){
559
+ this.hourMaxOriginal = o.hourMax;
560
+ this.minuteMaxOriginal = o.minuteMax;
561
+ this.secondMaxOriginal = o.secondMax;
562
+ }
563
+
564
+ if(dp_inst.settings.timeOnly || maxDateTimeDate.getTime() == dp_date.getTime()){
565
+ this._defaults.hourMax = maxDateTime.getHours();
566
+ if (this.hour >= this._defaults.hourMax) {
567
+ this.hour = this._defaults.hourMax;
568
+ this._defaults.minuteMax = maxDateTime.getMinutes();
569
+ if (this.minute >= this._defaults.minuteMax) {
570
+ this.minute = this._defaults.minuteMax;
571
+ this._defaults.secondMin = maxDateTime.getSeconds();
572
+ } else {
573
+ if(this.second > this._defaults.secondMax) this.second = this._defaults.secondMax;
574
+ this._defaults.secondMax = this.secondMaxOriginal;
575
+ }
576
+ } else {
577
+ this._defaults.minuteMax = this.minuteMaxOriginal;
578
+ this._defaults.secondMax = this.secondMaxOriginal;
579
+ }
580
+ }else{
581
+ this._defaults.hourMax = this.hourMaxOriginal;
582
+ this._defaults.minuteMax = this.minuteMaxOriginal;
583
+ this._defaults.secondMax = this.secondMaxOriginal;
584
+ }
585
+ }
586
+
587
+ if(adjustSliders !== undefined && adjustSliders === true){
588
+ this.hour_slider.slider("option", { min: this._defaults.hourMin, max: this._defaults.hourMax }).slider('value', this.hour);
589
+ this.minute_slider.slider("option", { min: this._defaults.minuteMin, max: this._defaults.minuteMax }).slider('value', this.minute);
590
+ this.second_slider.slider("option", { min: this._defaults.secondMin, max: this._defaults.secondMax }).slider('value', this.second);
591
+ }
592
+
593
+ },
594
+
595
+
596
+ //########################################################################
597
+ // when a slider moves, set the internal time...
598
+ // on time change is also called when the time is updated in the text field
599
+ //########################################################################
600
+ _onTimeChange: function() {
601
+ var hour = (this.hour_slider) ? this.hour_slider.slider('value') : false,
602
+ minute = (this.minute_slider) ? this.minute_slider.slider('value') : false,
603
+ second = (this.second_slider) ? this.second_slider.slider('value') : false,
604
+ timezone = (this.timezone_select) ? this.timezone_select.val() : false;
605
+
606
+ if (hour !== false) hour = parseInt(hour,10);
607
+ if (minute !== false) minute = parseInt(minute,10);
608
+ if (second !== false) second = parseInt(second,10);
609
+
610
+ var ampm = (hour < 12) ? 'AM' : 'PM';
611
+
612
+ // If the update was done in the input field, the input field should not be updated.
613
+ // If the update was done using the sliders, update the input field.
614
+ var hasChanged = (hour != this.hour || minute != this.minute || second != this.second || (this.ampm.length > 0 && this.ampm != ampm) || timezone != this.timezone);
615
+
616
+ if (hasChanged) {
617
+
618
+ if (hour !== false)this.hour = hour;
619
+ if (minute !== false) this.minute = minute;
620
+ if (second !== false) this.second = second;
621
+ if (timezone !== false) this.timezone = timezone;
622
+ this._limitMinMaxDateTime(this.inst, true);
623
+ }
624
+ if (this._defaults.ampm) this.ampm = ampm;
625
+
626
+ this._formatTime();
627
+ if (this.$timeObj) this.$timeObj.text(this.formattedTime);
628
+ this.timeDefined = true;
629
+ if (hasChanged) this._updateDateTime();
630
+ },
631
+
632
+ //########################################################################
633
+ // call custom onSelect.
634
+ // bind to sliders slidestop, and grid click.
635
+ //########################################################################
636
+ _onSelectHandler: function() {
637
+ var onSelect = this._defaults['onSelect'];
638
+ var inputEl = this.$input ? this.$input[0] : null;
639
+ if (onSelect && inputEl) {
640
+ onSelect.apply(inputEl, [this.formattedDateTime, this]);
641
+ }
642
+ },
643
+
644
+ //########################################################################
645
+ // format the time all pretty...
646
+ //########################################################################
647
+ _formatTime: function(time, format, ampm) {
648
+ if (ampm == undefined) ampm = this._defaults.ampm;
649
+ time = time || { hour: this.hour, minute: this.minute, second: this.second, ampm: this.ampm, timezone: this.timezone };
650
+ var tmptime = format || this._defaults.timeFormat.toString();
651
+
652
+ if (ampm) {
653
+ var hour12 = ((time.ampm == 'AM') ? (time.hour) : (time.hour % 12));
654
+ hour12 = (Number(hour12) === 0) ? 12 : hour12;
655
+ tmptime = tmptime.toString()
656
+ .replace(/hh/g, ((hour12 < 10) ? '0' : '') + hour12)
657
+ .replace(/h/g, hour12)
658
+ .replace(/mm/g, ((time.minute < 10) ? '0' : '') + time.minute)
659
+ .replace(/m/g, time.minute)
660
+ .replace(/ss/g, ((time.second < 10) ? '0' : '') + time.second)
661
+ .replace(/s/g, time.second)
662
+ .replace(/TT/g, time.ampm.toUpperCase())
663
+ .replace(/Tt/g, time.ampm.toUpperCase())
664
+ .replace(/tT/g, time.ampm.toLowerCase())
665
+ .replace(/tt/g, time.ampm.toLowerCase())
666
+ .replace(/T/g, time.ampm.charAt(0).toUpperCase())
667
+ .replace(/t/g, time.ampm.charAt(0).toLowerCase())
668
+ .replace(/z/g, time.timezone);
669
+ } else {
670
+ tmptime = tmptime.toString()
671
+ .replace(/hh/g, ((time.hour < 10) ? '0' : '') + time.hour)
672
+ .replace(/h/g, time.hour)
673
+ .replace(/mm/g, ((time.minute < 10) ? '0' : '') + time.minute)
674
+ .replace(/m/g, time.minute)
675
+ .replace(/ss/g, ((time.second < 10) ? '0' : '') + time.second)
676
+ .replace(/s/g, time.second)
677
+ .replace(/z/g, time.timezone);
678
+ tmptime = $.trim(tmptime.replace(/t/gi, ''));
679
+ }
680
+
681
+ if (arguments.length) return tmptime;
682
+ else this.formattedTime = tmptime;
683
+ },
684
+
685
+ //########################################################################
686
+ // update our input with the new date time..
687
+ //########################################################################
688
+ _updateDateTime: function(dp_inst) {
689
+ dp_inst = this.inst || dp_inst,
690
+ dt = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay),
691
+ dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
692
+ formatCfg = $.datepicker._getFormatConfig(dp_inst),
693
+ timeAvailable = dt !== null && this.timeDefined;
694
+ this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
695
+ var formattedDateTime = this.formattedDate;
696
+ if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0))
697
+ return;
698
+
699
+ if (this._defaults.timeOnly === true) {
700
+ formattedDateTime = this.formattedTime;
701
+ } else if (this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) {
702
+ formattedDateTime += this._defaults.separator + this.formattedTime;
703
+ }
704
+
705
+ this.formattedDateTime = formattedDateTime;
706
+
707
+ if(!this._defaults.showTimepicker) {
708
+ this.$input.val(this.formattedDate);
709
+ } else if (this.$altInput && this._defaults.altFieldTimeOnly === true) {
710
+ this.$altInput.val(this.formattedTime);
711
+ this.$input.val(this.formattedDate);
712
+ } else if(this.$altInput) {
713
+ this.$altInput.val(formattedDateTime);
714
+ this.$input.val(formattedDateTime);
715
+ } else {
716
+ this.$input.val(formattedDateTime);
717
+ }
718
+
719
+ this.$input.trigger("change");
720
+ }
721
+
722
+ });
723
+
724
+ $.fn.extend({
725
+ //########################################################################
726
+ // shorthand just to use timepicker..
727
+ //########################################################################
728
+ timepicker: function(o) {
729
+ o = o || {};
730
+ var tmp_args = arguments;
731
+
732
+ if (typeof o == 'object') tmp_args[0] = $.extend(o, { timeOnly: true });
733
+
734
+ return $(this).each(function() {
735
+ $.fn.datetimepicker.apply($(this), tmp_args);
736
+ });
737
+ },
738
+
739
+ //########################################################################
740
+ // extend timepicker to datepicker
741
+ //########################################################################
742
+ datetimepicker: function(o) {
743
+ o = o || {};
744
+ var $input = this,
745
+ tmp_args = arguments;
746
+
747
+ if (typeof(o) == 'string'){
748
+ if(o == 'getDate')
749
+ return $.fn.datepicker.apply($(this[0]), tmp_args);
750
+ else
751
+ return this.each(function() {
752
+ var $t = $(this);
753
+ $t.datepicker.apply($t, tmp_args);
754
+ });
755
+ }
756
+ else
757
+ return this.each(function() {
758
+ var $t = $(this);
759
+ $t.datepicker($.timepicker._newInst($t, o)._defaults);
760
+ });
761
+ }
762
+ });
763
+
764
+ //########################################################################
765
+ // the bad hack :/ override datepicker so it doesnt close on select
766
+ // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
767
+ //########################################################################
768
+ $.datepicker._base_selectDate = $.datepicker._selectDate;
769
+ $.datepicker._selectDate = function (id, dateStr) {
770
+ var inst = this._getInst($(id)[0]),
771
+ tp_inst = this._get(inst, 'timepicker');
772
+
773
+ if (tp_inst) {
774
+ tp_inst._limitMinMaxDateTime(inst, true);
775
+ inst.inline = inst.stay_open = true;
776
+ //This way the onSelect handler called from calendarpicker get the full dateTime
777
+ this._base_selectDate(id, dateStr + tp_inst._defaults.separator + tp_inst.formattedTime);
778
+ inst.inline = inst.stay_open = false;
779
+ this._notifyChange(inst);
780
+ this._updateDatepicker(inst);
781
+ }
782
+ else this._base_selectDate(id, dateStr);
783
+ };
784
+
785
+ //#############################################################################################
786
+ // second bad hack :/ override datepicker so it triggers an event when changing the input field
787
+ // and does not redraw the datepicker on every selectDate event
788
+ //#############################################################################################
789
+ $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
790
+ $.datepicker._updateDatepicker = function(inst) {
791
+ if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {
792
+
793
+ this._base_updateDatepicker(inst);
794
+
795
+ // Reload the time control when changing something in the input text field.
796
+ var tp_inst = this._get(inst, 'timepicker');
797
+ if(tp_inst) tp_inst._addTimePicker(inst);
798
+ }
799
+ };
800
+
801
+ //#######################################################################################
802
+ // third bad hack :/ override datepicker so it allows spaces and colan in the input field
803
+ //#######################################################################################
804
+ $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
805
+ $.datepicker._doKeyPress = function(event) {
806
+ var inst = $.datepicker._getInst(event.target),
807
+ tp_inst = $.datepicker._get(inst, 'timepicker');
808
+
809
+ if (tp_inst) {
810
+ if ($.datepicker._get(inst, 'constrainInput')) {
811
+ var ampm = tp_inst._defaults.ampm,
812
+ datetimeChars = tp_inst._defaults.timeFormat.toString()
813
+ .replace(/[hms]/g, '')
814
+ .replace(/TT/g, ampm ? 'APM' : '')
815
+ .replace(/Tt/g, ampm ? 'AaPpMm' : '')
816
+ .replace(/tT/g, ampm ? 'AaPpMm' : '')
817
+ .replace(/T/g, ampm ? 'AP' : '')
818
+ .replace(/tt/g, ampm ? 'apm' : '')
819
+ .replace(/t/g, ampm ? 'ap' : '') +
820
+ " " +
821
+ tp_inst._defaults.separator +
822
+ $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
823
+ chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
824
+ return event.ctrlKey || (chr < ' ' || !datetimeChars || datetimeChars.indexOf(chr) > -1);
825
+ }
826
+ }
827
+
828
+ return $.datepicker._base_doKeyPress(event);
829
+ };
830
+
831
+ //#######################################################################################
832
+ // Override key up event to sync manual input changes.
833
+ //#######################################################################################
834
+ $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
835
+ $.datepicker._doKeyUp = function (event) {
836
+ var inst = $.datepicker._getInst(event.target),
837
+ tp_inst = $.datepicker._get(inst, 'timepicker');
838
+
839
+ if (tp_inst) {
840
+ if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) {
841
+ try {
842
+ $.datepicker._updateDatepicker(inst);
843
+ }
844
+ catch (err) {
845
+ $.datepicker.log(err);
846
+ }
847
+ }
848
+ }
849
+
850
+ return $.datepicker._base_doKeyUp(event);
851
+ };
852
+
853
+ //#######################################################################################
854
+ // override "Today" button to also grab the time.
855
+ //#######################################################################################
856
+ $.datepicker._base_gotoToday = $.datepicker._gotoToday;
857
+ $.datepicker._gotoToday = function(id) {
858
+ this._base_gotoToday(id);
859
+ this._setTime(this._getInst($(id)[0]), new Date());
860
+ };
861
+
862
+ //#######################################################################################
863
+ // Disable & enable the Time in the datetimepicker
864
+ //#######################################################################################
865
+ $.datepicker._disableTimepickerDatepicker = function(target, date, withDate) {
866
+ var inst = this._getInst(target),
867
+ tp_inst = this._get(inst, 'timepicker');
868
+ $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
869
+ if (tp_inst) {
870
+ tp_inst._defaults.showTimepicker = false;
871
+ tp_inst._updateDateTime(inst);
872
+ }
873
+ };
874
+
875
+ $.datepicker._enableTimepickerDatepicker = function(target, date, withDate) {
876
+ var inst = this._getInst(target),
877
+ tp_inst = this._get(inst, 'timepicker');
878
+ $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
879
+ if (tp_inst) {
880
+ tp_inst._defaults.showTimepicker = true;
881
+ tp_inst._addTimePicker(inst); // Could be disabled on page load
882
+ tp_inst._updateDateTime(inst);
883
+ }
884
+ };
885
+
886
+ //#######################################################################################
887
+ // Create our own set time function
888
+ //#######################################################################################
889
+ $.datepicker._setTime = function(inst, date) {
890
+ var tp_inst = this._get(inst, 'timepicker');
891
+ if (tp_inst) {
892
+ var defaults = tp_inst._defaults,
893
+ // calling _setTime with no date sets time to defaults
894
+ hour = date ? date.getHours() : defaults.hour,
895
+ minute = date ? date.getMinutes() : defaults.minute,
896
+ second = date ? date.getSeconds() : defaults.second;
897
+
898
+ //check if within min/max times..
899
+ if ((hour < defaults.hourMin || hour > defaults.hourMax) || (minute < defaults.minuteMin || minute > defaults.minuteMax) || (second < defaults.secondMin || second > defaults.secondMax)) {
900
+ hour = defaults.hourMin;
901
+ minute = defaults.minuteMin;
902
+ second = defaults.secondMin;
903
+ }
904
+
905
+ if (tp_inst.hour_slider) tp_inst.hour_slider.slider('value', hour);
906
+ else tp_inst.hour = hour;
907
+ if (tp_inst.minute_slider) tp_inst.minute_slider.slider('value', minute);
908
+ else tp_inst.minute = minute;
909
+ if (tp_inst.second_slider) tp_inst.second_slider.slider('value', second);
910
+ else tp_inst.second = second;
911
+
912
+ tp_inst._onTimeChange();
913
+ tp_inst._updateDateTime(inst);
914
+ }
915
+ };
916
+
917
+ //#######################################################################################
918
+ // Create new public method to set only time, callable as $().datepicker('setTime', date)
919
+ //#######################################################################################
920
+ $.datepicker._setTimeDatepicker = function(target, date, withDate) {
921
+ var inst = this._getInst(target),
922
+ tp_inst = this._get(inst, 'timepicker');
923
+
924
+ if (tp_inst) {
925
+ this._setDateFromField(inst);
926
+ var tp_date;
927
+ if (date) {
928
+ if (typeof date == "string") {
929
+ tp_inst._parseTime(date, withDate);
930
+ tp_date = new Date();
931
+ tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second);
932
+ }
933
+ else tp_date = new Date(date.getTime());
934
+ if (tp_date.toString() == 'Invalid Date') tp_date = undefined;
935
+ this._setTime(inst, tp_date);
936
+ }
937
+ }
938
+
939
+ };
940
+
941
+ //#######################################################################################
942
+ // override setDate() to allow setting time too within Date object
943
+ //#######################################################################################
944
+ $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
945
+ $.datepicker._setDateDatepicker = function(target, date) {
946
+ var inst = this._getInst(target),
947
+ tp_date = (date instanceof Date) ? new Date(date.getTime()) : date;
948
+
949
+ this._updateDatepicker(inst);
950
+ this._base_setDateDatepicker.apply(this, arguments);
951
+ this._setTimeDatepicker(target, tp_date, true);
952
+ };
953
+
954
+ //#######################################################################################
955
+ // override getDate() to allow getting time too within Date object
956
+ //#######################################################################################
957
+ $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
958
+ $.datepicker._getDateDatepicker = function(target, noDefault) {
959
+ var inst = this._getInst(target),
960
+ tp_inst = this._get(inst, 'timepicker');
961
+
962
+ if (tp_inst) {
963
+ this._setDateFromField(inst, noDefault);
964
+ var date = this._getDate(inst);
965
+ if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second);
966
+ return date;
967
+ }
968
+ return this._base_getDateDatepicker(target, noDefault);
969
+ };
970
+
971
+ //#######################################################################################
972
+ // jQuery extend now ignores nulls!
973
+ //#######################################################################################
974
+ function extendRemove(target, props) {
975
+ $.extend(target, props);
976
+ for (var name in props)
977
+ if (props[name] === null || props[name] === undefined)
978
+ target[name] = props[name];
979
+ return target;
980
+ }
981
+
982
+ $.timepicker = new Timepicker(); // singleton instance
983
+ $.timepicker.version = "0.9.5";
984
+
985
+ })(jQuery);