JavaXT
|
|
DatePicker Classif(!javaxt) var javaxt={}; if(!javaxt.dhtml) javaxt.dhtml={}; //****************************************************************************** //** DatePicker Class //****************************************************************************** /** * Calendar component used select dates * ******************************************************************************/ javaxt.dhtml.DatePicker = function(parent, config) { this.className = "javaxt.dhtml.DatePicker"; var me = this; var defaultConfig = { /** Used to set the initial view. All we need is a month and a year. */ date: new Date(), /** Used to set the selection mode. Options are "day" or "week". */ selectionMode: "day", /** Day names or abbreviations to use in the column headers */ daysOfWeek: ["S","M","T","W","T","F","S"], /** Month names or abbreviations used in the header */ months : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], /** If true, allows users to deselect the current selection via mouse * click */ allowDeselect: true, /** Style for individual elements within the component. Note that you can * provide CSS class names instead of individual style definitions. */ style: { //Panel Style panel: { fontFamily: "helvetica,arial,verdana,sans-serif", backgroundColor: "#ffffff", border: "1px solid #b4cbdd", display: "inline-block" }, header: { backgroundColor: "#d9e7f8", height: "25px", lineHeight: "25px" }, //Title area title: { position: "absolute", width: "100%", whiteSpace: "nowrap", //fontFamily: "helvetica,arial,verdana,sans-serif", fontSize: "14px", fontWeight: "bold", color: "#555555", textAlign: "center", cursor: "default" }, next: { float: "right", borderRight: "2px solid #5e8be0", borderBottom: "2px solid #5e8be0", width: "7px", height: "7px", transform: "rotate(-45deg)", margin: "7px 10px 0 0", cursor: "pointer" }, back: { float: "left", borderRight: "2px solid #5e8be0", borderBottom: "2px solid #5e8be0", width: "7px", height: "7px", transform: "rotate(135deg)", margin: "7px 0 0 10px", cursor: "pointer" }, cell: { width: "18px", height: "18px", lineHeight: "18px", textAlign: "right", padding: "2px 4px 1px 0px", fontSize: "11px", color: "#000000", cursor: "pointer", border: "1px solid #ffffff", margin: "1px" }, cellHeader: { //overrides cell style for header cells color: "#233d6d", fontSize: "10px", lineHeight: "10px", paddingBottom: "0px", paddingTop: "0px", border: "0px", borderTop: "1px solid #bbccff", borderBottom: "1px solid #bbccff" }, previousMonth: { //overrides cell style for previous month color: "#aaaaaa" }, nextMonth: { //overrides cell style for next month color: "#aaaaaa" }, today: { width: "22px", height: "21px", border: "1px solid #FF7373", top: "-1px", left: "-1px" }, selectedRow: { }, selectedCell: { color: "#000000", fontWeight: "bold", backgroundColor: "#fff4bf", border: "1px solid #bfa52f" } } }; var currDate; var startDate; var mainDiv; var todayHighlightDiv; var cells = []; var selectionMode; //************************************************************************** //** Constructor //************************************************************************** /** Creates a new instance of this class. */ var init = function(){ if (typeof parent === "string"){ parent = document.getElementById(parent); } if (!parent) return; //Clone the config so we don't modify the original config object var clone = {}; merge(clone, config); //Merge clone with default config merge(clone, defaultConfig); config = clone; //Update style config keyword for legacy apps if (config.style.selected && !config.style.selectedCell){ config.style.selectedCell = config.style.selected; } //Get selection mode selectionMode = config.selectionMode; //Create container mainDiv = createElement('div', parent, config.style.panel); mainDiv.style.display = "table"; me.el = mainDiv; //Disable text selection mainDiv.unselectable="on"; mainDiv.onselectstart=function(){return false;}; mainDiv.onmousedown=function(){return false;}; //Create highlight div todayHighlightDiv = createElement('div', config.style.today); todayHighlightDiv.style.position = "absolute"; //Render month me.setDate(config.date); }; //************************************************************************** //** setDate //************************************************************************** /** Used to update the calendar and select the given date. */ this.setDate = function(date){ date = new Date(date.getFullYear(), date.getMonth(), date.getDate()); //Render date me.render(date); //Deselect current selection me.deselect(); //Select date for (var i=0; i<cells.length; i++){ var d = cells[i].date; if (d.getTime()===date.getTime()){ select(cells[i]); return; } } }; //************************************************************************** //** render //************************************************************************** /** Used to update the calendar and render a given date. */ this.render = function(date){ date = new Date(date.getFullYear(), date.getMonth(), date.getDate()); if (currDate && date.getTime()===currDate.getTime()) return; //Compute date range var range = computeRange(date); //Check whether we should render a new calendar if (startDate){ if (startDate.getTime()===range.startDate.getTime()){ //No need to re-render the calendar. Fire the onUpdate event //as needed and exit. if (currDate.getTime()!==date.getTime()){ currDate = date; me.onUpdate(new Date(currDate)); } return; } } //Set variables currDate = date; startDate = range.startDate; var currMonth = currDate.getMonth(); var numWeeks = range.numWeeks; //Clear the current content mainDiv.innerHTML = ""; //Create header var headerDiv = createElement('div', mainDiv, config.style.header); headerDiv.style.position = "relative"; var titleDiv = createElement('div', headerDiv, config.style.title); titleDiv.innerHTML = config.months[currMonth] + " " + currDate.getFullYear(); titleDiv.onclick = function(e){ me.onHeaderClick(headerDiv, e); }; var backDiv = createElement('div', headerDiv, config.style.back); backDiv.onclick = function(e){ e.preventDefault(); e.stopPropagation(); me.back(); }; var nextDiv = createElement('div', headerDiv, config.style.next); nextDiv.onclick = function(e){ e.preventDefault(); e.stopPropagation(); me.next(); }; //Create main table var table = createTable(mainDiv); table.style.fontFamily = "inherit"; table.style.textAlign = "inherit"; table.style.color = "inherit"; //Add cell headers var tr = table.addRow(); for (var i=0; i<7; i++){ var td = tr.addColumn(); td.style.fontFamily = "inherit"; td.innerHTML = config.daysOfWeek[i]; addStyle(td, "cell"); addStyle(td, "cellHeader"); } //Add cells cells = []; var d = new Date(startDate); for (var i=0; i<numWeeks; i++){ tr = table.addRow(); for (var j=0; j<7; j++){ //Create new column var td = tr.addColumn(); td.style.fontFamily = "inherit"; //Create cell var cell = createElement('div', td); cell.style.position = "relative"; cell.style.fontFamily = "inherit"; cell.date = new Date(d); cell.selected = false; cells.push(cell); //Create div inside the cell to render the date var div1 = createElement('div', cell); div1.style.fontFamily = "inherit"; div1.innerHTML = d.getDate(); //Set style addStyle(cell, "cell"); //Update style if prev or next month var month = d.getMonth(); var monthOffset = 0; if (month!==currMonth){ if (i===0){ monthOffset = -1; addStyle(cell, "previousMonth"); } else{ monthOffset = 1; addStyle(cell, "nextMonth"); } } cell.monthOffset = monthOffset; //On click event cell.onclick = function(e){ var cell = this; //Select cell(s) var dateRange = select(cell); //Fire onClick event me.onClick(new Date(cell.date), dateRange); }; d.setDate(d.getDate()+1); } } //Highlight today's date highlightTodaysDate(); //Create a task to highlight today's date at midnight function scheduledTask() { var now = new Date(); var night = new Date( now.getFullYear(), now.getMonth(), now.getDate() + 1, // the next day, ... 0, 0, 0 // ...at 00:00:00 hours ); var msToMidnight = night.getTime() - now.getTime(); setTimeout(function() { //Highlight Today's Date highlightTodaysDate(); //Re-run the scheduled task for the next day scheduledTask(); }, msToMidnight); } scheduledTask(); me.onUpdate(new Date(currDate)); }; //************************************************************************** //** highlightTodaysDate //************************************************************************** /** Used to highlight today's date. */ var highlightTodaysDate = function(){ if (todayHighlightDiv.parentNode){ var cell = todayHighlightDiv.parentNode; cell.removeChild(todayHighlightDiv); } var now = new Date(); for (var x=0; x<cells.length; x++){ var cell = cells[x]; var d = cell.date; if (d.getFullYear()===now.getFullYear() && d.getMonth()===now.getMonth() && d.getDate()===now.getDate()) cell.appendChild(todayHighlightDiv); } }; //************************************************************************** //** select //************************************************************************** /** Used to select a cell that corresponds to the given date. If the * selectionMode is set to week, an entire row will be selected. */ this.select = function(date){ if (!date) return; //Find cell that corresponds with the given date var cell; for (var x=0; x<cells.length; x++){ var _cell = cells[x]; var _date = _cell.date; if (_date.getDate()===date.getDate() && _date.getMonth()===date.getMonth() && _date.getFullYear()===date.getFullYear()){ cell = _cell; break; } } //Select the cell if (cell) select(cell); }; //************************************************************************** //** select //************************************************************************** /** Used to select a given cell. If the selectionMode is set to week, the * entire row will be selected. Returns an array of dates that correspond * to the selected cell(s). */ var select = function(cell){ if (selectionMode==="day"){ //Get date var d = new Date(cell.date); //Deselect previous selection for (var x=0; x<cells.length; x++){ var _cell = cells[x]; if (_cell.selected){ //If a user clicked on a selected cell.. if (cell===_cell){ if (config.allowDeselect){ deselect(_cell); return []; } else{ return [d]; } } else{ //Deselect previously selected cell deselect(_cell); } break; } } //Highlight cell cell.selected=true; addStyle(cell, "selectedCell"); //Return selected date return [d]; } else if (selectionMode==="week"){ //Update row style var parentRow = cell.parentNode.parentNode; addStyle(parentRow, "selectedRow"); //Get start/end dates for the week var d1 = new Date(parentRow.childNodes[0].childNodes[0].date); var d2 = new Date(parentRow.childNodes[parentRow.childNodes.length-1].childNodes[0].date); //Select row for (var x=0; x<cells.length; x++){ var _cell = cells[x]; if (_cell.parentNode.parentNode===parentRow){ //If a user clicked on a selected row... if (_cell.selected){ if (config.allowDeselect){ //Deselect row for (var y=0; y<cells.length; y++){ if (cells[y].selected) deselect(cells[y]); } return []; } else{ //Do nothing, the row is already selected return [d1,d2]; } } //Highlight cell _cell.selected=true; addStyle(_cell, "selectedCell"); } else{ //Deselect previously selected row var row = _cell.parentNode.parentNode; row.style = ''; row.className = ''; row.removeAttribute("class"); if (_cell.selected) deselect(_cell); } } //Return selected date range return [d1,d2]; } else{ return []; } }; //************************************************************************** //** deselect //************************************************************************** /** Used to deselect any selected cells in the calendar. */ this.deselect = function(){ for (var x=0; x<cells.length; x++){ var _cell = cells[x]; if (_cell.selected){ if (config.allowDeselect){ } deselect(_cell); } } }; //************************************************************************** //** deselect //************************************************************************** /** Used to deselect a given cell. */ var deselect = function(_cell){ _cell.selected=false; _cell.style = ''; _cell.className = ''; _cell.removeAttribute("class"); _cell.style.position = "relative"; addStyle(_cell, "cell"); switch(_cell.monthOffset){ case -1: addStyle(_cell, "previousMonth"); break; case 1: addStyle(_cell, "nextMonth"); break; } }; //************************************************************************** //** getSelectionMode //************************************************************************** this.getSelectionMode = function(){ return selectionMode; }; //************************************************************************** //** setSelectionMode //************************************************************************** this.setSelectionMode = function(str){ selectionMode = str; }; //************************************************************************** //** getSelectedCells //************************************************************************** this.getSelectedCells = function(){ var selectedCells = []; for (var x=0; x<cells.length; x++){ var cell = cells[x]; if (cell.selected) selectedCells.push(cell); } return selectedCells; }; //************************************************************************** //** onClick //************************************************************************** /** Called when a cell in the calendar is clicked. * @param date Date associated with the cell. * @param dateRange An array representing the selected date range. If the * selection mode is set to "week", the array will contain two entries: one * for the start date and one for the end date. Otherwise, the array will * contain only one entry. */ this.onClick = function(date, dateRange){}; //************************************************************************** //** onHeaderClick //************************************************************************** /** Called when the calendar title is clicked. Typically used to render * a month and year selection menu. */ this.onHeaderClick = function(headerDiv, e){}; //************************************************************************** //** onUpdate //************************************************************************** /** Called when the date picker is first rendered or whenever the month is * changed. */ this.onUpdate = function(date){}; //************************************************************************** //** back //************************************************************************** /** Used to render the previous month. */ this.back = function(){ var m = currDate.getMonth()-1; var d = currDate.getDate(); var y = currDate.getFullYear(); var x = new Date(y, m+1, 0).getDate(); if (d>x) console.log(d + " vs " + x); if (d>x) d = x; me.render(new Date(y, m, d)); }; //************************************************************************** //** next //************************************************************************** /** Used to render the next month. */ this.next = function(){ var m = currDate.getMonth()+1; var d = currDate.getDate(); var y = currDate.getFullYear(); var x = new Date(y, m+1, 0).getDate(); if (d>x) console.log(d + " vs " + x); if (d>x) d = x; me.render(new Date(y, m, d)); }; //************************************************************************** //** getMonth //************************************************************************** /** Returns the month rendered in the date picker (0-11) */ this.getMonth = function(){ return currDate.getMonth(); }; //************************************************************************** //** getYear //************************************************************************** /** Returns the year rendered in the date picker. */ this.getYear = function(){ return currDate.getFullYear(); }; //************************************************************************** //** computeRange //************************************************************************** /** Computes several key variables used to render the calndar including * start/end date and the total number of rows to render. Credit: * http://stackoverflow.com/a/2485172 */ var computeRange = function(d){ var year = d.getFullYear(); var month = d.getMonth()+1; var firstOfMonth = new Date(year, month-1, 1); var lastOfMonth = new Date(year, month, 0); var numWeeks = Math.ceil( (firstOfMonth.getDay() + lastOfMonth.getDate()) / 7); var startDate = new Date(firstOfMonth); startDate.setDate(startDate.getDate()-firstOfMonth.getDay()); var endDate = new Date(lastOfMonth); endDate.setDate(endDate.getDate()+(6-lastOfMonth.getDay())); return { numWeeks: numWeeks, startDate: startDate, endDate: endDate }; }; //************************************************************************** //** Utils //************************************************************************** var merge = javaxt.dhtml.utils.merge; var createElement = javaxt.dhtml.utils.createElement; var createTable = javaxt.dhtml.utils.createTable; var addStyle = function(el, style){ javaxt.dhtml.utils.addStyle(el, config.style[style]); }; init(); }; |