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