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