1.1 --- a/htdocs/styles.css Sun Sep 27 01:26:05 2015 +0200
1.2 +++ b/htdocs/styles.css Sun Sep 27 02:19:42 2015 +0200
1.3 @@ -1,6 +1,5 @@
1.4 /* Table styling. */
1.5
1.6 -table.calendar,
1.7 table.conflicts,
1.8 table.counters,
1.9 table.recurrence,
1.10 @@ -8,6 +7,34 @@
1.11 border: 2px solid #000;
1.12 }
1.13
1.14 +div.calendar {
1.15 + display: table;
1.16 +}
1.17 +
1.18 +div.dayheading {
1.19 + display: table-caption;
1.20 + text-align: center;
1.21 + width: 100%;
1.22 +}
1.23 +
1.24 +div.tablerow {
1.25 + display: table-row;
1.26 +}
1.27 +
1.28 +div.tableheading {
1.29 + display: table-header-group;
1.30 +}
1.31 +
1.32 +div.tablebody {
1.33 + display: table-row-group;
1.34 +}
1.35 +
1.36 +div.tablecell {
1.37 + display: table-cell;
1.38 + padding: 0.25em;
1.39 + width: 10em;
1.40 +}
1.41 +
1.42 colgroup#columns-request {
1.43 background-color: #eef;
1.44 }
1.45 @@ -16,73 +43,76 @@
1.46 background-color: #fee;
1.47 }
1.48
1.49 -th.requestheading {
1.50 +.requestheading {
1.51 background-color: #aaf;
1.52 }
1.53
1.54 -th.participantheading{
1.55 +.participantheading{
1.56 background-color: #faa;
1.57 }
1.58
1.59 -th.dayheading,
1.60 -th.mainheading {
1.61 +.dayheading,
1.62 +.mainheading {
1.63 background-color: #f85;
1.64 }
1.65
1.66 -th.timeslot,
1.67 -th.objectheading {
1.68 +.timeslot,
1.69 +.objectheading {
1.70 white-space: nowrap;
1.71 }
1.72
1.73 -th.objectheading {
1.74 +.objectheading {
1.75 background-color: #fca;
1.76 }
1.77
1.78 -th.timeslot {
1.79 +.timeslot {
1.80 padding-top: 0;
1.81 vertical-align: top;
1.82 }
1.83
1.84 -th.timeslot span.endpoint {
1.85 +.timeslot span.endpoint {
1.86 display: none;
1.87 font-size: smaller;
1.88 }
1.89
1.90 -td.event {
1.91 +.event {
1.92 background-color: #ff8;
1.93 - border: 2px solid #000;
1.94 + border-right: 2px solid #777;
1.95 }
1.96
1.97 -td.event.only-organising {
1.98 +.event.only-organising {
1.99 background-color: #afd;
1.100 }
1.101
1.102 -td.event.organising {
1.103 +.event.organising {
1.104 background-color: #af8;
1.105 }
1.106
1.107 -td.event.continued {
1.108 +.event.continued {
1.109 border-top: 2px dotted #000;
1.110 }
1.111
1.112 -td.event.continues {
1.113 +.event.continues {
1.114 border-bottom: 2px dotted #000;
1.115 }
1.116
1.117 -td.event:target {
1.118 - border-width: 4px;
1.119 +.event.ends {
1.120 + border-bottom: 2px solid #777;
1.121 +}
1.122 +
1.123 +.event:target {
1.124 background-color: #ee2;
1.125 }
1.126
1.127 -td.event.organising:target {
1.128 +.event.organising:target {
1.129 background-color: #5f4;
1.130 }
1.131
1.132 -td.event a {
1.133 +.event a {
1.134 color: #009;
1.135 }
1.136
1.137 -th.objectheading.error {
1.138 +.objectheading.error {
1.139 background-color: #f77;
1.140 }
1.141
1.142 @@ -136,17 +166,16 @@
1.143 display: inline;
1.144 }
1.145
1.146 -th.container,
1.147 -td.container {
1.148 +.container {
1.149 padding: 0; /* for regions covered by labels */
1.150 }
1.151
1.152 -th.dayheading:hover,
1.153 -th.dayheading:focus,
1.154 -th.timeslot:hover,
1.155 -th.timeslot:focus,
1.156 -td.container:hover,
1.157 -td.container:focus {
1.158 +.dayheading:hover,
1.159 +.dayheading:focus,
1.160 +.timeslot:hover,
1.161 +.timeslot:focus,
1.162 +.container:hover,
1.163 +.container:focus {
1.164 background-color: #af8;
1.165 }
1.166
1.167 @@ -161,8 +190,8 @@
1.168 text-align: center;
1.169 }
1.170
1.171 -td.container:hover label.newevent.popup,
1.172 -td.container:focus label.newevent.popup {
1.173 +.container:hover label.newevent.popup,
1.174 +.container:focus label.newevent.popup {
1.175 visibility: visible;
1.176 }
1.177
1.178 @@ -183,9 +212,10 @@
1.179
1.180 /* Hide calendar rows depending on the selected controls. */
1.181
1.182 -input#hidebusy:checked ~ .calendar tr.slot.busy,
1.183 -input#showdays:not(:checked) ~ .calendar thead.separator.empty,
1.184 -input#showdays:not(:checked) ~ .calendar tbody.points.empty,
1.185 +input#hidebusy:checked ~ .calendar div.slot.busy,
1.186 +input#showdays:not(:checked) ~ .calendar div.dayheading.empty,
1.187 +input#showdays:not(:checked) ~ .calendar div.tableheading.empty,
1.188 +input#showdays:not(:checked) ~ .calendar div.points.empty,
1.189
1.190 /* Hiding/showing end datetimes and start/end times. */
1.191
1.192 @@ -215,7 +245,7 @@
1.193
1.194 /* Show slot endpoints when hiding adjacent busy periods. */
1.195
1.196 -input#hidebusy:checked ~ .calendar th.timeslot span.endpoint {
1.197 +input#hidebusy:checked ~ .calendar .timeslot span.endpoint {
1.198 display: block;
1.199 }
1.200
1.201 @@ -283,11 +313,11 @@
1.202 }
1.203
1.204 label.showdays {
1.205 - border-left: 1em solid #faa; /* th.participantheading background-color */
1.206 + border-left: 1em solid #faa; /* .participantheading background-color */
1.207 }
1.208
1.209 label.hidebusy {
1.210 - border-left: 1em solid #af8; /* td.event background-color */
1.211 + border-left: 1em solid #af8; /* .event background-color */
1.212 }
1.213
1.214 label.reset {
2.1 --- a/imipweb/calendar.py Sun Sep 27 01:26:05 2015 +0200
2.2 +++ b/imipweb/calendar.py Sun Sep 27 02:19:42 2015 +0200
2.3 @@ -409,10 +409,7 @@
2.4
2.5 # Show the calendar itself.
2.6
2.7 - page.table(cellspacing=5, cellpadding=5, class_="calendar")
2.8 - self.show_calendar_participant_headings(partitioned_group_types, partitioned_group_sources, group_columns)
2.9 - self.show_calendar_days(days, partitioned_groups, partitioned_group_types, group_columns)
2.10 - page.table.close()
2.11 + self.show_calendar_days(days, partitioned_groups, partitioned_group_types, partitioned_group_sources, group_columns)
2.12
2.13 # End the form region.
2.14
2.15 @@ -443,8 +440,8 @@
2.16 for day in days:
2.17 daystr, dayid = self._day_value_and_identifier(day)
2.18 l.append("""\
2.19 -input.newevent.selector#%s:checked ~ table thead#region-%s label.day,
2.20 -input.newevent.selector#%s:checked ~ table tbody#region-%s label.timepoint""" % (dayid, dayid, dayid, dayid))
2.21 +input.newevent.selector#%s:checked ~ div div#region-%s label.day,
2.22 +input.newevent.selector#%s:checked ~ div div#region-%s label.timepoint""" % (dayid, dayid, dayid, dayid))
2.23
2.24 page.add(",\n".join(l))
2.25 page.add(""" {
2.26 @@ -480,7 +477,7 @@
2.27 for point, endpoint in intervals:
2.28 timestr, timeid = self._slot_value_and_identifier(point, endpoint)
2.29 l.append("""\
2.30 -input.newevent.selector#%s:checked ~ table th#region-%s label.timepoint""" % (timeid, timeid))
2.31 +input.newevent.selector#%s:checked ~ div div#region-%s label.timepoint""" % (timeid, timeid))
2.32
2.33 page.add(",\n".join(l))
2.34 page.add(""" {
2.35 @@ -491,7 +488,7 @@
2.36
2.37 page.style.close()
2.38
2.39 - def show_calendar_participant_headings(self, group_types, group_sources, group_columns):
2.40 + def show_calendar_participant_headings(self, group_types, group_sources, group_columns, is_empty):
2.41
2.42 """
2.43 Show headings for the participants and other scheduling contributors,
2.44 @@ -500,24 +497,20 @@
2.45
2.46 page = self.page
2.47
2.48 - page.colgroup(span=1, id="columns-timeslot")
2.49 -
2.50 - for group_type, columns in zip(group_types, group_columns):
2.51 - page.colgroup(span=max(columns, 1), id="columns-%s" % group_type)
2.52 -
2.53 - page.thead()
2.54 - page.tr()
2.55 - page.th("", class_="emptyheading")
2.56 + page.div(class_="tableheading%s" % (is_empty and " empty" or ""))
2.57 + page.div(class_="tablerow")
2.58 + page.div("", class_="tablecell emptyheading")
2.59
2.60 for group_type, source, columns in zip(group_types, group_sources, group_columns):
2.61 - page.th(source,
2.62 - class_=(group_type == "request" and "requestheading" or "participantheading"),
2.63 - colspan=max(columns, 1))
2.64 + page.div(source,
2.65 + class_="tablecell %s" % (group_type == "request" and "requestheading" or "participantheading"))
2.66 + for i in range(1, max(columns, 1)):
2.67 + page.div("", class_="tablecell %s" % (group_type == "request" and "requestheading" or "participantheading"))
2.68
2.69 - page.tr.close()
2.70 - page.thead.close()
2.71 + page.div.close()
2.72 + page.div.close()
2.73
2.74 - def show_calendar_days(self, days, partitioned_groups, partitioned_group_types, group_columns):
2.75 + def show_calendar_days(self, days, partitioned_groups, partitioned_group_types, partitioned_group_sources, group_columns):
2.76
2.77 """
2.78 Show calendar days, defined by a collection of 'days', the contributing
2.79 @@ -556,17 +549,18 @@
2.80
2.81 daystr, dayid = self._day_value_and_identifier(day)
2.82
2.83 - page.thead(class_="separator%s" % (is_empty and " empty" or ""), id="region-%s" % dayid)
2.84 - page.tr()
2.85 - page.th(class_="dayheading container", colspan=all_columns+1)
2.86 + page.div(class_="calendar")
2.87 +
2.88 + page.div(class_="dayheading container separator%s" % (is_empty and " empty" or ""), id="region-%s" % dayid)
2.89 self._day_heading(day)
2.90 - page.th.close()
2.91 - page.tr.close()
2.92 - page.thead.close()
2.93 + page.div.close()
2.94 +
2.95 + self.show_calendar_participant_headings(partitioned_group_types, partitioned_group_sources, group_columns, is_empty)
2.96
2.97 - page.tbody(class_="points%s" % (is_empty and " empty" or ""), id="region-%s" % dayid)
2.98 + page.div(class_="tablebody points%s" % (is_empty and " empty" or ""), id="region-%s" % dayid)
2.99 self.show_calendar_points(intervals, groups_for_day, partitioned_group_types, group_columns)
2.100 - page.tbody.close()
2.101 + page.div.close()
2.102 + page.div.close()
2.103
2.104 def show_calendar_points(self, intervals, groups, group_types, group_columns):
2.105
2.106 @@ -612,14 +606,14 @@
2.107 continuation and "daystart" or ""
2.108 ])
2.109
2.110 - page.tr(class_=css)
2.111 + page.div(class_="tablerow %s" % css)
2.112 if point.indicator == Point.PRINCIPAL:
2.113 timestr, timeid = self._slot_value_and_identifier(point, endpoint)
2.114 - page.th(class_="timeslot", id="region-%s" % timeid)
2.115 + page.div(class_="tablecell timeslot", id="region-%s" % timeid)
2.116 self._time_point(point, endpoint)
2.117 else:
2.118 - page.th()
2.119 - page.th.close()
2.120 + page.div(class_="tablecell")
2.121 + page.div.close()
2.122
2.123 # Obtain slots for the time point from each group.
2.124
2.125 @@ -662,47 +656,56 @@
2.126 # Points defining the ends of instant events should
2.127 # never define the start of new events.
2.128
2.129 - if point.indicator == Point.PRINCIPAL and (point.point == p.get_start() or continuation):
2.130 + first_cell_in_group = point.indicator == Point.PRINCIPAL and (point.point == p.get_start() or continuation)
2.131
2.132 - has_continued = continuation and point.point != p.get_start()
2.133 - will_continue = not ends_on_same_day(point.point, p.get_end(), tzid)
2.134 - is_organiser = p.organiser == self.user
2.135 + has_continued = continuation and point.point != p.get_start()
2.136 + will_continue = not endpoint and not ends_on_same_day(point.point, p.get_end(), tzid)
2.137 + is_organiser = p.organiser == self.user
2.138 +
2.139 + last_cell = not will_continue and (not endpoint or endpoint.point == p.get_end())
2.140
2.141 - css = " ".join([
2.142 - "event",
2.143 - has_continued and "continued" or "",
2.144 - will_continue and "continues" or "",
2.145 - p.transp == "ORG" and "only-organising" or is_organiser and "organising" or "attending",
2.146 - self._have_request(p.uid, p.recurrenceid, "COUNTER", True) and "counter" or "",
2.147 - ])
2.148 + css = " ".join([
2.149 + "event",
2.150 + has_continued and "continued" or "",
2.151 + will_continue and "continues" or "",
2.152 + last_cell and "ends" or "",
2.153 + p.transp == "ORG" and "only-organising" or is_organiser and "organising" or "attending",
2.154 + self._have_request(p.uid, p.recurrenceid, "COUNTER", True) and "counter" or "",
2.155 + ])
2.156
2.157 - # Only anchor the first cell of events.
2.158 - # Need to only anchor the first period for a recurring
2.159 - # event.
2.160 + # Only anchor the first cell of events.
2.161 + # Need to only anchor the first period for a recurring
2.162 + # event.
2.163
2.164 - html_id = "%s-%s-%s" % (group_type, p.uid, p.recurrenceid or "")
2.165 + html_id = "%s-%s-%s" % (group_type, p.uid, p.recurrenceid or "")
2.166
2.167 - if point.point == p.get_start() and html_id not in self.html_ids:
2.168 - page.td(class_=css, rowspan=span, id=html_id)
2.169 - self.html_ids.add(html_id)
2.170 - else:
2.171 - page.td(class_=css, rowspan=span)
2.172 + if point.point == p.get_start() and html_id not in self.html_ids:
2.173 + page.div(class_="tablecell %s" % css, id=html_id)
2.174 + self.html_ids.add(html_id)
2.175 + else:
2.176 + page.div(class_="tablecell %s" % css)
2.177
2.178 - # Only link to events if they are not being updated
2.179 - # by requests.
2.180 + # Continue event spans using empty cells.
2.181 +
2.182 + if not first_cell_in_group:
2.183 + pass
2.184 +
2.185 + # Only link to events if they are not being updated
2.186 + # by requests.
2.187
2.188 - if not p.summary or \
2.189 - group_type != "request" and self._have_request(p.uid, p.recurrenceid, None, True):
2.190 + elif not p.summary or \
2.191 + group_type != "request" and self._have_request(p.uid, p.recurrenceid, None, True):
2.192
2.193 - page.span(p.summary or "(Participant is busy)")
2.194 + page.span(p.summary or "(Participant is busy)")
2.195
2.196 - # Link to requests and events (including ones for
2.197 - # which counter-proposals exist).
2.198 + # Link to requests and events (including ones for
2.199 + # which counter-proposals exist).
2.200
2.201 - else:
2.202 - page.a(p.summary, href=self.link_to(p.uid, p.recurrenceid))
2.203 + else:
2.204 + page.a(p.summary, href=self.link_to(p.uid, p.recurrenceid))
2.205
2.206 - page.td.close()
2.207 + page.div.close()
2.208 +
2.209 else:
2.210 empty += 1
2.211
2.212 @@ -713,7 +716,7 @@
2.213 if empty:
2.214 self._empty_slot(point, endpoint, empty)
2.215
2.216 - page.tr.close()
2.217 + page.div.close()
2.218
2.219 def _day_heading(self, day):
2.220
2.221 @@ -767,11 +770,13 @@
2.222 """
2.223
2.224 page = self.page
2.225 - page.td(class_="empty%s" % (point.indicator == Point.PRINCIPAL and " container" or ""), colspan=colspan)
2.226 - if point.indicator == Point.PRINCIPAL:
2.227 - value, identifier = self._slot_value_and_identifier(point, endpoint)
2.228 - page.label("Select/deselect period", class_="newevent popup", for_=identifier)
2.229 - page.td.close()
2.230 +
2.231 + for i in range(0, colspan):
2.232 + page.div(class_="tablecell empty%s" % (point.indicator == Point.PRINCIPAL and " container" or ""))
2.233 + if point.indicator == Point.PRINCIPAL:
2.234 + value, identifier = self._slot_value_and_identifier(point, endpoint)
2.235 + page.label("Select/deselect period", class_="newevent popup", for_=identifier)
2.236 + page.div.close()
2.237
2.238 def _day_value_and_identifier(self, day):
2.239