]> code.delx.au - comingnext/blobdiff - comingNext/index.html
added check for disabled compatibility scaling on E6. This solves bug #3509394, howev...
[comingnext] / comingNext / index.html
index 0e80e7bf94c4a4cbe5764189495975d6c639f8a0..1e2f2a337b89ea7c63130de532e8377b41e42c3c 100644 (file)
@@ -7,23 +7,23 @@
 \r
 <style type="text/css">\r
 /* The following classes can be modified by widget settings */\r
-.background { }\r
-.backgroundFullscreen { }\r
+.background { color:#ffffff; background-color:#000000 }\r
+.backgroundFullscreen { color:#ffffff; background-color:#000000 }\r
 .weekDay { }\r
 .date { }\r
-.today { }\r
-.tomorrow { }\r
+.today { color:#ff0000 }\r
+.tomorrow { color:#0000ff }\r
 .time { }\r
-.now { }\r
+.now { color:#ff00ff }\r
 .description { }\r
-.icon { }\r
-.overdue {}\r
-.calendar1 {}\r
-.calendar2 {}\r
-.calendar3 {}\r
-.calendar4 {}\r
-.calendar5 {}\r
-.calendar6 {}\r
+.icon { width:15px; height:15px }\r
+.overdue { color:#ffff00 }\r
+.calendar1 { background-color:#0757cf }\r
+.calendar2 { background-color:#579f37 }\r
+.calendar3 { background-color:#ff9f07 }\r
+.calendar4 { background-color:#af8fef }\r
+.calendar5 { background-color:#57afbf }\r
+.calendar6 { background-color:#9fdf57 }\r
 </style>\r
 \r
 <script type="text/javascript" src="localizedTextStrings.js" charset="utf-8" />\r
@@ -56,7 +56,8 @@ var config = {
        daylightSavingOffset: { Type: 'Int', Default: 1, Value: 1,},\r
        hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},\r
        showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},\r
-       excludedCalendars: { Type: 'Array', Default: '', Value: '',},\r
+       excludedCalendars: { Type: 'Array', Default: [], Value: [],},\r
+       enableLogging: { Type: 'Bool', Default: false, Value: false,},\r
        cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},\r
        cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},\r
        cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},\r
@@ -82,11 +83,12 @@ var config = {
 // Nothing of interest from here on...\r
 //-------------------------------------------------------\r
 var panelNum = 0; // use 1 for second panel\r
-var version = "1.31";\r
+var version = "1.37";\r
 var versionURL = "http://comingnext.sourceforge.net/version.xml";\r
 var calendarService = null;\r
 var cacheEntriesHtml = [];\r
 var months_translated = [];\r
+var weekdays_translated = [];\r
 var orientation = '';\r
 var now = new Date();\r
 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about, 4 = check for update\r
@@ -98,6 +100,14 @@ var calendarList = [];
 var calendarColors = [];\r
 var updateTimer = null;\r
 var screenRotationTimer = null;\r
+var lastUpdateTime = now; // last time we updated the display\r
+var lastReloadTime = null; // last time we fetched calendar data\r
+var reloadInterval = 6 * 60 * 60 * 1000; // = 6 hours; time interval for reloading calendar data\r
+var errorOccured = false;\r
+var entryLists = null; // stores all fetched calendar entries until data is refreshed\r
+var statupSuccessful = false; // indicates if everything started up wihtout errors. If we detect an error after that, it might just be a temporary problem e.g. by a backup process.\r
+var use12hoursTimeFormat = false; // defines how time should be formated: 19:00 or 07:00 pm\r
+var timeFormatSeparator = ":"; // format time 19:00 or 19.00 depending on system setting\r
 \r
 // vars for daylight saving time\r
 var summertime = false; // true, if current date is in summer, false if in winter\r
@@ -117,17 +127,13 @@ var entryFields = [
        "InstanceEndTime"\r
 ];\r
 \r
-window.onload = init;\r
-window.onresize = updateScreen;\r
-window.onshow = updateScreen;\r
-\r
 function isLeapYear( year ) {\r
        if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )\r
                return true;\r
        else\r
                return false;\r
 }\r
-         \r
+\r
 function calcLeapYear(year, days)\r
 {\r
        if (isLeapYear(year))\r
@@ -192,6 +198,9 @@ function error(message)
 {\r
        console.info('Error: ' + message);\r
        document.getElementById("calendarList").innerHTML = 'Error: ' + message;\r
+       document.getElementById("fullscreenCalendarList").innerHTML = 'Error: ' + message;\r
+       errorOccured = true;\r
+       document.onclick = null;\r
 }\r
 \r
 function areDatesEqual(date1, date2)\r
@@ -235,7 +244,27 @@ function collectLocales()
                try {\r
                        var result = calendarService.IDataSource.Add(criteria);\r
                        if (result.ErrorCode)\r
-                               error(result.ErrorMessage);\r
+                               throw(result.ErrorMessage);\r
+               } catch (e) {\r
+                       error("collectLocales: " + e + ', line ' + e.line);\r
+               }\r
+       }\r
+       for (weekday = 0; weekday < 7; weekday++) {\r
+               var startDate = new Date(2000, 0, 2 + weekday); // date that we know for sure is a sunday\r
+\r
+               var item = new Object();\r
+               item.Type = "DayEvent";\r
+               item.StartTime = startDate;\r
+               item.Summary = "__weekday_temp" + weekday;\r
+\r
+               var criteria = new Object();\r
+               criteria.Type = "CalendarEntry";\r
+               criteria.Item = item;\r
+\r
+               try {\r
+                       var result = calendarService.IDataSource.Add(criteria);\r
+                       if (result.ErrorCode)\r
+                               throw(result.ErrorMessage);\r
                } catch (e) {\r
                        error("collectLocales: " + e + ', line ' + e.line);\r
                }\r
@@ -253,13 +282,11 @@ function collectLocales()
                        }\r
                }\r
                var result = calendarService.IDataSource.GetList(listFiltering);\r
-               if (result.ErrorCode) {\r
-                       error(result.ErrorMessage);\r
-                       return;\r
-               }\r
+               if (result.ErrorCode)\r
+                       throw(result.ErrorMessage);\r
                var list = result.ReturnValue;\r
        } catch(e) {\r
-               error(e + ', line ' + e.line);\r
+               error("collectLocales: " + e + ', line ' + e.line);\r
                return;\r
        }\r
        var ids = new Array();\r
@@ -269,7 +296,7 @@ function collectLocales()
                var dateArr = [];\r
 \r
                while (list && (entry = list.getNext()) != undefined) {\r
-                       dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/  /g,' ').split(' ');\r
+                       dateArr = (entry.StartTime + '').replace(/,/g,'').replace(/\./g,':').replace(/  /g,' ').split(' ');\r
                        var day = dateArr[1];\r
                        var month = dateArr[2];\r
                        var year = dateArr[3];\r
@@ -285,16 +312,53 @@ function collectLocales()
                                month = tmp;\r
                        }\r
 \r
-                       console.info(entry.StartTime + ' -> ' + month + ' ' + counter);\r
+                       log(entry.StartTime + ' -> ' + month + ' ' + counter);\r
                        ids[counter] = entry.id;\r
                        months_translated[month] = counter + 1;\r
                        counter++;\r
                }\r
        } catch(e) {\r
-               error(e + ', line ' + e.line);\r
+               error("collectLocales: " + e + ', line ' + e.line);\r
+               return;\r
+       }\r
+       try {\r
+               var startTime = new Date(2000,0,2);\r
+               var endTime = new Date(2000,0,9);\r
+               var listFiltering = {\r
+                       Type:'CalendarEntry', \r
+                       Filter:{\r
+                               StartRange: startTime,\r
+                               EndRange: endTime,\r
+                               SearchText: '__weekday_temp',\r
+                               Type: 'DayEvent'\r
+                       }\r
+               }\r
+               var result = calendarService.IDataSource.GetList(listFiltering);\r
+               if (result.ErrorCode)\r
+                       throw(result.ErrorMessage);\r
+               var weekdaylist = result.ReturnValue;\r
+       } catch(e) {\r
+               error("collectLocales: " + e + ', line ' + e.line);\r
+               return;\r
+       }\r
+       try {\r
+               var entry;\r
+               var counter2 = 0;\r
+               var curWeekday = "";\r
+\r
+               while (weekdaylist && (entry = weekdaylist.getNext()) != undefined) {\r
+                       detectTimeFormat(entry.StartTime + '');\r
+                       curWeekday = (entry.StartTime + '').split(',')[0];\r
+                       log(entry.StartTime + ' -> ' + curWeekday + ' ' + counter2);\r
+                       ids[counter + counter2] = entry.id;\r
+                       weekdays_translated[counter2] = curWeekday;\r
+                       counter2++;\r
+               }\r
+       } catch(e) {\r
+               error("collectLocales: " + e + ', line ' + e.line);\r
                return;\r
        }\r
-       console.info(ids);\r
+       log(ids);\r
        try {\r
                var criteria = new Object();\r
                criteria.Type = "CalendarEntry";\r
@@ -304,13 +368,26 @@ function collectLocales()
 \r
                var result = calendarService.IDataSource.Delete(criteria);\r
                if (result.ErrorCode)\r
-                       error(result.ErrorMessage);\r
+                       throw(result.ErrorMessage);\r
        } catch(e) {\r
                error('deleting temp calendar entries:' + e + ', line ' + e.line);\r
                return;\r
        }\r
 }\r
 \r
+function stringEndsWith(str, suffix)\r
+{\r
+       return str.indexOf(suffix, str.length - suffix.length) !== -1;\r
+}\r
+\r
+// detects the system's current time format by parsing a native calendar timestamp (this is the only reliable formating across all devices and firmwares)\r
+function detectTimeFormat(localeTimeString)\r
+{\r
+       localeTimeString = localeTimeString.toLowerCase();\r
+       use12hoursTimeFormat = stringEndsWith(localeTimeString, "am") || stringEndsWith(localeTimeString, "pm");\r
+       timeFormatSeparator = localeTimeString.indexOf(":") != -1 ? ":" : ".";\r
+}\r
+\r
 function requestNotification()\r
 {\r
        var criteria = new Object();\r
@@ -358,13 +435,14 @@ function cancelNotification()
 \r
 function callback(transId, eventCode, result)\r
 {\r
-       console.info("callback(): panelNum: %d  transId: %d  eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);\r
+       log("callback(): panelNum: " + panelNum + "  transId: " + transId + "  eventCode: " + eventCode + " result.ErrorCode: " + result.ErrorCode);\r
+       lastReloadTime = null; // force calendar data reload on next update\r
        updateData();\r
 }\r
 \r
 function settingsCallback(transId, eventCode, result)\r
 {\r
-       console.info("settingsCallback(): panelNum: %d  transId: %d  eventCode: %d result.ErrorCode: %d", panelNum, transId, eventCode, result.ErrorCode);\r
+       log("settingsCallback(): panelNum: " + panelNum + "  transId: " + transId + "  eventCode: " + eventCode + " result.ErrorCode: " + result.ErrorCode);\r
        loadSettings();\r
 }\r
 \r
@@ -379,49 +457,58 @@ function parseDate(dateString)
        Wednesday,  2009 August, 28 8.00.00 pm\r
        Wednesday,  2009 August, 28 08:00:00 PM\r
        */\r
+       var result = null;\r
 \r
-       if (dateString == "" || dateString == null)\r
-               return null;\r
-       var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/  /g,' ').split(' ');\r
-       if (dateArr.length != 5 && dateArr.length != 6)\r
-               return null;\r
-\r
-       // parse date\r
-       var weekDay = dateArr[0];\r
-       var day = dateArr[1];\r
-       var month = dateArr[2];\r
-       var year = dateArr[3];\r
-       // make sure month is set properly\r
-       if (isNaN(parseInt(day))) {\r
-               var tmp = day;\r
-               day = month;\r
-               month = tmp;\r
-       } else if (isNaN(parseInt(year))) {\r
-               var tmp = year;\r
-               year = month;\r
-               month = tmp;\r
+       if (dateString == "" || dateString == null || dateString == undefined)\r
+               return result;\r
+       if (dateString instanceof Date) {\r
+               // we already have a date object, no need to parse string here\r
+               result = dateString;\r
        }\r
-       // make sure day and year are set properly\r
-       if (Number(day) > Number(year)) {\r
-               var tmp = year;\r
-               year = day;\r
-               day = tmp;\r
+       else {\r
+               var dateArr = (dateString + '').replace(/,/g, '').replace(/\./g, ':').replace(/  /g, ' ').split(' ');\r
+               if (dateArr.length != 5 && dateArr.length != 6) \r
+                       return null;\r
+               \r
+               // parse date\r
+               var weekDay = dateArr[0];\r
+               var day = dateArr[1];\r
+               var month = dateArr[2];\r
+               var year = dateArr[3];\r
+               // make sure month is set properly\r
+               if (isNaN(parseInt(day))) {\r
+                       var tmp = day;\r
+                       day = month;\r
+                       month = tmp;\r
+               }\r
+               else \r
+                       if (isNaN(parseInt(year))) {\r
+                               var tmp = year;\r
+                               year = month;\r
+                               month = tmp;\r
+                       }\r
+               // make sure day and year are set properly\r
+               if (Number(day) > Number(year)) {\r
+                       var tmp = year;\r
+                       year = day;\r
+                       day = tmp;\r
+               }\r
+               month = months_translated[month];\r
+               \r
+               // parse time\r
+               var timeArr = dateArr[4].split(':');\r
+               if (timeArr.length != 3) \r
+                       return null;\r
+               var hours = Number(timeArr[0]);\r
+               var minutes = Number(timeArr[1]);\r
+               var seconds = Number(timeArr[2]);\r
+               if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12) \r
+                       hours += 12;\r
+               if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12) \r
+                       hours = 0;\r
+               \r
+               result = new Date(year, month - 1, day, hours, minutes, seconds);\r
        }\r
-       month = months_translated[month];\r
-\r
-       // parse time\r
-       var timeArr = dateArr[4].split(':');\r
-       if (timeArr.length != 3)\r
-               return null;\r
-       var hours = Number(timeArr[0]);\r
-       var minutes = Number(timeArr[1]);\r
-       var seconds = Number(timeArr[2]);\r
-       if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)\r
-               hours += 12;\r
-       if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)\r
-               hours = 0;\r
-\r
-       var result = new Date(year, month - 1, day, hours, minutes, seconds);\r
        \r
        // take care of daylight saving time\r
        if (config['enableDaylightSaving'].Value) {\r
@@ -432,17 +519,25 @@ function parseDate(dateString)
                // work around bug in Nokias calendar api resulting in dates within a different DST to be off by 1 hour\r
                if (summertime && !dateSummerTime) {\r
                        result = new Date(result.getTime() - 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // -1 hour\r
-                       console.info('parseDate(): fixing time -1h: ' + result);\r
+                       log('parseDate(): fixing time -1h: ' + result);\r
                }\r
                else if (!summertime && dateSummerTime) {\r
                        result = new Date(result.getTime() + 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // +1 hour\r
-                       console.info('parseDate(): fixing time +1h: ' + result);\r
+                       log('parseDate(): fixing time +1h: ' + result);\r
                }\r
        }\r
 \r
        return result;\r
 }\r
 \r
+function getWeekdayLocalized(date) {\r
+       var localizedString = date.toLocaleDateString();\r
+       if (localizedString.indexOf(",") == -1) {\r
+               return weekdays_translated[date.getDay()];\r
+       } else\r
+               return localizedString.split(',')[0];\r
+}\r
+\r
 // returns a short date as string ("31.12" or "12.31") based on the format string which should look like "Wednesday,  26 August, 2009 12:00:00 am"\r
 function formatDate(date, format)\r
 {\r
@@ -456,6 +551,13 @@ function formatDate(date, format)
        if (config['showTodayAsText'].Value && isTomorrow(date))\r
                return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';\r
 \r
+       if (format instanceof Date) {\r
+               // we don't know how to format this\r
+               if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')\r
+                       return day + config['dateSeparator'].Value + month;\r
+               else\r
+                       return month + config['dateSeparator'].Value + day;\r
+       }\r
        var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/  /g,' ').split(' ');\r
        if (dateArr.length != 5 && dateArr.length != 6) {\r
                // we don't know how to format this\r
@@ -502,85 +604,128 @@ function formatDate(date, format)
 function formatTime(date)\r
 {\r
        // date is a Date() object\r
-       date.setSeconds(0); // we don't care about seconds\r
-       var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string\r
-       if (time.replace(/\./, ':').split(':')[0].length < 2)\r
-               time = '0' + time;\r
+       var hour = date.getHours();\r
+       var minute = date.getMinutes();\r
+       \r
+       // don't use Date().toLocaleTimeString() as it is utterly broken on newer firmwares\r
+       if (use12hoursTimeFormat) {\r
+               var ap = "AM";\r
+               if (hour > 11)\r
+                       ap = "PM";\r
+               if (hour > 12)\r
+                       hour = hour - 12;\r
+               if (hour == 0)\r
+                       hour = 12;\r
+               if (hour < 10)\r
+                       hour = "0" + hour;\r
+               if (minute < 10)\r
+                       minute = "0" + minute;\r
+               time = hour + timeFormatSeparator + minute + " " + ap;\r
+       }\r
+       else {\r
+               if (hour < 10)\r
+                       hour = "0" + hour;\r
+               if (minute < 10)\r
+                       minute = "0" + minute;\r
+               time = hour + timeFormatSeparator + minute;\r
+       }\r
+       \r
        if (config['showNowAsText'].Value && date.getTime() == now.getTime())\r
                time = '<span class="now">' + config['nowText'].Value + '</span>';\r
+       log("formatTime(): " + time + ", use12hoursTimeFormat=" + use12hoursTimeFormat + ", timeFormatSeparator=" + timeFormatSeparator + ", date.toLocateTimeString(): " + date.toLocaleTimeString());\r
        return time;\r
 }\r
 \r
 function updateData()\r
 {\r
-       console.info('updateData()');\r
+       log('updateData()');\r
+       if (errorOccured) {\r
+               return;\r
+       }\r
 \r
        // check if we got additional or less calendars since our last update\r
        var newCalendarList = listCalendars();\r
+       if (newCalendarList == null) {\r
+               // Something went wrong fetching the calendars list.\r
+               // This usually happens when a backup is being made.\r
+               // Retry the next time updateData() is called by \r
+               // resetting errorOccured\r
+               log('updateData(): listCalendars() failed, trying again later...');\r
+               cacheEntriesHtml = ''; // make sure we replace the currently shown error message on the next update\r
+               errorOccured = false;\r
+               return;\r
+       }\r
        if (newCalendarList.length != calendarList.length) {\r
                calendarList = newCalendarList;\r
                updateCalendarColors();\r
                cancelNotification();\r
                requestNotification();\r
+               lastReloadTime = null; // force calendar data reload on this update\r
        }\r
-       \r
-       try {\r
-               // meetings have time\r
-               // note: anniveraries have a start time of 12:00am. So since we want to include them, we have to query the whole day and check if events have passed later\r
-               now = new Date();\r
-               summertime = isSummertime(now); // cache summer time info for today\r
-               var meetingList = [];\r
-               for(var i=0; i < calendarList.length; i++) {\r
-                       // ignore excluded calendars\r
-                       if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)\r
-                               continue;\r
-                       var meetingListFiltering = {\r
-                               Type:'CalendarEntry',\r
-                               Filter:{\r
-                                       CalendarName: calendarList[i],\r
-                                       StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),\r
-                                       EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))\r
-                               }\r
-                       }\r
-                       var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);\r
-                       if (meetingResult.ErrorCode != 0)\r
-                               throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);\r
-                       var list = meetingResult.ReturnValue;\r
-                       meetingList = meetingList.concat(listToArray(list, calendarList[i]));\r
-               }\r
-               console.info("updateData(): meetingList.sort()");\r
-               meetingList.sort(sortCalendarEntries);\r
 \r
-               // todos don't, they start on 00:00 hrs., but should be visible anyway\r
-               // this will generate a list of passed todos. We have to check if they have been marked as "done" yet\r
-               if (config['includeTodos'].Value) {\r
-                       var todayTodoList = [];\r
+       now = new Date();\r
+       \r
+       // only reload calendar data every 6 hours, visual updates occure more often\r
+       if (!lastReloadTime || now.getTime() - lastReloadTime.getTime() > reloadInterval) {\r
+               log('updateData(): reloading calendar data');\r
+               try {\r
+                       // meetings have time\r
+                       // note: anniveraries have a start time of 12:00am. So since we want to include them, we have to query the whole day and check if events have passed later\r
+                       summertime = isSummertime(now); // cache summer time info for today\r
+                       var meetingList = [];\r
                        for(var i=0; i < calendarList.length; i++) {\r
                                // ignore excluded calendars\r
                                if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)\r
                                        continue;\r
-                               var todayTodoListFiltering = {\r
+                               var meetingListFiltering = {\r
                                        Type:'CalendarEntry',\r
                                        Filter:{\r
                                                CalendarName: calendarList[i],\r
-                                               Type: 'ToDo',\r
-                                               StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),\r
-                                               EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))\r
+                                               StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),\r
+                                               EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))\r
                                        }\r
                                }\r
-                               var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);\r
-                               var list = todayTodoResult.ReturnValue;\r
-                               todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));\r
+                               var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);\r
+                               if (meetingResult.ErrorCode != 0)\r
+                                       throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);\r
+                               var list = meetingResult.ReturnValue;\r
+                               meetingList = meetingList.concat(listToArray(list, calendarList[i]));\r
                        }\r
-                       console.info("updateData(): todayTodoList.sort()");\r
-                       todayTodoList.sort(sortCalendarEntries);\r
-                       var entryLists = [todayTodoList, meetingList];\r
-               } else {\r
-                       var entryLists = [meetingList];\r
+                       log("updateData(): meetingList.sort()");\r
+                       meetingList.sort(sortCalendarEntries);\r
+\r
+                       // todos don't, they start on 00:00 hrs., but should be visible anyway\r
+                       // this will generate a list of passed todos. We have to check if they have been marked as "done" yet\r
+                       if (config['includeTodos'].Value) {\r
+                               var todayTodoList = [];\r
+                               for(var i=0; i < calendarList.length; i++) {\r
+                                       // ignore excluded calendars\r
+                                       if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)\r
+                                               continue;\r
+                                       var todayTodoListFiltering = {\r
+                                               Type:'CalendarEntry',\r
+                                               Filter:{\r
+                                                       CalendarName: calendarList[i],\r
+                                                       Type: 'ToDo',\r
+                                                       StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),\r
+                                                       EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))\r
+                                               }\r
+                                       }\r
+                                       var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);\r
+                                       var list = todayTodoResult.ReturnValue;\r
+                                       todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));\r
+                               }\r
+                               log("updateData(): todayTodoList.sort()");\r
+                               todayTodoList.sort(sortCalendarEntries);\r
+                               entryLists = [todayTodoList, meetingList];\r
+                       } else {\r
+                               entryLists = [meetingList];\r
+                       }\r
+                       lastReloadTime = new Date();\r
+               } catch(e) {\r
+                       error('loading Calendar items list:' + e + ', line ' + e.line);\r
+                       return;\r
                }\r
-       } catch(e) {\r
-               error('loading Calendar items list:' + e + ', line ' + e.line);\r
-               return;\r
        }\r
 \r
        try {\r
@@ -589,23 +734,26 @@ function updateData()
                var entryDate = '';\r
                var dateArr = [];\r
                var fontsize = 'normal';\r
+               var lineheight = 'normal';\r
                if (mode == 0) {\r
+                       fontsize = parseInt(72 / config['eventsPerWidget'].Value) + 'px';\r
+                       lineheight = parseInt(82 / config['eventsPerWidget'].Value) + 'px';\r
+                       \r
                        if (config['eventsPerWidget'].Value == 3) {\r
-                               fontsize = '17pt';\r
                                changeCssClass('.icon', 'width:20px; height:20px');\r
                        }\r
                        else if (config['eventsPerWidget'].Value == 5) {\r
-                               fontsize = '10pt';\r
                                changeCssClass('.icon', 'width:10px; height:10px');\r
                        }\r
                        else if (config['eventsPerWidget'].Value == 6) {\r
-                               fontsize = '8pt';\r
                                changeCssClass('.icon', 'width:8px; height:8px');\r
                        }\r
                }\r
                else\r
                        changeCssClass('.icon', config['cssStyle_icon'].Value);\r
-               var entriesHtml = '<table style="font-size:' + fontsize + ';">';\r
+               var entriesHtml = '<table style="font-size:' + fontsize + '; line-height:' + lineheight + ';">';\r
+               if (mode == 0)\r
+                       entriesHtml = '<table width="307" height="82"><tr><td>' + entriesHtml; // this is needed to center the actual content vertically\r
                var eventIds = [];\r
                var max;\r
                if (mode == 0)\r
@@ -613,16 +761,18 @@ function updateData()
                else\r
                        max = 30; // we can display a lot more events in fullscreen mode\r
 \r
-               var listinfo = "";\r
-               for (var i=0; i < entryLists.length; i++) {\r
-                       listinfo = listinfo + " " + entryLists[i].length;\r
-                       var entrieslist = "";\r
-                       for (var j=0; j < entryLists[i].length; j++) {\r
-                               entrieslist += entryLists[i][j].Summary + ", ";\r
+               if (config['enableLogging'].Value) {\r
+                       var listinfo = "";\r
+                       for (var i=0; i < entryLists.length; i++) {\r
+                               listinfo = listinfo + " " + entryLists[i].length;\r
+                               var entrieslist = "";\r
+                               for (var j=0; j < entryLists[i].length; j++) {\r
+                                       entrieslist += entryLists[i][j].Summary + ", ";\r
+                               }\r
+                               log("updateData(): entrieslist: " + entrieslist);\r
                        }\r
-                       console.info("updateData(): entrieslist: " + entrieslist);\r
+                       log("updateData(): inner loop, " + entryLists.length + " lists, [" + listinfo + "] entries");\r
                }\r
-               console.info("updateData(): inner loop, " + entryLists.length + " lists, [" + listinfo + "] entries");\r
                \r
                // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)\r
                for (var i=0; counter < max && i < entryLists.length; i++) {\r
@@ -637,18 +787,18 @@ function updateData()
                                                entryInfo += entryFields[k] + "=" + entry[entryFields[k]] + ",";\r
                                        }\r
                                }\r
-                               console.info(entryInfo);\r
+                               log(entryInfo);\r
 \r
                                // we don't want ToDos when includeTodos == false or when they are completed\r
                                if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {\r
-                                       console.info('skipping ' + entry.id );\r
+                                       log('skipping ' + entry.id );\r
                                        counter--;\r
                                        continue;\r
                                }\r
 \r
                                // make sure that we don't include an event twice (useful for ToDos that might come up twice)\r
                                if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {\r
-                                       console.info('skipped (already included) ' + entry.id);\r
+                                       log('skipped (already included) ' + entry.id);\r
                                        counter--;\r
                                        continue;\r
                                } else\r
@@ -656,7 +806,7 @@ function updateData()
 \r
                                // summary can be undefined!\r
                                var Summary = ((entry.Summary == null) ? '' : entry.Summary);\r
-                               if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)\r
+                               if (entry.Location != '' && entry.Location != undefined && config['showLocation'].Value)\r
                                        Summary += ', ' + entry.Location;\r
                                \r
                                // fix by yves: determine start and end dates/times\r
@@ -671,15 +821,23 @@ function updateData()
 \r
                                // Convert date/time string to Date object\r
                                var date = parseDate(entryDate);\r
-                               console.info('date: ' + date);\r
+                               log('date: ' + date);\r
                                var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));\r
-                               console.info('endDate: ' + endDate);\r
+                               log('endDate: ' + endDate);\r
+                               \r
+                               // check if Meeting is actually a DayEvent. Bug introduced by "Anna" updates to various Symbian^3 devices.\r
+                               // Note that this workaround is not 100% save! It might missinterpret some meetings as dayevents of starting and ending on 00:00\r
+                               if (entry.Type == 'Meeting' && date.getHours() == 0 && date.getMinutes() == 0 && \r
+                                       endDate != null && endDate.getHours() == 0 && endDate.getMinutes() == 0) {\r
+                                       log('fixing event type: changed from "Meeting" to "DayEvent".');\r
+                                       entry.Type = 'DayEvent';\r
+                               }\r
 \r
                                // check if meeting event has already passed\r
                                if (entry.Type == 'Meeting') {\r
                                        var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());\r
                                        if (now.getTime() > compareTime) {\r
-                                               console.info('skipping Meeting (already passed) ' + entry.id);\r
+                                               log('skipping Meeting (already passed) ' + entry.id);\r
                                                counter--;\r
                                                eventIds[entry.id] = 0;\r
                                                continue;\r
@@ -690,7 +848,7 @@ function updateData()
                                if (entry.Type == 'Anniversary') {\r
                                        var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);\r
                                        if (date.getTime() < tmp.getTime()) {\r
-                                               console.info('skipping Anniversary (already passed) ' + entry.id);\r
+                                               log('skipping Anniversary (already passed) ' + entry.id);\r
                                                counter--;\r
                                                eventIds[entry.id] = 0;\r
                                                continue;\r
@@ -700,9 +858,9 @@ function updateData()
                                // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed\r
                                if (entry.Type == 'DayEvent' && endDate != null) {\r
                                        endDate.setMinutes(endDate.getMinutes() - 1);\r
-                                       console.info('fixing DayEvent endDate: ' + endDate);\r
+                                       log('fixing DayEvent endDate: ' + endDate);\r
                                        if (now.getTime() > endDate.getTime()) {\r
-                                               console.info('event already passed ' + entry.id);\r
+                                               log('event already passed ' + entry.id);\r
                                                counter--;\r
                                                eventIds[entry.id] = 0;\r
                                                continue;\r
@@ -714,13 +872,13 @@ function updateData()
                                        // check if we are between start and endtime\r
                                        if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {\r
                                                date = now; // change appointment date/time to now\r
-                                               console.info('event is currently taking place: ' + date);\r
+                                               log('event is currently taking place: ' + date);\r
                                        }\r
                                }\r
 \r
                                // skip events for the first panel in case this is the second one and we're not in fullscreen mode\r
                                if (mode == 0 && panelNum > 0 && counter < panelNum * config['eventsPerWidget'].Value + 1) {\r
-                                       console.info('skipping (already in first widget) ' + entry.id);\r
+                                       log('skipping (already in first widget) ' + entry.id);\r
                                        continue;\r
                                }\r
                                \r
@@ -744,7 +902,9 @@ function updateData()
                                        // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.\r
                                        entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';\r
                                } else {\r
-                                       var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);\r
+                                       var weekDay = getWeekdayLocalized(date).substr(0,config['weekDayLength'].Value);\r
+                                       log('date.toLocaleDateString(): ' + date.toLocaleDateString());\r
+                                       log('weekDay: ' + weekDay);\r
                                        var time = formatTime(date);\r
                                        var dateStr = formatDate(date, entryDate);\r
                                        if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {\r
@@ -775,10 +935,13 @@ function updateData()
                        }\r
                }\r
                entriesHtml += '</table>';\r
+               if (mode == 0)\r
+                       entriesHtml = entriesHtml + '</td></tr></table>';\r
                if (config['showNothingText'].Value && entriesHtml == '<table></table>') {\r
                        var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);\r
                        entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';\r
                }\r
+               log("output: " + entriesHtml);\r
                if (cacheEntriesHtml != entriesHtml) {\r
                        if (mode == 0)\r
                                document.getElementById('calendarList').innerHTML = entriesHtml;\r
@@ -786,33 +949,56 @@ function updateData()
                                document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;\r
                        cacheEntriesHtml = entriesHtml;\r
                }\r
+               \r
+               lastUpdateTime = new Date();\r
        } catch(e) {\r
                error('displaying list:' + e + ', line ' + e.line);\r
                return;\r
        }\r
 }\r
 \r
+// called by handleOnShow() and onResize events\r
 function updateScreen()\r
 {\r
+       log('updateScreen(): mode=' + mode + ', window.innerHeight=' + window.innerHeight);\r
+\r
        // check if opening fullscreen\r
-       if( window.innerHeight > 91 && mode == 0) {\r
+\r
+       // Note: according to Nokia's documentation, an innerHeight of >91 is an indicator for fullscreen view. \r
+       // However a bug in E6's firmware causes different window widths and heights (disabled compatibility scaling). \r
+       // So far, values of 104 and 115 for window.innerHeight were reported, we use a safty margin here and check \r
+       // for 150 instead.\r
+       if( window.innerHeight > 150 && mode == 0) {\r
                mode = 1;\r
                cacheEntriesHtml = '';\r
                document.getElementById('body').style.backgroundImage = "";\r
                showFullscreen();\r
        }\r
-       else if (window.innerHeight <= 91 && mode != 0) {\r
+       else if (window.innerHeight <= 150 && mode != 0) {\r
                mode = 0;\r
                cacheEntriesHtml = '';\r
                showHomescreen();\r
        }\r
        \r
        if (mode == 0)\r
-               updateHomescreen();\r
+               updateHomescreen(); // check for screen rotation\r
        else if (mode == 1)\r
                updateFullscreen();\r
 }\r
 \r
+function handleOnShow()\r
+{\r
+       updateScreen();\r
+\r
+       var time = new Date();\r
+       if (time.getTime() - lastUpdateTime.getTime() > config['updateDataInterval'].Value * 60 * 1000) {\r
+               log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) / 1000 + 's)');\r
+               clearUpdateTimer();\r
+               updateData();\r
+               setUpdateTimer(); // reinitialize update timer\r
+       }\r
+}\r
+\r
 function launchCalendar()\r
 {\r
        try {\r
@@ -827,7 +1013,7 @@ function launchCalendar()
 \r
 function init()\r
 {\r
-       console.info('New widget instance starting up...');\r
+       log('New widget instance starting up...');\r
        \r
        try {\r
                // call calendar service\r
@@ -836,8 +1022,8 @@ function init()
                else\r
                        throw('device object does not exist');\r
        } catch(e) {\r
-               error('loading Calendar service: ' + e + ', line ' + e.line);\r
-               return;\r
+               error('loading Calendar service: ' + e + ', line ' + e.line + '<br /><a onclick="widget.openURL(\'http://comingnext.sf.net/help\'); return false;" href="http://comingnext.sf.net/help">' + getLocalizedText('menu.help') + '</a>');\r
+               //return;\r
        }\r
 \r
        calendarList = listCalendars();\r
@@ -854,12 +1040,25 @@ function init()
        else {\r
                mode = 1;\r
        }\r
-       console.info("init(): updateScreen()");\r
+       log("init(): updateScreen()");\r
        updateScreen();\r
        if (config['useBackgroundImage'].Value)\r
                // check for screen rotation every 1 secs\r
-               screenRotationTimer = window.setInterval('updateScreen()', 1000 * 1);\r
-       console.info("init(): finished...");\r
+               screenRotationTimer = window.setInterval('checkOrientation()', 1000 * 1);\r
+       \r
+       // call updateScreen() when widget changes from background to forground\r
+       window.widget.onshow = handleOnShow;\r
+\r
+       log("init(): finished...");\r
+       if (!errorOccured)\r
+               statupSuccessful = true;\r
+}\r
+\r
+function checkOrientation()\r
+{\r
+       //updateScreen();\r
+       if (mode == 0)\r
+               updateHomescreen(); // check for screen rotation\r
 }\r
 \r
 function setUpdateTimer()\r
@@ -874,7 +1073,7 @@ function clearUpdateTimer()
 \r
 function updateTimerCallback()\r
 {\r
-       console.info("updateTimerCallback()");\r
+       log("updateTimerCallback()");\r
        updateData();\r
 }\r
 \r
@@ -885,15 +1084,18 @@ function createMenu()
        var id = 0;\r
        var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);\r
        var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);\r
+       var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);\r
        var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);\r
        var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);\r
        menuSettings.onSelect = showSettings;\r
        menuAbout.onSelect = showAbout;\r
        menuCallApp.onSelect = launchCalendar;\r
        menuUpdate.onSelect = showUpdate;\r
+       menuHelp.onSelect = showHelp;\r
        window.menu.clear();\r
        window.menu.append(menuCallApp);\r
        window.menu.append(menuSettings);\r
+       window.menu.append(menuHelp);\r
        window.menu.append(menuUpdate);\r
        window.menu.append(menuAbout);  \r
 }\r
@@ -912,13 +1114,16 @@ function showSettings()
                                config[key].Value = document.forms[0].elements["settings." + key].value;\r
                        else if (config[key].Type == 'Int') {\r
                                config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);\r
-                               if (config[key].Value < 0)\r
+                               if (config[key].Value < 0 || isNaN(config[key].Value))\r
                                        config[key].Value = config[key].Default;\r
                        }\r
                        else if (config[key].Type == 'Bool')\r
                                config[key].Value = document.forms[0].elements["settings." + key].checked;\r
-                       else if (config[key].Type == 'UID')\r
+                       else if (config[key].Type == 'UID') {\r
                                config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);\r
+                               if (isNaN(config[key].Value))\r
+                                       config[key].Value = config[key].Default;\r
+                       }\r
                        else if (config[key].Type == 'Enum') {\r
                                config[key].Value = document.forms[0].elements["settings." + key].value;\r
                                if (config[key].ValidValues.indexOf(config[key].Value) == -1)\r
@@ -1013,22 +1218,27 @@ function getSettingsCalEntryId()
                var listFiltering = {\r
                        Type:'CalendarEntry', \r
                        Filter:{\r
-                               StartRange: new Date(2000, 0, 1),\r
-                               EndRange: new Date(2000, 0, 1),\r
+                               StartRange: new Date(1999, 11, 30), // note: due to Nokia's buggy calendar API, the settings event can be on 01.01.2000 AND on 31.12.1999, depending on when the calendar entry was created (in summer or winter). It is not even possible to narrow the search down to these two days (probably because of DST offsets). So we're looking for an event between 30.12.1999 and 02.01.2000!\r
+                               EndRange: new Date(2000, 0, 2),\r
                                SearchText: 'ComingNext Settings|',\r
                                Type: 'DayEvent'\r
                        }\r
                }\r
-               var result = calendarService.IDataSource.GetList(listFiltering);\r
-               if (result.ErrorCode) {\r
-                       error(result.ErrorMessage);\r
+               var result = null;\r
+               try {\r
+                       result = calendarService.IDataSource.GetList(listFiltering);\r
+                       if (result.ErrorCode)\r
+                               throw(result.ErrorMessage);\r
+               }\r
+               catch (e) {\r
+                       error("getSettingsCalEntryId: GetList() failed: " + e + ', line ' + e.line);\r
                        return;\r
                }\r
                var list = result.ReturnValue;\r
                var entry = list.getNext();\r
                if (entry != undefined) {\r
                        settingsCalEntryId = entry.LocalId;\r
-                       console.info("settingsCalEntryId=" + settingsCalEntryId);\r
+                       log("settingsCalEntryId=" + settingsCalEntryId);\r
                }\r
                else { // create settings item\r
                        var item = new Object();\r
@@ -1043,7 +1253,7 @@ function getSettingsCalEntryId()
                        try {\r
                                var result = calendarService.IDataSource.Add(criteria);\r
                                if (result.ErrorCode)\r
-                                       error(result.ErrorMessage);\r
+                                       throw(result.ErrorMessage);\r
                        } catch (e) {\r
                                error("getSettingsCalEntryId: " + e + ', line ' + e.line);\r
                        }\r
@@ -1068,14 +1278,19 @@ function loadSettings()
                        LocalId: settingsCalEntryId\r
                }\r
        }\r
-       var result = calendarService.IDataSource.GetList(listFiltering);\r
-       if (result.ErrorCode) {\r
-               error(result.ErrorMessage);\r
+       var result = null;\r
+       try     {\r
+               result = calendarService.IDataSource.GetList(listFiltering);\r
+               if (result.ErrorCode)\r
+                       throw(result.ErrorMessage);\r
+       }\r
+       catch (e) {\r
+               error("loadSettings: GetList() failed: " + e + ', line ' + e.line);\r
                return;\r
        }\r
        var entry = result.ReturnValue.getNext();\r
        if (entry != undefined) {\r
-               console.info("Loading Settings...");\r
+               log("Loading Settings...");\r
                // only reload settings if they chanced since the last reload\r
                if (settingsCache != entry.Summary)\r
                {\r
@@ -1086,25 +1301,39 @@ function loadSettings()
                                var pair = stringlist[i].split('=');\r
                                var key = pair[0];\r
                                var value = pair[1];\r
-                               console.info('stringlist: ' + key + '=\'' + value + '\'');\r
-                               if (config[key].Type == 'Int')\r
+                               if (key == null || value == null || config[key] == null) {\r
+                                       log('Warning: unknown or invalid setting: ' + stringlist[i]);\r
+                                       continue;\r
+                               }\r
+                               log('stringlist[' + i + ']: ' + key + '=\'' + value + '\'');\r
+                               if (config[key].Type == 'Int') {\r
                                        config[key].Value = Number(value);\r
+                                       if (isNaN(config[key].Value))\r
+                                               config[key].Value = config[key].Default;\r
+                               }\r
                                else if (config[key].Type == 'String')\r
                                        config[key].Value = value;\r
                                else if (config[key].Type == 'Bool')\r
                                        config[key].Value = (value == 'true')\r
                                else if (config[key].Type == 'Enum')\r
                                        config[key].Value = value;\r
-                               else if (config[key].Type == 'UID')\r
+                               else if (config[key].Type == 'UID') {\r
                                        config[key].Value = Number(value);\r
-                               else if (config[key].Type == 'Array')\r
+                                       if (isNaN(config[key].Value))\r
+                                               config[key].Value = config[key].Default;\r
+                               }\r
+                               else if (config[key].Type == 'Array') {\r
                                        config[key].Value = value.split("^");\r
+                                       if (config[key].Value.length == 1 && config[key].Value[0] == "") {\r
+                                               config[key].Value = [];\r
+                                       }\r
+                               }\r
                        }\r
                        settingsCache = entry.Summary;\r
                        updateCssClasses();\r
                }\r
                else {\r
-                       console.info("Settings already cached and did not change");\r
+                       log("Settings already cached and did not change");\r
                }\r
        }\r
        else {\r
@@ -1141,15 +1370,16 @@ function saveSettings()
        criteria.Type = "CalendarEntry";\r
        criteria.Item = item;\r
 \r
-       console.info("Saving settings to calendar entry: " + item.Summary);\r
+       log("Saving settings to calendar entry: " + item.Summary);\r
        try {\r
                var result = calendarService.IDataSource.Add(criteria);\r
                if (result.ErrorCode)\r
-                       error(result.ErrorMessage);\r
+                       throw(result.ErrorMessage);\r
        } catch (e) {\r
                error("saveSettings: " + e + ', line ' + e.line);\r
        }\r
        \r
+       lastReloadTime = null; // force calendar data reload on next update\r
        clearUpdateTimer();\r
        setUpdateTimer();\r
 }\r
@@ -1188,23 +1418,30 @@ function showAbout()
        document.getElementById("name").innerHTML = "Coming Next " + version;\r
 }\r
 \r
+function showHelp() {\r
+       widget.openURL('http://comingnext.sf.net/help');\r
+}\r
+\r
 function updateFullscreen()\r
 {\r
 }\r
 \r
 function showFullscreen()\r
 {\r
-       console.info("showFullscreen()");\r
+       log("showFullscreen()");\r
        hideViews();\r
        document.getElementById("fullscreenView").style.display = "block";\r
        document.getElementById('body').className = "backgroundFullscreen";\r
-       document.onclick = launchCalendar;\r
+       if (!errorOccured)\r
+               document.onclick = launchCalendar;\r
        createMenu();\r
        updateData();\r
 }\r
 \r
 function getBackgroundImage()\r
 {\r
+       if (errorOccured)\r
+               return '';\r
        var bgImage;\r
        if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[0]) // internal\r
                bgImage = 'background_' + orientation + '.png';\r
@@ -1216,14 +1453,22 @@ function getBackgroundImage()
 function updateHomescreen()\r
 {\r
        if (config['useBackgroundImage'].Value) {\r
+               // check if we have a completely unknown screen resolution\r
+               var screenHeight = screen.height;\r
+               var screenWidth = screen.width;\r
+               if (screenHeight != 640 && screenHeight != 480 && screenHeight != 360)\r
+                       screenHeight = 360; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code\r
+               if (screenWidth != 640 && screenWidth != 480 && screenWidth != 360)\r
+                       screenWidth = 640; // we can only assume we're in portrait mode, so we set the screen dims as needed for the following code\r
+               \r
                // check for screen rotation\r
-               if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {\r
+               if (orientation != 'portrait' && ((screenWidth == 360 && screenHeight == 640) || (screenWidth == 640 && screenHeight == 480))) {\r
                        window.widget.prepareForTransition("fade");\r
                        orientation = 'portrait';\r
                        document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';\r
                        document.getElementById('body').style.backgroundColor = 'none';\r
                        window.widget.performTransition();\r
-               } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {\r
+               } else if (orientation != 'landscape' && ((screenWidth == 640 && screenHeight == 360) || (screenWidth == 480 && screenHeight == 640))) {\r
                        window.widget.prepareForTransition("fade");\r
                        orientation = 'landscape';\r
                        document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';\r
@@ -1239,7 +1484,7 @@ function updateHomescreen()
 \r
 function showHomescreen()\r
 {\r
-       console.info("showHomescreen()");\r
+       log("showHomescreen()");\r
        hideViews();\r
        document.getElementById("homescreenView").style.display = "block";\r
        document.getElementById('body').className = "background";\r
@@ -1322,6 +1567,10 @@ function hideViews()
 \r
 function listCalendars()\r
 {\r
+       if (errorOccured) {\r
+               return null;\r
+       }\r
+\r
        try {\r
                var criteria = {\r
                        Type:'Calendar', \r
@@ -1341,7 +1590,7 @@ function listCalendars()
                while (( item = calendarListIterator.getNext()) != undefined ) {\r
                        calendars[count++] = item;\r
                }\r
-               console.info("Available Calendars: " + calendars.join(", "));\r
+               log("Available Calendars: " + calendars.join(", "));\r
                return calendars;\r
        } catch(e) {\r
                error('listing calendars:' + e + ', line ' + e.line);\r
@@ -1368,14 +1617,14 @@ function listToArray(list, calendarName)
                array.push(itemCopy);\r
                txt += array[array.length - 1].Summary + ", ";\r
        }\r
-       console.info("listToArray(): " + txt);\r
+       log("listToArray(): " + txt);\r
        return array;\r
 }\r
 \r
 function sortCalendarEntries(a, b)\r
 {\r
        var atime, btime;\r
-       console.info("sortCalendarEntries(" + a.Summary + "," + b.Summary + ")");\r
+       log("sortCalendarEntries(" + a.Summary + "," + b.Summary + ")");\r
        \r
        if (a.InstanceStartTime != null) {\r
                atime = a.InstanceStartTime;\r
@@ -1434,6 +1683,33 @@ function sortCalendarEntries(a, b)
                        }\r
                }\r
        }\r
+       // NOTE: events my have no date information at all. In that case, we list events without date first\r
+       else if (atime && !btime) {\r
+               return 1;\r
+       }\r
+       else if (!atime && btime) {\r
+               return -1;\r
+       }\r
+       else if (!atime && !btime) {\r
+               // sort by type\r
+               if (a.Type != b.Type) {\r
+                       if (a.Type < b.Type) {\r
+                               return -1;\r
+                       }\r
+                       else if (a.Type > b.Type) {\r
+                               return 1;\r
+                       }\r
+               }\r
+               // sort by description\r
+               else if (a.Summary && b.Summary && a.Summary != b.Summary) {\r
+                       if (a.Summary < b.Summary) {\r
+                               return -1;\r
+                       }\r
+                       else if (a.Summary > b.Summary) {\r
+                               return 1;\r
+                       }\r
+               }\r
+       }\r
 \r
        return 0;\r
 }\r
@@ -1443,25 +1719,33 @@ function updateCalendarColors()
        var maxColors = 6;\r
        calendarColors = [];\r
        if (calendarList.length > maxColors) {\r
-               console.info("updateCalendarColors(): Warning: more calendars than available indicator colors");\r
+               log("updateCalendarColors(): Warning: more calendars than available indicator colors");\r
        }\r
        for(var i=0; i < calendarList.length; i++) {\r
                calendarColors[calendarList[i]] = (i % maxColors) + 1;\r
        }\r
 }\r
 \r
+function log(message)\r
+{\r
+       if (config['enableLogging'].Value) {\r
+               console.info(message);\r
+       }\r
+}\r
+\r
 </script>\r
 \r
 <style type="text/css">\r
-table { margin:0px; padding:0px; border-spacing:0px; }\r
-td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }\r
+a { color:#aaccff }\r
+table { margin:0px; padding:0px; border-spacing:0px; border-collapse: collapse; }\r
+td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; margin:0px; }\r
 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }\r
 .settingsInfo { display:none; font-style:italic; }\r
 .title { font-weight:bold; font-size:14pt; }\r
 .textInput { width:90%; }\r
 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }\r
-#homescreenView { width: 315px; height:91px; overflow:hidden; }\r
-#calendarList { position:absolute; left:5px; top:4px; width:295px; height:75px; overflow:hidden; }\r
+#homescreenView { width: 312px; height:82px; overflow:hidden; }\r
+#calendarList { position:absolute; left:5px; top:0px; width:307px; height:82px; overflow:hidden; }\r
 #name { text-align:center; }\r
 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }\r
 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }\r
@@ -1469,9 +1753,9 @@ hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; borde
 \r
 </head>\r
 \r
-<body id="body" class="background">\r
+<body onload="javascript:setTimeout('init()', 10)" onresize="javascript:updateScreen()" id="body" class="background">\r
 <div id="homescreenView">\r
-       <div id="calendarList"></div>\r
+       <div id="calendarList">loading...</div>\r
 </div>\r
 <div id="fullscreenView" style="display:none;">\r
        <img src="Icon.png" id="smallappicon">\r
@@ -1493,13 +1777,17 @@ hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; borde
        <p>Contributions:</p>\r
                <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>\r
                <p class="credits">Manfred Hanselmann (DST support)</p>\r
-               <p class="credits">Christophe Milsent (translation support & french translation)</p>\r
-               <p class="credits">Flavio Nathan (portuguese-brazilian translation)</p>\r
-               <p class="credits">Tokeda (russian translation)</p>\r
-               <p class="credits">Marcella Ferrari (italian translation)</p>\r
-               <p class="credits">Venos (italian translation)</p>\r
+               <p class="credits">Christophe Milsent (translation support & French translation)</p>\r
+               <p class="credits">Flavio Nathan (Portuguese-Brazilian translation)</p>\r
+               <p class="credits">Tokeda (Russian translation)</p>\r
+               <p class="credits">Marcella Ferrari (Italian translation)</p>\r
+               <p class="credits">Venos (Italian translation)</p>\r
+               <p class="credits">Francisco Rodero (Catalan translation)</p>\r
+               <p class="credits">zbigzbig20 (Polish translation)</p>\r
+               <p class="credits">Streamkeskus (Finnish translation)</p>\r
+               <p class="credits">renek (Czech translation)</p>\r
        <p>This software is open source and licensed under the GPLv3.</p>\r
-       <p>Visit <a href="http://sourceforge.net/projects/comingnext">sourceforge.net/projects/comingnext</a> for free updates.</p>\r
+       <p>Visit <a onclick="widget.openURL('http://comingnext.sf.net/'); return false;" href="http://comingnext.sf.net/">comingnext.sf.net</a> for free updates.</p>\r
        <hr />\r
 </div>\r
 <div id="updateView" style="display:none">\r