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