web/data/cubicweb.calendar.js
changeset 0 b97547f5f1fa
child 1419 7ff24154351d
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 /*
       
     2  *  This file contains Calendar utilities
       
     3  *  :organization: Logilab
       
     4  *  :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6  */
       
     7 
       
     8 CubicWeb.require('python.js');
       
     9 CubicWeb.require('ajax.js');
       
    10 
       
    11 // IMPORTANT NOTE: the variables DAYNAMES AND MONTHNAMES will be added
       
    12 //                 by cubicweb automatically
       
    13 
       
    14 // dynamically computed (and cached)
       
    15 var _CAL_HEADER = null;
       
    16 
       
    17 TODAY = new Date();
       
    18 
       
    19 
       
    20 /*
       
    21  * Calendar (graphical) widget
       
    22  * public methods are :
       
    23  *   __init__ :
       
    24  *    @param containerId: the DOM node's ID where the calendar will be displayed
       
    25  *    @param inputId: which input needs to be updated when a date is selected
       
    26  *    @param year, @param month: year and month to be displayed
       
    27  *    @param cssclass: CSS class of the calendar widget (default is commandCal)
       
    28  *
       
    29  *   show() / hide():
       
    30  *    show or hide the calendar widget
       
    31  *
       
    32  *   toggle():
       
    33  *    show (resp. hide) the calendar if it's hidden (resp. displayed)
       
    34  * 
       
    35  *   displayNextMonth(): (resp. displayPreviousMonth())
       
    36  *    update the calendar to display next (resp. previous) month
       
    37  */
       
    38 Calendar = function(containerId, inputId, year, month, cssclass) {
       
    39     this.containerId = containerId;
       
    40     this.inputId = inputId;
       
    41     this.year = year;
       
    42     this.month = month-1; // Javascript's counter starts at 0 for january
       
    43     this.cssclass = cssclass || "popupCalendar";
       
    44     this.visible = false;
       
    45     this.domtable = null;
       
    46 
       
    47     this.cellprops = { 'onclick'     : function() {dateSelected(this, containerId); },
       
    48 		       'onmouseover' : function() {this.style.fontWeight = 'bold'; },
       
    49 		       'onmouseout'  : function() {this.style.fontWeight = 'normal';}
       
    50 		     }
       
    51 
       
    52     this.todayprops = jQuery.extend({}, this.cellprops, {'class' : 'today'});
       
    53 
       
    54     this._rowdisplay = function(row) {
       
    55 	return TR(null, map(partial(TD, this.cellprops), row));
       
    56     }
       
    57 
       
    58     this._makecell = function(cellinfo) {
       
    59 	return TD(cellinfo[0], cellinfo[1]);
       
    60     }
       
    61 
       
    62     /* utility function (the only use for now is inside the calendar) */
       
    63     this._uppercaseFirst = function(s) { return s.charAt(0).toUpperCase(); }
       
    64     
       
    65     /* accepts the cells data and builds the corresponding TR nodes
       
    66      * @param rows a list of list of couples (daynum, cssprops)
       
    67      */
       
    68     this._domForRows = function(rows) {
       
    69 	var lines = []
       
    70 	for (i=0; i<rows.length; i++) {
       
    71 	    lines.push(TR(null, map(this._makecell, rows[i])));
       
    72 	}
       
    73 	return lines;
       
    74     }
       
    75 
       
    76     /* builds the calendar headers */
       
    77     this._headdisplay = function(row) {
       
    78 	if (_CAL_HEADER) {
       
    79 	    return _CAL_HEADER;
       
    80 	}
       
    81 	daynames = map(this._uppercaseFirst, DAYNAMES);
       
    82 	_CAL_HEADER = TR(null, map(partial(TH, null), daynames));
       
    83 	return _CAL_HEADER;
       
    84     }
       
    85     
       
    86     this._getrows = function() {
       
    87 	var rows = [];
       
    88 	var firstday = new Date(this.year, this.month, 1);
       
    89 	var stopdate = firstday.nextMonth();
       
    90 	var curdate = firstday.sub(firstday.getRealDay());
       
    91 	while (curdate.getTime() < stopdate) {
       
    92 	    var row = []
       
    93 	    for (var i=0; i<7; i++) {
       
    94 		if (curdate.getMonth() == this.month) {
       
    95 		    props = curdate.equals(TODAY) ? this.todayprops:this.cellprops;
       
    96 		    row.push([props, curdate.getDate()]);
       
    97 		} else {
       
    98 		    row.push([this.cellprops, ""]);
       
    99 		}
       
   100 		curdate.iadd(1);
       
   101 	    }
       
   102 	    rows.push(row);
       
   103 	}
       
   104 	return rows;
       
   105     }
       
   106 
       
   107     this._makecal = function() {
       
   108 	var rows = this._getrows();
       
   109 	var monthname = MONTHNAMES[this.month] + " " + this.year;
       
   110 	var prevlink = "javascript: togglePreviousMonth('" + this.containerId + "');";
       
   111 	var nextlink = "javascript: toggleNextMonth('" + this.containerId + "');";
       
   112 	this.domtable = TABLE({'class': this.cssclass},
       
   113 			      THEAD(null, TR(null,
       
   114 					     TH(null, A({'href' : prevlink}, "<<")),
       
   115 					     TH({'colspan' : 5, 'style' : "text-align: center;"}, monthname),
       
   116 					     TH(null, A({'href' : nextlink}, ">>")))),
       
   117 			      TBODY(null,
       
   118 				    this._headdisplay(),
       
   119 				    this._domForRows(rows))
       
   120 			     );
       
   121 	return this.domtable;
       
   122     }
       
   123 
       
   124     this._updateDiv = function() {
       
   125 	if (!this.domtable) {
       
   126 	    this._makecal();
       
   127 	}
       
   128 	jqNode(this.containerId).empty().append(this.domtable);
       
   129 	// replaceChildNodes($(this.containerId), this.domtable);
       
   130     }
       
   131 
       
   132     this.displayNextMonth = function() {
       
   133 	this.domtable = null;
       
   134 	if (this.month == 11) {
       
   135 	    this.year++;
       
   136 	}
       
   137 	this.month = (this.month+1) % 12;
       
   138 	this._updateDiv();
       
   139     }
       
   140 
       
   141     this.displayPreviousMonth = function() {
       
   142 	this.domtable = null;
       
   143 	if (this.month == 0) {
       
   144 	    this.year--;
       
   145 	}
       
   146 	this.month = (this.month+11) % 12;
       
   147 	this._updateDiv();
       
   148     }
       
   149     
       
   150     this.show = function() {
       
   151 	if (!this.visible) {
       
   152 	    container = jqNode(this.containerId);
       
   153 	    if (!this.domtable) {
       
   154 		this._makecal();
       
   155 	    }
       
   156 	    container.empty().append(this.domtable);
       
   157 	    toggleVisibility(container);
       
   158 	    this.visible = true;
       
   159 	}
       
   160     }
       
   161 
       
   162     this.hide = function(event) {
       
   163 	var self;
       
   164 	if (event) {
       
   165 	    self = event.data.self;
       
   166 	} else {
       
   167 	    self = this;
       
   168 	}
       
   169 	if (self.visible) {
       
   170 	    toggleVisibility(self.containerId);
       
   171 	    self.visible = false;
       
   172 	}
       
   173     }
       
   174 
       
   175     this.toggle = function() {
       
   176 	if (this.visible) {
       
   177 	    this.hide();
       
   178 	}
       
   179 	else {
       
   180 	    this.show();
       
   181 	}
       
   182     }
       
   183 
       
   184     // call hide() when the user explicitly sets the focus on the matching input
       
   185     jqNode(inputId).bind('focus', {'self': this}, this.hide); // connect(inputId, 'onfocus', this, 'hide');
       
   186 };
       
   187 
       
   188 // keep track of each calendar created
       
   189 Calendar.REGISTRY = {};
       
   190 
       
   191 /*
       
   192  * popup / hide calendar associated to `containerId`
       
   193  */	    
       
   194 function toggleCalendar(containerId, inputId, year, month) {
       
   195     var cal = Calendar.REGISTRY[containerId];
       
   196     if (!cal) {
       
   197 	cal = new Calendar(containerId, inputId, year, month);
       
   198 	Calendar.REGISTRY[containerId] = cal;
       
   199     }
       
   200     /* hide other calendars */
       
   201     for (containerId in Calendar.REGISTRY) {
       
   202 	var othercal = Calendar.REGISTRY[containerId];
       
   203 	if (othercal !== cal) {
       
   204 	    othercal.hide();
       
   205 	}
       
   206     }
       
   207     cal.toggle();
       
   208 }
       
   209 
       
   210 
       
   211 /*
       
   212  * ask for next month to calendar displayed in `containerId`
       
   213  */
       
   214 function toggleNextMonth(containerId) {
       
   215     var cal = Calendar.REGISTRY[containerId];
       
   216     cal.displayNextMonth();
       
   217 }
       
   218 
       
   219 /*
       
   220  * ask for previous month to calendar displayed in `containerId`
       
   221  */
       
   222 function togglePreviousMonth(containerId) {
       
   223     var cal = Calendar.REGISTRY[containerId];
       
   224     cal.displayPreviousMonth();
       
   225 }
       
   226 
       
   227 
       
   228 /*
       
   229  * Callback called when the user clicked on a cell in the popup calendar
       
   230  */
       
   231 function dateSelected(cell, containerId) {
       
   232     var cal = Calendar.REGISTRY[containerId];
       
   233     var input = getNode(cal.inputId);
       
   234     // XXX: the use of innerHTML might cause problems, but it seems to be
       
   235     //      the only way understood by both IE and Mozilla. Otherwise,
       
   236     //      IE accepts innerText and mozilla accepts textContent
       
   237     var selectedDate = new Date(cal.year, cal.month, cell.innerHTML, 12);
       
   238     var xxx = remote_exec("format_date", toISOTimestamp(selectedDate));
       
   239     input.value = xxx;
       
   240     cal.hide();
       
   241 }
       
   242 
       
   243 function whichElement(e)
       
   244 {
       
   245 var targ;
       
   246 if (!e)
       
   247   {
       
   248   var e=window.event;
       
   249   }
       
   250 if (e.target)
       
   251   {
       
   252   targ=e.target;
       
   253   }
       
   254 else if (e.srcElement)
       
   255   {
       
   256   targ=e.srcElement;
       
   257   }
       
   258 if (targ.nodeType==3) // defeat Safari bug
       
   259   {
       
   260   targ = targ.parentNode;
       
   261   }
       
   262   return targ;
       
   263 }
       
   264 
       
   265 function getPosition(element) {
       
   266   var left;
       
   267   var top;
       
   268   var offset;
       
   269   // TODO: deal scrollbar positions also!
       
   270   left = element.offsetLeft;
       
   271   top = element.offsetTop;
       
   272 
       
   273   if (element.offsetParent != null)
       
   274     {
       
   275       offset = getPosition(element.offsetParent);
       
   276       left = left + offset[0];
       
   277       top = top + offset[1];
       
   278       
       
   279     }
       
   280   return [left, top];
       
   281 }
       
   282 
       
   283 function getMouseInBlock(event) {
       
   284   var elt = event.target;
       
   285   var x = event.clientX;
       
   286   var y = event.clientY;
       
   287   var w = elt.clientWidth;
       
   288   var h = elt.clientHeight;
       
   289   var offset = getPosition(elt);
       
   290 
       
   291   x = 1.0*(x-offset[0])/w;
       
   292   y = 1.0*(y-offset[1])/h;
       
   293   return [x, y];
       
   294 }
       
   295 function getHourFromMouse(event, hmin, hmax) {
       
   296   var pos = getMouseInBlock(event);
       
   297   var y = pos[1];
       
   298   return Math.floor((hmax-hmin)*y + hmin);
       
   299 }
       
   300 
       
   301 function addCalendarItem(event, hmin, hmax, year, month, day, duration, baseurl) {
       
   302   var hour = getHourFromMouse(event, hmin, hmax);
       
   303 
       
   304   if (0<=hour && hour < 24) {
       
   305     baseurl += "&start="+year+"%2F"+month+"%2F"+day+"%20"+hour+":00";
       
   306     baseurl += "&stop="+year+"%2F"+month+"%2F"+day+"%20"+(hour+duration)+":00";
       
   307     
       
   308     stopPropagation(event);
       
   309     window.location.assign(baseurl);
       
   310     return false;
       
   311   }
       
   312   return true;
       
   313 }
       
   314 
       
   315 function stopPropagation(event) {
       
   316   event.cancelBubble = true;
       
   317   if (event.stopPropagation) event.stopPropagation();  
       
   318 }
       
   319      
       
   320 CubicWeb.provide('calendar.js');