1 | // |
---|
2 | // CalendarView (for Prototype) |
---|
3 | // calendarview.org |
---|
4 | // |
---|
5 | // Maintained by Justin Mecham <justin@aspect.net> |
---|
6 | // |
---|
7 | // Portions Copyright 2002-2005 Mihai Bazon |
---|
8 | // |
---|
9 | // This calendar is based very loosely on the Dynarch Calendar in that it was |
---|
10 | // used as a base, but completely gutted and more or less rewritten in place |
---|
11 | // to use the Prototype JavaScript library. |
---|
12 | // |
---|
13 | // As such, CalendarView is licensed under the terms of the GNU Lesser General |
---|
14 | // Public License (LGPL). More information on the Dynarch Calendar can be |
---|
15 | // found at: |
---|
16 | // |
---|
17 | // www.dynarch.com/projects/calendar |
---|
18 | // |
---|
19 | |
---|
20 | var Calendar = Class.create() |
---|
21 | |
---|
22 | //------------------------------------------------------------------------------ |
---|
23 | // Constants |
---|
24 | //------------------------------------------------------------------------------ |
---|
25 | |
---|
26 | Calendar.VERSION = '1.2' |
---|
27 | |
---|
28 | Calendar.DAY_NAMES = new Array( |
---|
29 | 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', |
---|
30 | 'Sunday' |
---|
31 | ) |
---|
32 | |
---|
33 | Calendar.SHORT_DAY_NAMES = new Array( |
---|
34 | 'S', 'M', 'T', 'W', 'T', 'F', 'S', 'S' |
---|
35 | ) |
---|
36 | |
---|
37 | Calendar.MONTH_NAMES = new Array( |
---|
38 | 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', |
---|
39 | 'September', 'October', 'November', 'December' |
---|
40 | ) |
---|
41 | |
---|
42 | Calendar.SHORT_MONTH_NAMES = new Array( |
---|
43 | 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', |
---|
44 | 'Dec' |
---|
45 | ) |
---|
46 | |
---|
47 | Calendar.NAV_PREVIOUS_YEAR = -2 |
---|
48 | Calendar.NAV_PREVIOUS_MONTH = -1 |
---|
49 | Calendar.NAV_TODAY = 0 |
---|
50 | Calendar.NAV_NEXT_MONTH = 1 |
---|
51 | Calendar.NAV_NEXT_YEAR = 2 |
---|
52 | |
---|
53 | //------------------------------------------------------------------------------ |
---|
54 | // Static Methods |
---|
55 | //------------------------------------------------------------------------------ |
---|
56 | |
---|
57 | // This gets called when the user presses a mouse button anywhere in the |
---|
58 | // document, if the calendar is shown. If the click was outside the open |
---|
59 | // calendar this function closes it. |
---|
60 | Calendar._checkCalendar = function(event) { |
---|
61 | if (!window._popupCalendar) |
---|
62 | return false |
---|
63 | if (Element.descendantOf(Event.element(event), window._popupCalendar.container)) |
---|
64 | return |
---|
65 | window._popupCalendar.callCloseHandler() |
---|
66 | return Event.stop(event) |
---|
67 | } |
---|
68 | |
---|
69 | //------------------------------------------------------------------------------ |
---|
70 | // Event Handlers |
---|
71 | //------------------------------------------------------------------------------ |
---|
72 | |
---|
73 | Calendar.handleMouseDownEvent = function(event) |
---|
74 | { |
---|
75 | Event.observe(document, 'mouseup', Calendar.handleMouseUpEvent) |
---|
76 | Event.stop(event) |
---|
77 | } |
---|
78 | |
---|
79 | // XXX I am not happy with how clicks of different actions are handled. Need to |
---|
80 | // clean this up! |
---|
81 | Calendar.handleMouseUpEvent = function(event) |
---|
82 | { |
---|
83 | var el = Event.element(event) |
---|
84 | var calendar = el.calendar |
---|
85 | var isNewDate = false |
---|
86 | |
---|
87 | // If the element that was clicked on does not have an associated Calendar |
---|
88 | // object, return as we have nothing to do. |
---|
89 | if (!calendar) return false |
---|
90 | |
---|
91 | // Clicked on a day |
---|
92 | if (typeof el.navAction == 'undefined') |
---|
93 | { |
---|
94 | if (calendar.currentDateElement) { |
---|
95 | Element.removeClassName(calendar.currentDateElement, 'selected') |
---|
96 | Element.addClassName(el, 'selected') |
---|
97 | calendar.shouldClose = (calendar.currentDateElement == el) |
---|
98 | if (!calendar.shouldClose) calendar.currentDateElement = el |
---|
99 | } |
---|
100 | calendar.date.setDateOnly(el.date) |
---|
101 | isNewDate = true |
---|
102 | calendar.shouldClose = !el.hasClassName('otherDay') |
---|
103 | var isOtherMonth = !calendar.shouldClose |
---|
104 | if (isOtherMonth) calendar.update(calendar.date) |
---|
105 | } |
---|
106 | |
---|
107 | // Clicked on an action button |
---|
108 | else |
---|
109 | { |
---|
110 | var date = new Date(calendar.date) |
---|
111 | |
---|
112 | if (el.navAction == Calendar.NAV_TODAY) |
---|
113 | date.setDateOnly(new Date()) |
---|
114 | |
---|
115 | var year = date.getFullYear() |
---|
116 | var mon = date.getMonth() |
---|
117 | function setMonth(m) { |
---|
118 | var day = date.getDate() |
---|
119 | var max = date.getMonthDays(m) |
---|
120 | if (day > max) date.setDate(max) |
---|
121 | date.setMonth(m) |
---|
122 | } |
---|
123 | switch (el.navAction) { |
---|
124 | |
---|
125 | // Previous Year |
---|
126 | case Calendar.NAV_PREVIOUS_YEAR: |
---|
127 | if (year > calendar.minYear) |
---|
128 | date.setFullYear(year - 1) |
---|
129 | break |
---|
130 | |
---|
131 | // Previous Month |
---|
132 | case Calendar.NAV_PREVIOUS_MONTH: |
---|
133 | if (mon > 0) { |
---|
134 | setMonth(mon - 1) |
---|
135 | } |
---|
136 | else if (year-- > calendar.minYear) { |
---|
137 | date.setFullYear(year) |
---|
138 | setMonth(11) |
---|
139 | } |
---|
140 | break |
---|
141 | |
---|
142 | // Today |
---|
143 | case Calendar.NAV_TODAY: |
---|
144 | break |
---|
145 | |
---|
146 | // Next Month |
---|
147 | case Calendar.NAV_NEXT_MONTH: |
---|
148 | if (mon < 11) { |
---|
149 | setMonth(mon + 1) |
---|
150 | } |
---|
151 | else if (year < calendar.maxYear) { |
---|
152 | date.setFullYear(year + 1) |
---|
153 | setMonth(0) |
---|
154 | } |
---|
155 | break |
---|
156 | |
---|
157 | // Next Year |
---|
158 | case Calendar.NAV_NEXT_YEAR: |
---|
159 | if (year < calendar.maxYear) |
---|
160 | date.setFullYear(year + 1) |
---|
161 | break |
---|
162 | |
---|
163 | } |
---|
164 | |
---|
165 | if (!date.equalsTo(calendar.date)) { |
---|
166 | calendar.setDate(date) |
---|
167 | isNewDate = true |
---|
168 | } else if (el.navAction == 0) { |
---|
169 | isNewDate = (calendar.shouldClose = true) |
---|
170 | } |
---|
171 | } |
---|
172 | |
---|
173 | if (isNewDate) event && calendar.callSelectHandler() |
---|
174 | if (calendar.shouldClose) event && calendar.callCloseHandler() |
---|
175 | |
---|
176 | Event.stopObserving(document, 'mouseup', Calendar.handleMouseUpEvent) |
---|
177 | |
---|
178 | return Event.stop(event) |
---|
179 | } |
---|
180 | |
---|
181 | Calendar.defaultSelectHandler = function(calendar) |
---|
182 | { |
---|
183 | if (!calendar.dateField) return false |
---|
184 | |
---|
185 | // Update dateField value |
---|
186 | if (calendar.dateField.tagName == 'DIV') |
---|
187 | { |
---|
188 | if(calendar.clickToDateField) |
---|
189 | Element.update(calendar.dateField, calendar.date.print(calendar.dateFormat)) |
---|
190 | else |
---|
191 | Element.update(calendar.endDateField, calendar.date.print(calendar.dateFormat)) |
---|
192 | calendar.setClickToDateField(!calendar.clickToDateField); |
---|
193 | calendar.testPeriod(calendar.dateField.innerHTML,calendar.endDateField.innerHTML); |
---|
194 | } |
---|
195 | else if (calendar.dateField.tagName == 'INPUT') { |
---|
196 | calendar.dateField.value = calendar.date.print(calendar.dateFormat) } |
---|
197 | |
---|
198 | // Trigger the onchange callback on the dateField, if one has been defined |
---|
199 | if (typeof calendar.dateField.onchange == 'function') |
---|
200 | calendar.dateField.onchange() |
---|
201 | |
---|
202 | // Call the close handler, if necessary |
---|
203 | if (calendar.shouldClose) calendar.callCloseHandler() |
---|
204 | } |
---|
205 | |
---|
206 | Calendar.defaultCloseHandler = function(calendar) |
---|
207 | { |
---|
208 | calendar.hide() |
---|
209 | } |
---|
210 | |
---|
211 | |
---|
212 | //------------------------------------------------------------------------------ |
---|
213 | // Calendar Setup |
---|
214 | //------------------------------------------------------------------------------ |
---|
215 | |
---|
216 | Calendar.setup = function(params) |
---|
217 | { |
---|
218 | |
---|
219 | function param_default(name, def) { |
---|
220 | if (!params[name]) params[name] = def |
---|
221 | } |
---|
222 | |
---|
223 | param_default('dateField', null) |
---|
224 | param_default('endDateField', null) |
---|
225 | param_default('triggerElement', null) |
---|
226 | param_default('parentElement', null) |
---|
227 | param_default('selectHandler', null) |
---|
228 | param_default('closeHandler', null) |
---|
229 | |
---|
230 | // In-Page Calendar |
---|
231 | if (params.parentElement) |
---|
232 | { |
---|
233 | var calendar = new Calendar(params.parentElement) |
---|
234 | calendar.setSelectHandler(params.selectHandler || Calendar.defaultSelectHandler) |
---|
235 | if (params.dateFormat) |
---|
236 | calendar.setDateFormat(params.dateFormat) |
---|
237 | if (params.dateField) { |
---|
238 | calendar.setDateField(params.dateField) |
---|
239 | calendar.parseDate(calendar.dateField.innerHTML || calendar.dateField.value) |
---|
240 | } |
---|
241 | |
---|
242 | if (params.endDateField) { |
---|
243 | calendar.setEndDateField(params.endDateField) |
---|
244 | calendar.parseDate(calendar.endDateField.innerHTML || calendar.endDateField.value) |
---|
245 | } |
---|
246 | calendar.show() |
---|
247 | return calendar |
---|
248 | } |
---|
249 | |
---|
250 | // Popup Calendars |
---|
251 | // |
---|
252 | // XXX There is significant optimization to be had here by creating the |
---|
253 | // calendar and storing it on the page, but then you will have issues with |
---|
254 | // multiple calendars on the same page. |
---|
255 | else |
---|
256 | { |
---|
257 | var triggerElement = $(params.triggerElement || params.dateField) |
---|
258 | triggerElement.onclick = function() { |
---|
259 | var calendar = new Calendar() |
---|
260 | calendar.setSelectHandler(params.selectHandler || Calendar.defaultSelectHandler) |
---|
261 | calendar.setCloseHandler(params.closeHandler || Calendar.defaultCloseHandler) |
---|
262 | if (params.dateFormat) |
---|
263 | calendar.setDateFormat(params.dateFormat) |
---|
264 | if (params.dateField) { |
---|
265 | calendar.setDateField(params.dateField) |
---|
266 | calendar.parseDate(calendar.dateField.innerHTML || calendar.dateField.value) |
---|
267 | } |
---|
268 | if (params.endDateField) { |
---|
269 | calendar.setEndDateField(params.endDateField) |
---|
270 | calendar.parseDate(calendar.endDateField.innerHTML || calendar.endDateField.value) |
---|
271 | } |
---|
272 | |
---|
273 | if (params.dateField) |
---|
274 | Date.parseDate(calendar.dateField.value || calendar.dateField.innerHTML, calendar.dateFormat) |
---|
275 | if (params.endDateField) |
---|
276 | Date.parseDate(calendar.endDateField.value || calendar.endDateField.innerHTML, calendar.dateFormat) |
---|
277 | |
---|
278 | calendar.showAtElement(triggerElement) |
---|
279 | return calendar |
---|
280 | } |
---|
281 | } |
---|
282 | |
---|
283 | } |
---|
284 | |
---|
285 | |
---|
286 | |
---|
287 | //------------------------------------------------------------------------------ |
---|
288 | // Calendar Instance |
---|
289 | //------------------------------------------------------------------------------ |
---|
290 | |
---|
291 | Calendar.prototype = { |
---|
292 | |
---|
293 | // The HTML Container Element |
---|
294 | container: null, |
---|
295 | |
---|
296 | // Callbacks |
---|
297 | selectHandler: null, |
---|
298 | closeHandler: null, |
---|
299 | |
---|
300 | // Configuration |
---|
301 | minYear: 1900, |
---|
302 | maxYear: 2100, |
---|
303 | dateFormat: '%Y-%m-%d', |
---|
304 | |
---|
305 | // Dates |
---|
306 | date: new Date(), |
---|
307 | currentDateElement: null, |
---|
308 | |
---|
309 | // Status |
---|
310 | shouldClose: false, |
---|
311 | isPopup: true, |
---|
312 | |
---|
313 | dateField: null, |
---|
314 | endDateField: null, |
---|
315 | clickToDateField: true, |
---|
316 | |
---|
317 | //---------------------------------------------------------------------------- |
---|
318 | // Initialize |
---|
319 | //---------------------------------------------------------------------------- |
---|
320 | |
---|
321 | initialize: function(parent) |
---|
322 | { |
---|
323 | if (parent) |
---|
324 | this.create($(parent)) |
---|
325 | else |
---|
326 | this.create() |
---|
327 | }, |
---|
328 | |
---|
329 | |
---|
330 | |
---|
331 | //---------------------------------------------------------------------------- |
---|
332 | // Update / (Re)initialize Calendar |
---|
333 | //---------------------------------------------------------------------------- |
---|
334 | |
---|
335 | update: function(date) |
---|
336 | { |
---|
337 | var calendar = this |
---|
338 | var today = new Date() |
---|
339 | var thisYear = today.getFullYear() |
---|
340 | var thisMonth = today.getMonth() |
---|
341 | var thisDay = today.getDate() |
---|
342 | var month = date.getMonth(); |
---|
343 | var dayOfMonth = date.getDate(); |
---|
344 | |
---|
345 | // Ensure date is within the defined range |
---|
346 | if (date.getFullYear() < this.minYear) |
---|
347 | date.setFullYear(this.minYear) |
---|
348 | else if (date.getFullYear() > this.maxYear) |
---|
349 | date.setFullYear(this.maxYear) |
---|
350 | |
---|
351 | this.date = new Date(date) |
---|
352 | |
---|
353 | // Calculate the first day to display (including the previous month) |
---|
354 | date.setDate(1) |
---|
355 | date.setDate(-(date.getDay()) + 1) |
---|
356 | |
---|
357 | // Fill in the days of the month |
---|
358 | Element.getElementsBySelector(this.container, 'tbody tr').each( |
---|
359 | function(row, i) { |
---|
360 | var rowHasDays = false |
---|
361 | row.immediateDescendants().each( |
---|
362 | function(cell, j) { |
---|
363 | var day = date.getDate() |
---|
364 | var dayOfWeek = date.getDay() |
---|
365 | var isCurrentMonth = (date.getMonth() == month) |
---|
366 | |
---|
367 | // Reset classes on the cell |
---|
368 | cell.className = '' |
---|
369 | cell.date = new Date(date) |
---|
370 | cell.update(day) |
---|
371 | |
---|
372 | // Account for days of the month other than the current month |
---|
373 | if (!isCurrentMonth) |
---|
374 | cell.addClassName('otherDay') |
---|
375 | else |
---|
376 | rowHasDays = true |
---|
377 | |
---|
378 | // Ensure the current day is selected |
---|
379 | if (isCurrentMonth && day == dayOfMonth) { |
---|
380 | cell.addClassName('selected') |
---|
381 | calendar.currentDateElement = cell |
---|
382 | } |
---|
383 | |
---|
384 | // Today |
---|
385 | if (date.getFullYear() == thisYear && date.getMonth() == thisMonth && day == thisDay) |
---|
386 | cell.addClassName('today') |
---|
387 | |
---|
388 | // Weekend |
---|
389 | if ([0, 6].indexOf(dayOfWeek) != -1) |
---|
390 | cell.addClassName('weekend') |
---|
391 | |
---|
392 | // Set the date to tommorrow |
---|
393 | date.setDate(day + 1) |
---|
394 | } |
---|
395 | ) |
---|
396 | // Hide the extra row if it contains only days from another month |
---|
397 | !rowHasDays ? row.hide() : row.show() |
---|
398 | } |
---|
399 | ) |
---|
400 | |
---|
401 | this.container.getElementsBySelector('td.title')[0].update( |
---|
402 | Calendar.MONTH_NAMES[month] + ' ' + this.date.getFullYear() |
---|
403 | ) |
---|
404 | }, |
---|
405 | |
---|
406 | |
---|
407 | |
---|
408 | //---------------------------------------------------------------------------- |
---|
409 | // Create/Draw the Calendar HTML Elements |
---|
410 | //---------------------------------------------------------------------------- |
---|
411 | |
---|
412 | create: function(parent) |
---|
413 | { |
---|
414 | |
---|
415 | // If no parent was specified, assume that we are creating a popup calendar. |
---|
416 | if (!parent) { |
---|
417 | parent = document.getElementsByTagName('body')[0] |
---|
418 | this.isPopup = true |
---|
419 | } else { |
---|
420 | this.isPopup = false |
---|
421 | } |
---|
422 | |
---|
423 | // Calendar Table |
---|
424 | var table = new Element('table') |
---|
425 | |
---|
426 | // Calendar Header |
---|
427 | var thead = new Element('thead') |
---|
428 | table.appendChild(thead) |
---|
429 | |
---|
430 | // Title Placeholder |
---|
431 | var row = new Element('tr') |
---|
432 | var cell = new Element('td', { colSpan: 7 } ) |
---|
433 | cell.addClassName('title') |
---|
434 | row.appendChild(cell) |
---|
435 | thead.appendChild(row) |
---|
436 | |
---|
437 | // Calendar Navigation |
---|
438 | row = new Element('tr') |
---|
439 | this._drawButtonCell(row, '«', 1, Calendar.NAV_PREVIOUS_YEAR) |
---|
440 | this._drawButtonCell(row, '‹', 1, Calendar.NAV_PREVIOUS_MONTH) |
---|
441 | this._drawButtonCell(row, 'Today', 3, Calendar.NAV_TODAY) |
---|
442 | this._drawButtonCell(row, '›', 1, Calendar.NAV_NEXT_MONTH) |
---|
443 | this._drawButtonCell(row, '»', 1, Calendar.NAV_NEXT_YEAR) |
---|
444 | thead.appendChild(row) |
---|
445 | |
---|
446 | // Day Names |
---|
447 | row = new Element('tr') |
---|
448 | for (var i = 0; i < 7; ++i) { |
---|
449 | cell = new Element('th').update(Calendar.SHORT_DAY_NAMES[i]) |
---|
450 | if (i == 0 || i == 6) |
---|
451 | cell.addClassName('weekend') |
---|
452 | row.appendChild(cell) |
---|
453 | } |
---|
454 | thead.appendChild(row) |
---|
455 | |
---|
456 | // Calendar Days |
---|
457 | var tbody = table.appendChild(new Element('tbody')) |
---|
458 | for (i = 6; i > 0; --i) { |
---|
459 | row = tbody.appendChild(new Element('tr')) |
---|
460 | row.addClassName('days') |
---|
461 | for (var j = 7; j > 0; --j) { |
---|
462 | cell = row.appendChild(new Element('td')) |
---|
463 | cell.calendar = this |
---|
464 | } |
---|
465 | } |
---|
466 | |
---|
467 | // Calendar Container (div) |
---|
468 | this.container = new Element('div') |
---|
469 | this.container.addClassName('calendar') |
---|
470 | if (this.isPopup) { |
---|
471 | this.container.setStyle({ position: 'absolute', display: 'none' }) |
---|
472 | this.container.addClassName('popup') |
---|
473 | } |
---|
474 | this.container.appendChild(table) |
---|
475 | |
---|
476 | // Initialize Calendar |
---|
477 | this.update(this.date) |
---|
478 | |
---|
479 | // Observe the container for mousedown events |
---|
480 | Event.observe(this.container, 'mousedown', Calendar.handleMouseDownEvent) |
---|
481 | |
---|
482 | // Append to parent element |
---|
483 | parent.appendChild(this.container) |
---|
484 | |
---|
485 | }, |
---|
486 | |
---|
487 | _drawButtonCell: function(parent, text, colSpan, navAction) |
---|
488 | { |
---|
489 | var cell = new Element('td') |
---|
490 | if (colSpan > 1) cell.colSpan = colSpan |
---|
491 | cell.className = 'button' |
---|
492 | cell.calendar = this |
---|
493 | cell.navAction = navAction |
---|
494 | cell.innerHTML = text |
---|
495 | cell.unselectable = 'on' // IE |
---|
496 | parent.appendChild(cell) |
---|
497 | return cell |
---|
498 | }, |
---|
499 | |
---|
500 | |
---|
501 | |
---|
502 | //------------------------------------------------------------------------------ |
---|
503 | // Callbacks |
---|
504 | //------------------------------------------------------------------------------ |
---|
505 | |
---|
506 | // Calls the Select Handler (if defined) |
---|
507 | callSelectHandler: function() |
---|
508 | { |
---|
509 | if (this.selectHandler) |
---|
510 | this.selectHandler(this, this.date.print(this.dateFormat)) |
---|
511 | }, |
---|
512 | |
---|
513 | // Calls the Close Handler (if defined) |
---|
514 | callCloseHandler: function() |
---|
515 | { |
---|
516 | if (this.closeHandler) |
---|
517 | this.closeHandler(this) |
---|
518 | }, |
---|
519 | |
---|
520 | |
---|
521 | |
---|
522 | //------------------------------------------------------------------------------ |
---|
523 | // Calendar Display Functions |
---|
524 | //------------------------------------------------------------------------------ |
---|
525 | |
---|
526 | // Shows the Calendar |
---|
527 | show: function() |
---|
528 | { |
---|
529 | this.container.show() |
---|
530 | if (this.isPopup) { |
---|
531 | window._popupCalendar = this |
---|
532 | Event.observe(document, 'mousedown', Calendar._checkCalendar) |
---|
533 | } |
---|
534 | }, |
---|
535 | |
---|
536 | // Shows the calendar at the given absolute position |
---|
537 | showAt: function (x, y) |
---|
538 | { |
---|
539 | this.container.setStyle({ left: x + 'px', top: y + 'px' }) |
---|
540 | this.show() |
---|
541 | }, |
---|
542 | |
---|
543 | // Shows the Calendar at the coordinates of the provided element |
---|
544 | showAtElement: function(element) |
---|
545 | { |
---|
546 | var pos = Position.cumulativeOffset(element) |
---|
547 | this.showAt(pos[0], pos[1]) |
---|
548 | }, |
---|
549 | |
---|
550 | // Hides the Calendar |
---|
551 | hide: function() |
---|
552 | { |
---|
553 | if (this.isPopup) |
---|
554 | Event.stopObserving(document, 'mousedown', Calendar._checkCalendar) |
---|
555 | this.container.hide() |
---|
556 | }, |
---|
557 | |
---|
558 | |
---|
559 | |
---|
560 | //------------------------------------------------------------------------------ |
---|
561 | // Miscellaneous |
---|
562 | //------------------------------------------------------------------------------ |
---|
563 | |
---|
564 | // Tries to identify the date represented in a string. If successful it also |
---|
565 | // calls this.setDate which moves the calendar to the given date. |
---|
566 | parseDate: function(str, format) |
---|
567 | { |
---|
568 | if (!format) |
---|
569 | format = this.dateFormat |
---|
570 | this.setDate(Date.parseDate(str, format)) |
---|
571 | }, |
---|
572 | |
---|
573 | |
---|
574 | |
---|
575 | //------------------------------------------------------------------------------ |
---|
576 | // Getters/Setters |
---|
577 | //------------------------------------------------------------------------------ |
---|
578 | |
---|
579 | setSelectHandler: function(selectHandler) |
---|
580 | { |
---|
581 | this.selectHandler = selectHandler |
---|
582 | }, |
---|
583 | |
---|
584 | setCloseHandler: function(closeHandler) |
---|
585 | { |
---|
586 | this.closeHandler = closeHandler |
---|
587 | }, |
---|
588 | |
---|
589 | setDate: function(date) |
---|
590 | { |
---|
591 | if (!date.equalsTo(this.date)) |
---|
592 | this.update(date) |
---|
593 | }, |
---|
594 | |
---|
595 | setDateFormat: function(format) |
---|
596 | { |
---|
597 | this.dateFormat = format |
---|
598 | }, |
---|
599 | |
---|
600 | setDateField: function(field) |
---|
601 | { |
---|
602 | this.dateField = $(field) |
---|
603 | }, |
---|
604 | |
---|
605 | setEndDateField: function(endField) |
---|
606 | { |
---|
607 | this.endDateField = $(endField) |
---|
608 | }, |
---|
609 | |
---|
610 | setRange: function(minYear, maxYear) |
---|
611 | { |
---|
612 | this.minYear = minYear |
---|
613 | this.maxYear = maxYear |
---|
614 | }, |
---|
615 | |
---|
616 | setClickToDateField: function(isClickToDateField) |
---|
617 | { |
---|
618 | this.clickToDateField = isClickToDateField; |
---|
619 | } |
---|
620 | } |
---|
621 | |
---|
622 | // global object that remembers the calendar |
---|
623 | window._popupCalendar = null |
---|
624 | |
---|
625 | |
---|
626 | |
---|
627 | |
---|
628 | |
---|
629 | |
---|
630 | |
---|
631 | |
---|
632 | |
---|
633 | |
---|
634 | |
---|
635 | |
---|
636 | |
---|
637 | |
---|
638 | |
---|
639 | |
---|
640 | |
---|
641 | |
---|
642 | |
---|
643 | |
---|
644 | |
---|
645 | |
---|
646 | |
---|
647 | |
---|
648 | |
---|
649 | |
---|
650 | |
---|
651 | |
---|
652 | |
---|
653 | //============================================================================== |
---|
654 | // |
---|
655 | // Date Object Patches |
---|
656 | // |
---|
657 | // This is pretty much untouched from the original. I really would like to get |
---|
658 | // rid of these patches if at all possible and find a cleaner way of |
---|
659 | // accomplishing the same things. It's a shame Prototype doesn't extend Date at |
---|
660 | // all. |
---|
661 | // |
---|
662 | //============================================================================== |
---|
663 | |
---|
664 | Date.DAYS_IN_MONTH = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) |
---|
665 | Date.SECOND = 1000 /* milliseconds */ |
---|
666 | Date.MINUTE = 60 * Date.SECOND |
---|
667 | Date.HOUR = 60 * Date.MINUTE |
---|
668 | Date.DAY = 24 * Date.HOUR |
---|
669 | Date.WEEK = 7 * Date.DAY |
---|
670 | |
---|
671 | // Parses Date |
---|
672 | Date.parseDate = function(str, fmt) { |
---|
673 | var today = new Date(); |
---|
674 | var y = 0; |
---|
675 | var m = -1; |
---|
676 | var d = 0; |
---|
677 | var a = str.split(/\W+/); |
---|
678 | var b = fmt.match(/%./g); |
---|
679 | var i = 0, j = 0; |
---|
680 | var hr = 0; |
---|
681 | var min = 0; |
---|
682 | |
---|
683 | for (i = 0; i < a.length; ++i) { |
---|
684 | if (!a[i]) continue; |
---|
685 | switch (b[i]) { |
---|
686 | case "%d": |
---|
687 | case "%e": |
---|
688 | d = parseInt(a[i], 10); |
---|
689 | break; |
---|
690 | case "%m": |
---|
691 | m = parseInt(a[i], 10) - 1; |
---|
692 | break; |
---|
693 | case "%Y": |
---|
694 | case "%y": |
---|
695 | y = parseInt(a[i], 10); |
---|
696 | (y < 100) && (y += (y > 29) ? 1900 : 2000); |
---|
697 | break; |
---|
698 | case "%b": |
---|
699 | case "%B": |
---|
700 | for (j = 0; j < 12; ++j) { |
---|
701 | if (Calendar.MONTH_NAMES[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { |
---|
702 | m = j; |
---|
703 | break; |
---|
704 | } |
---|
705 | } |
---|
706 | break; |
---|
707 | case "%H": |
---|
708 | case "%I": |
---|
709 | case "%k": |
---|
710 | case "%l": |
---|
711 | hr = parseInt(a[i], 10); |
---|
712 | break; |
---|
713 | case "%P": |
---|
714 | case "%p": |
---|
715 | if (/pm/i.test(a[i]) && hr < 12) |
---|
716 | hr += 12; |
---|
717 | else if (/am/i.test(a[i]) && hr >= 12) |
---|
718 | hr -= 12; |
---|
719 | break; |
---|
720 | case "%M": |
---|
721 | min = parseInt(a[i], 10); |
---|
722 | break; |
---|
723 | } |
---|
724 | } |
---|
725 | if (isNaN(y)) y = today.getFullYear(); |
---|
726 | if (isNaN(m)) m = today.getMonth(); |
---|
727 | if (isNaN(d)) d = today.getDate(); |
---|
728 | if (isNaN(hr)) hr = today.getHours(); |
---|
729 | if (isNaN(min)) min = today.getMinutes(); |
---|
730 | if (y != 0 && m != -1 && d != 0) |
---|
731 | return new Date(y, m, d, hr, min, 0); |
---|
732 | y = 0; m = -1; d = 0; |
---|
733 | for (i = 0; i < a.length; ++i) { |
---|
734 | if (a[i].search(/[a-zA-Z]+/) != -1) { |
---|
735 | var t = -1; |
---|
736 | for (j = 0; j < 12; ++j) { |
---|
737 | if (Calendar.MONTH_NAMES[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; } |
---|
738 | } |
---|
739 | if (t != -1) { |
---|
740 | if (m != -1) { |
---|
741 | d = m+1; |
---|
742 | } |
---|
743 | m = t; |
---|
744 | } |
---|
745 | } else if (parseInt(a[i], 10) <= 12 && m == -1) { |
---|
746 | m = a[i]-1; |
---|
747 | } else if (parseInt(a[i], 10) > 31 && y == 0) { |
---|
748 | y = parseInt(a[i], 10); |
---|
749 | (y < 100) && (y += (y > 29) ? 1900 : 2000); |
---|
750 | } else if (d == 0) { |
---|
751 | d = a[i]; |
---|
752 | } |
---|
753 | } |
---|
754 | if (y == 0) |
---|
755 | y = today.getFullYear(); |
---|
756 | if (m != -1 && d != 0) |
---|
757 | return new Date(y, m, d, hr, min, 0); |
---|
758 | return today; |
---|
759 | }; |
---|
760 | |
---|
761 | // Returns the number of days in the current month |
---|
762 | Date.prototype.getMonthDays = function(month) { |
---|
763 | var year = this.getFullYear() |
---|
764 | if (typeof month == "undefined") |
---|
765 | month = this.getMonth() |
---|
766 | if (((0 == (year % 4)) && ( (0 != (year % 100)) || (0 == (year % 400)))) && month == 1) |
---|
767 | return 29 |
---|
768 | else |
---|
769 | return Date.DAYS_IN_MONTH[month] |
---|
770 | }; |
---|
771 | |
---|
772 | // Returns the number of day in the year |
---|
773 | Date.prototype.getDayOfYear = function() { |
---|
774 | var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); |
---|
775 | var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0); |
---|
776 | var time = now - then; |
---|
777 | return Math.floor(time / Date.DAY); |
---|
778 | }; |
---|
779 | |
---|
780 | /** Returns the number of the week in year, as defined in ISO 8601. */ |
---|
781 | Date.prototype.getWeekNumber = function() { |
---|
782 | var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); |
---|
783 | var DoW = d.getDay(); |
---|
784 | d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu |
---|
785 | var ms = d.valueOf(); // GMT |
---|
786 | d.setMonth(0); |
---|
787 | d.setDate(4); // Thu in Week 1 |
---|
788 | return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1; |
---|
789 | }; |
---|
790 | |
---|
791 | /** Checks date and time equality */ |
---|
792 | Date.prototype.equalsTo = function(date) { |
---|
793 | return ((this.getFullYear() == date.getFullYear()) && |
---|
794 | (this.getMonth() == date.getMonth()) && |
---|
795 | (this.getDate() == date.getDate()) && |
---|
796 | (this.getHours() == date.getHours()) && |
---|
797 | (this.getMinutes() == date.getMinutes())); |
---|
798 | }; |
---|
799 | |
---|
800 | /** Set only the year, month, date parts (keep existing time) */ |
---|
801 | Date.prototype.setDateOnly = function(date) { |
---|
802 | var tmp = new Date(date); |
---|
803 | this.setDate(1); |
---|
804 | this.setFullYear(tmp.getFullYear()); |
---|
805 | this.setMonth(tmp.getMonth()); |
---|
806 | this.setDate(tmp.getDate()); |
---|
807 | }; |
---|
808 | |
---|
809 | /** Prints the date in a string according to the given format. */ |
---|
810 | Date.prototype.print = function (str) { |
---|
811 | var m = this.getMonth(); |
---|
812 | var d = this.getDate(); |
---|
813 | var y = this.getFullYear(); |
---|
814 | var wn = this.getWeekNumber(); |
---|
815 | var w = this.getDay(); |
---|
816 | var s = {}; |
---|
817 | var hr = this.getHours(); |
---|
818 | var pm = (hr >= 12); |
---|
819 | var ir = (pm) ? (hr - 12) : hr; |
---|
820 | var dy = this.getDayOfYear(); |
---|
821 | if (ir == 0) |
---|
822 | ir = 12; |
---|
823 | var min = this.getMinutes(); |
---|
824 | var sec = this.getSeconds(); |
---|
825 | s["%a"] = Calendar.SHORT_DAY_NAMES[w]; // abbreviated weekday name [FIXME: I18N] |
---|
826 | s["%A"] = Calendar.DAY_NAMES[w]; // full weekday name |
---|
827 | s["%b"] = Calendar.SHORT_MONTH_NAMES[m]; // abbreviated month name [FIXME: I18N] |
---|
828 | s["%B"] = Calendar.MONTH_NAMES[m]; // full month name |
---|
829 | // FIXME: %c : preferred date and time representation for the current locale |
---|
830 | s["%C"] = 1 + Math.floor(y / 100); // the century number |
---|
831 | s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31) |
---|
832 | s["%e"] = d; // the day of the month (range 1 to 31) |
---|
833 | // FIXME: %D : american date style: %m/%d/%y |
---|
834 | // FIXME: %E, %F, %G, %g, %h (man strftime) |
---|
835 | s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format) |
---|
836 | s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format) |
---|
837 | s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366) |
---|
838 | s["%k"] = hr; // hour, range 0 to 23 (24h format) |
---|
839 | s["%l"] = ir; // hour, range 1 to 12 (12h format) |
---|
840 | s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12 |
---|
841 | s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59 |
---|
842 | s["%n"] = "\n"; // a newline character |
---|
843 | s["%p"] = pm ? "PM" : "AM"; |
---|
844 | s["%P"] = pm ? "pm" : "am"; |
---|
845 | // FIXME: %r : the time in am/pm notation %I:%M:%S %p |
---|
846 | // FIXME: %R : the time in 24-hour notation %H:%M |
---|
847 | s["%s"] = Math.floor(this.getTime() / 1000); |
---|
848 | s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59 |
---|
849 | s["%t"] = "\t"; // a tab character |
---|
850 | // FIXME: %T : the time in 24-hour notation (%H:%M:%S) |
---|
851 | s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn; |
---|
852 | s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON) |
---|
853 | s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN) |
---|
854 | // FIXME: %x : preferred date representation for the current locale without the time |
---|
855 | // FIXME: %X : preferred time representation for the current locale without the date |
---|
856 | s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99) |
---|
857 | s["%Y"] = y; // year with the century |
---|
858 | s["%%"] = "%"; // a literal '%' character |
---|
859 | |
---|
860 | return str.gsub(/%./, function(match) { return s[match] || match }); |
---|
861 | }; |
---|
862 | |
---|
863 | Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear; |
---|
864 | Date.prototype.setFullYear = function(y) { |
---|
865 | var d = new Date(this); |
---|
866 | d.__msh_oldSetFullYear(y); |
---|
867 | if (d.getMonth() != this.getMonth()) |
---|
868 | this.setDate(28); |
---|
869 | this.__msh_oldSetFullYear(y); |
---|
870 | } |
---|