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