-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlisting1.html
executable file
·569 lines (449 loc) · 24 KB
/
listing1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<!-- BEGIN META TAG INFO -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="home" href="http://developer.apple.com/">
<link rel="find" href="http://developer.apple.com/search/">
<link rel="stylesheet" type="text/css" href="../../documentation/css/adcstyle.css" title="fonts">
<script language="JavaScript" src="../../documentation/js/adc.js" type="text/javascript"></script>
<!-- END META TAG INFO -->
<!-- BEGIN TITLE -->
<title>Reminders - /ReadMe.txt</title>
<!-- END TITLE -->
<script language="JavaScript">
function JumpToNewPage() {
window.location=document.scpopupmenu.gotop.value;
return true;
}
</script>
</head>
<!-- BEGIN BODY OPEN -->
<body>
<!--END BODY OPEN -->
<!-- START CENTER OPEN -->
<center>
<!-- END CENTER OPEN -->
<!-- BEGIN LOGO AND SEARCH -->
<!--#include virtual="/includes/adcnavbar"-->
<!-- END LOGO AND SEARCH -->
<!-- START BREADCRUMB -->
<div id="breadcrumb">
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td scope="row"><img width="340" height="10" src="images/1dot.gif" alt=""></td>
<td><img width="340" height="10" src="images/1dot.gif" alt=""></td>
</tr>
<tr valign="middle">
<td align="left" colspan="2">
<a href="http://developer.apple.com/">ADC Home</a> > <a href="../../referencelibrary/index.html#//apple_ref/doc/uid/TP30000943" target="_top">Reference Library</a> > <a href="../index.html#//apple_ref/doc/uid/TP30000925" target="_top">Sample Code</a> > <a href="../AppleApplications/index.html#//apple_ref/doc/uid/TP30000925-TP30000418" target="_top">Apple Applications</a> > <a href="../AppleApplications/idxDashboard-date.html#//apple_ref/doc/uid/TP30000925-TP30000418-TP40001366">Dashboard</a> > <A HREF="javascript:location.replace('index.html');">Reminders</A> >
</td>
</tr>
<tr>
<td colspan="2" scope="row"><img width="680" height="35" src="images/1dot.gif" alt=""></td>
</tr>
</table>
</div>
<!-- END BREADCRUMB -->
<!-- START MAIN CONTENT -->
<!-- START TITLE GRAPHIC AND INTRO-->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td><h1><div id="pagehead">Reminders</div></h1></td>
</tr>
</table>
<!-- END TITLE GRAPHIC AND INTRO -->
<!-- START WIDE COLUMN -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td id="scdetails">
<h2>/ReadMe.txt</h2>
<form name="scpopupmenu" onSubmit="return false;" method=post>
<p><strong>View Source Code:</strong>
<select name="gotop" onChange="JumpToNewPage();" style="width:340px"><option selected value="ingnore">Select File</option>
<option value="listing1.html">/ReadMe.txt</option>
<option value="listing2.html">/Reminders.wdgt/Reminders.css</option>
<option value="listing3.html">/Reminders.wdgt/Reminders.html</option>
<option value="listing4.html">/Reminders.wdgt/Reminders.js</option>
<option value="listing5.html">/Reminders.wdgt/UserCalendarPreferences.js</option>
<option value="listing6.html">/RemindersPlugIn/CalendarItems.h</option>
<option value="listing7.html">/RemindersPlugIn/CalendarItems.m</option>
<option value="listing8.html">/RemindersPlugIn/NSColorHexadecimalValue.h</option>
<option value="listing9.html">/RemindersPlugIn/NSColorHexadecimalValue.m</option>
<option value="listing10.html">/RemindersPlugIn/RemindersPlugIn.h</option>
<option value="listing11.html">/RemindersPlugIn/RemindersPlugIn.m</option>
<option value="listing12.html">/RemindersPlugIn/RemindersPlugIn_Prefix.pch</option></select>
</p>
</form>
<p><strong><a href="Reminders.zip">Download Sample</a></strong> (“Reminders.zip”, 349.1K)<BR>
<strong><a href="Reminders.dmg">Download Sample</a></strong> (“Reminders.dmg”, 324.6K)</p>
<!--
<p><strong><a href="#">Download Sample</a></strong> (“filename.sit”, 500K)</p>
-->
</td>
</tr>
<tr>
<td scope="row"><img width="680" height="10" src="images/1dot.gif" alt=""><br>
<img height="1" width="680" src="images/1dot_919699.gif" alt=""><br>
<img width="680" height="20" src="images/1dot.gif" alt=""></td>
</tr>
<tr>
<td scope="row">
<!--googleon: index -->
<pre class="sourcecodebox">Reminders illustrates how you can use a WebKit plug-in to extend the
functionality of a Dashboard widget. It also shows how to share complex data
from Objective-C to JavaScript, update and retrieve widget preferences, and call
JavaScript from a WebKit plug-in and vice versa.
It is a Dashboard widget that displays upcoming iCal events and to do items
occurring within the year. Users can see all events (starting now) and
incomplete tasks ending before the end of the year without launching iCal. All
iCal changes are instantly added to the widget. The widget shows the date and
title of each event or to do item, the start and end time of each event, and the
priority of each to do item. The "all-day" tag is used in lieu of start and end
time for events lasting all day long. Users are informed when there are no
available events, calendars, or to do items.
Reminders displays data from multiple calendars at once. However, you can
choose to restrict your widget to some specific calendars. It contains a menu
bar that enables users to toggle between events and to do items. A set of
checkboxes, in the back of the widget, allows users to choose which calendars
should be seen in the widget. The widget saves and remembers the users' calendar
preferences.
Reminders uses the RemindersPlugIn plug-in to query the CalendarStore framework
for iCal events, to do items, and calendars. RemindersPlugIn is a Cocoa bundle
that provides data used by the widget through JavaScript callbacks; the
JavaScript enabled widget can access specific Objective-C methods [in the
plug-in] while the plug-in can call the widget's JavaScript functions.
Using the sample
1. Add some calendars, events, or to do items to iCal. For testing purposes,
Reminders add a fictitious calendar with an event and to do item if your iCal
is empty.
2. Click the Reminders.wdgt to install the widget and open it in Dashboard.
3. Toggle between the "Events" and "To Do Items" buttons to view events and to
do items. Click the info button to switch to the back of the widget. Set your
calendar preferences by checking one or more calendars. Use the Done
button to return to the front of the widget.
Building the sample
This sample consists of a widget (Reminders) and plug-in (RemindersPlugIn).
Thus, building the sample consists of implementing RemindersPlugIn in Xcode and
creating Reminders in Dashcode.
Creating the plug-in RemindersPlugIn
In the steps below, we are going to create and set up RemindersPlugIn in Xcode,
then add classes to it.
Setting up the Project
This section shows how to create and set up RemindersPlugIn.
1. Create a new Project
RemindersPlugIn is a Cocoa bundle.
Open Xcode, choose File > New Project and select Cocoa Bundle under Bundle in
the New Project Assistant window. Click Next and save the project as
RemindersPlugIn.
2. Add the WebKit and CalendarStore frameworks
Open the Frameworks and Libraries group in the Groups & Files list of the
project window and then right-click or control-click the Linked Frameworks
subgroup.
Choose Add > Existing Frameworks from the pop-up menu and navigate to the
/System/Library/Frameworks/ folder to select and add the WebKit.framework and
CalendarStore.framework to the project.
3. Add the minimum system version Info.plist key
This project uses the CalendarStore framework that is only available in Mac OS
X 10.5 and later. Set the LSMinimumSystemVersion to 10.5 so users can receive
notices informing them about the minimum system requirement when they attempt to
run the application on a system earlier than Mac OS X 10.5.
Double-click the Info.plist file in the resource area of the project and copy
and paste the following lines into the file.
<key>LSMinimumSystemVersion</key>
<string>10.5</string>
4. Set the Target SDK to Mac OS X 10.5
Double-click the RemindersPlugIn project icon in the Groups & Files list to
open the project info window. Go to the General tab and set the "Cross-Develop
Using Target SDK" to Mac OS X 10.5.
5. Set the bundle identifier and principal class
The bundler identifier uniquely identifies your plug-in. The principal class
identifies the main class used to define your plug-in's functionality; its name
should be the same as the name of your project.
Choose Project > Edit Active Target "RemindersPlugin" and click the Properties
tab.
Fill out the identifier and principal class text fields with a bundle
identifier of your choice and RemindersPlugin, respectively.
Read the Code Loading Programming Topics for Cocoa > Creating Loadable Bundles
and Code Loading Programming Topics for Cocoa > Preventing Name Conflicts
sections to learn more about bundle identifiers.
6. Set the Wrapper Extension.
Double-click the "RemindersPlugIn" target, select the Build pane, and search
for the Wrapper Extension setting. Set the Wrapper Extension's value to
widgetplugin.
Adding Classes
We just set up our Xcode project. In the steps below, we are going to add the
NSColorHexadecimalValue, CalendarItems, and RemindersPlugIn classes to our
project. These classes are needed for querying the CalendarStore framework,
parsing and processing data, and exposing Objective-C methods and instance
variables to JavaScript and vice versa.
7. Create the NSColorHexadecimalValue's class files
Select File > New File and click Objective-C class under Cocoa in the New File
Assistant window. Name the file NSColorHexadecimalValue.
8. Create the NSColorHexadecimalValue class
NSColorHexadecimalValue is an Objective-C category that adds the
"hexadecimalStringValue" method to the NSColor class. "hexadecimalStringValue"
returns the hexadecimal value of a color.
The declaration of the NSColorHexadecimalValue interface (in the
NSColorHexadecimalValue.h file) is shown below:
@interface NSColor(NSColorHexadecimalValue)
-(NSString *)hexadecimalStringValue;
The NSColorHexadecimalValue.m file, which contains the category implementation,
describes how to implement "hexadecimalStringValue."
9. Create the CalendarItems's class files
Proceed as done in step 7 and name the file CalendarItems.
CalendarItems is an Objective-C class that stores specific information about a
calendar (uid, title, color), event (start date, title, color, start and end
time), and to do item (start date, title, color, start and end time).
See below for its interface declaration:
extern NSString * const kJSSelectorPrefix;
@interface CalendarItems : NSObject {
NSString *js_uid;
NSString *js_startDate;
NSString *js_title;
NSString *js_color;
NSString *js_timeOrPriority;
}
@property(copy, readwrite) NSString *js_uid;
@property(copy, readwrite) NSString *js_startDate;
@property(copy, readwrite) NSString *js_title;
@property(copy, readwrite) NSString *js_color;
@property(copy, readwrite) NSString *js_timeOrPriority;
-(id)initWithStartDate:(NSString *)date
title:(NSString *)aTitle
color:(NSString *)aColor
timeOrPriority:(NSString *)aValue;
-(id)initWithUID:(NSString *)aUid title:(NSString *)aTitle color:(NSString
*)aColor;
CalendarItems.m implements the WebKit's isKeyExcludedFromWebScript and
webScriptNameForKey methods, which allows its exposure to JavaScript.
Note that step b is optional. Follow that step if you want to use custom names
for your instance variables.
a) Identify all instance variables that will be shared with JavaScript
The isKeyExcludedFromWebScript method determines which Objective-C properties
should be available to JavaScript. In this sample, all CalendarItems instance
variables are exposed to JavaScript. See below for an implementation of
isKeyExcludedFromWebScript :
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name
{
NSString* keyName = [[[NSString alloc] initWithUTF8String: name]
autorelease];
return !([ keyName hasPrefix:kJSSelectorPrefix]);
}
b) Provides mapping between Objective-C and JavaScript instance variable names
The plug-in adds a "js" to the beginning of any property that is to be exposed
to JavaScript. The webScriptNameForKey method returns friendly names for the
exposed Objective-C instance variables. In this sample, all properties are
exposed to JavaScript without the "js" prefix. See below for an implementation
of webScriptNameForKey:
+ (NSString *)webScriptNameForKey:(const char *)name
{
NSString* keyName = [[[NSString alloc] initWithUTF8String: name]
autorelease];
if ([ keyName hasPrefix:kJSSelectorPrefix] &&
([ keyName length] > [kJSSelectorPrefix length]))
{
return [keyName substringFromIndex:[kJSSelectorPrefix length]];
}
return nil;
}
10. Create the RemindersPlugIn class' s files
Proceed as done in step 7 and name the file RemindersPlugIn.
11. Create the RemindersPlugIn class
Add the "#import <WebKit/WebKit.h>" to the beginning of the RemindersPlugIn.h
file.
Add both "#import <CalendarStore/CalendarStore.h>" and "#import
"NSColorHexadecimalValue.h" to the beginning of the RemindersPlugIn.m file.
This process involves several steps that are described from number 12 to 15.
12. Add code for exporting Objective-C methods to JavaScript
The plug-in defines a WebScriptObject object and implements some functions that
allows communication between the widget and itself.
Note that step b is optional. Follow that step if you want to use custom names
for your methods.
a) Identify the object that contains methods and properties to be exported to
JavaScript
This step consists of providing a name to the plug-in and using the Web Kit's
windowScriptObjectAvailable method to bind this name to the above mentioned
WebScriptObject object.See below for an implementation of
windowScriptObjectAvailable:
- (void)windowScriptObjectAvailable:(WebScriptObject*)scriptObject
{
// our plug-in will be called as RemindersPlugIn from JavaScript
[scriptObject setValue:self forKey:@"RemindersPlugIn"];
webScriptObject = scriptObject;
[webScriptObject retain];
}
b) Provide mapping between Objective-C and JavaScript method names
The plug-in adds a "js" to the beginning of any method that will be exposed to
JavaScript. The Web Kit's webScriptNameForSelector method returns friendly names
for these exposed Objective-C methods. In this sample, all Objective-C methods
will be used in JavaScript without the "js" prefix. For instance, the
Objective-C "js_calendarToDoItems" method will be exposed as
"calendarToDoItems" in JavaScript. See below for webScriptNameForSelector's
implementation:
NSString * const kJSSelectorPrefix = @"js_";
+ (NSString *)webScriptNameForSelector:(SEL)aSelector
{
NSString* selectorName = NSStringFromSelector(aSelector);
if ([selectorName hasPrefix:kJSSelectorPrefix] && ([selectorName length] >
[kJSSelectorPrefix length]))
{
return [[[selectorName substringFromIndex:[kJSSelectorPrefix length]]
componentsSeparatedByString: @":"] objectAtIndex: 0];
}
return nil;
}
c) Allow JavaScript to access Objective-C methods and properties
This step consists of identifying all methods and instance variables that will
be shared with JavaScript.
i) Identify all methods that will be shared with JavaScript
The Web Kit's isSelectorExcludedFromWebScript determines which Objective-C
methods should be exposed to JavaScript. In this sample, all methods with a "js"
prefix are shared with JavaScript. Below is an implementation of
isSelectorExcludedFromWebScript:
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
{
return !([NSStringFromSelector(aSelector) hasPrefix:kJSSelectorPrefix]);
}
13. Fetching and parsing data
The "fetchCalendarEvents" and "fetchToDoItems" are used to retrieve events and
to do items, respectively. They both receive a list of calendars' uids. They
return data from specific calendars if the list is not empty and all available
calendars otherwise. Both methods use the "fetchCalendarsWithUIDs" to get all
calendars corresponding to the specified uids.
Data parsing is accomplished through the "parseCalendarEvents,"
"parseCalendars," and "parseCalendarToDoItems" methods that respectively parse
events, calendars, and to do items information. All three methods build an array
of CalendarItems objects.
14. Calling the plug-in from JavaScript
JavaScript can access the plug-in's " js_calendarEvents,"
"js_calendarToDoItems," "js_calendars," and "js_nameForCalendarWithUIDs"
methods. They are rendered in JavaScript without the "js" prefix. For instance,
the "js_nameForCalendarWithUIDs" will be accessed in JavaScript as follows:
RemindersPlugIn.nameForCalendarWithUIDs
15. Calling JavaScript from the plug-in
Both "eventsChanged" and "tasksChanged" methods call the JavaScript
"reloadEventsOrToDoItems" from the plug-in whenever users add, remove, or update
events or to do items. This is accomplished using the WebScriptObject object and
the evaluateWebScript method as seen in the code below:
- (void)eventsChanged:(NSNotification *)notification
{
[webScriptObject evaluateWebScript:@"reloadEventsOrToDoItems(false)"];
}
16. Build the sample
Click the Build toolbar item in the project window to compile the plug-in.
Creating the widget Reminders
We have just created the plug-in part, what follows are the steps for creating
the widget that uses the plug-in. We are going to set up the widget, build its
interface, and add functionalities for fetching and displaying data on its front
and back.
Note: The widget consists of several files; however, the most important files
are: Reminders.js, Reminders.html, Reminders.css, and
UserCalendarPreferences.js.
1. Create the widget
Reminders was created using Dashcode and consists of HTML, CSS, and JavaScript
files. Open Dashcode, select File > new Project, and choose the widget's custom
template. Save the new project as Reminders.
2. Set up the widget's identifier
Click the widget attributes pane in the window to access the widget's
properties. Fill the widget identifier with the RemindersPlugIn's bundle
identifier.
3. Set up the widget's plugin
The widget plugin field holds our plug-in's name. Use the "choose" button in
the widget's properties window to select the RemindersPlugIn.widgetplugin folder
(RemindersPlugIn/build/Release/RemindersPlugIn.widgetplugin).
4. Add the widget's preferrredCalendar key
The widget uses the preferrredCalendar key to save the user's calendar
preferences.
Click the "+" sign in the Localization area to add the preferredCalendar key.
5. Create the widget's interface
Our widget has front and back interfaces. Both interfaces were created using
controls available in Dashcode's library.
Choose Window > Show Library and click the Parts button to see all available
Dashcode's controls. We built the widget by dragging each of its parts (buttons,
scroll areas, rectangles) from the Parts Library to the widget's body on the
canvas.
6. Implement the front of the widget
The Reminders.js file contains all required JavaScript functions for the front
of the widget. Its "showEvents" and "showToDoItems" functions are activated when
users respectively click the "Events" or "To Do Items" buttons on the widget's
menu bar.
Both functions call the RemindersPlugIn object to retrieve events and to do
items belonging to all or specific calendars, respectively. "showEvents" calls
the plug-in using the following line:
RemindersPlugIn.calendarEvents(preferredCalendars)
Its "displayResults" function receives an array of CalendarItems objects and a
boolean value; it shows events if the value is false and to do items otherwise.
The "showErrorMessage" function adds specific error messages on the widget. Both
"displayResults" and "showErrorMessage" write data to the widget's scroll area.
We need to refresh the scroll area once we are done appending data to it. Use
the following line to refresh the scroll area:
document.getElementById(scrollArea).object.refresh()
Note: Below are problems that you may encounter if you do not refresh the
scroll area after changing its content:
-Sliders might not be visible when the content is too large for the scroll
area.
-If you move the slider up or down while the widget displays either events or
to do items data, then switches to the other view, the slider will stay at its
previous position instead of jumping to the top of the scroll area.
Users can switch to the back of the widget using the "showBack" function.
7. Implement the back of the widget
The UserCalendarPreferences.js file defines all required JavaScript functions
for the back of the widget. It uses the "displayUserCalendarPreferences"
function to fetch and display all available calendars. It calls the
"retrieveSelectedCalendars" function to get all calendars checked by the user
and update the custom "preferredCalendars" key.
All calendars are selected by default when the widget first loads.
"retrieveSelectedCalendars" shows a warning message and icon if users uncheck
all of them. The following line shows how to update the "preferredCalendars"
key:
widget.setPreferenceForKey(preferredCalendars,"preferredCalendars")
The "userCalendarPreferences" function checks the widget's "preferredCalendars"
key using widget.preferenceForKey("preferredCalendars") and returns
the user's calendar preferences. Use the "showFront" function to return to the
front of the widget.
8. Build the sample
Click Run in the toolbar to test the widget.
Click File > Deploy Widget to Dashboard to install the widget on your computer.
Feedback and Bug Reports
Please send all feedback about this sample by using the Feedback form on the
bottom of the sample's webpage.
Please submit any bug reports about this sample to the Bug Reporting
<http://developer.apple.com/bugreporter> page.
Copyright (C) 2008 Apple Inc. All rights reserved.</pre>
<!--googleoff: index -->
</td>
</tr>
</table>
<!-- END WIDE COLUMN -->
<!-- END MAIN CONTENT -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td><div style="width: 100%; height: 1px; background-color: #919699; margin-top: 5px; margin-bottom: 15px"></div></td>
</tr>
<tr>
<td align="center"><br/>
<table border="0" cellpadding="0" cellspacing="0" class="graybox">
<tr>
<th>Did this document help you?</th>
</tr>
<tr>
<td>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=1&url=/samplecode/Reminders/listing1.html%3Fid%3DDTS40007877-1.0&media=dvd" target=_new>Yes</a>: Tell us what works for you.</div>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=2&url=/samplecode/Reminders/listing1.html%3Fid%3DDTS40007877-1.0&media=dvd" target=_new>It’s good, but:</a> Report typos, inaccuracies, and so forth.</div>
<div><a href="http://developer.apple.com/feedback/?v=3&url=/samplecode/Reminders/listing1.html%3Fid%3DDTS40007877-1.0&media=dvd" target=_new>It wasn’t helpful</a>: Tell us what would have helped.</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- START BOTTOM APPLE NAVIGATION -->
<!--#include virtual="/includes/footer"-->
<!-- END BOTTOM APPLE NAVIGATION -->
<!-- START CENTER CLOSE -->
</center>
<!-- END CENTER CLOSE -->
</body>
</html>