imip-agent

Changeset

771:3ecd1cdae909
2015-09-27 Paul Boddie raw files shortlog changelog graph Switched to using div elements and table-based display properties. imipweb-client-simplification
htdocs/styles.css (file) imipweb/calendar.py (file)
     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