liquid_feedback_frontend

view static/gregor.js/gregor.js @ 1304:0bae319805ff

Added tag v3.2.1 for changeset 0160d9a01d0a
author jbe
date Fri May 06 09:44:16 2016 +0200 (2016-05-06)
parents 77260f05fd4b
children
line source
1 //
2 // Copyright (c) 2009 Public Software Group e. V., Berlin
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
24 //
25 // All date calculations are based on the gregorian calender, also for
26 // dates before 1582 (before the gegorian calendar was introduced).
27 // The supported range is from January 1st 0001 up to December 31st 9999.
28 //
29 // gregor_daycount({year: <year>, month: <month>, day: <day>}) returns
30 // the number of days between the given date and January 1st 0001 (greg.).
31 //
32 // gregor_completeDate({year: <year>, month: <month>, day: <day>}) returns
33 // a structure (an object) with the following properties:
34 // - daycount (days since January 1st 0001, see gregor_daycount)
35 // - year (with century)
36 // - month (from 1 to 12)
37 // - day (from 1 to 28, 29, 30 or 31)
38 // - iso_string (string with format YYYY-MM-DD)
39 // - us_weekday (from 0 for Sunday to 6 for Monday)
40 // - iso_weekday (from 0 for Monday to 6 for Sunday)
41 // - iso_weekyear (Year containing greater part of week st. w. Monday)
42 // - iso_week (from 1 to 52 or 53)
43 // - us_week (from 1 to 53 or 54)
44 //
45 // The structure (the object) passed as parameter to gregor_daycount or
46 // gregor_completeDate may describe a date in the following ways:
47 // - daycount
48 // - year, month, day
49 // - year, us_week, us_weekday
50 // - year, iso_week, iso_weekday
51 // - iso_weekyear, iso_week, iso_weekday
52 //
53 // gregor_sheet({...}) returns a calendar sheet as DOM object. The
54 // structure (the object) passed to the function as an argument is altered
55 // by the function and used to store state variables. Initially it can
56 // contain the following fields:
57 // - year (year to show, defaults to todays year)
58 // - month (month to show, defaults to todays month)
59 // - today (structure describing a day, e.g. year, month, day)
60 // - selected (structure describing a day, e.g. year, month, day)
61 // - navigation ("enabled", "disabled", "hidden", default "enabled")
62 // - week_mode ("iso" or "us", defaults to "iso")
63 // - week_numbers ("left", "right" or null, defaults to null)
64 // - month_names (e.g. ["January", "Feburary", ...])
65 // - weekday_names (e.g. ["Mon", "Tue", ...] or ["Sun", "Mon", ...])
66 // - day_callback (function to render a cell)
67 // - select_callback (function to be called, when a date was selected)
68 // - element (for internal use only)
69 // If "today" is undefined, it is automatically intitialized with the
70 // current clients date. If "selected" is undefined or null, no date is
71 // be initially selected. It is mandatory to provide month_names and
72 // weekday_names.
73 //
74 // gregor_addGui({...}) alters a referenced input field in a way that
75 // focussing on it causes a calendar being shown at its right side, where a
76 // date can be selected. The structure (the object) passed to this function
77 // is only evaluated once, and never modified. All options except "element"
78 // of the gregor_sheet({...}) function may be used as options to
79 // gregor_addGui({...}) as well. In addition an "element_id" must be
80 // provided as argument, containing the id of a text input field. The
81 // behaviour caused by the options "selected" and "select_callback" are
82 // slightly different: If "selected" === undefined, then the current value
83 // of the text field referenced by "element_id" will be parsed and used as
84 // date selection. If "selected" === null, then no date will be initially
85 // selected, and the text field will be cleared. The "select_callback"
86 // function is always called once with the pre-selected date (or with null,
87 // if no date is initially selected). Whenever the selected date is changed
88 // or unselected later, the callback function is called again with the new
89 // date (or with null, in case of deselection). When the relaxed argument is set
90 // the calendar will not normalize the parsed date. Thats usefull if you wan't to
91 // allow relaxed input.
92 //
93 // EXAMPLE:
94 //
95 // <input type="text" id="test_field" name="test_field" value=""/>
96 // <script type="text/javascript">
97 // gregor_addGui({
98 // element_id: 'test_field',
99 // month_names: [
100 // "January", "February", "March", "April", "May", "June",
101 // "July", "August", "September", "October", "November", "December"
102 // ],
103 // weekday_names: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
104 // week_numbers: "left"
105 // });
106 // </script>
107 //
112 // Internal constants and helper functions for date calculation
115 gregor_c1 = 365; // days of a non-leap year
116 gregor_c4 = 4 * gregor_c1 + 1; // days of a full 4 year cycle
117 gregor_c100 = 25 * gregor_c4 - 1; // days of a full 100 year cycle
118 gregor_c400 = 4 * gregor_c100 + 1; // days of a full 400 year cycle
120 gregor_normalMonthOffsets = [
121 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
122 ];
124 function gregor_getMonthOffset(year, month) {
125 if (
126 (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) &&
127 month > 2
128 ) return gregor_normalMonthOffsets[month-1] + 1;
129 else return gregor_normalMonthOffsets[month-1];
130 }
132 function gregor_formatInteger(int, digits) {
133 var result = int.toFixed();
134 if (digits != null) {
135 while (result.length < digits) result = "0" + result;
136 }
137 return result;
138 }
142 // Calculate days since January 1st 0001 (Gegorian) for a given date
145 function gregor_daycount(obj) {
146 if (
147 obj.daycount >= 0 && obj.daycount <= 3652058 && obj.daycount % 1 == 0
148 ) {
149 return obj.daycount;
150 } else if (
151 obj.year >= 1 && obj.year <= 9999 && obj.year % 1 == 0 &&
152 obj.month >= 1 && obj.month <= 12 && obj.month % 1 == 0 &&
153 obj.day >= 0 && obj.day <= 31 && obj.day % 1 == 0
154 ) {
155 var n400 = Math.floor((obj.year-1) / 400);
156 var r400 = (obj.year-1) % 400;
157 var n100 = Math.floor(r400 / 100);
158 var r100 = r400 % 100;
159 var n4 = Math.floor(r100 / 4);
160 var n1 = r100 % 4;
161 return (
162 gregor_c400 * n400 +
163 gregor_c100 * n100 +
164 gregor_c4 * n4 +
165 gregor_c1 * n1 +
166 gregor_getMonthOffset(obj.year, obj.month) + (obj.day - 1)
167 );
168 } else if (
169 (
170 (
171 obj.year >= 1 && obj.year <= 9999 &&
172 obj.year % 1 == 0 && obj.iso_weekyear == null
173 ) || (
174 obj.iso_weekyear >= 1 && obj.iso_weekyear <= 9999 &&
175 obj.iso_weekyear % 1 == 0 && obj.year == null
176 )
177 ) &&
178 obj.iso_week >= 0 && obj.iso_week <= 53 && obj.iso_week % 1 == 0 &&
179 obj.iso_weekday >= 0 && obj.iso_weekday <= 6 &&
180 obj.iso_weekday % 1 == 0
181 ) {
182 var jan4th = gregor_daycount({
183 year: (obj.year != null) ? obj.year : obj.iso_weekyear,
184 month: 1,
185 day: 4
186 });
187 var monday0 = jan4th - (jan4th % 7) - 7; // monday of week 0
188 return monday0 + 7 * obj.iso_week + obj.iso_weekday;
189 } else if (
190 obj.year >= 1 && obj.year <= 9999 && obj.year % 1 == 0 &&
191 obj.us_week >= 0 && obj.us_week <= 54 && obj.us_week % 1 == 0 &&
192 obj.us_weekday >= 0 && obj.us_weekday <= 6 && obj.us_weekday % 1 == 0
193 ) {
194 var jan1st = gregor_daycount({
195 year: obj.year,
196 month: 1,
197 day: 1
198 });
199 var sunday0 = jan1st - ((jan1st+1) % 7) - 7; // sunday of week 0
200 return sunday0 + 7 * obj.us_week + obj.us_weekday;
201 }
202 }
206 // Calculate all calendar related numbers for a given date
209 function gregor_completeDate(obj) {
210 if (
211 obj.daycount >= 0 && obj.daycount <= 3652058 && obj.daycount % 1 == 0
212 ) {
213 var daycount = obj.daycount;
214 var n400 = Math.floor(daycount / gregor_c400);
215 var r400 = daycount % gregor_c400;
216 var n100 = Math.floor(r400 / gregor_c100);
217 var r100 = r400 % gregor_c100;
218 if (n100 == 4) { n100 = 3; r100 = gregor_c100; }
219 var n4 = Math.floor(r100 / gregor_c4);
220 var r4 = r100 % gregor_c4;
221 var n1 = Math.floor(r4 / gregor_c1);
222 var r1 = r4 % gregor_c1;
223 if (n1 == 4) { n1 = 3; r1 = gregor_c1; }
224 var year = 1 + 400 * n400 + 100 * n100 + 4 * n4 + n1;
225 var month = 1 + Math.floor(r1 / 31);
226 var monthOffset = gregor_getMonthOffset(year, month);
227 if (month < 12) {
228 var nextMonthOffset = gregor_getMonthOffset(year, month + 1);
229 if (r1 >= nextMonthOffset) {
230 month++;
231 monthOffset = nextMonthOffset;
232 }
233 }
234 var day = 1 + r1 - monthOffset;
235 var iso_string = ("" +
236 gregor_formatInteger(year, 4) + "-" +
237 gregor_formatInteger(month, 2) + "-" +
238 gregor_formatInteger(day, 2)
239 );
240 var us_weekday = (daycount+1) % 7;
241 var iso_weekday = daycount % 7;
242 var iso_weekyear = year;
243 if (
244 month == 1 && (
245 (day == 3 && iso_weekday == 6) ||
246 (day == 2 && iso_weekday >= 5) ||
247 (day == 1 && iso_weekday >= 4)
248 )
249 ) iso_weekyear--;
250 else if (
251 month == 12 && (
252 (day == 29 && iso_weekday == 0) ||
253 (day == 30 && iso_weekday <= 1) ||
254 (day == 31 && iso_weekday <= 2)
255 )
256 ) iso_weekyear++;
257 var jan4th = gregor_daycount({year: iso_weekyear, month: 1, day: 4});
258 var monday0 = jan4th - (jan4th % 7) - 7; // monday of week 0
259 var iso_week = Math.floor((daycount - monday0) / 7);
260 var jan1st = gregor_daycount({year: year, month: 1, day: 1});
261 var sunday0 = jan1st - ((jan1st+1) % 7) - 7; // sunday of week 0
262 var us_week = Math.floor((daycount - sunday0) / 7);
263 return {
264 daycount: daycount,
265 year: year,
266 month: month,
267 day: day,
268 iso_string: iso_string,
269 us_weekday: (daycount+1) % 7, // 0 = Sunday
270 iso_weekday: daycount % 7, // 0 = Monday
271 iso_weekyear: iso_weekyear,
272 iso_week: iso_week,
273 us_week: us_week
274 };
275 } else if (obj.daycount == null) {
276 var daycount = gregor_daycount(obj);
277 if (daycount != null) {
278 return gregor_completeDate({daycount: gregor_daycount(obj)});
279 }
280 }
281 }
285 // Test gregor_daycount and gregor_completeDate for consistency
286 // (Debugging only)
289 function gregor_test() {
290 for (i=650000; i<900000; i++) {
291 var obj = gregor_completeDate({daycount: i});
292 var j;
293 j = gregor_daycount({
294 year: obj.year,
295 month: obj.month,
296 day: obj.day
297 });
298 if (i != j) { alert("ERROR"); return; }
299 j = gregor_daycount({
300 iso_weekyear: obj.iso_weekyear,
301 iso_week: obj.iso_week,
302 iso_weekday: obj.iso_weekday
303 });
304 if (i != j) { alert("ERROR"); return; }
305 j = gregor_daycount({
306 year: obj.year,
307 iso_week:
308 (obj.iso_weekyear == obj.year + 1) ? 53 :
309 (obj.iso_weekyear == obj.year - 1) ? 0 :
310 obj.iso_week,
311 iso_weekday: obj.iso_weekday
312 });
313 if (i != j) { alert("ERROR"); return; }
314 j = gregor_daycount({
315 year: obj.year,
316 us_week: obj.us_week,
317 us_weekday: obj.us_weekday
318 });
319 if (i != j) { alert("ERROR"); return; }
320 }
321 alert("SUCCESS");
322 }
326 // Graphical calendar
329 gregor_iso_weekday_css = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
331 function gregor_sheet(args) {
333 // setting args.today and args.selected
334 if (args.today === undefined) {
335 var js_date = new Date();
336 args.today = gregor_completeDate({
337 year: js_date.getFullYear(),
338 month: js_date.getMonth() + 1,
339 day: js_date.getDate()
340 });
341 } else if (args.today != null) {
342 args.today = gregor_completeDate(args.today);
343 }
344 if (args.selected == "today") {
345 args.selected = args.today;
346 } else if (args.selected != null) {
347 args.selected = gregor_completeDate(args.selected);
348 }
350 // setting args.year and args.month
351 if (args.year == null) {
352 if (args.selected != null) args.year = args.selected.year;
353 else args.year = args.today.year;
354 }
355 if (args.month == null) {
356 if (args.selected != null) args.month = args.selected.month;
357 else args.month = args.today.month;
358 }
360 // setting first_day
361 var first_day = gregor_completeDate({
362 year: args.year,
363 month: args.month,
364 day: 1
365 });
366 if (first_day == null) return;
368 // checking args.navigation, args.week_mode and args.week_numbers
369 if (args.navigation == null) args.navigation = "enabled";
370 else if (
371 args.navigation != "enabled" &&
372 args.navigation != "disabled" &&
373 args.navigation != "hidden"
374 ) return;
375 if (args.week_mode == null) args.week_mode = "iso";
376 else if (args.week_mode != "iso" && args.week_mode != "us") return;
377 if (
378 args.week_numbers != null &&
379 args.week_numbers != "left" &&
380 args.week_numbers != "right"
381 ) return;
383 // checking args.month.names and args.weekday_names
384 if (args.month_names.length != 12) return;
385 if (args.weekday_names.length != 7) return;
387 // calculating number of days in month
388 var count;
389 if (args.year < 9999 || args.month < 12) {
390 count = gregor_daycount({
391 year: (args.month == 12) ? (args.year + 1) : args.year,
392 month: (args.month == 12) ? 1 : (args.month + 1),
393 day: 1
394 }) - first_day.daycount;
395 } else {
396 // workaround for year 9999
397 count = 31;
398 }
400 // locale variables for UI construction
401 var table, row, cell, element;
403 // take table element from args.element,
404 // if neccessary create and store it
405 if (args.element == null) {
406 table = document.createElement("table");
407 args.element = table;
408 } else {
409 table = args.element;
410 while (table.firstChild) table.removeChild(table.firstChild);
411 }
413 // set CSS class of table according to args.week_numbers
414 if (args.week_numbers == "left") {
415 table.className = "gregor_sheet gregor_weeks_left";
416 } else if (args.week_numbers == "right") {
417 table.className = "gregor_sheet gregor_weeks_right";
418 } else {
419 table.className = "gregor_sheet gregor_weeks_none";
420 }
422 // begin of table head
423 var thead = document.createElement("thead");
425 // navigation
426 if (args.navigation != "hidden") {
428 // UI head row containing the year
429 row = document.createElement("tr");
430 row.className = "gregor_year_row";
431 cell = document.createElement("th");
432 cell.className = "gregor_year";
433 cell.colSpan = args.week_numbers ? 8 : 7;
434 if (args.navigation == "enabled") {
435 element = document.createElement("a");
436 element.className = "gregor_turn gregor_turn_left";
437 element.style.cssFloat = "left";
438 element.style.styleFloat = "left";
439 element.href = "#";
440 element.onclick = function() {
441 if (args.year > 1) args.year--;
442 gregor_sheet(args);
443 return false;
444 }
445 element.ondblclick = element.onclick;
446 element.appendChild(document.createTextNode("<<"));
447 cell.appendChild(element);
448 element = document.createElement("a");
449 element.className = "gregor_turn gregor_turn_right";
450 element.style.cssFloat = "right";
451 element.style.styleFloat = "right";
452 element.href = "#";
453 element.onclick = function() {
454 if (args.year < 9999) args.year++;
455 gregor_sheet(args);
456 return false;
457 }
458 element.ondblclick = element.onclick;
459 element.appendChild(document.createTextNode(">>"));
460 cell.appendChild(element);
461 }
462 cell.appendChild(document.createTextNode(first_day.year));
463 row.appendChild(cell);
464 thead.appendChild(row);
466 // UI head row containing the month
467 row = document.createElement("tr");
468 row.className = "gregor_month_row";
469 cell = document.createElement("th");
470 cell.className = "gregor_month";
471 cell.colSpan = args.week_numbers ? 8 : 7;
472 if (args.navigation == "enabled") {
473 element = document.createElement("a");
474 element.className = "gregor_turn gregor_turn_left";
475 element.style.cssFloat = "left";
476 element.style.styleFloat = "left";
477 element.href = "#";
478 element.onclick = function() {
479 if (args.year > 1 || args.month > 1) {
480 args.month--;
481 if (args.month < 1) {
482 args.month = 12;
483 args.year--;
484 }
485 }
486 gregor_sheet(args);
487 return false;
488 }
489 element.ondblclick = element.onclick;
490 element.appendChild(document.createTextNode("<<"));
491 cell.appendChild(element);
492 element = document.createElement("a");
493 element.className = "gregor_turn gregor_turn_right";
494 element.style.cssFloat = "right";
495 element.style.styleFloat = "right";
496 element.href = "#";
497 element.onclick = function() {
498 if (args.year < 9999 || args.month < 12) {
499 args.month++;
500 if (args.month > 12) {
501 args.month = 1;
502 args.year++;
503 }
504 }
505 gregor_sheet(args);
506 return false;
507 }
508 element.ondblclick = element.onclick;
509 element.appendChild(document.createTextNode(">>"));
510 cell.appendChild(element);
511 }
512 cell.appendChild(document.createTextNode(
513 args.month_names[first_day.month-1]
514 ));
515 row.appendChild(cell);
516 thead.appendChild(row);
518 // end of navigation
519 }
521 // UI weekday row
522 row = document.createElement("tr");
523 row.className = "gregor_weekday_row";
524 if (args.week_numbers == "left") {
525 cell = document.createElement("th");
526 cell.className = "gregor_corner";
527 row.appendChild(cell);
528 }
529 for (var i=0; i<7; i++) {
530 cell = document.createElement("th");
531 cell.className = (
532 "gregor_weekday gregor_" +
533 gregor_iso_weekday_css[(args.week_mode == "us") ? ((i+6)%7) : i]
534 );
535 cell.appendChild(document.createTextNode(args.weekday_names[i]));
536 row.appendChild(cell);
537 }
538 if (args.week_numbers == "right") {
539 cell = document.createElement("th");
540 cell.className = "gregor_corner";
541 row.appendChild(cell);
542 }
543 thead.appendChild(row);
545 // end of table head and begin of table body
546 table.appendChild(thead);
547 var tbody = document.createElement("tbody");
549 // definition of insert_week function
550 var week = (
551 (args.week_mode == "us") ? first_day.us_week : first_day.iso_week
552 );
553 insert_week = function() {
554 cell = document.createElement("th");
555 cell.className = "gregor_week";
556 cell.appendChild(document.createTextNode(
557 (week < 10) ? ("0" + week) : week)
558 );
559 week++;
560 if (
561 args.week_mode == "iso" && (
562 (
563 args.month == 1 && week > 52
564 ) || (
565 args.month == 12 && week == 53 && (
566 first_day.iso_weekday == 0 ||
567 first_day.iso_weekday == 5 ||
568 first_day.iso_weekday == 6
569 )
570 )
571 )
572 ) week = 1;
573 row.appendChild(cell);
574 }
576 // output data fields
577 row = document.createElement("tr");
578 if (args.week_numbers == "left") insert_week();
579 var filler_count = (
580 (args.week_mode == "us") ? first_day.us_weekday : first_day.iso_weekday
581 );
582 for (var i=0; i<filler_count; i++) {
583 cell = document.createElement("td");
584 cell.className = "gregor_empty";
585 row.appendChild(cell);
586 }
587 for (var i=0; i<count; i++) {
588 var date = gregor_completeDate({daycount: first_day.daycount + i});
589 if (row == null) {
590 row = document.createElement("tr");
591 if (args.week_numbers == "left") insert_week();
592 }
593 cell = document.createElement("td");
594 var cssClass = (
595 "gregor_day gregor_" + gregor_iso_weekday_css[date.iso_weekday]
596 );
597 if (args.today != null && date.daycount == args.today.daycount) {
598 cssClass += " gregor_today";
599 }
600 if (args.selected != null && date.daycount == args.selected.daycount) {
601 cssClass += " gregor_selected";
602 }
603 cell.className = cssClass;
604 if (args.day_callback) {
605 args.day_callback(cell, date);
606 } else {
607 element = document.createElement("a");
608 element.href = "#";
609 var generate_onclick = function(date) {
610 return function() {
611 args.selected = date;
612 gregor_sheet(args);
613 if (args.select_callback != null) {
614 args.select_callback(date);
615 }
616 return false;
617 }
618 }
619 element.onclick = generate_onclick(date);
620 element.ondblclick = element.onclick;
621 element.appendChild(document.createTextNode(first_day.day + i));
622 cell.appendChild(element);
623 }
624 row.appendChild(cell);
625 if (row.childNodes.length == ((args.week_numbers == "left") ? 8 : 7)) {
626 if (args.week_numbers == "right") insert_week();
627 tbody.appendChild(row);
628 row = null;
629 }
630 }
631 if (row != null) {
632 while (row.childNodes.length < ((args.week_numbers == "left") ? 8 : 7)) {
633 cell = document.createElement("td");
634 cell.className = "gregor_empty";
635 row.appendChild(cell);
636 }
637 if (args.week_numbers == "right") insert_week();
638 tbody.appendChild(row);
639 }
641 // end of table body
642 table.appendChild(tbody);
644 // return table
645 return table;
646 }
650 // Rich form field support
653 function gregor_getAbsoluteLeft(elem) {
654 var result = 0;
655 while (elem && elem.style.position != "static") {
656 result += elem.offsetLeft;
657 elem = elem.offsetParent;
658 }
659 return result;
660 }
662 function gregor_getAbsoluteTop(elem) {
663 var result = 0;
664 while (elem && elem.style.position != "static") {
665 result += elem.offsetTop;
666 elem = elem.offsetParent;
667 }
668 return result;
669 }
671 function gregor_formatDate(format, date) {
672 if (date == null) return "";
673 var result = format;
674 result = result.replace(/Y+/, function(s) {
675 if (s.length == 2) return gregor_formatInteger(date.year % 100, 2);
676 else return gregor_formatInteger(date.year, s.length);
677 });
678 result = result.replace(/M+/, function(s) {
679 return gregor_formatInteger(date.month, s.length);
680 });
681 result = result.replace(/D+/, function(s) {
682 return gregor_formatInteger(date.day, s.length);
683 });
684 return result;
685 }
687 function gregor_map2digitYear(y2, maxYear) {
688 var guess = Math.floor(maxYear / 100) * 100 + y2;
689 if (guess <= maxYear) return guess;
690 else return guess - 100;
691 }
693 function gregor_parseDate(format, string, terminated, maxYear) {
694 var numericParts, formatParts;
695 numericParts = string.match(/^\s*([0-9]{4})-([0-9]{2})-([0-9]{2})\s*$/);
696 if (numericParts != null) {
697 return gregor_completeDate({
698 year: numericParts[1],
699 month: numericParts[2],
700 day: numericParts[3]
701 });
702 }
703 numericParts = string.match(/[0-9]+/g);
704 if (numericParts == null) return null;
705 formatParts = format.match(/[YMD]+/g);
706 if (numericParts.length != formatParts.length) return null;
707 if (
708 !terminated && (
709 numericParts[numericParts.length-1].length <
710 formatParts[formatParts.length-1].length
711 )
712 ) return null;
713 var year, month, day;
714 for (var i=0; i<numericParts.length; i++) {
715 var numericPart = numericParts[i];
716 var formatPart = formatParts[i];
717 if (formatPart.match(/^Y+$/)) {
718 if (numericPart.length == 2 && maxYear != null) {
719 year = gregor_map2digitYear(parseInt(numericPart, 10), maxYear);
720 } else if (numericPart.length > 2) {
721 year = parseInt(numericPart, 10);
722 }
723 } else if (formatPart.match(/^M+$/)) {
724 month = parseInt(numericPart, 10);
725 } else if (formatPart.match(/^D+$/)) {
726 day = parseInt(numericPart, 10);
727 } else {
728 //alert("Not implemented.");
729 return null;
730 }
731 }
732 return gregor_completeDate({year: year, month: month, day: day});
733 }
735 function gregor_addGui(args) {
737 // copy argument structure
738 var state = {};
739 for (key in args) state[key] = args[key];
741 // unset state.element, which should never be set anyway
742 state.element = null;
744 // save original values of "year" and "month" options
745 var original_year = state.year;
746 var original_month = state.month;
748 // get text field element
749 var element = document.getElementById(state.element_id);
750 state.element_id = null;
752 // setup state.today, state.selected and state.format options
753 if (state.today === undefined) {
754 var js_date = new Date();
755 state.today = gregor_completeDate({
756 year: js_date.getFullYear(),
757 month: js_date.getMonth() + 1,
758 day: js_date.getDate()
759 });
760 } else if (state.today != null) {
761 state.today = gregor_completeDate(args.today);
762 }
763 if (state.selected == "today") {
764 state.selected = state.today;
765 } else if (args.selected != null) {
766 state.selected = gregor_completeDate(state.selected);
767 }
768 if (state.format == null) state.format = "YYYY-MM-DD";
770 // using state.future to calculate maxYear (for 2 digit year conversions)
771 var maxYear = (state.today == null) ? null : (
772 state.today.year +
773 ((state.future == null) ? 12 : state.future)
774 );
776 // hook into state.select_callback
777 var select_callback = state.select_callback;
778 state.select_callback = function(date) {
779 element.value = gregor_formatDate(state.format, date);
780 if (select_callback) select_callback(date);
781 };
782 // function to parse text field and update calendar sheet state
783 var updateSheet = function(terminated) {
784 var date = gregor_parseDate(
785 state.format, element.value, terminated, maxYear
786 );
787 if (date) {
788 state.year = null;
789 state.month = null;
790 }
791 state.selected = date;
792 gregor_sheet(state);
793 };
795 // Initial synchronization
796 if (state.selected === undefined) updateSheet(true);
797 if (!state.relaxed)
798 element.value = gregor_formatDate(state.format, state.selected);
800 if (select_callback) select_callback(state.selected);
802 // variables storing popup status
803 var visible = false;
804 var focus = false;
805 var protection = false;
807 // event handlers for text field
808 element.onfocus = function() {
809 focus = true;
810 if (!visible) {
811 state.year = original_year;
812 state.month = original_month;
813 gregor_sheet(state);
814 state.element.style.position = "absolute";
815 state.element.style.top = gregor_getAbsoluteTop(element) + element.offsetHeight;
816 state.element.style.left = gregor_getAbsoluteLeft(element);
817 state.element.onmousedown = function() {
818 protection = true;
819 };
820 state.element.onmouseup = function() {
821 protection = false;
822 element.focus();
823 };
824 state.element.onmouseout = state.element.onmouseup;
825 element.parentNode.appendChild(state.element);
826 visible = true;
827 }
828 };
829 element.onblur = function() {
830 focus = false;
831 window.setTimeout(function() {
832 if (visible && !focus && !protection) {
833 updateSheet(true);
834 if(!state.relaxed)
835 element.value = gregor_formatDate(state.format, state.selected);
836 if (select_callback) select_callback(state.selected);
837 state.element.parentNode.removeChild(state.element);
838 visible = false;
839 protection = false;
840 }
841 }, 1);
842 };
843 element.onkeyup = function() {
844 updateSheet(false);
845 if (select_callback) select_callback(state.selected);
846 };
848 }

Impressum / About Us