initial attempt at event cache
authorMichael J. Rubinsky <mrubinsk@horde.org>
Thu, 18 Nov 2010 05:24:02 +0000 (00:24 -0500)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Thu, 18 Nov 2010 05:24:02 +0000 (00:24 -0500)
kronolith/js/mobile.js

index 9d5fb7c..4885074 100644 (file)
 
     /**
      * List of calendars we are displaying
-     **/
+     */
     calendars:  [],
 
     /**
      * List of calendars that are currently loaded for the current view
-     **/
+     */
     loadedCalendars: [],
 
     /**
-     * List of events being displayed on the day view
-     **/
-    events: [],
-
-    /**
-     * Event cache. For now, only used for month view.
+     * Event cache
      */
     ecache: {},
     cacheStart: null,
     cacheEnd: null,
+
     /**
      * The currently displayed view
      **/
     },
 
     /**
-     * Load all events between start and end time. Returns short version of
-     * event info.
+     * Load all events between start and end time.
      *
      * @param Date start
      * @param Date end
+     * @param string view  The view we are loading for (month, day)
      */
-    loadEvents: function(start, end, view)
+    loadEvents: function(firstDay, lastDay, view)
     {
+        // Clear out the loaded cal cache
         KronolithMobile.loadedCalendars = [];
-        KronolithMobile.events = [];
-        start = start.toString('yyyyMMdd');
-        end = end.toString('yyyyMMdd');
-        for (cal in KronolithMobile.calendars) {
-            cal = KronolithMobile.calendars[cal];
+
+        $.each(KronolithMobile.calendars, function(key, cal) {
+            var startDay = firstDay.clone() , endDay = lastDay.clone(),
+            cals = KronolithMobile.ecache[cal[0]];
+            if (typeof cals != 'undefined' &&
+                typeof cals[cal[1]] != 'undefined') {
+
+                cals = cals[cal[1]];
+                c = cals[startDay.dateString()];
+                while (typeof c != 'undefined' && startDay.isBefore(endDay)) {
+                    KronolithMobile.loadedCalendars.push(cal.join('|'));
+                    if (KronolithMobile.view != 'month') {
+                        KronolithMobile.insertEvents([startDay, startDay], view, cal.join('|'));
+                    }
+                    startDay.add(1).day();
+                    c = cals[startDay.dateString()];
+                }
+
+                c = cals[endDay.dateString()];
+                while (typeof c != 'undefined' && !startDay.isAfter(endDay)) {
+                    KronolithMobile.loadedCalendars.push(cal);
+                    if (KronolithMobile.view != 'month') {
+                        KronolithMobile.insertEvents([endDay, endDay], view, cal.join('|'));
+                    }
+                    endDay.add(-1).day();
+                    c = cals[endDay.dateString()];
+                }
+                if (startDay.compareTo(endDay) > 0) {
+                    return;
+                }
+            }
+
+            var start = startDay.dateString(), end = endDay.dateString();
             KronolithMobile.doAction('listEvents',
                                      {
                                        'start': start,
                                        'end': end,
-                                       'cal': cal[0] + '|' + cal[1],
+                                       'cal': cal.join('|'),
                                        'view': view,
                                        'sig': start + end + (Math.random() + '').slice(2)
                                      },
                                      KronolithMobile.loadEventsCallback
             );
-        }
+        });
     },
 
     /**
-     * Used as sorting funtion to Array.sort for sorting events by start time
+     * Sort a collection of events as returned from the ecache
      */
     sortEvents: function(events)
     {
-        return  events.sort(function(a, b) {
-           sortA = a.e.s;
-           sortB = b.e.s;
+        var e = [];
+
+        // Need a native array to sort.
+        $.each(events, function(id, event) {
+            e.push(event);
+        });
+        return  e.sort(function(a, b) {
+           sortA = a.sort;
+           sortB = b.sort;
            return (sortA < sortB) ? -1 : (sortA > sortB) ? 1 : 0;
          });
     },
     {
         var start = KronolithMobile.parseDate(data.response.sig.substr(0, 8)),
             end = KronolithMobile.parseDate(data.response.sig.substr(8, 8)),
-            dates = [start, end], list, events;
+            dates = [start, end], view = data.response.view, list, events;
 
         data = data.response;
         KronolithMobile.loadedCalendars.push(data.cal);
+        KronolithMobile.storeCache(data.events, data.cal, dates, true);
+        KronolithMobile.insertEvents(dates, view, data.cal);
+    },
 
-        if (KronolithMobile.view == 'day') {
-            if (data.events) {
-                $.each(data.events, function(datestring, events) {
-                    $.each(events, function(index, event) {
-                        KronolithMobile.events.push({ 'e': event, 'id': index, 'cal': data.cal });
-                    });
-                });
-            }
+    //@TODO rename to renderView?
+    insertEvents: function(dates, view, cal)
+    {
+        var date, events, list;
+        switch (view) {
+            case 'day':
+                // Make sure all calendars are loaded before rendering the view.
+                // @TODO: Implement LIFO queue as in kronolith.js
+                if (KronolithMobile.loadedCalendars.length != KronolithMobile.calendars.length
+                    || KronolithMobile.view != view) {
 
-            if (KronolithMobile.loadedCalendars.length == KronolithMobile.calendars.length) {
-                events = KronolithMobile.sortEvents(KronolithMobile.events);
+                    KronolithMobile.timeoutId = window.setTimeout(function() {KronolithMobile.insertEvents(dates, view);}, 0);
+                    return;
+                }
+
+                if (KronolithMobile.timeoutId) {
+                    window.clearTimeout(KronolithMobile.timeoutId);
+                    KronolithMobile.timoutId = false;
+                }
+                date = dates[0].clone();
+                events = KronolithMobile.getCacheForDate(date.dateString());
+
+                events = KronolithMobile.sortEvents(events);
                 list = $('<ul>').attr({'data-role': 'listview'});
                 $.each(events, function(index, event) {
-                    list.append(KronolithMobile.buildDayEvent(event.cal, event.e, event.id));
+                    list.append(KronolithMobile.buildDayEvent(event));
                 });
                 if (!list.children().length) {
                     list.append($('<li>').text(Kronolith.text.noevents));
                 }
                 list.listview();
                 $("#dayview [data-role=content]").append(list);
-            }
-        } else {
-            // Month
-            KronolithMobile.storeCache(data.events, data.cal, dates, true);
-            if (KronolithMobile.loadedCalendars.length == KronolithMobile.calendars.length) {
-                day = dates[0].clone();
+                break;
+
+            case 'month':
+                var day = dates[0].clone();
                 while (!day.isAfter(dates[1])) {
                     date = day.dateString();
-                    events = KronolithMobile.getCacheForDate(date);
+                    events = KronolithMobile.getCacheForDate(date, cal);
                     $.each(events, function(key, event) {
                         $('#kronolithMonth' + date).addClass('kronolithSelected');
                     });
                     day.next().day();
                 }
-            }
+                break;
         }
     },
 
      * @param object event  The event object returned from the ajax request.
      * @param string id     The event identifier
      */
-    buildDayEvent: function(cal, event, id)
+    buildDayEvent: function(event)
     {
-        var type = cal.split('|')[0], c = cal.split('|')[1],
-        d = $('<div>').attr({ 'style': 'color:' + Kronolith.conf.calendars[type][c].bg }),
+        var id;
+        if ($.isEmptyObject(event)) {
+          return;
+        }
+        var cal = event.calendar, type = cal.split('|')[0], c = cal.split('|')[1],
+        d = $('<div>').attr({'style': 'color:' + Kronolith.conf.calendars[type][c].bg}),
         item = $('<li>'), a;
 
         // Time
         d.append(timeWrapper).append(e).append(l);
 
         // Add the link to view the event detail.
-        a = $('<a>').attr({'href': '#eventview'}).click(function(e) {
+        a = $('<a>').attr({'href': '#eventview'}).click(function(ev) {
             $('#eventview [data-role=content] ul').detach();
-            KronolithMobile.loadEvent(cal, id, Date.parse(event.e));
+            KronolithMobile.loadEvent(cal, event.id, Date.parse(event.e));
         }).append(d);
 
         return item.append(a);
     loadEvent: function(cal, id, d)
     {
         KronolithMobile.doAction('getEvent',
-                                 { 'cal': cal, 'id': id, 'date': d.toString('yyyyMMdd') },
+                                 {'cal': cal, 'id': id, 'date': d.toString('yyyyMMdd')},
                                  KronolithMobile.loadEventCallback);
     },
 
      */
     loadEventCallback: function(data)
     {
-         var event, list, text;
          if (!data.response.event) {
              // @TODO: Error handling.
              return;
          }
 
-         event = data.response.event;
-
+         var event = data.response.event;
          var ul = KronolithMobile.buildEventView(event);
          $('#eventview [data-role=content]').append(ul);
     },
      */
     buildEventView: function(e)
     {
-         var list = $('<ul>').addClass('kronolithEventDetail').attr({ 'data-role': 'listview', 'data-inset': true });
+         var list = $('<ul>').addClass('kronolithEventDetail').attr({'data-role': 'listview', 'data-inset': true});
          var loc = false, t;
 
          // Title and location
          // Location
          if (e.gl) {
              loc = $('<div>').addClass('kronolithEventDetailLocation')
-                .append($('<a>').attr({ 'data-style': 'b', 'href': 'http://maps.google.com?q=' + encodeURIComponent(e.gl.lat + ',' + e.gl.lon) }).text(e.l));
+                .append($('<a>').attr({'data-style': 'b', 'href': 'http://maps.google.com?q=' + encodeURIComponent(e.gl.lat + ',' + e.gl.lon)}).text(e.l));
          } else if (e.l) {
              loc = $('<div>').addClass('kronolithEventDetailLocation')
-                .append($('<a>').attr({ 'href': 'http://maps.google.com?q=' + encodeURIComponent(e.l) }).text(e.l));
+                .append($('<a>').attr({'href': 'http://maps.google.com?q=' + encodeURIComponent(e.l)}).text(e.l));
          }
          if (loc) {
              list.append($('<li>').append(loc));
 
          // url
          if (e.u) {
-           list.append($('<li>').append($('<a>').attr({ 'rel': 'external', 'href': e.u }).text(e.u)));
+           list.append($('<li>').append($('<a>').attr({'rel': 'external', 'href': e.u}).text(e.u)));
          }
 
          list.listview();
          return list;
     },
 
+    /**
+     * Advance the day view by one day
+     */
     showNextDay: function()
     {
-        $("#dayview [data-role=content] ul").detach();
+        $('#dayview [data-role=content] ul').detach();
         var d = $('.kronolithDayDate').data('date');
         d.addDays(1);
-        $(".kronolithDayDate").text(d.toString('ddd') + ' ' + d.toString('d'));
+        $('.kronolithDayDate').text(d.toString('ddd') + ' ' + d.toString('d'));
         $('.kronolithDayDate').data('date', d);
         KronolithMobile.loadEvents(d, d, 'day');
     },
 
+    /**
+     * Move the day view back by one day
+     */
     showPrevDay: function()
     {
-        $("#dayview [data-role=content] ul").detach();
+        $('#dayview [data-role=content] ul').detach();
         var d = $('.kronolithDayDate').data('date');
         d.addDays(-1);
-        $(".kronolithDayDate").text(d.toString('ddd') + ' ' + d.toString('d'));
+        $('.kronolithDayDate').text(d.toString('ddd') + ' ' + d.toString('d'));
         KronolithMobile.loadEvents(d, d, 'day');
     },
 
+    /**
+     * Advance the month view ahead one month.
+     * Note that we manually call insertEvents here
+     */
     showPrevMonth: function()
     {
         var d = KronolithMobile.parseDate($('#kronolithMinicalDate').data('date'));
         var dates = KronolithMobile.viewDates(d, 'month');
         KronolithMobile.loadEvents(dates[0], dates[1], 'month');
         KronolithMobile.buildCal(d);
+        KronolithMobile.insertEvents(dates, 'month');
     },
 
     showNextMonth: function()
         var dates = KronolithMobile.viewDates(d, 'month');
         KronolithMobile.loadEvents(dates[0], dates[1], 'month');
         KronolithMobile.buildCal(d);
+                KronolithMobile.insertEvents(dates, 'month');
     },
 
     /**
             }
 
             // Insert day cell.
-            td = $('<td>').attr({ 'id': 'kronolithMonth' + dateString }).data('date', dateString);
+            td = $('<td>').attr({'id': 'kronolithMonth' + dateString}).data('date', dateString);
             if (day.getMonth() != date.getMonth()) {
                 td.addClass('kronolithMinicalEmpty');
             }
                 event.end = Date.parse(event.e);
                 event.sort = event.start.toString('HHmmss')
                     + (240000 - parseInt(event.end.toString('HHmmss'), 10)).toPaddedString(6);
+                event.id = k;
             });
             // Store events in cache.
             $.extend(calHash[key], date);