cubicweb/web/data/cubicweb.calendar.js
changeset 11057 0b59724cb3f2
parent 7529 2fdc310be7cd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/web/data/cubicweb.calendar.js	Sat Jan 16 13:48:51 2016 +0100
@@ -0,0 +1,361 @@
+/**
+ *  This file contains Calendar utilities
+ *  :organization: Logilab
+ *  :copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+ *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+ */
+
+// IMPORTANT NOTE: the variables DAYNAMES AND MONTHNAMES will be added
+//                 by cubicweb automatically
+// dynamically computed (and cached)
+var _CAL_HEADER = null;
+
+TODAY = new Date();
+
+/**
+ * .. class:: Calendar
+ *
+ *   Calendar (graphical) widget
+ *
+ *   public methods are :
+ *
+ *   __init__ :
+ *    :attr:`containerId`: the DOM node's ID where the calendar will be displayed
+ *    :attr:`inputId`: which input needs to be updated when a date is selected
+ *    :attr:`year`, :attr:`month`: year and month to be displayed
+ *    :attr:`cssclass`: CSS class of the calendar widget (default is 'commandCal')
+ *
+ *   show() / hide():
+ *    show or hide the calendar widget
+ *
+ *   toggle():
+ *    show (resp. hide) the calendar if it's hidden (resp. displayed)
+ *
+ *   displayNextMonth(): (resp. displayPreviousMonth())
+ *    update the calendar to display next (resp. previous) month
+ */
+Calendar = function(containerId, inputId, year, month, cssclass) {
+    this.containerId = containerId;
+    this.inputId = inputId;
+    this.year = year;
+    this.month = month - 1; // Javascript's counter starts at 0 for january
+    this.cssclass = cssclass || "popupCalendar";
+    this.visible = false;
+    this.domtable = null;
+
+    this.cellprops = {
+        'onclick': function() {
+            dateSelected(this, containerId);
+        },
+        'onmouseover': function() {
+            this.style.fontWeight = 'bold';
+        },
+        'onmouseout': function() {
+            this.style.fontWeight = 'normal';
+        }
+    };
+
+    this.todayprops = jQuery.extend({},
+    this.cellprops, {
+        'class': 'today'
+    });
+
+    this._rowdisplay = function(row) {
+        var _td = function(elt) {
+            return TD(this.cellprops, elt);
+        };
+        return TR(null, $.map(row, _td));
+    };
+
+    this._makecell = function(cellinfo) {
+        return TD(cellinfo[0], cellinfo[1]);
+    };
+
+    /**
+     * .. function:: Calendar._uppercaseFirst(s)
+     *
+     *    utility function (the only use for now is inside the calendar)
+     */
+    this._uppercaseFirst = function(s) {
+        return s.charAt(0).toUpperCase();
+    };
+
+    /**
+     * .. function:: Calendar._domForRows(rows)
+     *
+     *    accepts the cells data and builds the corresponding TR nodes
+     *
+     * * `rows`, a list of list of couples (daynum, cssprops)
+     */
+    this._domForRows = function(rows) {
+        var lines = [];
+        for (i = 0; i < rows.length; i++) {
+            lines.push(TR(null, $.map(rows[i], this._makecell)));
+        }
+        return lines;
+    };
+
+    /**
+     * .. function:: Calendar._headdisplay(row)
+     *
+     *    builds the calendar headers
+     */
+    this._headdisplay = function(row) {
+        if (_CAL_HEADER) {
+            return _CAL_HEADER;
+        }
+        var self = this;
+        var _th = function(day) {
+            return TH(null, self._uppercaseFirst(day));
+        };
+        return TR(null, $.map(DAYNAMES, _th));
+    };
+
+    this._getrows = function() {
+        var rows = [];
+        var firstday = new Date(this.year, this.month, 1);
+        var stopdate = firstday.nextMonth();
+        var curdate = firstday.sub(firstday.getRealDay());
+        while (curdate.getTime() < stopdate) {
+            var row = [];
+            for (var i = 0; i < 7; i++) {
+                if (curdate.getMonth() == this.month) {
+                    props = curdate.equals(TODAY) ? this.todayprops: this.cellprops;
+                    row.push([props, curdate.getDate()]);
+                } else {
+                    row.push([this.cellprops, ""]);
+                }
+                curdate.iadd(1);
+            }
+            rows.push(row);
+        }
+        return rows;
+    };
+
+    this._makecal = function() {
+        var rows = this._getrows();
+        var monthname = MONTHNAMES[this.month] + " " + this.year;
+        var prevlink = "javascript: togglePreviousMonth('" + this.containerId + "');";
+        var nextlink = "javascript: toggleNextMonth('" + this.containerId + "');";
+        this.domtable = TABLE({
+            'class': this.cssclass
+        },
+        THEAD(null, TR(null, TH(null, A({
+            'href': prevlink
+        },
+        "<<")),
+        // IE 6/7 requires colSpan instead of colspan
+        TH({
+            'colSpan': 5,
+            'colspan': 5,
+            'style': "text-align: center;"
+        },
+        monthname), TH(null, A({
+            'href': nextlink
+        },
+        ">>")))), TBODY(null, this._headdisplay(), this._domForRows(rows)));
+        return this.domtable;
+    };
+
+    this._updateDiv = function() {
+        if (!this.domtable) {
+            this._makecal();
+        }
+        cw.jqNode(this.containerId).empty().append(this.domtable);
+        // replaceChildNodes($(this.containerId), this.domtable);
+    };
+
+    this.displayNextMonth = function() {
+        this.domtable = null;
+        if (this.month == 11) {
+            this.year++;
+        }
+        this.month = (this.month + 1) % 12;
+        this._updateDiv();
+    };
+
+    this.displayPreviousMonth = function() {
+        this.domtable = null;
+        if (this.month == 0) {
+            this.year--;
+        }
+        this.month = (this.month + 11) % 12;
+        this._updateDiv();
+    };
+
+    this.show = function() {
+        if (!this.visible) {
+            var container = cw.jqNode(this.containerId);
+            if (!this.domtable) {
+                this._makecal();
+            }
+            container.empty().append(this.domtable);
+            toggleVisibility(container);
+            this.visible = true;
+        }
+    };
+
+    this.hide = function(event) {
+        var self;
+        if (event) {
+            self = event.data.self;
+        } else {
+            self = this;
+        }
+        if (self.visible) {
+            toggleVisibility(self.containerId);
+            self.visible = false;
+        }
+    };
+
+    this.toggle = function() {
+        if (this.visible) {
+            this.hide();
+        }
+        else {
+            this.show();
+        }
+    };
+
+    // call hide() when the user explicitly sets the focus on the matching input
+    cw.jqNode(inputId).bind('focus', {
+        'self': this
+    },
+    this.hide); // connect(inputId, 'onfocus', this, 'hide');
+};
+
+/**
+ * .. data:: Calendar.REGISTRY
+ *
+ *     keep track of each calendar created
+ */
+Calendar.REGISTRY = {};
+
+/**
+ * .. function:: toggleCalendar(containerId, inputId, year, month)
+ *
+ *    popup / hide calendar associated to `containerId`
+ */
+function toggleCalendar(containerId, inputId, year, month) {
+    var cal = Calendar.REGISTRY[containerId];
+    if (!cal) {
+        cal = new Calendar(containerId, inputId, year, month);
+        Calendar.REGISTRY[containerId] = cal;
+    }
+    /* hide other calendars */
+    for (containerId in Calendar.REGISTRY) {
+        var othercal = Calendar.REGISTRY[containerId];
+        if (othercal !== cal) {
+            othercal.hide();
+        }
+    }
+    cal.toggle();
+}
+
+/**
+ * .. function:: toggleNextMonth(containerId)
+ *
+ *    ask for next month to calendar displayed in `containerId`
+ */
+function toggleNextMonth(containerId) {
+    var cal = Calendar.REGISTRY[containerId];
+    cal.displayNextMonth();
+}
+
+/**
+ * .. function:: togglePreviousMonth(containerId)
+ *
+ *    ask for previous month to calendar displayed in `containerId`
+ */
+function togglePreviousMonth(containerId) {
+    var cal = Calendar.REGISTRY[containerId];
+    cal.displayPreviousMonth();
+}
+
+/**
+ * .. function:: dateSelected(cell, containerId)
+ *
+ *    callback called when the user clicked on a cell in the popup calendar
+ */
+function dateSelected(cell, containerId) {
+    var cal = Calendar.REGISTRY[containerId];
+    var input = cw.getNode(cal.inputId);
+    // XXX: the use of innerHTML might cause problems, but it seems to be
+    //      the only way understood by both IE and Mozilla. Otherwise,
+    //      IE accepts innerText and mozilla accepts textContent
+    var selectedDate = new Date(cal.year, cal.month, cell.innerHTML, 12);
+    input.value = remoteExec("format_date", cw.utils.toISOTimestamp(selectedDate));
+    cal.hide();
+}
+
+function whichElement(e) {
+    var targ;
+    if (!e) {
+        e = window.event;
+    }
+    if (e.target) {
+        targ = e.target;
+    }
+    else if (e.srcElement) {
+        targ = e.srcElement;
+    }
+    if (targ.nodeType == 3) // defeat Safari bug
+    {
+        targ = targ.parentNode;
+    }
+    return targ;
+}
+
+function getPosition(element) {
+    var left;
+    var top;
+    var offset;
+    // TODO: deal scrollbar positions also!
+    left = element.offsetLeft;
+    top = element.offsetTop;
+
+    if (element.offsetParent != null) {
+        offset = getPosition(element.offsetParent);
+        left = left + offset[0];
+        top = top + offset[1];
+
+    }
+    return [left, top];
+}
+
+function getMouseInBlock(event) {
+    var elt = event.target;
+    var x = event.clientX;
+    var y = event.clientY;
+    var w = elt.clientWidth;
+    var h = elt.clientHeight;
+    var offset = getPosition(elt);
+
+    x = 1.0 * (x - offset[0]) / w;
+    y = 1.0 * (y - offset[1]) / h;
+    return [x, y];
+}
+function getHourFromMouse(event, hmin, hmax) {
+    var pos = getMouseInBlock(event);
+    var y = pos[1];
+    return Math.floor((hmax - hmin) * y + hmin);
+}
+
+function addCalendarItem(event, hmin, hmax, year, month, day, duration, baseurl) {
+    var hour = getHourFromMouse(event, hmin, hmax);
+
+    if (0 <= hour && hour < 24) {
+        baseurl += "&start=" + year + "%2F" + month + "%2F" + day + "%20" + hour + ":00";
+        baseurl += "&stop=" + year + "%2F" + month + "%2F" + day + "%20" + (hour + duration) + ":00";
+
+        stopPropagation(event);
+        window.location.assign(baseurl);
+        return false;
+    }
+    return true;
+}
+
+function stopPropagation(event) {
+    event.cancelBubble = true;
+    if (event.stopPropagation) event.stopPropagation();
+}