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