bsw@11: // bsw@11: // Copyright (c) 2009 Public Software Group e. V., Berlin bsw@11: // bsw@11: // Permission is hereby granted, free of charge, to any person obtaining a bsw@11: // copy of this software and associated documentation files (the bsw@11: // "Software"), to deal in the Software without restriction, including bsw@11: // without limitation the rights to use, copy, modify, merge, publish, bsw@11: // distribute, sublicense, and/or sell copies of the Software, and to bsw@11: // permit persons to whom the Software is furnished to do so, subject to bsw@11: // the following conditions: bsw@11: // bsw@11: // The above copyright notice and this permission notice shall be included bsw@11: // in all copies or substantial portions of the Software. bsw@11: // bsw@11: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS bsw@11: // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF bsw@11: // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. bsw@11: // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY bsw@11: // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, bsw@11: // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE bsw@11: // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bsw@11: // bsw@11: bsw@11: // bsw@11: // All date calculations are based on the gregorian calender, also for bsw@11: // dates before 1582 (before the gegorian calendar was introduced). bsw@11: // The supported range is from January 1st 0001 up to December 31st 9999. bsw@11: // bsw@11: // gregor_daycount({year: , month: , day: }) returns bsw@11: // the number of days between the given date and January 1st 0001 (greg.). bsw@11: // bsw@11: // gregor_completeDate({year: , month: , day: }) returns bsw@11: // a structure (an object) with the following properties: bsw@11: // - daycount (days since January 1st 0001, see gregor_daycount) bsw@11: // - year (with century) bsw@11: // - month (from 1 to 12) bsw@11: // - day (from 1 to 28, 29, 30 or 31) bsw@11: // - iso_string (string with format YYYY-MM-DD) bsw@11: // - us_weekday (from 0 for Sunday to 6 for Monday) bsw@11: // - iso_weekday (from 0 for Monday to 6 for Sunday) bsw@11: // - iso_weekyear (Year containing greater part of week st. w. Monday) bsw@11: // - iso_week (from 1 to 52 or 53) bsw@11: // - us_week (from 1 to 53 or 54) bsw@11: // bsw@11: // The structure (the object) passed as parameter to gregor_daycount or bsw@11: // gregor_completeDate may describe a date in the following ways: bsw@11: // - daycount bsw@11: // - year, month, day bsw@11: // - year, us_week, us_weekday bsw@11: // - year, iso_week, iso_weekday bsw@11: // - iso_weekyear, iso_week, iso_weekday bsw@11: // bsw@11: // gregor_sheet({...}) returns a calendar sheet as DOM object. The bsw@11: // structure (the object) passed to the function as an argument is altered bsw@11: // by the function and used to store state variables. Initially it can bsw@11: // contain the following fields: bsw@11: // - year (year to show, defaults to todays year) bsw@11: // - month (month to show, defaults to todays month) bsw@11: // - today (structure describing a day, e.g. year, month, day) bsw@11: // - selected (structure describing a day, e.g. year, month, day) bsw@11: // - navigation ("enabled", "disabled", "hidden", default "enabled") bsw@11: // - week_mode ("iso" or "us", defaults to "iso") bsw@11: // - week_numbers ("left", "right" or null, defaults to null) bsw@11: // - month_names (e.g. ["January", "Feburary", ...]) bsw@11: // - weekday_names (e.g. ["Mon", "Tue", ...] or ["Sun", "Mon", ...]) bsw@11: // - day_callback (function to render a cell) bsw@11: // - select_callback (function to be called, when a date was selected) bsw@11: // - element (for internal use only) bsw@11: // If "today" is undefined, it is automatically intitialized with the bsw@11: // current clients date. If "selected" is undefined or null, no date is bsw@11: // be initially selected. It is mandatory to provide month_names and bsw@11: // weekday_names. bsw@11: // bsw@11: // gregor_addGui({...}) alters a referenced input field in a way that bsw@11: // focussing on it causes a calendar being shown at its right side, where a bsw@11: // date can be selected. The structure (the object) passed to this function bsw@11: // is only evaluated once, and never modified. All options except "element" bsw@11: // of the gregor_sheet({...}) function may be used as options to bsw@11: // gregor_addGui({...}) as well. In addition an "element_id" must be bsw@11: // provided as argument, containing the id of a text input field. The bsw@11: // behaviour caused by the options "selected" and "select_callback" are bsw@11: // slightly different: If "selected" === undefined, then the current value bsw@11: // of the text field referenced by "element_id" will be parsed and used as bsw@11: // date selection. If "selected" === null, then no date will be initially bsw@11: // selected, and the text field will be cleared. The "select_callback" bsw@11: // function is always called once with the pre-selected date (or with null, bsw@11: // if no date is initially selected). Whenever the selected date is changed bsw@11: // or unselected later, the callback function is called again with the new poelzi@146: // date (or with null, in case of deselection). When the relaxed argument is set poelzi@146: // the calendar will not normalize the parsed date. Thats usefull if you wan't to poelzi@146: // allow relaxed input. bsw@11: // bsw@11: // EXAMPLE: bsw@11: // bsw@11: // bsw@11: // bsw@11: // bsw@11: bsw@11: bsw@11: bsw@11: bsw@11: // Internal constants and helper functions for date calculation bsw@11: bsw@11: bsw@11: gregor_c1 = 365; // days of a non-leap year bsw@11: gregor_c4 = 4 * gregor_c1 + 1; // days of a full 4 year cycle bsw@11: gregor_c100 = 25 * gregor_c4 - 1; // days of a full 100 year cycle bsw@11: gregor_c400 = 4 * gregor_c100 + 1; // days of a full 400 year cycle bsw@11: bsw@11: gregor_normalMonthOffsets = [ bsw@11: 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 bsw@11: ]; bsw@11: bsw@11: function gregor_getMonthOffset(year, month) { bsw@11: if ( bsw@11: (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) && bsw@11: month > 2 bsw@11: ) return gregor_normalMonthOffsets[month-1] + 1; bsw@11: else return gregor_normalMonthOffsets[month-1]; bsw@11: } bsw@11: bsw@11: function gregor_formatInteger(int, digits) { bsw@11: var result = int.toFixed(); bsw@11: if (digits != null) { bsw@11: while (result.length < digits) result = "0" + result; bsw@11: } bsw@11: return result; bsw@11: } bsw@11: bsw@11: bsw@11: bsw@11: // Calculate days since January 1st 0001 (Gegorian) for a given date bsw@11: bsw@11: bsw@11: function gregor_daycount(obj) { bsw@11: if ( bsw@11: obj.daycount >= 0 && obj.daycount <= 3652058 && obj.daycount % 1 == 0 bsw@11: ) { bsw@11: return obj.daycount; bsw@11: } else if ( bsw@11: obj.year >= 1 && obj.year <= 9999 && obj.year % 1 == 0 && bsw@11: obj.month >= 1 && obj.month <= 12 && obj.month % 1 == 0 && bsw@11: obj.day >= 0 && obj.day <= 31 && obj.day % 1 == 0 bsw@11: ) { bsw@11: var n400 = Math.floor((obj.year-1) / 400); bsw@11: var r400 = (obj.year-1) % 400; bsw@11: var n100 = Math.floor(r400 / 100); bsw@11: var r100 = r400 % 100; bsw@11: var n4 = Math.floor(r100 / 4); bsw@11: var n1 = r100 % 4; bsw@11: return ( bsw@11: gregor_c400 * n400 + bsw@11: gregor_c100 * n100 + bsw@11: gregor_c4 * n4 + bsw@11: gregor_c1 * n1 + bsw@11: gregor_getMonthOffset(obj.year, obj.month) + (obj.day - 1) bsw@11: ); bsw@11: } else if ( bsw@11: ( bsw@11: ( bsw@11: obj.year >= 1 && obj.year <= 9999 && bsw@11: obj.year % 1 == 0 && obj.iso_weekyear == null bsw@11: ) || ( bsw@11: obj.iso_weekyear >= 1 && obj.iso_weekyear <= 9999 && bsw@11: obj.iso_weekyear % 1 == 0 && obj.year == null bsw@11: ) bsw@11: ) && bsw@11: obj.iso_week >= 0 && obj.iso_week <= 53 && obj.iso_week % 1 == 0 && bsw@11: obj.iso_weekday >= 0 && obj.iso_weekday <= 6 && bsw@11: obj.iso_weekday % 1 == 0 bsw@11: ) { bsw@11: var jan4th = gregor_daycount({ bsw@11: year: (obj.year != null) ? obj.year : obj.iso_weekyear, bsw@11: month: 1, bsw@11: day: 4 bsw@11: }); bsw@11: var monday0 = jan4th - (jan4th % 7) - 7; // monday of week 0 bsw@11: return monday0 + 7 * obj.iso_week + obj.iso_weekday; bsw@11: } else if ( bsw@11: obj.year >= 1 && obj.year <= 9999 && obj.year % 1 == 0 && bsw@11: obj.us_week >= 0 && obj.us_week <= 54 && obj.us_week % 1 == 0 && bsw@11: obj.us_weekday >= 0 && obj.us_weekday <= 6 && obj.us_weekday % 1 == 0 bsw@11: ) { bsw@11: var jan1st = gregor_daycount({ bsw@11: year: obj.year, bsw@11: month: 1, bsw@11: day: 1 bsw@11: }); bsw@11: var sunday0 = jan1st - ((jan1st+1) % 7) - 7; // sunday of week 0 bsw@11: return sunday0 + 7 * obj.us_week + obj.us_weekday; bsw@11: } bsw@11: } bsw@11: bsw@11: bsw@11: bsw@11: // Calculate all calendar related numbers for a given date bsw@11: bsw@11: bsw@11: function gregor_completeDate(obj) { bsw@11: if ( bsw@11: obj.daycount >= 0 && obj.daycount <= 3652058 && obj.daycount % 1 == 0 bsw@11: ) { bsw@11: var daycount = obj.daycount; bsw@11: var n400 = Math.floor(daycount / gregor_c400); bsw@11: var r400 = daycount % gregor_c400; bsw@11: var n100 = Math.floor(r400 / gregor_c100); bsw@11: var r100 = r400 % gregor_c100; bsw@11: if (n100 == 4) { n100 = 3; r100 = gregor_c100; } bsw@11: var n4 = Math.floor(r100 / gregor_c4); bsw@11: var r4 = r100 % gregor_c4; bsw@11: var n1 = Math.floor(r4 / gregor_c1); bsw@11: var r1 = r4 % gregor_c1; bsw@11: if (n1 == 4) { n1 = 3; r1 = gregor_c1; } bsw@11: var year = 1 + 400 * n400 + 100 * n100 + 4 * n4 + n1; bsw@11: var month = 1 + Math.floor(r1 / 31); bsw@11: var monthOffset = gregor_getMonthOffset(year, month); bsw@11: if (month < 12) { bsw@11: var nextMonthOffset = gregor_getMonthOffset(year, month + 1); bsw@11: if (r1 >= nextMonthOffset) { bsw@11: month++; bsw@11: monthOffset = nextMonthOffset; bsw@11: } bsw@11: } bsw@11: var day = 1 + r1 - monthOffset; bsw@11: var iso_string = ("" + bsw@11: gregor_formatInteger(year, 4) + "-" + bsw@11: gregor_formatInteger(month, 2) + "-" + bsw@11: gregor_formatInteger(day, 2) bsw@11: ); bsw@11: var us_weekday = (daycount+1) % 7; bsw@11: var iso_weekday = daycount % 7; bsw@11: var iso_weekyear = year; bsw@11: if ( bsw@11: month == 1 && ( bsw@11: (day == 3 && iso_weekday == 6) || bsw@11: (day == 2 && iso_weekday >= 5) || bsw@11: (day == 1 && iso_weekday >= 4) bsw@11: ) bsw@11: ) iso_weekyear--; bsw@11: else if ( bsw@11: month == 12 && ( bsw@11: (day == 29 && iso_weekday == 0) || bsw@11: (day == 30 && iso_weekday <= 1) || bsw@11: (day == 31 && iso_weekday <= 2) bsw@11: ) bsw@11: ) iso_weekyear++; bsw@11: var jan4th = gregor_daycount({year: iso_weekyear, month: 1, day: 4}); bsw@11: var monday0 = jan4th - (jan4th % 7) - 7; // monday of week 0 bsw@11: var iso_week = Math.floor((daycount - monday0) / 7); bsw@11: var jan1st = gregor_daycount({year: year, month: 1, day: 1}); bsw@11: var sunday0 = jan1st - ((jan1st+1) % 7) - 7; // sunday of week 0 bsw@11: var us_week = Math.floor((daycount - sunday0) / 7); bsw@11: return { bsw@11: daycount: daycount, bsw@11: year: year, bsw@11: month: month, bsw@11: day: day, bsw@11: iso_string: iso_string, bsw@11: us_weekday: (daycount+1) % 7, // 0 = Sunday bsw@11: iso_weekday: daycount % 7, // 0 = Monday bsw@11: iso_weekyear: iso_weekyear, bsw@11: iso_week: iso_week, bsw@11: us_week: us_week bsw@11: }; bsw@11: } else if (obj.daycount == null) { bsw@11: var daycount = gregor_daycount(obj); bsw@11: if (daycount != null) { bsw@11: return gregor_completeDate({daycount: gregor_daycount(obj)}); bsw@11: } bsw@11: } bsw@11: } bsw@11: bsw@11: bsw@11: bsw@11: // Test gregor_daycount and gregor_completeDate for consistency bsw@11: // (Debugging only) bsw@11: bsw@11: bsw@11: function gregor_test() { bsw@11: for (i=650000; i<900000; i++) { bsw@11: var obj = gregor_completeDate({daycount: i}); bsw@11: var j; bsw@11: j = gregor_daycount({ bsw@11: year: obj.year, bsw@11: month: obj.month, bsw@11: day: obj.day bsw@11: }); bsw@11: if (i != j) { alert("ERROR"); return; } bsw@11: j = gregor_daycount({ bsw@11: iso_weekyear: obj.iso_weekyear, bsw@11: iso_week: obj.iso_week, bsw@11: iso_weekday: obj.iso_weekday bsw@11: }); bsw@11: if (i != j) { alert("ERROR"); return; } bsw@11: j = gregor_daycount({ bsw@11: year: obj.year, bsw@11: iso_week: bsw@11: (obj.iso_weekyear == obj.year + 1) ? 53 : bsw@11: (obj.iso_weekyear == obj.year - 1) ? 0 : bsw@11: obj.iso_week, bsw@11: iso_weekday: obj.iso_weekday bsw@11: }); bsw@11: if (i != j) { alert("ERROR"); return; } bsw@11: j = gregor_daycount({ bsw@11: year: obj.year, bsw@11: us_week: obj.us_week, bsw@11: us_weekday: obj.us_weekday bsw@11: }); bsw@11: if (i != j) { alert("ERROR"); return; } bsw@11: } bsw@11: alert("SUCCESS"); bsw@11: } bsw@11: bsw@11: bsw@11: bsw@11: // Graphical calendar bsw@11: bsw@11: bsw@11: gregor_iso_weekday_css = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]; bsw@11: bsw@11: function gregor_sheet(args) { bsw@11: bsw@11: // setting args.today and args.selected bsw@11: if (args.today === undefined) { bsw@11: var js_date = new Date(); bsw@11: args.today = gregor_completeDate({ bsw@11: year: js_date.getFullYear(), bsw@11: month: js_date.getMonth() + 1, bsw@11: day: js_date.getDate() bsw@11: }); bsw@11: } else if (args.today != null) { bsw@11: args.today = gregor_completeDate(args.today); bsw@11: } bsw@11: if (args.selected == "today") { bsw@11: args.selected = args.today; bsw@11: } else if (args.selected != null) { bsw@11: args.selected = gregor_completeDate(args.selected); bsw@11: } bsw@11: bsw@11: // setting args.year and args.month bsw@11: if (args.year == null) { bsw@11: if (args.selected != null) args.year = args.selected.year; bsw@11: else args.year = args.today.year; bsw@11: } bsw@11: if (args.month == null) { bsw@11: if (args.selected != null) args.month = args.selected.month; bsw@11: else args.month = args.today.month; bsw@11: } bsw@11: bsw@11: // setting first_day bsw@11: var first_day = gregor_completeDate({ bsw@11: year: args.year, bsw@11: month: args.month, bsw@11: day: 1 bsw@11: }); bsw@11: if (first_day == null) return; bsw@11: bsw@11: // checking args.navigation, args.week_mode and args.week_numbers bsw@11: if (args.navigation == null) args.navigation = "enabled"; bsw@11: else if ( bsw@11: args.navigation != "enabled" && bsw@11: args.navigation != "disabled" && bsw@11: args.navigation != "hidden" bsw@11: ) return; bsw@11: if (args.week_mode == null) args.week_mode = "iso"; bsw@11: else if (args.week_mode != "iso" && args.week_mode != "us") return; bsw@11: if ( bsw@11: args.week_numbers != null && bsw@11: args.week_numbers != "left" && bsw@11: args.week_numbers != "right" bsw@11: ) return; bsw@11: bsw@11: // checking args.month.names and args.weekday_names bsw@11: if (args.month_names.length != 12) return; bsw@11: if (args.weekday_names.length != 7) return; bsw@11: bsw@11: // calculating number of days in month bsw@11: var count; bsw@11: if (args.year < 9999 || args.month < 12) { bsw@11: count = gregor_daycount({ bsw@11: year: (args.month == 12) ? (args.year + 1) : args.year, bsw@11: month: (args.month == 12) ? 1 : (args.month + 1), bsw@11: day: 1 bsw@11: }) - first_day.daycount; bsw@11: } else { bsw@11: // workaround for year 9999 bsw@11: count = 31; bsw@11: } bsw@11: bsw@11: // locale variables for UI construction bsw@11: var table, row, cell, element; bsw@11: bsw@11: // take table element from args.element, bsw@11: // if neccessary create and store it bsw@11: if (args.element == null) { bsw@11: table = document.createElement("table"); bsw@11: args.element = table; bsw@11: } else { bsw@11: table = args.element; bsw@11: while (table.firstChild) table.removeChild(table.firstChild); bsw@11: } bsw@11: bsw@11: // set CSS class of table according to args.week_numbers bsw@11: if (args.week_numbers == "left") { bsw@11: table.className = "gregor_sheet gregor_weeks_left"; bsw@11: } else if (args.week_numbers == "right") { bsw@11: table.className = "gregor_sheet gregor_weeks_right"; bsw@11: } else { bsw@11: table.className = "gregor_sheet gregor_weeks_none"; bsw@11: } bsw@11: bsw@11: // begin of table head bsw@11: var thead = document.createElement("thead"); bsw@11: bsw@11: // navigation bsw@11: if (args.navigation != "hidden") { bsw@11: bsw@11: // UI head row containing the year bsw@11: row = document.createElement("tr"); bsw@11: row.className = "gregor_year_row"; bsw@11: cell = document.createElement("th"); bsw@11: cell.className = "gregor_year"; bsw@11: cell.colSpan = args.week_numbers ? 8 : 7; bsw@11: if (args.navigation == "enabled") { bsw@11: element = document.createElement("a"); bsw@11: element.className = "gregor_turn gregor_turn_left"; bsw@11: element.style.cssFloat = "left"; bsw@11: element.style.styleFloat = "left"; bsw@11: element.href = "#"; bsw@11: element.onclick = function() { bsw@11: if (args.year > 1) args.year--; bsw@11: gregor_sheet(args); bsw@11: return false; bsw@11: } bsw@11: element.ondblclick = element.onclick; bsw@11: element.appendChild(document.createTextNode("<<")); bsw@11: cell.appendChild(element); bsw@11: element = document.createElement("a"); bsw@11: element.className = "gregor_turn gregor_turn_right"; bsw@11: element.style.cssFloat = "right"; bsw@11: element.style.styleFloat = "right"; bsw@11: element.href = "#"; bsw@11: element.onclick = function() { bsw@11: if (args.year < 9999) args.year++; bsw@11: gregor_sheet(args); bsw@11: return false; bsw@11: } bsw@11: element.ondblclick = element.onclick; bsw@11: element.appendChild(document.createTextNode(">>")); bsw@11: cell.appendChild(element); bsw@11: } bsw@11: cell.appendChild(document.createTextNode(first_day.year)); bsw@11: row.appendChild(cell); bsw@11: thead.appendChild(row); bsw@11: bsw@11: // UI head row containing the month bsw@11: row = document.createElement("tr"); bsw@11: row.className = "gregor_month_row"; bsw@11: cell = document.createElement("th"); bsw@11: cell.className = "gregor_month"; bsw@11: cell.colSpan = args.week_numbers ? 8 : 7; bsw@11: if (args.navigation == "enabled") { bsw@11: element = document.createElement("a"); bsw@11: element.className = "gregor_turn gregor_turn_left"; bsw@11: element.style.cssFloat = "left"; bsw@11: element.style.styleFloat = "left"; bsw@11: element.href = "#"; bsw@11: element.onclick = function() { bsw@11: if (args.year > 1 || args.month > 1) { bsw@11: args.month--; bsw@11: if (args.month < 1) { bsw@11: args.month = 12; bsw@11: args.year--; bsw@11: } bsw@11: } bsw@11: gregor_sheet(args); bsw@11: return false; bsw@11: } bsw@11: element.ondblclick = element.onclick; bsw@11: element.appendChild(document.createTextNode("<<")); bsw@11: cell.appendChild(element); bsw@11: element = document.createElement("a"); bsw@11: element.className = "gregor_turn gregor_turn_right"; bsw@11: element.style.cssFloat = "right"; bsw@11: element.style.styleFloat = "right"; bsw@11: element.href = "#"; bsw@11: element.onclick = function() { bsw@11: if (args.year < 9999 || args.month < 12) { bsw@11: args.month++; bsw@11: if (args.month > 12) { bsw@11: args.month = 1; bsw@11: args.year++; bsw@11: } bsw@11: } bsw@11: gregor_sheet(args); bsw@11: return false; bsw@11: } bsw@11: element.ondblclick = element.onclick; bsw@11: element.appendChild(document.createTextNode(">>")); bsw@11: cell.appendChild(element); bsw@11: } bsw@11: cell.appendChild(document.createTextNode( bsw@11: args.month_names[first_day.month-1] bsw@11: )); bsw@11: row.appendChild(cell); bsw@11: thead.appendChild(row); bsw@11: bsw@11: // end of navigation bsw@11: } bsw@11: bsw@11: // UI weekday row bsw@11: row = document.createElement("tr"); bsw@11: row.className = "gregor_weekday_row"; bsw@11: if (args.week_numbers == "left") { bsw@11: cell = document.createElement("th"); bsw@11: cell.className = "gregor_corner"; bsw@11: row.appendChild(cell); bsw@11: } bsw@11: for (var i=0; i<7; i++) { bsw@11: cell = document.createElement("th"); bsw@11: cell.className = ( bsw@11: "gregor_weekday gregor_" + bsw@11: gregor_iso_weekday_css[(args.week_mode == "us") ? ((i+6)%7) : i] bsw@11: ); bsw@11: cell.appendChild(document.createTextNode(args.weekday_names[i])); bsw@11: row.appendChild(cell); bsw@11: } bsw@11: if (args.week_numbers == "right") { bsw@11: cell = document.createElement("th"); bsw@11: cell.className = "gregor_corner"; bsw@11: row.appendChild(cell); bsw@11: } bsw@11: thead.appendChild(row); bsw@11: bsw@11: // end of table head and begin of table body bsw@11: table.appendChild(thead); bsw@11: var tbody = document.createElement("tbody"); bsw@11: bsw@11: // definition of insert_week function bsw@11: var week = ( bsw@11: (args.week_mode == "us") ? first_day.us_week : first_day.iso_week bsw@11: ); bsw@11: insert_week = function() { bsw@11: cell = document.createElement("th"); bsw@11: cell.className = "gregor_week"; bsw@11: cell.appendChild(document.createTextNode( bsw@11: (week < 10) ? ("0" + week) : week) bsw@11: ); bsw@11: week++; bsw@11: if ( bsw@11: args.week_mode == "iso" && ( bsw@11: ( bsw@11: args.month == 1 && week > 52 bsw@11: ) || ( bsw@11: args.month == 12 && week == 53 && ( bsw@11: first_day.iso_weekday == 0 || bsw@11: first_day.iso_weekday == 5 || bsw@11: first_day.iso_weekday == 6 bsw@11: ) bsw@11: ) bsw@11: ) bsw@11: ) week = 1; bsw@11: row.appendChild(cell); bsw@11: } bsw@11: bsw@11: // output data fields bsw@11: row = document.createElement("tr"); bsw@11: if (args.week_numbers == "left") insert_week(); bsw@11: var filler_count = ( bsw@11: (args.week_mode == "us") ? first_day.us_weekday : first_day.iso_weekday bsw@11: ); bsw@11: for (var i=0; i 2) { bsw@11: year = parseInt(numericPart, 10); bsw@11: } bsw@11: } else if (formatPart.match(/^M+$/)) { bsw@11: month = parseInt(numericPart, 10); bsw@11: } else if (formatPart.match(/^D+$/)) { bsw@11: day = parseInt(numericPart, 10); bsw@11: } else { bsw@11: //alert("Not implemented."); bsw@11: return null; bsw@11: } bsw@11: } bsw@11: return gregor_completeDate({year: year, month: month, day: day}); bsw@11: } bsw@11: bsw@11: function gregor_addGui(args) { bsw@11: bsw@11: // copy argument structure bsw@11: var state = {}; bsw@11: for (key in args) state[key] = args[key]; bsw@11: bsw@11: // unset state.element, which should never be set anyway bsw@11: state.element = null; bsw@11: bsw@11: // save original values of "year" and "month" options bsw@11: var original_year = state.year; bsw@11: var original_month = state.month; bsw@11: bsw@11: // get text field element bsw@11: var element = document.getElementById(state.element_id); bsw@11: state.element_id = null; bsw@11: bsw@11: // setup state.today, state.selected and state.format options bsw@11: if (state.today === undefined) { bsw@11: var js_date = new Date(); bsw@11: state.today = gregor_completeDate({ bsw@11: year: js_date.getFullYear(), bsw@11: month: js_date.getMonth() + 1, bsw@11: day: js_date.getDate() bsw@11: }); bsw@11: } else if (state.today != null) { bsw@11: state.today = gregor_completeDate(args.today); bsw@11: } bsw@11: if (state.selected == "today") { bsw@11: state.selected = state.today; bsw@11: } else if (args.selected != null) { bsw@11: state.selected = gregor_completeDate(state.selected); bsw@11: } bsw@11: if (state.format == null) state.format = "YYYY-MM-DD"; bsw@11: bsw@11: // using state.future to calculate maxYear (for 2 digit year conversions) bsw@11: var maxYear = (state.today == null) ? null : ( bsw@11: state.today.year + bsw@11: ((state.future == null) ? 12 : state.future) bsw@11: ); bsw@11: bsw@11: // hook into state.select_callback bsw@11: var select_callback = state.select_callback; bsw@11: state.select_callback = function(date) { bsw@11: element.value = gregor_formatDate(state.format, date); bsw@11: if (select_callback) select_callback(date); bsw@11: }; bsw@11: // function to parse text field and update calendar sheet state bsw@11: var updateSheet = function(terminated) { bsw@11: var date = gregor_parseDate( bsw@11: state.format, element.value, terminated, maxYear bsw@11: ); bsw@11: if (date) { bsw@11: state.year = null; bsw@11: state.month = null; bsw@11: } bsw@11: state.selected = date; bsw@11: gregor_sheet(state); bsw@11: }; bsw@11: bsw@11: // Initial synchronization bsw@11: if (state.selected === undefined) updateSheet(true); poelzi@146: if (!state.relaxed) poelzi@146: element.value = gregor_formatDate(state.format, state.selected); poelzi@146: bsw@11: if (select_callback) select_callback(state.selected); bsw@11: bsw@11: // variables storing popup status bsw@11: var visible = false; bsw@11: var focus = false; bsw@11: var protection = false; bsw@11: bsw@11: // event handlers for text field bsw@11: element.onfocus = function() { bsw@11: focus = true; bsw@11: if (!visible) { bsw@11: state.year = original_year; bsw@11: state.month = original_month; bsw@11: gregor_sheet(state); bsw@11: state.element.style.position = "absolute"; bsw@11: state.element.style.top = gregor_getAbsoluteTop(element) + element.offsetHeight; bsw@11: state.element.style.left = gregor_getAbsoluteLeft(element); bsw@11: state.element.onmousedown = function() { bsw@11: protection = true; bsw@11: }; bsw@11: state.element.onmouseup = function() { bsw@11: protection = false; bsw@11: element.focus(); bsw@11: }; bsw@11: state.element.onmouseout = state.element.onmouseup; bsw@11: element.parentNode.appendChild(state.element); bsw@11: visible = true; bsw@11: } bsw@11: }; bsw@11: element.onblur = function() { bsw@11: focus = false; bsw@11: window.setTimeout(function() { bsw@11: if (visible && !focus && !protection) { bsw@11: updateSheet(true); poelzi@146: if(!state.relaxed) poelzi@146: element.value = gregor_formatDate(state.format, state.selected); bsw@11: if (select_callback) select_callback(state.selected); bsw@11: state.element.parentNode.removeChild(state.element); bsw@11: visible = false; bsw@11: protection = false; bsw@11: } bsw@11: }, 1); bsw@11: }; bsw@11: element.onkeyup = function() { bsw@11: updateSheet(false); bsw@11: if (select_callback) select_callback(state.selected); bsw@11: }; bsw@11: bsw@11: } bsw@11: