]> code.delx.au - comingnext/blob - comingNextB/index.html
updated release date in readme
[comingnext] / comingNextB / 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 </style>
21
22 <script type="text/javascript" src="localizedTextStrings.js" charset="utf-8" />
23
24 <script>
25 // valid types for the config object are 'Int', 'Bool', 'String', 'Enum', 'UID'
26 var config = {
27 monthRange: { Type: 'Int', Default: 2, Value: 2,},
28 includeTodos: { Type: 'Bool', Default: true, Value: true,},
29 useBackgroundImage: { Type: 'Bool', Default: true, Value: true,},
30 backgroundImageLocation: { Type: 'Enum', Default: 'internal', Value: 'internal', ValidValues: ['internal', 'external']},
31 showCombinedDateTime: { Type: 'Bool', Default: false, Value: false,},
32 showLocation: { Type: 'Bool', Default: true, Value: true,},
33 showTodayAsText: { Type: 'Bool', Default: true, Value: true,},
34 todayText: { Type: 'String', Default: getLocalizedText('settings.default.todayText'), Value: getLocalizedText('settings.default.todayText'),},
35 tomorrowText: { Type: 'String', Default: getLocalizedText('settings.default.tomorrowText'), Value: getLocalizedText('settings.default.tomorrowText'),},
36 showNowAsText: { Type: 'Bool', Default: true, Value: true,},
37 nowText: { Type: 'String', Default: getLocalizedText('settings.default.nowText'), Value: getLocalizedText('settings.default.nowText'),},
38 dateSeparator: { Type: 'String', Default: getLocalizedText('settings.default.dateSeparator'), Value: getLocalizedText('settings.default.dateSeparator'),},
39 dateFormat: { Type: 'Enum', Default: 'auto', Value: 'auto', ValidValues: ['auto', 'DDMM', 'MMDD'],},
40 weekDayLength: { Type: 'Int', Default: 2, Value: 2,},
41 updateDataInterval: { Type: 'Int', Default: 5, Value: 5,},
42 calendarApp: { Type: 'UID', Default: 0x10005901, Value: 0x10005901,},
43 eventsPerWidget: { Type: 'Int', Default: 4, Value: 4,},
44 showNothingText: { Type: 'Bool', Default: true, Value: true,},
45 nothingText: { Type: 'String', Default: getLocalizedText('settings.default.nothingText'), Value: getLocalizedText('settings.default.nothingText'),},
46 enableDaylightSaving: { Type: 'Bool', Default: true, Value: true,},
47 hideWidgetOnCalendarOpen: { Type: 'Bool', Default: false, Value: false,},
48 cssStyle_background: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
49 cssStyle_backgroundFullscreen: { Type: 'String', Default: 'color:#ffffff; background-color:#000000', Value: 'color:#ffffff; background-color:#000000',},
50 cssStyle_weekDay: { Type: 'String', Default: '', Value: '',},
51 cssStyle_date: { Type: 'String', Default: '', Value: '',},
52 cssStyle_today: { Type: 'String', Default: 'color:#ff0000', Value: 'color:#ff0000',},
53 cssStyle_tomorrow: { Type: 'String', Default: 'color:#0000ff', Value: 'color:#0000ff',},
54 cssStyle_time: { Type: 'String', Default: '', Value: '',},
55 cssStyle_now: { Type: 'String', Default: 'color:#ff00ff', Value: 'color:#ff00ff',},
56 cssStyle_description: { Type: 'String', Default: '', Value: '',},
57 cssStyle_icon: { Type: 'String', Default: 'width:15px; height:15px', Value: 'width:15px; height:15px',},
58 }
59
60
61
62 //-------------------------------------------------------
63 // Nothing of interest from here on...
64 //-------------------------------------------------------
65 var panelNum = 0; // use 1 for second panel
66 var version = "1.25";
67 var versionURL = "http://comingnext.sourceforge.net/version.xml";
68 var calendarService = null;
69 var cacheEntriesHtml = [];
70 var months_translated = [];
71 var orientation = '';
72 var now = new Date();
73 var mode = 0; // 0 = homescreen, 1 = fullscreen, 2 = settings, 3 = about, 4 = check for update
74 var reqV = null;
75
76 // vars for daylight saving time
77 var daylightsavingWinter = 0;
78 var daylightsavingSummer = 0;
79 var summertime = false;
80
81 window.onload = init;
82 window.onresize = updateScreen;
83 window.onshow = updateScreen;
84
85 function isLeapYear( year ) {
86 if (( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
87 return true;
88 else
89 return false;
90 }
91
92 function calcLeapYear(year, days)
93 {
94 if (isLeapYear(year))
95 return ++days;
96 else
97 return days;
98 }
99
100 function subToSunday(myDate, year, days, prevMonthDays)
101 {
102 for (i = myDate.getDay(); i > 0 ;i--)
103 days--;
104 days -= prevMonthDays;
105 days = isLeapYear(year) ? --days : days;
106 return days;
107 }
108
109 function calcDaylightSaving()
110 {
111 var thisYearS = new Date(now.getFullYear(), 3, 0, 0, 0, 0 );
112 var thisYearW = new Date(now.getFullYear(), 10, 0, 0, 0, 0 );
113 var nextYearS = new Date(now.getFullYear() + 1, 3, 0, 0, 0, 0 );
114 var nextYearW = new Date(now.getFullYear() + 1, 10, 0, 0, 0, 0 );
115 var summer = false;
116 var winter = false;
117
118 thisYearSDays = nextYearSDays = 90;
119 thisYearWDays = nextYearWDays = 304;
120
121 thisYearSDays = calcLeapYear(now.getFullYear(), thisYearSDays);
122 thisYearWDays = calcLeapYear(now.getFullYear(), thisYearWDays);
123 nextYearSDays = calcLeapYear(now.getFullYear() + 1, nextYearSDays);
124 nextYearWDays = calcLeapYear(now.getFullYear() + 1, nextYearWDays);
125
126 thisYearSDays = subToSunday(thisYearS, now.getFullYear(), thisYearSDays, 59);
127 thisYearWDays = subToSunday(thisYearW, now.getFullYear(), thisYearWDays, 273);
128 nextYearSDays = subToSunday(nextYearS, now.getFullYear() + 1, nextYearSDays, 59);
129 nextYearWDays = subToSunday(nextYearW, now.getFullYear() + 1, nextYearWDays, 273);
130
131 daylightsavingSummer = new Date (now.getFullYear(), 03-1, thisYearSDays, 2, 0, 0);
132 daylightsavingWinter = new Date (now.getFullYear(), 10-1, thisYearWDays, 2, 0, 0);
133 if (daylightsavingSummer < now) {
134 daylightsavingSummer = new Date (now.getFullYear()+1, 03-1, nextYearSDays, 2, 0, 0);
135 var summer = true;
136 }
137 if (daylightsavingWinter < now) {
138 daylightsavingWinter = new Date (now.getFullYear()+1, 10-1, nextYearWDays, 2, 0, 0);
139 var winter = true;
140 }
141 if (summer && !winter)
142 summertime = true;
143 else
144 summertime = false;
145 }
146
147 function error(message)
148 {
149 console.info('Error: ' + message);
150 document.getElementById("calendarList").innerHTML = 'Error: ' + message;
151 }
152
153 function isToday(date)
154 {
155 if (date.getDate() == now.getDate() && date.getMonth() == now.getMonth())
156 return true;
157 return false;
158 }
159
160 function isTomorrow(date)
161 {
162 if ((date.getDate() == now.getDate() + 1 && date.getMonth() == now.getMonth()) ||
163 (date.getDate() == 0 && date.getMonth() == now.getMonth() + 1) ||
164 (date.getDate() == 0 && date.getMonth() == now.getMonth() + 1 && date.getYear() == now.getYear() + 1))
165 return true;
166 return false;
167 }
168
169 function collectLocales()
170 {
171 var tmpyear = ((panelNum == 0) ? 2000 : 2001);
172 var month = 0;
173
174 if (months_translated.length > 0)
175 return;
176 for (month = 0; month < 12; month++) {
177 var startDate = new Date(tmpyear, month, 15);
178
179 var item = new Object();
180 item.Type = "DayEvent";
181 item.StartTime = startDate;
182 item.Summary = "__temp" + month;
183
184 var criteria = new Object();
185 criteria.Type = "CalendarEntry";
186 criteria.Item = item;
187
188 try {
189 var result = calendarService.IDataSource.Add(criteria);
190 if (result.ErrorCode)
191 error(result.ErrorMessage);
192 } catch (e) {
193 error("collectLocales: " + e + ', line ' + e.line);
194 }
195 }
196 try {
197 var startTime = new Date(tmpyear,0,1);
198 var endTime = new Date(tmpyear,11,31);
199 var listFiltering = {
200 Type:'CalendarEntry',
201 Filter:{
202 StartRange: startTime,
203 EndRange: endTime,
204 SearchText: '__temp',
205 Type: 'DayEvent'
206 }
207 }
208 var result = calendarService.IDataSource.GetList(listFiltering);
209 if (result.ErrorCode) {
210 error(result.ErrorMessage);
211 return;
212 }
213 var list = result.ReturnValue;
214 } catch(e) {
215 error(e + ', line ' + e.line);
216 return;
217 }
218 var ids = new Array();
219 try {
220 var entry;
221 var counter = 0;
222 var dateArr = [];
223
224 while (list && (entry = list.getNext()) != undefined) {
225 dateArr = entry.StartTime.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
226 var day = dateArr[1];
227 var month = dateArr[2];
228 var year = dateArr[3];
229
230 // make sure month is set properly
231 if (isNaN(parseInt(day))) {
232 var tmp = day;
233 day = month;
234 month = tmp;
235 } else if (isNaN(parseInt(year))) {
236 var tmp = year;
237 year = month;
238 month = tmp;
239 }
240
241 console.info(entry.StartTime + ' -> ' + month + ' ' + counter);
242 ids[counter] = entry.id;
243 months_translated[month] = counter + 1;
244 counter++;
245 }
246 } catch(e) {
247 error(e + ', line ' + e.line);
248 return;
249 }
250 console.info(ids);
251 try {
252 var criteria = new Object();
253 criteria.Type = "CalendarEntry";
254 criteria.Data = {
255 IdList: ids
256 }
257
258 var result = calendarService.IDataSource.Delete(criteria);
259 if (result.ErrorCode)
260 error(result.ErrorMessage);
261 } catch(e) {
262 error('deleting temp calendar entries:' + e + ', line ' + e.line);
263 return;
264 }
265 }
266
267 function requestNotification()
268 {
269 var criteria = new Object();
270 criteria.Type = "CalendarEntry";
271
272 try {
273 var result = calendarService.IDataSource.RequestNotification(criteria, callback);
274 if (result.ErrorCode)
275 error('loading Calendar items list');
276 } catch (e) {
277 error("requestNotification: " + e + ', line ' + e.line);
278 }
279 }
280
281 function callback(transId, eventCode, result)
282 {
283 updateData();
284 }
285
286 function parseDate(dateString)
287 {
288 /*
289 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:
290 Wednesday, 26 August, 2009 24:00:00
291 Wednesday, 26 August, 2009 12:00:00 am
292 Wednesday, August 26, 2009 12:00:00 am
293 Wednesday, 2009 August, 26 12:00:00 am
294 Wednesday, 2009 August, 28 8.00.00 pm
295 Wednesday, 2009 August, 28 08:00:00 PM
296 */
297
298 if (dateString == "" || dateString == null)
299 return null;
300 var dateArr = dateString.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
301 if (dateArr.length != 5 && dateArr.length != 6)
302 return null;
303
304 // parse date
305 var weekDay = dateArr[0];
306 var day = dateArr[1];
307 var month = dateArr[2];
308 var year = dateArr[3];
309 // make sure month is set properly
310 if (isNaN(parseInt(day))) {
311 var tmp = day;
312 day = month;
313 month = tmp;
314 } else if (isNaN(parseInt(year))) {
315 var tmp = year;
316 year = month;
317 month = tmp;
318 }
319 // make sure day and year are set properly
320 if (Number(day) > Number(year)) {
321 var tmp = year;
322 year = day;
323 day = tmp;
324 }
325 month = months_translated[month];
326
327 // parse time
328 var timeArr = dateArr[4].split(':');
329 if (timeArr.length != 3)
330 return null;
331 var hours = Number(timeArr[0]);
332 var minutes = Number(timeArr[1]);
333 var seconds = Number(timeArr[2]);
334 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'pm' && hours < 12)
335 hours += 12;
336 if (dateArr.length == 6 && dateArr[5].toLowerCase() == 'am' && hours == 12)
337 hours = 0;
338
339 console.info('year=' + year + ' month=' + month + ' day=' + day + ' hours=' + hours + ' minutes=' + minutes+ ' seconds=' + seconds);
340
341 // take care of daylight saving time
342 if (config['enableDaylightSaving'].Value) {
343 var date = new Date(year, month - 1, day, hours, minutes, seconds);
344 if (summertime && date > daylightsavingWinter && date < daylightsavingSummer)
345 hours -= 1;
346 else if (!summertime && date > daylightsavingSummer && date < daylightsavingWinter)
347 hours += 1;
348 }
349
350 return new Date(year, month - 1, day, hours, minutes, seconds);
351 }
352
353 // 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"
354 function formatDate(date, format)
355 {
356 var day = date.getDate().toString();
357 var month = (date.getMonth() + 1).toString();
358 while (day.length < 2) { day = '0' + day; }
359 while (month.length < 2) { month = '0' + month; }
360
361 if (config['showTodayAsText'].Value && isToday(date))
362 return '<span class="today">' + config['todayText'].Value + '</span>';
363 if (config['showTodayAsText'].Value && isTomorrow(date))
364 return '<span class="tomorrow">' + config['tomorrowText'].Value + '</span>';
365
366 var dateArr = format.replace(/,/g,'').replace(/\./g,':').replace(/ /g,' ').split(' ');
367 if (dateArr.length != 5 && dateArr.length != 6) {
368 // we don't know how to format this
369 if (config['dateFormat'].Value == 'auto' || config['dateFormat'].Value == 'DDMM')
370 return day + config['dateSeparator'].Value + month;
371 else
372 return month + config['dateSeparator'].Value + day;
373 }
374
375 var dayFirst = true;
376 if (config['dateFormat'].Value == 'MMDD')
377 dayFirst = false;
378 else if (config['dateFormat'].Value == 'DDMM')
379 dayFirst = true;
380 else {
381 // config['dateFormat'].Value == 'auto', try to detect system setting
382 // parse date
383 var day_ = dateArr[1];
384 var month_ = dateArr[2];
385 var year_ = dateArr[3];
386 // make sure month is set properly
387 if (isNaN(parseInt(day_))) {
388 var tmp = day_;
389 day_ = month_;
390 month_ = tmp;
391 dayFirst = false;
392 } else if (isNaN(parseInt(year_))) {
393 var tmp = year_;
394 year_ = month_;
395 month_ = tmp;
396 dayFirst = true;
397 }
398 // make sure day and year are set properly
399 if (Number(day_) > Number(year_))
400 dayFirst = false;
401 }
402
403 if (dayFirst)
404 return day + config['dateSeparator'].Value + month;
405 else
406 return month + config['dateSeparator'].Value + day;
407 }
408
409 function formatTime(date)
410 {
411 // date is a Date() object
412 date.setSeconds(0); // we don't care about seconds
413 var time = date.toLocaleTimeString().replace(/[\.:]00/, ''); // remove seconds from string
414 if (time.replace(/\./, ':').split(':')[0].length < 2)
415 time = '0' + time;
416 if (config['showNowAsText'].Value && date.getTime() == now.getTime())
417 time = '<span class="now">' + config['nowText'].Value + '</span>';
418 return time;
419 }
420
421 function updateData()
422 {
423 console.info('updateData()');
424 calcDaylightSaving();
425 try {
426 // meetings have time
427 // 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
428 now = new Date();
429 var meetingListFiltering = {
430 Type:'CalendarEntry',
431 Filter:{
432 StartRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)),
433 EndRange: (new Date(now.getFullYear(), now.getMonth() + config['monthRange'].Value, now.getDate(), 0, 0, 0))
434 }
435 }
436 var meetingResult = calendarService.IDataSource.GetList(meetingListFiltering);
437 if (meetingResult.ErrorCode != 0)
438 throw("Error fetching calendar data: " + meetingResult.ErrorCode + ': ' + meetingResult.ErrorMessage);
439 var meetingList = meetingResult.ReturnValue;
440
441 // todos don't, they start on 00:00 hrs., but should be visible anyway
442 // this will generate a list of passed todos. We have to check if they have been marked as "done" yet
443 if (config['includeTodos'].Value) {
444 var todayTodoListFiltering = {
445 Type:'CalendarEntry',
446 Filter:{
447 Type: 'ToDo',
448 StartRange: (new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), 0, 0, 0)),
449 EndRange: (new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1))
450 }
451 }
452 var todayTodoResult = calendarService.IDataSource.GetList(todayTodoListFiltering);
453 var todayTodoList = todayTodoResult.ReturnValue;
454 var entryLists = [todayTodoList, meetingList];
455 } else {
456 var entryLists = [meetingList];
457 }
458 } catch(e) {
459 error('loading Calendar items list:' + e + ', line ' + e.line);
460 return;
461 }
462
463 try {
464 var entry;
465 var counter = 0;
466 var entryDate = '';
467 var dateArr = [];
468 var entriesHtml = '<table>';
469 var eventIds = [];
470 var max;
471 if (mode == 0)
472 max = ((panelNum == 0) ? config['eventsPerWidget'].Value : 2 * config['eventsPerWidget'].Value);
473 else
474 max = 30; // we can display a lot more events in fullscreen mode
475
476 // the first outer loop iteration is for passed ToDos, the second loop is for all upcomming events (may also include ToDos)
477 for (var i=0; counter < max && i < entryLists.length; i++) {
478 while (counter < max && (entry = entryLists[i].getNext()) != undefined) {
479 counter++;
480
481 // output event info for debugging
482 console.info(
483 'event: Id=' + entry.id +
484 ',Type=' + entry.Type +
485 ',Summary=' + entry.Summary +
486 ',Location=' + entry.Location +
487 ',Status=' + entry.Status +
488 ',StartTime=' + entry.StartTime +
489 ',EndTime=' + entry.EndTime +
490 ',InstanceStartTime=' + entry.InstanceStartTime +
491 ',InstanceEndTime=' + entry.InstanceEndTime
492 );
493
494 // we don't want ToDos when includeTodos == false or when they are completed
495 if (entry.Type == 'ToDo' && (entry.Status == "TodoCompleted" || !config['includeTodos'].Value)) {
496 console.info('skipping ' + entry.id );
497 counter--;
498 continue;
499 }
500
501 // make sure that we don't include an event twice (useful for ToDos that might come up twice)
502 if (eventIds[entry.id] == 1 && entry.Type == 'ToDo') {
503 console.info('skipped (already included) ' + entry.id);
504 counter--;
505 continue;
506 } else
507 eventIds[entry.id] = 1;
508
509 // summary can be undefined!
510 var Summary = ((entry.Summary == null) ? '' : entry.Summary);
511 if (entry.Type == 'Meeting' && entry.Location != '' && config['showLocation'].Value)
512 Summary += ', ' + entry.Location;
513
514 // fix by yves: determine start and end dates/times
515 entryStartTime = ((entry.InstanceStartTime == null) ? entry.StartTime : entry.InstanceStartTime);
516 entryEndTime = ((entry.InstanceEndTime == null) ? entry.EndTime : entry.InstanceEndTime);
517
518 // there can be ToDos that have no date at all!
519 if (entry.Type == 'ToDo' && entry.EndTime == null)
520 entryDate = ""; // this will cause parseDate(entryDate) to return null;
521 else
522 entryDate = ((entry.Type == 'ToDo') ? entryEndTime : entryStartTime); // ToDo's use their EndTime, the rest use StartTime
523
524 // Convert date/time string to Date object
525 var date = parseDate(entryDate);
526 console.info('date: ' + date);
527 var endDate = ((entryEndTime == null) ? null : parseDate(entryEndTime));
528 console.info('endDate: ' + endDate);
529
530 // check if meeting event has already passed
531 if (entry.Type == 'Meeting') {
532 var compareTime = ((endDate == null) ? date.getTime() : endDate.getTime());
533 if (now.getTime() > compareTime) {
534 console.info('skipping Meeting (already passed) ' + entry.id);
535 counter--;
536 eventIds[entry.id] = 0;
537 continue;
538 }
539 }
540
541 // check if anniversary passed (not sure why they are in the list, the query was only for today - nokia?)
542 if (entry.Type == 'Anniversary') {
543 var tmp = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0,0,0);
544 if (date.getTime() < tmp.getTime()) {
545 console.info('skipping Anniversary (already passed) ' + entry.id);
546 counter--;
547 eventIds[entry.id] = 0;
548 continue;
549 }
550 }
551
552 // fix DayEvents end time. End times are off by 1 Second. It's possible that the event has already passed
553 if (entry.Type == 'DayEvent' && endDate != null) {
554 endDate.setMinutes(endDate.getMinutes() - 1);
555 console.info('fixing DayEvent endDate: ' + endDate);
556 if (now.getTime() > endDate.getTime()) {
557 console.info('event already passed ' + entry.id);
558 counter--;
559 eventIds[entry.id] = 0;
560 continue;
561 }
562 }
563
564 // check if the event is currently taking place
565 if (entryStartTime != null && entryEndTime != null && date != null && endDate != null) {
566 // check if we are between start and endtime
567 if ((date.getTime() < now.getTime()) && (now.getTime() < endDate.getTime())) {
568 date = now; // change appointment date/time to now
569 console.info('event is currently taking place: ' + date);
570 }
571 }
572
573 // skip events for the first panel in case this is the second one and we're not in fullscreen mode
574 if (mode == 0 && panelNum == 1 && counter < config['eventsPerWidget'].Value + 1) {
575 console.info('skipping (already in first widget) ' + entry.id);
576 continue;
577 }
578
579 // generate html output
580 entriesHtml += '<tr><td><img class="icon" src="' + entry.Type + '.png" /></td>';
581 if(date == null) {
582 // some languages have very strange locale date formats, can't parse all those. Also some todos don't have dates at all.
583 entriesHtml += '<td colspan="4"><span class="date">' + entryDate + '</span> ';
584 } else {
585 var weekDay = date.toLocaleDateString().substr(0,config['weekDayLength'].Value);
586 var time = formatTime(date);
587 var dateStr = formatDate(date, entryDate);
588 if (entry.Type == 'ToDo' || entry.Type == 'Anniversary' || entry.Type == 'DayEvent' || entry.Type == 'Reminder') {
589 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value) // show weekday if the date string is not text. looks odd otherwise
590 entriesHtml += '<td colspan="4" width="1px"><span class="date">' + dateStr + '</span> ';
591 else
592 entriesHtml += '<td class="weekDay" width="1px">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
593 } else if (entry.Type == 'Meeting') {
594 if (config['showCombinedDateTime'].Value) {
595 if (isToday(date))
596 entriesHtml += '<td width="1px" colspan="4"><span class="today">' + time + '</span> ';
597 else if (isTomorrow(date))
598 entriesHtml += '<td width="1px" colspan="4"><span class="tomorrow">' + dateStr + '</span> <span class="time">' + time + '</span> ';
599 else
600 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td colspan="2">';
601 } else {
602 if ((isToday(date) || isTomorrow(date)) && config['showTodayAsText'].Value)
603 entriesHtml += '<td colspan="4" width="1px"><span class="today">' + dateStr + '</span> <span class="time">' + time + '</span> ';
604 else
605 entriesHtml += '<td width="1px" class="weekDay">' + weekDay + '</td><td width="1px" class="date">' + dateStr + '</td><td width="1px" class="time">' + time + '</td><td>';
606 }
607 }
608 }
609 entriesHtml += '<span class="description">' + Summary + '</span></td></tr>';
610 }
611 }
612 entriesHtml += '</table>';
613 if (config['showNothingText'].Value && entriesHtml == '<table></table>') {
614 var text = config['nothingText'].Value.replace(/%d/, config['monthRange'].Value);
615 entriesHtml = '<div style="width:295px; height:75px; text-align:center; line-height:75px; overflow:visible;">' + text + '</div>';
616 }
617 if (cacheEntriesHtml != entriesHtml) {
618 if (mode == 0)
619 document.getElementById('calendarList').innerHTML = entriesHtml;
620 else
621 document.getElementById('fullscreenCalendarList').innerHTML = entriesHtml;
622 cacheEntriesHtml = entriesHtml;
623 }
624 } catch(e) {
625 error('displaying list:' + e + ', line ' + e.line);
626 return;
627 }
628 }
629
630 function updateScreen()
631 {
632 // check if opening fullscreen
633 if( window.innerHeight > 91 && mode == 0) {
634 mode = 1;
635 cacheEntriesHtml = '';
636 document.getElementById('body').style.backgroundImage = "";
637 showFullscreen();
638 }
639 else if (window.innerHeight <= 91 && mode != 0) {
640 mode = 0;
641 cacheEntriesHtml = '';
642 showHomescreen();
643 }
644
645 if (mode == 0)
646 updateHomescreen();
647 else if (mode == 1)
648 updateFullscreen();
649 }
650
651 function launchCalendar()
652 {
653 try {
654 widget.openApplication(config['calendarApp'].Value, "");
655 if (config['hideWidgetOnCalendarOpen'].Value)
656 window.close();
657 } catch(e) {
658 error('starting Calendar App');
659 return;
660 }
661 }
662
663 function init()
664 {
665 console.info('New widget instance starting up...');
666
667 try {
668 // call calendar service
669 if (device != "undefined")
670 calendarService = device.getServiceObject("Service.Calendar", "IDataSource");
671 else
672 throw('device object does not exist');
673 } catch(e) {
674 error('loading Calendar service: ' + e + ', line ' + e.line);
675 return;
676 }
677
678 loadSettings();
679 updateCssClasses();
680 collectLocales();
681 //updateData();
682 requestNotification();
683 window.setInterval('updateData()', 1000 * 60 * config['updateDataInterval'].Value);
684
685 mode = 0;
686 showHomescreen();
687 updateScreen();
688 if (config['useBackgroundImage'].Value)
689 // check for screen rotation every 1 secs
690 window.setInterval('updateScreen()', 1000 * 1);
691 }
692
693 function createMenu()
694 {
695 window.menu.setLeftSoftkeyLabel("",null);
696 window.menu.setRightSoftkeyLabel("",null);
697 var id = 0;
698 var menuSettings = new MenuItem(getLocalizedText('menu.settings'), id++);
699 var menuCallApp = new MenuItem(getLocalizedText('menu.openCalendarApp'), id++);
700 var menuUpdate = new MenuItem(getLocalizedText('menu.update'), id++);
701 var menuAbout = new MenuItem(getLocalizedText('menu.about'), id++);
702 menuSettings.onSelect = showSettings;
703 menuAbout.onSelect = showAbout;
704 menuCallApp.onSelect = launchCalendar;
705 menuUpdate.onSelect = showUpdate;
706 window.menu.clear();
707 window.menu.append(menuCallApp);
708 window.menu.append(menuSettings);
709 window.menu.append(menuUpdate);
710 window.menu.append(menuAbout);
711 }
712
713 function showSettings()
714 {
715 mode = 2;
716 hideViews();
717 document.getElementById("settingsView").style.display = "block";
718 document.onclick = null;
719
720 window.menu.setLeftSoftkeyLabel(getLocalizedText('settings.save'), function()
721 {
722 for (var key in config) {
723 if (config[key].Type == 'String')
724 config[key].Value = document.forms[0].elements["settings." + key].value;
725 else if (config[key].Type == 'Int') {
726 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
727 if (config[key].Value < 0)
728 config[key].Value = config[key].Default;
729 }
730 else if (config[key].Type == 'Bool')
731 config[key].Value = document.forms[0].elements["settings." + key].checked;
732 else if (config[key].Type == 'UID')
733 config[key].Value = parseInt(document.forms[0].elements["settings." + key].value);
734 else if (config[key].Type == 'Enum') {
735 config[key].Value = document.forms[0].elements["settings." + key].value;
736 if (config[key].ValidValues.indexOf(config[key].Value) == -1)
737 config[key].Value = config[key].Default;
738 }
739 }
740
741 updateCssClasses();
742
743 saveSettings();
744
745 mode = 1;
746 showFullscreen();
747 });
748 window.menu.setRightSoftkeyLabel(getLocalizedText('settings.cancel'), function()
749 {
750 mode = 1;
751 showFullscreen();
752 });
753
754 var settingsHtml = '<form>';
755 for (var key in config) {
756 if (config[key].Type == 'String')
757 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 />';
758 else if (config[key].Type == 'Int')
759 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 />';
760 else if (config[key].Type == 'Bool')
761 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 />';
762 else if (config[key].Type == 'UID')
763 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 />';
764 else if (config[key].Type == 'Enum') {
765 settingsHtml += '<table><tr><td>' + getLocalizedText('settings.name.' + key) + '<br /><select name="settings.' + key + '" size="1">';
766 for(var i = 0; i < config[key].ValidValues.length; i++)
767 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>';
768 settingsHtml += '</select></div></td>' + printHintBox(getLocalizedText('settings.info.' + key)) + '<hr />';
769 }
770 }
771 settingsHtml += '<input name="reset" type="button" value="' + getLocalizedText('settings.restoreDefaults') + '" onclick="javascript:restoreDefaultSettings();showSettings();" />';
772 settingsHtml += '</form>';
773 document.getElementById("settingsList").innerHTML = settingsHtml;
774 }
775
776 function changeCssClass(classname, properties)
777 {
778 for(var i = 0; i < document.styleSheets[0]['cssRules'].length; i++)
779 {
780 if (document.styleSheets[0]['cssRules'][i].selectorText == classname) {
781 document.styleSheets[0].deleteRule(i);
782 document.styleSheets[0].insertRule(classname + ' { ' + properties + ' }', document.styleSheets[0]['cssRules'].length);
783 break;
784 }
785 }
786 }
787
788 function updateCssClasses()
789 {
790 for(var key in config) {
791 changeCssClass(getLocalizedText('settings.name.' + key), config[key].Value);
792 }
793 }
794
795 function restoreDefaultSettings()
796 {
797 for (var key in config)
798 config[key].Value = config[key].Default;
799 }
800
801 function loadSettings()
802 {
803 for (var key in config) {
804 if (widget.preferenceForKey(key)) {
805 if (config[key].Type == 'Int')
806 config[key].Value = Number(widget.preferenceForKey(key));
807 else if (config[key].Type == 'String')
808 config[key].Value = widget.preferenceForKey(key);
809 else if (config[key].Type == 'Bool')
810 config[key].Value = (widget.preferenceForKey(key) == 'true')
811 else if (config[key].Type == 'Enum')
812 config[key].Value = widget.preferenceForKey(key);
813 else if (config[key].Type == 'UID')
814 config[key].Value = Number(widget.preferenceForKey(key));
815 }
816 else
817 config[key].Value = config[key].Default;
818 console.info('Settings: ' + key + '=\'' + config[key].Value + '\'');
819 }
820 }
821
822 function saveSettings()
823 {
824 for (var key in config) {
825 if (config[key].Type == 'Int')
826 widget.setPreferenceForKey(config[key].Value.toString(), key);
827 else if (config[key].Type == 'String')
828 widget.setPreferenceForKey(config[key].Value, key);
829 else if (config[key].Type == 'Bool')
830 widget.setPreferenceForKey(config[key].Value ? 'true' : 'false', key);
831 else if (config[key].Type == 'Enum')
832 widget.setPreferenceForKey(config[key].Value, key);
833 else if (config[key].Type == 'UID')
834 widget.setPreferenceForKey(config[key].Value.toString(), key);
835 }
836 }
837
838 function toggleVisibility(elementId)
839 {
840 if (document.getElementById(elementId).style.display == "none")
841 document.getElementById(elementId).style.display = "block";
842 else
843 document.getElementById(elementId).style.display = "none";
844 }
845
846 var uniqueId = 0;
847 function printHintBox(text)
848 {
849 uniqueId++;
850 return '<td width="1%" align="right" onclick="javascript:toggleVisibility(\'info' + uniqueId + '\')">' + getLocalizedText('settings.help') + '</td></tr></table>'+
851 '<div class="settingsInfo" id="info' + uniqueId + '">' + text + '</div>';
852 }
853
854 function showAbout()
855 {
856 mode = 3;
857 hideViews();
858 document.getElementById("aboutView").style.display = "block";
859 document.onclick = null;
860
861 window.menu.setLeftSoftkeyLabel(" ", function(){});
862 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
863 {
864 mode = 1;
865 showFullscreen();
866 });
867
868 //document.getElementById("aboutView").innerHTML = 'aboutView';
869 document.getElementById("name").innerHTML = "Coming Next " + version;
870 }
871
872 function updateFullscreen()
873 {
874 }
875
876 function showFullscreen()
877 {
878 hideViews();
879 document.getElementById("fullscreenView").style.display = "block";
880 document.getElementById('body').className = "backgroundFullscreen";
881 document.onclick = launchCalendar;
882 createMenu();
883 updateData();
884 }
885
886 function getBackgroundImage()
887 {
888 var bgImage;
889 if (config['backgroundImageLocation'].Value == config['backgroundImageLocation'].ValidValues[0]) // internal
890 bgImage = 'background_' + orientation + '.png';
891 else
892 bgImage = 'C:/Data/background_' + panelNum + '_' + orientation + '.png';
893 return bgImage;
894 }
895
896 function updateHomescreen()
897 {
898 if (config['useBackgroundImage'].Value) {
899 // check for screen rotation
900 if (orientation != 'portrait' && screen.width == 360 && screen.height == 640) {
901 window.widget.prepareForTransition("fade");
902 orientation = 'portrait';
903 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
904 document.getElementById('body').style.backgroundColor = 'none';
905 window.widget.performTransition();
906 } else if (orientation != 'landscape' && screen.width == 640 && screen.height == 360) {
907 window.widget.prepareForTransition("fade");
908 orientation = 'landscape';
909 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
910 document.getElementById('body').style.backgroundColor = 'none';
911 window.widget.performTransition();
912 }
913 else if (document.getElementById('body').style.backgroundImage == "")
914 {
915 document.getElementById('body').style.backgroundImage = 'url(' + getBackgroundImage() + ')';
916 }
917 }
918 }
919
920 function showHomescreen()
921 {
922 hideViews();
923 document.getElementById("homescreenView").style.display = "block";
924 document.getElementById('body').className = "background";
925 document.onclick = null;
926 updateData();
927 }
928
929 function getLocalizedText(p_Txt)
930 {
931 if (localizedText[p_Txt])
932 return localizedText[p_Txt];
933 else
934 return 'ERROR: missing translation for ' + p_Txt;
935 }
936
937 function showUpdate()
938 {
939 mode = 4;
940 hideViews();
941 document.getElementById("updateView").style.display = "block";
942 document.onclick = null;
943
944 window.menu.setLeftSoftkeyLabel(getLocalizedText('update.checknow'), function(){
945 checkForUpdate();
946 });
947 window.menu.setRightSoftkeyLabel(getLocalizedText('softkey.back'), function()
948 {
949 mode = 1;
950 showFullscreen();
951 });
952
953 document.getElementById("currentVersion").innerHTML = getLocalizedText("update.current") + version;
954 checkForUpdate();
955 }
956
957 function checkForUpdate()
958 {
959 // asynch XHR to server url
960 reqV = new XMLHttpRequest();
961 reqV.onreadystatechange = checkForUpdateCallback;
962 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.checking");
963 reqV.open("GET", versionURL, true);
964 reqV.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); // disable caching
965 reqV.send(null);
966 }
967
968 function checkForUpdateCallback()
969 {
970 if (reqV.readyState == 4) {
971 if (reqV.status == 200) {
972 var resultXml = reqV.responseText;
973 if (resultXml) {
974 var div = document.getElementById("tmp");
975 div.innerHTML = resultXml;
976 var newVersion = div.getElementsByTagName('version')[0].innerHTML;
977 var newVersionURL = div.getElementsByTagName('url')[0].innerHTML;
978 div.innerHTML = "";
979 if (version != newVersion) {
980 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.download").replace(/%1/, newVersion).replace(/%2/, newVersionURL);
981 }
982 else {
983 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.nonewversion");
984 }
985 }
986 }
987 else {
988 document.getElementById("updateDiv").innerHTML = getLocalizedText("update.error") + reqV.status + " " + reqV.responseText;
989 }
990 }
991 }
992
993 function hideViews()
994 {
995 document.getElementById("homescreenView").style.display = "none";
996 document.getElementById("fullscreenView").style.display = "none";
997 document.getElementById("aboutView").style.display = "none";
998 document.getElementById("settingsView").style.display = "none";
999 document.getElementById("updateView").style.display = "none";
1000 }
1001 </script>
1002
1003 <style type="text/css">
1004 table { margin:0px; padding:0px; border-spacing:0px; }
1005 td { padding:0px 5px 0px 0px; white-space:nowrap; overflow:hidden; }
1006 hr { color:#ffffff; background-color:#ffffff; height:1px; text-align:left; border-style:none; }
1007 .settingsInfo { display:none; font-style:italic; }
1008 .title { font-weight:bold; font-size:14pt; }
1009 .textInput { width:90%; }
1010 .credits { margin-left:40px; text-indent: -20px; margin-bottom:0px; }
1011 #homescreenView { width: 315px; height:91px; overflow:hidden; }
1012 #calendarList { position:absolute; left:10px; top:4px; width:295px; height:75px; overflow:hidden; }
1013 #name { text-align:center; }
1014 #appicon { display: block; margin-left: auto; margin-right: auto; margin-top: 10px; }
1015 #smallappicon { width:22px; height:22px; margin-right:10px; float:left; }
1016 </style>
1017
1018 </head>
1019
1020 <body id="body" class="background">
1021 <div id="homescreenView">
1022 <div id="calendarList"></div>
1023 </div>
1024 <div id="fullscreenView" style="display:none;">
1025 <img src="Icon.png" id="smallappicon">
1026 <h1 class="title">Coming Next</h1>
1027 <hr />
1028 <div id="fullscreenCalendarList">loading...</div>
1029 </div>
1030 <div id="settingsView" style="display:none">
1031 <img src="Icon.png" id="smallappicon">
1032 <h1 class="title">Settings</h1>
1033 <hr />
1034 <div id="settingsList"></div>
1035 </div>
1036 <div id="aboutView" style="display:none">
1037 <img src="Icon.png" id="appicon">
1038 <h1 id="name">Coming Next</h1>
1039 <hr />
1040 <p>Created by Dr. Cochambre and Michael Prager.</p>
1041 <p>Contributions:</p>
1042 <p class="credits">Paul Moore (bug fixes, new features and code cleanup)</p>
1043 <p class="credits">Manfred Hanselmann (DST support)</p>
1044 <p class="credits">Christophe Milsent (translation support & french translation</p>
1045 <p class="credits">Flavio Nathan (portuguese-brazilian translation</p>
1046 <p>This software is open source and licensed under the GPLv3.</p>
1047 <p>Visit sourceforge.net/projects/comingnext for free updates.</p>
1048 <hr />
1049 </div>
1050 <div id="updateView" style="display:none">
1051 <img src="Icon.png" id="smallappicon">
1052 <h1 class="title">Check for update</h1>
1053 <hr />
1054 <div id="currentVersion">Coming Next ??</div>
1055 <div id="updateDiv"></div>
1056 <div id="tmp" style="display:none;"></div>
1057 </div>
1058 </body>
1059
1060 </html>