]> code.delx.au - comingnext/blob - comingNext/index.html
79e03a02ab796b3d03738bf3f47a74fa9bb41553
[comingnext] / comingNext / index.html
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5
6 <title>Coming Next</title>
7
8 <style type="text/css">
9 /* The following classes can be modified by widget settings */
10 .background { color:#ffffff; background-color:#000000 }
11 .backgroundFullscreen { color:#ffffff; background-color:#000000 }
12 .weekDay { }
13 .date { }
14 .today { color:#ff0000 }
15 .tomorrow { color:#0000ff }
16 .time { }
17 .now { color:#ff00ff }
18 .description { }
19 .icon { width:15px; height:15px }
20 .overdue { color:#ffff00 }
21 .calendar1 { background-color:#0757cf }
22 .calendar2 { background-color:#579f37 }
23 .calendar3 { background-color:#ff9f07 }
24 .calendar4 { background-color:#af8fef }
25 .calendar5 { background-color:#57afbf }
26 .calendar6 { background-color:#9fdf57 }
27 </style>
28
29 <script type="text/javascript" src="localizedTextStrings.js" charset="utf-8" />
30
31 <script>
32 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID', 'Array'
33 var config = {
34 monthRange: { Type: 'Int', Default: 2, Value: 2,},
35 includeTodos: { Type: 'Bool', Default: true, Value: true,},
36 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
37 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
38 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
39 showLocation: { Type: 'Bool', Default: true, Value: true,},
40 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
41 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
42 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
43 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
44 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
45 markOverdueTodos: { Type: 'Bool', Default: true, Value: true,},
46 overdueText: {Type: 'String', Default: getLocalizedText('settings.default.overdueText'), Value: getLocalizedText('settings.default.overdueText'),},
47 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
48 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
49 weekDayLength: { Type: 'Int', Default: 2, Value: 2,},
50 updateDataInterval: { Type: 'Int', Default: 5, Value: 5,},
51 calendarApp: { Type: 'UID', Default: 0x10005901, Value: 0x10005901,},
52 eventsPerWidget: { Type: 'Int', Default: 4, Value: 4,},
53 showNothingText: { Type: 'Bool', Default: true, Value: true,},
54 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
55 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
56 daylightSavingOffset: { Type: 'Int', Default: 1, Value: 1,},
57 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
58 showCalendarIndicator: { Type: 'Bool', Default: true, Value: true,},
59 excludedCalendars: { Type: 'Array', Default: [], Value: [],},
60 enableLogging: { Type: 'Bool', Default: false, Value: false,},
61 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
62 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
63 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
64 cssStyle_date: { Type: 'String', Default: '', Value: '',},
65 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
66 cssStyle_tomorrow: { Type: 'String', Default: 'color:#0000ff', Value: 'color:#0000ff',},
67 cssStyle_time: { Type: 'String', Default: '', Value: '',},
68 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
69 cssStyle_description: { Type: 'String', Default: '', Value: '',},
70 cssStyle_icon: { Type: 'String', Default: 'width:15px; height:15px', Value: 'width:15px; height:15px',},
71 cssStyle_overdue: { Type: 'String', Default: 'color:#ffff00', Value: 'color:#ffff00',},
72 cssStyle_calendar1: { Type: 'String', Default: 'background-color:#0757cf', Value: 'background-color:#0757cf',},
73 cssStyle_calendar2: { Type: 'String', Default: 'background-color:#579f37', Value: 'background-color:#579f37',},
74 cssStyle_calendar3: { Type: 'String', Default: 'background-color:#ff9f07', Value: 'background-color:#ff9f07',},
75 cssStyle_calendar4: { Type: 'String', Default: 'background-color:#af8fef', Value: 'background-color:#af8fef',},
76 cssStyle_calendar5: { Type: 'String', Default: 'background-color:#57afbf', Value: 'background-color:#57afbf',},
77 cssStyle_calendar6: { Type: 'String', Default: 'background-color:#9fdf57', Value: 'background-color:#9fdf57',},
78 }
79
80
81
82 //-------------------------------------------------------
83 // Nothing of interest from here on...
84 //-------------------------------------------------------
85 var panelNum = 0; // use 1 for second panel
86 var version = "1.32";
87 var versionURL = "http://comingnext.sourceforge.net/version.xml";
88 var calendarService = null;
89 var cacheEntriesHtml = [];
90 var months_translated = [];
91 var orientation = '';
92 var now = new Date();
93 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about, 4 = check for update
94 var reqV = null;
95 var settingsCalEntryId = null;
96 var settingsCache = null;
97 var notificationRequests = new Array();
98 var calendarList = [];
99 var calendarColors = [];
100 var updateTimer = null;
101 var screenRotationTimer = null;
102 var lastUpdateTime = now; // last time we updated the display
103 var lastReloadTime = null; // last time we fetched calendar data
104 var reloadInterval = 6 * 60 * 60 * 1000; // = 6 hours; time interval for reloading calendar data
105 var errorOccured = false;
106 var entryLists = null; // stores all fetched calendar entries until data is refreshed
107
108 // vars for daylight saving time
109 var summertime = false; // true, if current date is in summer, false if in winter
110 var daylightSavingDates = new Object(); // caches calculated DST winter and summer time shift dates
111
112 // this is a list of data fields a calendar event can have
113 var entryFields = [
114 "id",
115 "Type",
116 "CalendarName",
117 "Summary",
118 "Location",
119 "Status",
120 "StartTime",
121 "EndTime",
122 "InstanceStartTime",
123 "InstanceEndTime"
124 ];
125
126 function isLeapYear( year ) {
127 if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
128 return true;
129 else
130 return false;
131 }
132
133 function calcLeapYear(year, days)
134 {
135 if (isLeapYear(year))
136 return ++days;
137 else
138 return days;
139 }
140
141 function subToSunday(myDate, year, days, prevMonthDays)
142 {
143 for (i = myDate.getDay(); i > 0 ;i--)
144 days--;
145 days -= prevMonthDays;
146 days = isLeapYear(year) ? --days : days;
147 return days;
148 }
149
150 function isSummertime(curDate)
151 {
152 var summer = false;
153 var winter = false;
154
155 // if we already calculated DST summer and winter time dates for this year, use cached values
156 var dst = daylightSavingDates[curDate.getFullYear()];
157 if (!dst) {
158 var thisYearS = new Date(curDate.getFullYear(), 3, 0, 0, 0, 0 );
159 var thisYearW = new Date(curDate.getFullYear(), 10, 0, 0, 0, 0 );
160 var nextYearS = new Date(curDate.getFullYear() + 1, 3, 0, 0, 0, 0 );
161 var nextYearW = new Date(curDate.getFullYear() + 1, 10, 0, 0, 0, 0 );
162
163 thisYearSDays = nextYearSDays = 90;
164 thisYearWDays = nextYearWDays = 304;
165
166 thisYearSDays = calcLeapYear(curDate.getFullYear(), thisYearSDays);
167 thisYearWDays = calcLeapYear(curDate.getFullYear(), thisYearWDays);
168 nextYearSDays = calcLeapYear(curDate.getFullYear() + 1, nextYearSDays);
169 nextYearWDays = calcLeapYear(curDate.getFullYear() + 1, nextYearWDays);
170
171 thisYearSDays = subToSunday(thisYearS, curDate.getFullYear(), thisYearSDays, 59);
172 thisYearWDays = subToSunday(thisYearW, curDate.getFullYear(), thisYearWDays, 273);
173 nextYearSDays = subToSunday(nextYearS, curDate.getFullYear() + 1, nextYearSDays, 59);
174 nextYearWDays = subToSunday(nextYearW, curDate.getFullYear() + 1, nextYearWDays, 273);
175
176 dst = {
177 Summer: new Date (curDate.getFullYear(), 03-1, thisYearSDays, 2, 0, 0),
178 Winter: new Date (curDate.getFullYear(), 10-1, thisYearWDays, 2, 0, 0),
179 }
180 daylightSavingDates[curDate.getFullYear()] = dst;
181 }
182
183 if (dst.Summer < curDate)
184 summer = true;
185 if (dst.Winter < curDate)
186 winter = true;
187 if (summer && !winter)
188 return true;
189 else
190 return false;
191 }
192
193 function error(message)
194 {
195 console.info('Error: ' + message);
196 document.getElementById("calendarList").innerHTML = 'Error: ' + message;
197 document.getElementById("fullscreenCalendarList").innerHTML = 'Error: ' + message;
198 errorOccured = true;
199 document.onclick = null;
200 }
201
202 function areDatesEqual(date1, date2)
203 {
204 return (date1.getFullYear() == date2.getFullYear() &&
205 date1.getMonth() == date2.getMonth() &&
206 date1.getDate() == date2.getDate());
207 }
208
209 function isTomorrow(date)
210 {
211 // tommorow = now + 1 day
212 // ToDo: some days can be shorter as 24 hours(daylight saving change day)
213 return areDatesEqual(date, new Date (now.getTime() + 24*60*60*1000));
214 }
215
216 function isToday(date)
217 {
218 return areDatesEqual(date, now);
219 }
220
221 function collectLocales()
222 {
223 var tmpyear = 2000 + panelNum;
224 var month = 0;
225
226 if (months_translated.length > 0)
227 return;
228 for (month = 0; month < 12; month++) {
229 var startDate = new Date(tmpyear, month, 15);
230
231 var item = new Object();
232 item.Type = "DayEvent";
233 item.StartTime = startDate;
234 item.Summary = "__temp" + month;
235
236 var criteria = new Object();
237 criteria.Type = "CalendarEntry";
238 criteria.Item = item;
239
240 try {
241 var result = calendarService.IDataSource.Add(criteria);
242 if (result.ErrorCode)
243 error(result.ErrorMessage);
244 } catch (e) {
245 error("collectLocales: " + e + ', line ' + e.line);
246 }
247 }
248 try {
249 var startTime = new Date(tmpyear,0,1);
250 var endTime = new Date(tmpyear,11,31);
251 var listFiltering = {
252 Type:'CalendarEntry',
253 Filter:{
254 StartRange: startTime,
255 EndRange: endTime,
256 SearchText: '__temp',
257 Type: 'DayEvent'
258 }
259 }
260 var result = calendarService.IDataSource.GetList(listFiltering);
261 if (result.ErrorCode) {
262 error(result.ErrorMessage);
263 return;
264 }
265 var list = result.ReturnValue;
266 } catch(e) {
267 error(e + ', line ' + e.line);
268 return;
269 }
270 var ids = new Array();
271 try {
272 var entry;
273 var counter = 0;
274 var dateArr = [];
275
276 while (list && (entry = list.getNext()) != undefined) {
277 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
278 var day = dateArr[1];
279 var month = dateArr[2];
280 var year = dateArr[3];
281
282 // make sure month is set properly
283 if (isNaN(parseInt(day))) {
284 var tmp = day;
285 day = month;
286 month = tmp;
287 } else if (isNaN(parseInt(year))) {
288 var tmp = year;
289 year = month;
290 month = tmp;
291 }
292
293 log(entry.StartTime + ' -> ' + month + ' ' + counter);
294 ids[counter] = entry.id;
295 months_translated[month] = counter + 1;
296 counter++;
297 }
298 } catch(e) {
299 error(e + ', line ' + e.line);
300 return;
301 }
302 log(ids);
303 try {
304 var criteria = new Object();
305 criteria.Type = "CalendarEntry";
306 criteria.Data = {
307 IdList: ids
308 }
309
310 var result = calendarService.IDataSource.Delete(criteria);
311 if (result.ErrorCode)
312 error(result.ErrorMessage);
313 } catch(e) {
314 error('deleting temp calendar entries:' + e + ', line ' + e.line);
315 return;
316 }
317 }
318
319 function requestNotification()
320 {
321 var criteria = new Object();
322 criteria.Type = "CalendarEntry";
323 criteria.Filter = new Object();
324 for(var i=0; i < calendarList.length; i++) {
325 criteria.Filter.CalendarName = calendarList[i];
326 try {
327 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria, callback);
328 if (notificationRequest.ErrorCode)
329 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
330 notificationRequests.push(notificationRequest);
331 } catch (e) {
332 error("requestNotification: " + e + ', line ' + e.line);
333 }
334 }
335
336 var criteria2 = new Object();
337 criteria2.Type = "CalendarEntry";
338 criteria2.Filter = new Object();
339 criteria2.Filter.LocalIdList = new Array();
340 criteria2.Filter.LocalIdList[0] = settingsCalEntryId;
341 try {
342 var notificationRequest = calendarService.IDataSource.RequestNotification(criteria2, settingsCallback);
343 if (notificationRequest.ErrorCode)
344 error('requestNotification failed with error code ' + notificationRequest.ErrorCode);
345 notificationRequests.push(notificationRequest);
346 } catch (e) {
347 error("requestNotification: " + e + ', line ' + e.line);
348 }
349 }
350
351 function cancelNotification()
352 {
353 for(var i=0; i < notificationRequests.length; i++) {
354 try {
355 var result = calendarService.IDataSource.Cancel(notificationRequests[i]);
356 if (result.ErrorCode)
357 error('cancelNotification failed with error code ' + result.ErrorCode);
358 } catch (e) {
359 error("cancelNotification: " + e + ', line ' + e.line);
360 }
361 }
362 }
363
364 function callback(transId, eventCode, result)
365 {
366 log("callback(): panelNum: " + panelNum + " transId: " + transId + " eventCode: " + eventCode + " result.ErrorCode: " + result.ErrorCode);
367 lastReloadTime = null; // force calendar data reload on next update
368 updateData();
369 }
370
371 function settingsCallback(transId, eventCode, result)
372 {
373 log("settingsCallback(): panelNum: " + panelNum + " transId: " + transId + " eventCode: " + eventCode + " result.ErrorCode: " + result.ErrorCode);
374 loadSettings();
375 }
376
377 function parseDate(dateString)
378 {
379 /*
380 Dates my look very differently. Also keep in mind that the names are localized!!! These are the possibilities depending on the users date format setting:
381 Wednesday, 26 August, 2009 24:00:00
382 Wednesday, 26 August, 2009 12:00:00 am
383 Wednesday, August 26, 2009 12:00:00 am
384 Wednesday, 2009 August, 26 12:00:00 am
385 Wednesday, 2009 August, 28 8.00.00 pm
386 Wednesday, 2009 August, 28 08:00:00 PM
387 */
388
389 if (dateString == "" || dateString == null)
390 return null;
391 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
392 if (dateArr.length != 5 && dateArr.length != 6)
393 return null;
394
395 // parse date
396 var weekDay = dateArr[0];
397 var day = dateArr[1];
398 var month = dateArr[2];
399 var year = dateArr[3];
400 // make sure month is set properly
401 if (isNaN(parseInt(day))) {
402 var tmp = day;
403 day = month;
404 month = tmp;
405 } else if (isNaN(parseInt(year))) {
406 var tmp = year;
407 year = month;
408 month = tmp;
409 }
410 // make sure day and year are set properly
411 if (Number(day) > Number(year)) {
412 var tmp = year;
413 year = day;
414 day = tmp;
415 }
416 month = months_translated[month];
417
418 // parse time
419 var timeArr = dateArr[4].split(':');
420 if (timeArr.length != 3)
421 return null;
422 var hours = Number(timeArr[0]);
423 var minutes = Number(timeArr[1]);
424 var seconds = Number(timeArr[2]);
425 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)
426 hours += 12;
427 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)
428 hours = 0;
429
430 var result = new Date(year, month - 1, day, hours, minutes, seconds);
431
432 // take care of daylight saving time
433 if (config['enableDaylightSaving'].Value) {
434
435 // determine if date is in summer or winter time
436 var dateSummerTime = isSummertime(result);
437
438 // work around bug in Nokias calendar api resulting in dates within a different DST to be off by 1 hour
439 if (summertime && !dateSummerTime) {
440 result = new Date(result.getTime() - 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // -1 hour
441 log('parseDate(): fixing time -1h: ' + result);
442 }
443 else if (!summertime && dateSummerTime) {
444 result = new Date(result.getTime() + 1000 * 60 * 60 * config['daylightSavingOffset'].Value); // +1 hour
445 log('parseDate(): fixing time +1h: ' + result);
446 }
447 }
448
449 return result;
450 }
451
452 // 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"
453 function formatDate(date, format)
454 {
455 var day = date.getDate().toString();
456 var month = (date.getMonth() + 1).toString();
457 while (day.length < 2) { day = '0' + day; }
458 while (month.length < 2) { month = '0' + month; }
459
460 if (config['showTodayAsText'].Value && isToday(date))
461 return '<span class="today">' + config['todayText'].Value + '</span>';
462 if (config['showTodayAsText'].Value && isTomorrow(date))
463 return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';
464
465 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
466 if (dateArr.length != 5 && dateArr.length != 6) {
467 // we don't know how to format this
468 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
469 return day + config['dateSeparator'].Value + month;
470 else
471 return month + config['dateSeparator'].Value + day;
472 }
473
474 var dayFirst = true;
475 if (config['dateFormat'].Value == 'MMDD')
476 dayFirst = false;
477 else if (config['dateFormat'].Value == 'DDMM')
478 dayFirst = true;
479 else {
480 // config['dateFormat'].Value == 'auto', try to detect system setting
481 // parse date
482 var day_ = dateArr[1];
483 var month_ = dateArr[2];
484 var year_ = dateArr[3];
485 // make sure month is set properly
486 if (isNaN(parseInt(day_))) {
487 var tmp = day_;
488 day_ = month_;
489 month_ = tmp;
490 dayFirst = false;
491 } else if (isNaN(parseInt(year_))) {
492 var tmp = year_;
493 year_ = month_;
494 month_ = tmp;
495 dayFirst = true;
496 }
497 // make sure day and year are set properly
498 if (Number(day_) > Number(year_))
499 dayFirst = false;
500 }
501
502 if (dayFirst)
503 return day + config['dateSeparator'].Value + month;
504 else
505 return month + config['dateSeparator'].Value + day;
506 }
507
508 function formatTime(date)
509 {
510 // date is a Date() object
511 date.setSeconds(0); // we don't care about seconds
512 var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string
513 if (time.replace(/\./, ':').split(':')[0].length < 2)
514 time = '0' + time;
515 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
516 time = '<span class="now">' + config['nowText'].Value + '</span>';
517 return time;
518 }
519
520 function updateData()
521 {
522 log('updateData()');
523 if (errorOccured) {
524 return;
525 }
526
527 // check if we got additional or less calendars since our last update
528 var newCalendarList = listCalendars();
529 if (newCalendarList.length != calendarList.length) {
530 calendarList = newCalendarList;
531 updateCalendarColors();
532 cancelNotification();
533 requestNotification();
534 lastReloadTime = null; // force calendar data reload on this update
535 }
536
537 now = new Date();
538
539 // only reload calendar data every 6 hours, visual updates occure more often
540 if (!lastReloadTime || now.getTime() - lastReloadTime.getTime() > reloadInterval) {
541 log('updateData(): reloading calendar data');
542 try {
543 // meetings have time
544 // 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
545 summertime = isSummertime(now); // cache summer time info for today
546 var meetingList = [];
547 for(var i=0; i < calendarList.length; i++) {
548 // ignore excluded calendars
549 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)
550 continue;
551 var meetingListFiltering = {
552 Type:'CalendarEntry',
553 Filter:{
554 CalendarName: calendarList[i],
555 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),
556 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))
557 }
558 }
559 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
560 if (meetingResult.ErrorCode != 0)
561 throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
562 var list = meetingResult.ReturnValue;
563 meetingList = meetingList.concat(listToArray(list, calendarList[i]));
564 }
565 log("updateData(): meetingList.sort()");
566 meetingList.sort(sortCalendarEntries);
567
568 // todos don't, they start on 00:00 hrs., but should be visible anyway
569 // this will generate a list of passed todos. We have to check if they have been marked as "done" yet
570 if (config['includeTodos'].Value) {
571 var todayTodoList = [];
572 for(var i=0; i < calendarList.length; i++) {
573 // ignore excluded calendars
574 if (config['excludedCalendars'].Value.indexOf(calendarList[i]) != -1)
575 continue;
576 var todayTodoListFiltering = {
577 Type:'CalendarEntry',
578 Filter:{
579 CalendarName: calendarList[i],
580 Type: 'ToDo',
581 StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),
582 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))
583 }
584 }
585 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
586 var list = todayTodoResult.ReturnValue;
587 todayTodoList = todayTodoList.concat(listToArray(list, calendarList[i]));
588 }
589 log("updateData(): todayTodoList.sort()");
590 todayTodoList.sort(sortCalendarEntries);
591 entryLists = [todayTodoList, meetingList];
592 } else {
593 entryLists = [meetingList];
594 }
595 lastReloadTime = new Date();
596 } catch(e) {
597 error('loading Calendar items list:' + e + ', line ' + e.line);
598 return;
599 }
600 }
601
602 try {
603 var entry;
604 var counter = 0;
605 var entryDate = '';
606 var dateArr = [];
607 var fontsize = 'normal';
608 if (mode == 0) {
609 if (config['eventsPerWidget'].Value == 3) {
610 fontsize = '17pt';
611 changeCssClass('.icon', 'width:20px; height:20px');
612 }
613 else if (config['eventsPerWidget'].Value == 5) {
614 fontsize = '10pt';
615 changeCssClass('.icon', 'width:10px; height:10px');
616 }
617 else if (config['eventsPerWidget'].Value == 6) {
618 fontsize = '8pt';
619 changeCssClass('.icon', 'width:8px; height:8px');
620 }
621 }
622 else
623 changeCssClass('.icon', config['cssStyle_icon'].Value);
624 var entriesHtml = '<table style="font-size:' + fontsize + ';">';
625 var eventIds = [];
626 var max;
627 if (mode == 0)
628 max = (panelNum + 1) * config['eventsPerWidget'].Value;
629 else
630 max = 30; // we can display a lot more events in fullscreen mode
631
632 if (config['enableLogging'].Value) {
633 var listinfo = "";
634 for (var i=0; i < entryLists.length; i++) {
635 listinfo = listinfo + " " + entryLists[i].length;
636 var entrieslist = "";
637 for (var j=0; j < entryLists[i].length; j++) {
638 entrieslist += entryLists[i][j].Summary + ", ";
639 }
640 log("updateData(): entrieslist: " + entrieslist);
641 }
642 log("updateData(): inner loop, " + entryLists.length + " lists, [" + listinfo + "] entries");
643 }
644
645 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
646 for (var i=0; counter < max && i < entryLists.length; i++) {
647 for (var j=0; (counter < max) && (j < entryLists[i].length); j++) {
648 entry = entryLists[i][j];
649 counter++;
650
651 // output event info for debugging
652 var entryInfo = "event: ";
653 for(var k=0; k < entryFields.length; ++k) {
654 if (entry[entryFields[k]] != undefined) {
655 entryInfo += entryFields[k] + "=" + entry[entryFields[k]] + ",";
656 }
657 }
658 log(entryInfo);
659
660 // we don't want ToDos when includeTodos == false or when they are completed
661 if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {
662 log('skipping ' + entry.id );
663 counter--;
664 continue;
665 }
666
667 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
668 if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {
669 log('skipped (already included) ' + entry.id);
670 counter--;
671 continue;
672 } else
673 eventIds[entry.id] = 1;
674
675 // summary can be undefined!
676 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
677 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
678 Summary += ', ' + entry.Location;
679
680 // fix by yves: determine start and end dates/times
681 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
682 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
683
684 // there can be ToDos that have no date at all!
685 if (entry.Type == 'ToDo' && entry.EndTime == null)
686 entryDate = ""; // this will cause parseDate(entryDate) to return null;
687 else
688 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
689
690 // Convert date/time string to Date object
691 var date = parseDate(entryDate);
692 log('date: ' + date);
693 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
694 log('endDate: ' + endDate);
695
696 // check if meeting event has already passed
697 if (entry.Type == 'Meeting') {
698 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
699 if (now.getTime() > compareTime) {
700 log('skipping Meeting (already passed) ' + entry.id);
701 counter--;
702 eventIds[entry.id] = 0;
703 continue;
704 }
705 }
706
707 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
708 if (entry.Type == 'Anniversary') {
709 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
710 if (date.getTime() < tmp.getTime()) {
711 log('skipping Anniversary (already passed) ' + entry.id);
712 counter--;
713 eventIds[entry.id] = 0;
714 continue;
715 }
716 }
717
718 // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed
719 if (entry.Type == 'DayEvent' && endDate != null) {
720 endDate.setMinutes(endDate.getMinutes() - 1);
721 log('fixing DayEvent endDate: ' + endDate);
722 if (now.getTime() > endDate.getTime()) {
723 log('event already passed ' + entry.id);
724 counter--;
725 eventIds[entry.id] = 0;
726 continue;
727 }
728 }
729
730 // check if the event is currently taking place
731 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
732 // check if we are between start and endtime
733 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
734 date = now; // change appointment date/time to now
735 log('event is currently taking place: ' + date);
736 }
737 }
738
739 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
740 if (mode == 0 && panelNum > 0 && counter < panelNum * config['eventsPerWidget'].Value + 1) {
741 log('skipping (already in first widget) ' + entry.id);
742 continue;
743 }
744
745 // mark overdue todos
746 var overdue = false;
747 if (entry.Type == 'ToDo' && date != null) {
748 var tmp1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
749 var tmp2 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
750 if (tmp1.getTime() < tmp2.getTime()) {
751 overdue = true;
752 }
753 }
754
755 // generate html output
756 entriesHtml += '<tr>';
757 if (config['showCalendarIndicator'].Value && calendarList.length - config['excludedCalendars'].Value.length > 1) {
758 entriesHtml += '<td><span class="calendar' + calendarColors[entry.CalendarName] + '">&nbsp;</span></td>';
759 }
760 entriesHtml += '<td><img class="icon" src="' + entry.Type + '.png" /></td>';
761 if(date == null) {
762 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
763 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
764 } else {
765 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
766 var time = formatTime(date);
767 var dateStr = formatDate(date, entryDate);
768 if (entry.Type == 'ToDo' && overdue && config['markOverdueTodos'].Value) {
769 dateStr = '<span class="overdue">' + config['overdueText'].Value + '</span>';
770 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
771 } else if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
772 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
773 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
774 else
775 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
776 } else if (entry.Type == 'Meeting') {
777 if (config['showCombinedDateTime'].Value) {
778 if (isToday(date))
779 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
780 else if (isTomorrow(date))
781 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
782 else
783 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
784 } else {
785 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
786 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
787 else
788 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
789 }
790 }
791 }
792 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
793 }
794 }
795 entriesHtml += '</table>';
796 if (config['showNothingText'].Value && entriesHtml == '<table></table>') {
797 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
798 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';
799 }
800 if (cacheEntriesHtml != entriesHtml) {
801 if (mode == 0)
802 document.getElementById('calendarList').innerHTML = entriesHtml;
803 else
804 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
805 cacheEntriesHtml = entriesHtml;
806 }
807
808 lastUpdateTime = new Date();
809 } catch(e) {
810 error('displaying list:' + e + ', line ' + e.line);
811 return;
812 }
813 }
814
815 // called by handleOnShow() and onResize events
816 function updateScreen()
817 {
818 log('updateScreen()');
819
820 // check if opening fullscreen
821 if( window.innerHeight > 91 && mode == 0) {
822 mode = 1;
823 cacheEntriesHtml = '';
824 document.getElementById('body').style.backgroundImage = "";
825 showFullscreen();
826 }
827 else if (window.innerHeight <= 91 && mode != 0) {
828 mode = 0;
829 cacheEntriesHtml = '';
830 showHomescreen();
831 }
832
833 if (mode == 0)
834 updateHomescreen(); // check for screen rotation
835 else if (mode == 1)
836 updateFullscreen();
837 }
838
839 function handleOnShow()
840 {
841 updateScreen();
842
843 var time = new Date();
844 if (time.getTime() - lastUpdateTime.getTime() > config['updateDataInterval'].Value * 60 * 1000) {
845 log('updateScreen(): force updateData() because last update was too long ago (' + (time.getTime() - lastUpdateTime.getTime()) / 1000 + 's)');
846 clearUpdateTimer();
847 updateData();
848 setUpdateTimer(); // reinitialize update timer
849 }
850 }
851
852 function launchCalendar()
853 {
854 try {
855 widget.openApplication(config['calendarApp'].Value, "");
856 if (config['hideWidgetOnCalendarOpen'].Value)
857 window.close();
858 } catch(e) {
859 error('starting Calendar App');
860 return;
861 }
862 }
863
864 function init()
865 {
866 log('New widget instance starting up...');
867
868 try {
869 // call calendar service
870 if (device != "undefined")
871 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
872 else
873 throw('device object does not exist');
874 } catch(e) {
875 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>');
876 //return;
877 }
878
879 calendarList = listCalendars();
880 loadSettings();
881 updateCalendarColors();
882 collectLocales();
883 //updateData();
884 requestNotification();
885 document.getElementById("settingsTitle").innerHTML = getLocalizedText('menu.settings');
886 setUpdateTimer();
887 if (window.innerHeight > 91) {
888 mode = 0; // we're starting fullscreen, we set mode to homescreen in order to let updateScreen() do all the work for us
889 }
890 else {
891 mode = 1;
892 }
893 log("init(): updateScreen()");
894 updateScreen();
895 if (config['useBackgroundImage'].Value)
896 // check for screen rotation every 1 secs
897 screenRotationTimer = window.setInterval('checkOrientation()', 1000 * 1);
898
899 // call updateScreen() when widget changes from background to forground
900 window.widget.onshow = handleOnShow;
901
902 log("init(): finished...");
903 }
904
905 function checkOrientation()
906 {
907 //updateScreen();
908 if (mode == 0)
909 updateHomescreen(); // check for screen rotation
910 }
911
912 function setUpdateTimer()
913 {
914 updateTimer = window.setInterval('updateTimerCallback()', 1000 * 60 * config['updateDataInterval'].Value);
915 }
916
917 function clearUpdateTimer()
918 {
919 window.clearInterval(updateTimer);
920 }
921
922 function updateTimerCallback()
923 {
924 log("updateTimerCallback()");
925 updateData();
926 }
927
928 function createMenu()
929 {
930 window.menu.setLeftSoftkeyLabel("",null);
931 window.menu.setRightSoftkeyLabel("",null);
932 var id = 0;
933 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
934 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
935 var menuHelp = new MenuItem(getLocalizedText('menu.help'), id++);
936 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
937 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
938 menuSettings.onSelect = showSettings;
939 menuAbout.onSelect = showAbout;
940 menuCallApp.onSelect = launchCalendar;
941 menuUpdate.onSelect = showUpdate;
942 menuHelp.onSelect = showHelp;
943 window.menu.clear();
944 window.menu.append(menuCallApp);
945 window.menu.append(menuSettings);
946 window.menu.append(menuHelp);
947 window.menu.append(menuUpdate);
948 window.menu.append(menuAbout);
949 }
950
951 function showSettings()
952 {
953 mode = 2;
954 hideViews();
955 document.getElementById("settingsView").style.display = "block";
956 document.onclick = null;
957
958 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
959 {
960 for (var key in config) {
961 if (config[key].Type == 'String')
962 config[key].Value = document.forms[0].elements["settings." + key].value;
963 else if (config[key].Type == 'Int') {
964 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
965 if (config[key].Value < 0)
966 config[key].Value = config[key].Default;
967 }
968 else if (config[key].Type == 'Bool')
969 config[key].Value = document.forms[0].elements["settings." + key].checked;
970 else if (config[key].Type == 'UID')
971 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
972 else if (config[key].Type == 'Enum') {
973 config[key].Value = document.forms[0].elements["settings." + key].value;
974 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
975 config[key].Value = config[key].Default;
976 }
977 else if (config[key].Type == 'Array') {
978 if (key == 'excludedCalendars') {
979 config[key].Value = new Array();
980 for(var i=0; i < calendarList.length; i++) {
981 var element = document.forms[0].elements["settings." + key + "." + calendarList[i]];
982 if (element != null && element.checked == false)
983 config[key].Value.push(calendarList[i]);
984 }
985 }
986 }
987 }
988
989 updateCssClasses();
990
991 saveSettings();
992
993 mode = 1;
994 showFullscreen();
995 });
996 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
997 {
998 mode = 1;
999 showFullscreen();
1000 });
1001
1002 var settingsHtml = '<form>';
1003 for (var key in config) {
1004 if (config[key].Type == 'String') {
1005 var prefix = "";
1006 if (key.substring(0,9) == "cssStyle_")
1007 prefix = getLocalizedText('settings.cssStyle_prefix');
1008 settingsHtml += '<table><tr><td>' + prefix + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1009 }
1010 else if (config[key].Type == 'Int')
1011 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="' + config[key].Value + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1012 else if (config[key].Type == 'Bool')
1013 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><input name="settings.' + key + '" type="checkbox" value="true" ' + (config[key].Value ? 'checked="checked"' : '') + '/></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1014 else if (config[key].Type == 'UID')
1015 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><input class="textInput" name="settings.' + key + '" type="text" value="0x' + config[key].Value.toString(16) + '" /></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1016 else if (config[key].Type == 'Enum') {
1017 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><select name="settings.' + key + '" size="1">';
1018 for(var i = 0; i < config[key].ValidValues.length; i++)
1019 settingsHtml += '<option value="' + config[key].ValidValues[i] + '"' + (config[key].Value == config[key].ValidValues[i] ? ' selected="selected"' : '') + '>' + getLocalizedText('settings.validValues.' + key + '.' + config[key].ValidValues[i]) + '</option>';
1020 settingsHtml += '</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1021 }
1022 else if (config[key].Type == 'Array') {
1023 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br />';
1024 if (key == 'excludedCalendars') {
1025 for(var i=0; i < calendarList.length; i++) {
1026 var checked = 'checked="checked"';
1027 if (config[key].Value.indexOf(calendarList[i]) != -1)
1028 checked = '';
1029 settingsHtml += '<input name="settings.' + key + '.' + calendarList[i] + '" type="checkbox" value="' + calendarList[i] + '" ' + checked + '/> ' + calendarList[i] + '<br />';
1030 }
1031 }
1032 settingsHtml += '</td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
1033 }
1034 }
1035 settingsHtml += '<input name="reset" type="button" value="' + getLocalizedText('settings.restoreDefaults') + '" onclick="javascript:restoreDefaultSettings();showSettings();" />';
1036 settingsHtml += '</form>';
1037 document.getElementById("settingsList").innerHTML = settingsHtml;
1038 }
1039
1040 function changeCssClass(classname, properties)
1041 {
1042 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
1043 {
1044 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
1045 document.styleSheets[0].deleteRule(i);
1046 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
1047 break;
1048 }
1049 }
1050 }
1051
1052 function updateCssClasses()
1053 {
1054 for(var key in config) {
1055 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
1056 }
1057 }
1058
1059 function getSettingsCalEntryId()
1060 {
1061 if (settingsCalEntryId == null) {
1062 // check if entry already exists
1063 var listFiltering = {
1064 Type:'CalendarEntry',
1065 Filter:{
1066 StartRange: new Date(2000, 0, 1),
1067 EndRange: new Date(2000, 0, 1),
1068 SearchText: 'ComingNext Settings|',
1069 Type: 'DayEvent'
1070 }
1071 }
1072 var result = calendarService.IDataSource.GetList(listFiltering);
1073 if (result.ErrorCode) {
1074 error(result.ErrorMessage);
1075 return;
1076 }
1077 var list = result.ReturnValue;
1078 var entry = list.getNext();
1079 if (entry != undefined) {
1080 settingsCalEntryId = entry.LocalId;
1081 log("settingsCalEntryId=" + settingsCalEntryId);
1082 }
1083 else { // create settings item
1084 var item = new Object();
1085 item.Type = "DayEvent";
1086 item.StartTime = new Date(2000, 0, 1);
1087 item.Summary = "ComingNext Settings|";
1088
1089 var criteria = new Object();
1090 criteria.Type = "CalendarEntry";
1091 criteria.Item = item;
1092
1093 try {
1094 var result = calendarService.IDataSource.Add(criteria);
1095 if (result.ErrorCode)
1096 error(result.ErrorMessage);
1097 } catch (e) {
1098 error("getSettingsCalEntryId: " + e + ', line ' + e.line);
1099 }
1100
1101 getSettingsCalEntryId();
1102 }
1103 }
1104 }
1105
1106 function restoreDefaultSettings()
1107 {
1108 for (var key in config)
1109 config[key].Value = config[key].Default;
1110 }
1111
1112 function loadSettings()
1113 {
1114 getSettingsCalEntryId();
1115 var listFiltering = {
1116 Type:'CalendarEntry',
1117 Filter:{
1118 LocalId: settingsCalEntryId
1119 }
1120 }
1121 var result = calendarService.IDataSource.GetList(listFiltering);
1122 if (result.ErrorCode) {
1123 error(result.ErrorMessage);
1124 return;
1125 }
1126 var entry = result.ReturnValue.getNext();
1127 if (entry != undefined) {
1128 log("Loading Settings...");
1129 // only reload settings if they chanced since the last reload
1130 if (settingsCache != entry.Summary)
1131 {
1132 restoreDefaultSettings();
1133 var stringlist = entry.Summary.split("|");
1134 // skip the first two entries, those contain header and version info
1135 for(var i = 2; i < stringlist.length - 1; i++) {
1136 var pair = stringlist[i].split('=');
1137 var key = pair[0];
1138 var value = pair[1];
1139 log('stringlist: ' + key + '=\'' + value + '\'');
1140 if (config[key].Type == 'Int')
1141 config[key].Value = Number(value);
1142 else if (config[key].Type == 'String')
1143 config[key].Value = value;
1144 else if (config[key].Type == 'Bool')
1145 config[key].Value = (value == 'true')
1146 else if (config[key].Type == 'Enum')
1147 config[key].Value = value;
1148 else if (config[key].Type == 'UID')
1149 config[key].Value = Number(value);
1150 else if (config[key].Type == 'Array') {
1151 config[key].Value = value.split("^");
1152 if (config[key].Value.length == 1 && config[key].Value[0] == "") {
1153 config[key].Value = [];
1154 }
1155 }
1156 }
1157 settingsCache = entry.Summary;
1158 updateCssClasses();
1159 }
1160 else {
1161 log("Settings already cached and did not change");
1162 }
1163 }
1164 else {
1165 error("Failed to load settings, calendar entry could not be found");
1166 }
1167 }
1168
1169 function saveSettings()
1170 {
1171 getSettingsCalEntryId();
1172 var item = new Object();
1173 item.Type = "DayEvent";
1174 item.StartTime = new Date(2000, 0, 1);
1175 item.LocalId = settingsCalEntryId;
1176 item.Summary = "ComingNext Settings|" + version + "|";
1177
1178 for (var key in config) {
1179 if (config[key].Type == 'Int')
1180 item.Summary += key + "=" + config[key].Value.toString() + "|";
1181 else if (config[key].Type == 'String')
1182 item.Summary += key + "=" + config[key].Value + "|";
1183 else if (config[key].Type == 'Bool')
1184 item.Summary += key + "=" + (config[key].Value ? 'true' : 'false') + "|";
1185 else if (config[key].Type == 'Enum')
1186 item.Summary += key + "=" + config[key].Value + "|";
1187 else if (config[key].Type == 'UID')
1188 item.Summary += key + "=" + config[key].Value.toString() + "|";
1189 else if (config[key].Type == 'Array')
1190 item.Summary += key + "=" + config[key].Value.join("^") + "|";
1191 }
1192 settingsCache = item.Summary;
1193
1194 var criteria = new Object();
1195 criteria.Type = "CalendarEntry";
1196 criteria.Item = item;
1197
1198 log("Saving settings to calendar entry: " + item.Summary);
1199 try {
1200 var result = calendarService.IDataSource.Add(criteria);
1201 if (result.ErrorCode)
1202 error(result.ErrorMessage);
1203 } catch (e) {
1204 error("saveSettings: " + e + ', line ' + e.line);
1205 }
1206
1207 lastReloadTime = null; // force calendar data reload on next update
1208 clearUpdateTimer();
1209 setUpdateTimer();
1210 }
1211
1212 function toggleVisibility(elementId)
1213 {
1214 if (document.getElementById(elementId).style.display == "none")
1215 document.getElementById(elementId).style.display = "block";
1216 else
1217 document.getElementById(elementId).style.display = "none";
1218 }
1219
1220 var uniqueId = 0;
1221 function printHintBox(text)
1222 {
1223 uniqueId++;
1224 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '</td></tr></table>'+
1225 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
1226 }
1227
1228 function showAbout()
1229 {
1230 mode = 3;
1231 hideViews();
1232 document.getElementById("aboutView").style.display = "block";
1233 document.onclick = null;
1234
1235 window.menu.setLeftSoftkeyLabel(" ", function(){});
1236 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1237 {
1238 mode = 1;
1239 showFullscreen();
1240 });
1241
1242 //document.getElementById("aboutView").innerHTML = 'aboutView';
1243 document.getElementById("name").innerHTML = "Coming Next " + version;
1244 }
1245
1246 function showHelp() {
1247 widget.openURL('http://comingnext.sf.net/help');
1248 }
1249
1250 function updateFullscreen()
1251 {
1252 }
1253
1254 function showFullscreen()
1255 {
1256 log("showFullscreen()");
1257 hideViews();
1258 document.getElementById("fullscreenView").style.display = "block";
1259 document.getElementById('body').className = "backgroundFullscreen";
1260 if (!errorOccured)
1261 document.onclick = launchCalendar;
1262 createMenu();
1263 updateData();
1264 }
1265
1266 function getBackgroundImage()
1267 {
1268 if (errorOccured)
1269 return '';
1270 var bgImage;
1271 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[0]) // internal
1272 bgImage = 'background_' + orientation + '.png';
1273 else
1274 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
1275 return bgImage;
1276 }
1277
1278 function updateHomescreen()
1279 {
1280 if (config['useBackgroundImage'].Value) {
1281 // check for screen rotation
1282 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
1283 window.widget.prepareForTransition("fade");
1284 orientation = 'portrait';
1285 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1286 document.getElementById('body').style.backgroundColor = 'none';
1287 window.widget.performTransition();
1288 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
1289 window.widget.prepareForTransition("fade");
1290 orientation = 'landscape';
1291 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1292 document.getElementById('body').style.backgroundColor = 'none';
1293 window.widget.performTransition();
1294 }
1295 else if (document.getElementById('body').style.backgroundImage == "")
1296 {
1297 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
1298 }
1299 }
1300 }
1301
1302 function showHomescreen()
1303 {
1304 log("showHomescreen()");
1305 hideViews();
1306 document.getElementById("homescreenView").style.display = "block";
1307 document.getElementById('body').className = "background";
1308 document.onclick = null;
1309 updateData();
1310 }
1311
1312 function getLocalizedText(p_Txt)
1313 {
1314 if (localizedText[p_Txt])
1315 return localizedText[p_Txt];
1316 else
1317 return 'ERROR: missing translation for ' + p_Txt;
1318 }
1319
1320 function showUpdate()
1321 {
1322 mode = 4;
1323 hideViews();
1324 document.getElementById("updateView").style.display = "block";
1325 document.onclick = null;
1326
1327 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
1328 checkForUpdate();
1329 });
1330 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
1331 {
1332 mode = 1;
1333 showFullscreen();
1334 });
1335
1336 document.getElementById("currentVersion").innerHTML = getLocalizedText("update.current") + version;
1337 checkForUpdate();
1338 }
1339
1340 function checkForUpdate()
1341 {
1342 // asynch XHR to server url
1343 reqV = new XMLHttpRequest();
1344 reqV.onreadystatechange = checkForUpdateCallback;
1345 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.checking");
1346 reqV.open("GET", versionURL, true);
1347 reqV.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
1348 reqV.send(null);
1349 }
1350
1351 function checkForUpdateCallback()
1352 {
1353 if (reqV.readyState == 4) {
1354 if (reqV.status == 200) {
1355 var resultXml = reqV.responseText;
1356 if (resultXml) {
1357 var div = document.getElementById("tmp");
1358 div.innerHTML = resultXml;
1359 var newVersion = div.getElementsByTagName('version')[0].innerHTML;
1360 var newVersionURL = div.getElementsByTagName('url')[0].innerHTML;
1361 div.innerHTML = "";
1362 if (version != newVersion) {
1363 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.download").replace(/%1/, newVersion).replace(/%2/, newVersionURL);
1364 }
1365 else {
1366 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.nonewversion");
1367 }
1368 }
1369 }
1370 else {
1371 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.error") + reqV.status + " " + reqV.responseText;
1372 }
1373 }
1374 }
1375
1376 function hideViews()
1377 {
1378 document.getElementById("homescreenView").style.display = "none";
1379 document.getElementById("fullscreenView").style.display = "none";
1380 document.getElementById("aboutView").style.display = "none";
1381 document.getElementById("settingsView").style.display = "none";
1382 document.getElementById("updateView").style.display = "none";
1383 }
1384
1385 function listCalendars()
1386 {
1387 try {
1388 var criteria = {
1389 Type:'Calendar',
1390 Filter:{
1391 DefaultCalendar: false
1392 }
1393 }
1394
1395 var calendarsResult = calendarService.IDataSource.GetList(criteria);
1396 if (calendarsResult.ErrorCode != 0)
1397 throw("Error fetching list of calendars: " + calendarsResult.ErrorCode + ': ' + calendarsResult.ErrorMessage);
1398 var calendarListIterator = calendarsResult.ReturnValue;
1399
1400 var calendars = [];
1401 var count = 0;
1402 var item;
1403 while (( item = calendarListIterator.getNext()) != undefined ) {
1404 calendars[count++] = item;
1405 }
1406 log("Available Calendars: " + calendars.join(", "));
1407 return calendars;
1408 } catch(e) {
1409 error('listing calendars:' + e + ', line ' + e.line);
1410 return null;
1411 }
1412 }
1413
1414 // Copies all objects and their properties to an array. Data is copied so nothing gets lost when the reference is removed
1415 // Note: this will also set the "CalendarName" property of every entry to the name specified by the calendarName parameter if it has not been defined already
1416 function listToArray(list, calendarName)
1417 {
1418 var array = new Array();
1419 var item;
1420 var txt = "";
1421 while (( item = list.getNext()) != undefined ) {
1422 var itemCopy = new Object();
1423 for(var i=0; i < entryFields.length; i++) {
1424 itemCopy[entryFields[i]] = item[entryFields[i]];
1425 }
1426 // for some reason, the CalendarName property is never correctly queried, so we assign it manually here
1427 if (!itemCopy['CalendarName']) {
1428 itemCopy['CalendarName'] = calendarName;
1429 }
1430 array.push(itemCopy);
1431 txt += array[array.length - 1].Summary + ", ";
1432 }
1433 log("listToArray(): " + txt);
1434 return array;
1435 }
1436
1437 function sortCalendarEntries(a, b)
1438 {
1439 var atime, btime;
1440 log("sortCalendarEntries(" + a.Summary + "," + b.Summary + ")");
1441
1442 if (a.InstanceStartTime != null) {
1443 atime = a.InstanceStartTime;
1444 }
1445 else if (a.StartTime != null) {
1446 atime = a.StartTime;
1447 }
1448 else if (a.InstanceEndTime != null) {
1449 atime = a.InstanceEndTime;
1450 }
1451 else if (a.EndTime != null) {
1452 atime = a.EndTime;
1453 }
1454
1455 if (b.InstanceStartTime != null) {
1456 btime = b.InstanceStartTime;
1457 }
1458 else if (b.StartTime != null) {
1459 btime = b.StartTime;
1460 }
1461 else if (b.InstanceEndTime != null) {
1462 btime = b.InstanceEndTime;
1463 }
1464 else if (b.EndTime != null) {
1465 btime = b.EndTime;
1466 }
1467
1468 if (atime && btime) {
1469
1470 atime = parseDate(atime);
1471 btime = parseDate(btime);
1472
1473 // sort by date & time
1474 if (atime < btime) {
1475 return -1;
1476 }
1477 else if (atime > btime) {
1478 return 1;
1479 }
1480 // sort by type
1481 else if (a.Type != b.Type) {
1482 if (a.Type < b.Type) {
1483 return -1;
1484 }
1485 else if (a.Type > b.Type) {
1486 return 1;
1487 }
1488 }
1489 // sort by description
1490 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1491 if (a.Summary < b.Summary) {
1492 return -1;
1493 }
1494 else if (a.Summary > b.Summary) {
1495 return 1;
1496 }
1497 }
1498 }
1499 // NOTE: events my have no date information at all. In that case, we list events without date first
1500 else if (atime && !btime) {
1501 return 1;
1502 }
1503 else if (!atime && btime) {
1504 return -1;
1505 }
1506 else if (!atime && !btime) {
1507 // sort by type
1508 if (a.Type != b.Type) {
1509 if (a.Type < b.Type) {
1510 return -1;
1511 }
1512 else if (a.Type > b.Type) {
1513 return 1;
1514 }
1515 }
1516 // sort by description
1517 else if (a.Summary && b.Summary && a.Summary != b.Summary) {
1518 if (a.Summary < b.Summary) {
1519 return -1;
1520 }
1521 else if (a.Summary > b.Summary) {
1522 return 1;
1523 }
1524 }
1525 }
1526
1527 return 0;
1528 }
1529
1530 function updateCalendarColors()
1531 {
1532 var maxColors = 6;
1533 calendarColors = [];
1534 if (calendarList.length > maxColors) {
1535 log("updateCalendarColors(): Warning: more calendars than available indicator colors");
1536 }
1537 for(var i=0; i < calendarList.length; i++) {
1538 calendarColors[calendarList[i]] = (i % maxColors) + 1;
1539 }
1540 }
1541
1542 function log(message) {
1543 if (config['enableLogging'].Value) {
1544 console.info(message);
1545 }
1546 }
1547
1548 </script>
1549
1550 <style type="text/css">
1551 a { color:#aaccff }
1552 table { margin:0px; padding:0px; border-spacing:0px; }
1553 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
1554 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
1555 .settingsInfo { display:none; font-style:italic; }
1556 .title { font-weight:bold; font-size:14pt; }
1557 .textInput { width:90%; }
1558 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }
1559 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1560 #calendarList { position:absolute; left:5px; top:4px; width:295px; height:75px; overflow:hidden; }
1561 #name { text-align:center; }
1562 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }
1563 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1564 </style>
1565
1566 </head>
1567
1568 <body onload="javascript:init()" onresize="javascript:updateScreen()" id="body" class="background">
1569 <div id="homescreenView">
1570 <div id="calendarList"></div>
1571 </div>
1572 <div id="fullscreenView" style="display:none;">
1573 <img src="Icon.png" id="smallappicon">
1574 <h1 class="title">Coming Next</h1>
1575 <hr />
1576 <div id="fullscreenCalendarList">loading...</div>
1577 </div>
1578 <div id="settingsView" style="display:none">
1579 <img src="Icon.png" id="smallappicon">
1580 <h1 id="settingsTitle" class="title">Settings</h1>
1581 <hr />
1582 <div id="settingsList"></div>
1583 </div>
1584 <div id="aboutView" style="display:none">
1585 <img src="Icon.png" id="appicon">
1586 <h1 id="name">Coming Next</h1>
1587 <hr />
1588 <p>Created by Dr. Cochambre and Michael Prager.</p>
1589 <p>Contributions:</p>
1590 <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>
1591 <p class="credits">Manfred Hanselmann (DST support)</p>
1592 <p class="credits">Christophe Milsent (translation support & french translation)</p>
1593 <p class="credits">Flavio Nathan (portuguese-brazilian translation)</p>
1594 <p class="credits">Tokeda (russian translation)</p>
1595 <p class="credits">Marcella Ferrari (italian translation)</p>
1596 <p class="credits">Venos (italian translation)</p>
1597 <p>This software is open source and licensed under the GPLv3.</p>
1598 <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>
1599 <hr />
1600 </div>
1601 <div id="updateView" style="display:none">
1602 <img src="Icon.png" id="smallappicon">
1603 <h1 class="title">Check for update</h1>
1604 <hr />
1605 <div id="currentVersion">Coming Next ??</div>
1606 <div id="updateDiv"></div>
1607 <div id="tmp" style="display:none;"></div>
1608 </div>
1609 </body>
1610
1611 </html>