itunes-connect 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,195 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe ItunesConnect::Commands::Report do
4
+ before(:each) do
5
+ @cmd = ItunesConnect::Commands::Report.new(mock(:null_object => true))
6
+ @defaults = {
7
+ :db => '/tmp/store.db',
8
+ :summarize? => false,
9
+ :to => nil,
10
+ :from => nil,
11
+ :country => nil,
12
+ :no_header? => false,
13
+ :delimiter => "\t",
14
+ :total? => false
15
+ }
16
+ end
17
+
18
+ describe 'with valid execution arguments' do
19
+ before(:each) do
20
+ @store = mock(ItunesConnect::Store)
21
+ ItunesConnect::Store.should_receive(:new).
22
+ with("/tmp/store.db").
23
+ and_return(@store)
24
+ @io = StringIO.new
25
+ @data = [
26
+ mock(:report_date => Date.parse('2009/09/09'), :country => 'US',
27
+ :install_count => 1, :update_count => 2),
28
+ mock(:report_date => Date.parse('2009/09/09'), :country => 'GB',
29
+ :install_count => 3, :update_count => 4)
30
+ ]
31
+ end
32
+
33
+ it 'should request counts with no options with no qualifiers' do
34
+ @store.should_receive(:counts).and_return(@data)
35
+ clip = stub(@defaults.merge(:db => '/tmp/store.db'))
36
+ @cmd.execute!(clip, [], @io)
37
+ @io.string.should == <<EOF
38
+ Date\tCountry\tInstalls\tUpgrades
39
+ 2009-09-09\tUS\t1\t2
40
+ 2009-09-09\tGB\t3\t4
41
+ EOF
42
+ end
43
+
44
+ it 'should output data with other options' do
45
+ @store.should_receive(:counts).
46
+ with(:to => Date.parse('2009/09/09'),
47
+ :from => Date.parse('2009/09/01'),
48
+ :country => 'US').
49
+ and_return(@data)
50
+
51
+ clip = stub(@defaults.merge(:db => '/tmp/store.db',
52
+ :to => Date.parse('2009/09/09'),
53
+ :from => Date.parse('2009/09/01'),
54
+ :country => 'US'))
55
+
56
+ @cmd.execute!(clip, [], @io)
57
+ @io.string.should == <<EOF
58
+ Date\tCountry\tInstalls\tUpgrades
59
+ 2009-09-09\tUS\t1\t2
60
+ 2009-09-09\tGB\t3\t4
61
+ EOF
62
+ end
63
+
64
+ it 'should suppress the header when requested' do
65
+ @store.should_receive(:counts). and_return(@data)
66
+ clip = stub(@defaults.merge(:no_header? => true))
67
+ @cmd.execute!(clip, [], @io)
68
+ @io.string.should == <<EOF
69
+ 2009-09-09\tUS\t1\t2
70
+ 2009-09-09\tGB\t3\t4
71
+ EOF
72
+ end
73
+
74
+ it 'should separate fields with specified delimiter' do
75
+ @store.should_receive(:counts). and_return(@data)
76
+ clip = stub(@defaults.merge(:delimiter => "|"))
77
+ @cmd.execute!(clip, [], @io)
78
+ @io.string.should == <<EOF
79
+ Date|Country|Installs|Upgrades
80
+ 2009-09-09|US|1|2
81
+ 2009-09-09|GB|3|4
82
+ EOF
83
+ end
84
+
85
+ it 'should output totals when the "totals" flag is specified' do
86
+ @store.should_receive(:counts). and_return(@data)
87
+ clip = stub(@defaults.merge(:total? => true))
88
+ @cmd.execute!(clip, [], @io)
89
+ @io.string.should == <<EOF
90
+ Date\tCountry\tInstalls\tUpgrades
91
+ 2009-09-09\tUS\t1\t2
92
+ 2009-09-09\tGB\t3\t4
93
+ Total\t-\t4\t6
94
+ EOF
95
+ end
96
+ end
97
+
98
+ describe 'with :group option specified' do
99
+ before(:each) do
100
+ @store = mock(ItunesConnect::Store)
101
+ ItunesConnect::Store.should_receive(:new).
102
+ with('/tmp/store.db').
103
+ and_return(@store)
104
+
105
+ @io = StringIO.new
106
+ @data = [
107
+ mock(:country => 'US', :install_count => 1, :update_count => 2),
108
+ mock(:country => 'GB', :install_count => 3, :update_count => 4)
109
+ ]
110
+ end
111
+
112
+ it 'should request grouped country data' do
113
+ @store.should_receive(:country_counts).and_return(@data)
114
+ clip = stub(@defaults.merge(:summarize? => true))
115
+ @cmd.execute!(clip, [], @io)
116
+ @io.string.should == <<EOF
117
+ Country\tInstalls\tUpgrades
118
+ US\t1\t2
119
+ GB\t3\t4
120
+ EOF
121
+ end
122
+
123
+ it 'should suppress the header when requested' do
124
+ @store.should_receive(:country_counts).and_return(@data)
125
+ clip = stub(@defaults.merge(:summarize? => true, :no_header? => true))
126
+ @cmd.execute!(clip, [], @io)
127
+ @io.string.should == <<EOF
128
+ US\t1\t2
129
+ GB\t3\t4
130
+ EOF
131
+ end
132
+
133
+ it 'should separate fields with the specified delimiter' do
134
+ @store.should_receive(:country_counts).and_return(@data)
135
+ clip = stub(@defaults.merge(:summarize? => true, :delimiter => '|'))
136
+ @cmd.execute!(clip, [], @io)
137
+ @io.string.should == <<EOF
138
+ Country|Installs|Upgrades
139
+ US|1|2
140
+ GB|3|4
141
+ EOF
142
+ end
143
+
144
+ it 'should output totals when the "totals" flag is specified' do
145
+ @store.should_receive(:country_counts).and_return(@data)
146
+ clip = stub(@defaults.merge(:summarize? => true, :total? => true))
147
+ @cmd.execute!(clip, [], @io)
148
+ @io.string.should == <<EOF
149
+ Country\tInstalls\tUpgrades
150
+ US\t1\t2
151
+ GB\t3\t4
152
+ Total\t4\t6
153
+ EOF
154
+ end
155
+ end
156
+
157
+ describe 'with invalid execution arguments' do
158
+ it 'should require the :db option' do
159
+ lambda { @cmd.execute! }.should raise_error(ArgumentError)
160
+ end
161
+ end
162
+
163
+ describe 'command-line option parsing' do
164
+ it 'should add appropriate options to a given Clip' do
165
+ clip = mock("Clip")
166
+ clip.should_receive(:opt).
167
+ with('b', 'db',
168
+ :desc => 'Dump report to sqlite DB at the given path')
169
+ clip.should_receive(:opt).
170
+ with('c', 'country',
171
+ :desc => 'A two-letter country code to filter results with')
172
+ clip.should_receive(:opt).
173
+ with('f', 'from',
174
+ :desc => 'The starting date, inclusive')
175
+ clip.should_receive(:opt).
176
+ with('t', 'to',
177
+ :desc => 'The ending date, inclusive')
178
+ clip.should_receive(:flag).
179
+ with('s', 'summarize',
180
+ :desc => 'Summarize results by country code')
181
+ clip.should_receive(:flag).
182
+ with('n', 'no-header',
183
+ :desc => 'Suppress the column headers on output')
184
+ clip.should_receive(:opt).
185
+ with('d', 'delimiter',
186
+ :desc => 'The delimiter to use for output (normally TAB)',
187
+ :default => "\t")
188
+ clip.should_receive(:flag).
189
+ with('o', 'total', :desc => 'Add totals at the end of the report')
190
+
191
+ ItunesConnect::Commands::Report.new(clip)
192
+ end
193
+ end
194
+
195
+ end
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe ItunesConnect::Commands do
4
+
5
+ describe 'all' do
6
+ it 'should return all available command' do
7
+ ItunesConnect::Commands.all.should == [
8
+ ItunesConnect::Commands::Download,
9
+ ItunesConnect::Commands::Import,
10
+ ItunesConnect::Commands::Report,
11
+ ItunesConnect::Commands::Help
12
+ ]
13
+ end
14
+ end
15
+
16
+ describe 'for_name' do
17
+ before(:each) do
18
+ @clip = mock(:null_object => true)
19
+ end
20
+
21
+ it 'should return Download for download' do
22
+ ItunesConnect::Commands.for_name('download', @clip).
23
+ should be_kind_of(ItunesConnect::Commands::Download)
24
+ end
25
+
26
+ it 'should return Import for import' do
27
+ ItunesConnect::Commands.for_name('import', @clip).
28
+ should be_kind_of(ItunesConnect::Commands::Import)
29
+ end
30
+
31
+ it 'should return Report for report' do
32
+ ItunesConnect::Commands.for_name('report', @clip).
33
+ should be_kind_of(ItunesConnect::Commands::Report)
34
+ end
35
+
36
+ it 'should return Help for help' do
37
+ ItunesConnect::Commands.for_name('help', @clip).
38
+ should be_kind_of(ItunesConnect::Commands::Help)
39
+ end
40
+
41
+
42
+ it 'should return nil for other names' do
43
+ ItunesConnect::Commands.for_name('foobar', @clip).should be_nil
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require "fakeweb"
3
+
4
+ describe ItunesConnect::Connection do
5
+ before(:each) do
6
+ @itc = ItunesConnect::Connection.new("foo", "bar")
7
+ FakeWeb.allow_net_connect = false
8
+ end
9
+
10
+ it 'should reject invalid periods' do
11
+ lambda {
12
+ @itc.get_report(Date.today - 1, $stdout, "Invalid")
13
+ }.should raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'should reject dates newer than yesterday' do
17
+ lambda {
18
+ @itc.get_report(Date.today, $stdout)
19
+ }.should raise_error(ArgumentError)
20
+
21
+ lambda {
22
+ @itc.get_report(Date.today + 1, $stdout)
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ end
@@ -0,0 +1,365 @@
1
+ HTTP/1.1 200 Apple
2
+ Date: Tue, 25 Aug 2009 18:19:18 GMT
3
+ Server: Apache/1.3.33 (Darwin) mod_ssl/2.8.24 OpenSSL/0.9.7l
4
+ Cache-Control: max-age=60
5
+ Expires: Tue, 25 Aug 2009 18:20:18 GMT
6
+ cache-control: private
7
+ cache-control: no-cache
8
+ cache-control: no-store
9
+ cache-control: must-revalidate
10
+ cache-control: max-age=0
11
+ expires: Tue, 25-Aug-2009 16:36:29 GMT
12
+ pragma: no-cache
13
+ content-length: 11640
14
+ Keep-Alive: timeout=15, max=100
15
+ Connection: Keep-Alive
16
+ Content-Type: text/html
17
+
18
+ <html>
19
+ <head>
20
+ <title>Piano-Sign In</title>
21
+
22
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
23
+ <meta name="Author" content="Apple Computer, Inc.">
24
+
25
+ <link rel="home" href="http://www.apple.com/">
26
+ <link rel="index" href="http://www.apple.com/find/sitemap.html">
27
+
28
+
29
+
30
+ <script language="JavaScript">
31
+
32
+ function browserAlert()
33
+ {
34
+ //alert(navigator.userAgent);
35
+ //alert(document.getElementById("checkAlrt").value);
36
+
37
+ var alertMessage ="The browser you are using is not supported by this application. The application may not work as expected. Supported browsers are:- Safari 2 and above in Mac & IE 6.0 and above in Windows & Firefox 2 and above in both Mac and Windows";
38
+ if(document.getElementById("checkAlrt").value!="Message Displayed")
39
+ {
40
+
41
+ var browserType = navigator.userAgent;
42
+ var s = browserType.length;
43
+ var i = browserType.lastIndexOf("/")+1;
44
+ //alert("Split :::::" + navigator.userAgent.split("/"));
45
+ tempVersion = navigator.userAgent.split("/");
46
+ //alert(tempVersion[3]);
47
+ var iSafari = browserType.indexOf("Safari");
48
+ var iNetscape = browserType.indexOf("Netscape");
49
+ var iFirefox = browserType.indexOf("Firefox");
50
+ var iIE = browserType.indexOf("IE");
51
+ var iMac = browserType.indexOf("Mac");
52
+ var iOpera = browserType.indexOf("Opera");
53
+
54
+
55
+ if(iSafari < 0 && iFirefox < 0 && iIE < 0 )
56
+ {
57
+ alert(alertMessage);
58
+ }
59
+
60
+ else
61
+ {
62
+
63
+ if(iSafari > -1)
64
+ {
65
+ if(browserType.substring(s,i) < 412)
66
+ {
67
+ alert(alertMessage);
68
+ }
69
+ }
70
+
71
+
72
+ if(iFirefox > -1)
73
+ {
74
+ if(browserType.substring(i,s) < 2.0)
75
+ {
76
+ alert(alertMessage);
77
+ }
78
+ }
79
+
80
+
81
+ if(iIE > -1)
82
+ {
83
+ temp=navigator.userAgent.split("MSIE")
84
+ version=parseFloat(temp[1])
85
+ //alert(version);
86
+ if(version < 6.0 || iMac > 1)
87
+ {
88
+ alert(alertMessage);
89
+ }
90
+ }
91
+
92
+ }
93
+ }
94
+ }
95
+
96
+
97
+
98
+ </script>
99
+
100
+
101
+ </head>
102
+ <body onLoad="browserAlert()">
103
+
104
+ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
105
+ "http://www.w3.org/TR/html4/loose.dtd">
106
+ <html>
107
+ <head>
108
+ <title>Piano-Sign In</title>
109
+ <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
110
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
111
+ <meta http-equiv="pics-label" content='(pics-1.1 "http://www.icra.org/ratingsv02.html" l gen true for "http://www.apple.com" r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true for "http://www.apple.com" r (n 0 s 0 v 0 l 0))'>
112
+ <meta http-equiv="Expires" content="Fri, 26 Mar 1999 23:59:59 GMT">
113
+ <meta name="Author" content="Apple Computer, Inc.">
114
+
115
+ <link rel="home" href="http://www.apple.com/">
116
+ <link rel="index" href="http://www.apple.com/find/sitemap.html">
117
+
118
+
119
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/index.css" media="screen">
120
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/global.css" media="screen">
121
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/fonts.css" media="screen">
122
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/Pianonav.css" media="screen">
123
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/job.css" media="screen">
124
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/build.css" media="print">
125
+
126
+ <SCRIPT LANGUAGE="javascript">
127
+
128
+ appleOver = new Image()
129
+ appleOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/AppleOr.png"
130
+
131
+ storeOver = new Image()
132
+ storeOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/StoreOr.png"
133
+
134
+ macOver = new Image()
135
+ macOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/MacOr.png"
136
+
137
+ tunesOver = new Image()
138
+ tunesOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/TunesOr.png"
139
+
140
+ phoneOver = new Image()
141
+ phoneOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/PhoneOr.png"
142
+
143
+ downloadOver = new Image()
144
+ downloadOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/DownloadOr.png"
145
+
146
+ supportOver = new Image()
147
+ supportOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/SupportOr.png"
148
+
149
+ originalOver = new Image()
150
+ originalOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/TunesOr.png"
151
+
152
+
153
+
154
+
155
+ function original() {
156
+ document.emp.src = originalOver.src; return true;
157
+ }
158
+
159
+
160
+ function apple() {
161
+ document.emp.src = appleOver.src; return true;
162
+ }
163
+
164
+ function store() {
165
+ document.emp.src = storeOver.src; return true;
166
+ }
167
+
168
+ function mac() {
169
+ document.emp.src = macOver.src; return true;
170
+ }
171
+
172
+ function tunes() {
173
+ document.emp.src = tunesOver.src; return true;
174
+ }
175
+
176
+ function phone() {
177
+ document.emp.src = phoneOver.src; return true;
178
+ }
179
+
180
+ function download() {
181
+ document.emp.src = downloadOver.src; return true;
182
+ }
183
+
184
+ function support() {
185
+ document.emp.src = supportOver.src; return true;
186
+ }
187
+
188
+
189
+ </SCRIPT>
190
+
191
+
192
+ </head>
193
+ <body marginwidth=0 marginheight=0 topmargin=0 leftmargin=0 bgcolor="#FFFFFF" background="">
194
+
195
+ <br>
196
+ <IMG NAME="emp" SRC="/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/TunesOr.png" USEMAP="#map1" border = 0>
197
+
198
+
199
+
200
+ <MAP NAME="map1">
201
+
202
+ <AREA
203
+ HREF="http://www.apple.com/" ALT="Apple" TITLE="Apple"
204
+ SHAPE=RECT COORDS="0,0,116,190" onMouseOver="apple()" onMouseOut="original()" onClick= "appleMouseClick()">
205
+ <AREA
206
+ HREF="http://store.apple.com/" ALT="Store" TITLE="Store"
207
+ SHAPE=RECT COORDS="0,0,233,190" onMouseOver="store()" onMouseOut="original()" onClick= "zoomout1()">
208
+ <AREA
209
+ HREF="http://www.apple.com/mac/" ALT="Mac" TITLE="Mac"
210
+ SHAPE=RECT COORDS="0,0,348,190" onMouseOver="mac()" onMouseOut="original()" onClick= "zoomout()">
211
+ <AREA
212
+ HREF="http://www.apple.com/itunes/" ALT="iTunes" TITLE="iTunes"
213
+ SHAPE=RECT COORDS="0,0,465,190" onMouseOver="tunes()" onMouseOut="original()" onClick= "zoomout()">
214
+
215
+ <AREA
216
+ HREF="http://www.apple.com/iphone/" ALT="iPhone" TITLE="iPhone"
217
+ SHAPE=RECT COORDS="0,0,583,190" onMouseOver="phone()" onMouseOut="original()" onClick= "zoomout()">
218
+
219
+ <AREA
220
+ HREF="http://www.apple.com/downloads/" ALT="Downloads" TITLE="Downloads"
221
+ SHAPE=RECT COORDS="0,0,699,190" onMouseOver="download()" onMouseOut="original()" onClick= "zoomout()">
222
+
223
+ <AREA
224
+ HREF="http://www.apple.com/support/" ALT="Support" TITLE="Support"
225
+ SHAPE=RECT COORDS="0,0,817,190" onMouseOver="support()" onMouseOut="original()" onClick= "zoomout()"></MAP>
226
+ </body>
227
+ </html>
228
+
229
+
230
+
231
+
232
+ <input Id="checkAlrt" type="hidden" name="2" />
233
+ <form method="post" action="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.4">
234
+ <br>
235
+ <table>
236
+ <tr>
237
+ <td class="cellLeft">
238
+ <h2><p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="https://phobos.apple.com/images/labelconnect/txt_title_itunesvips.gif" alt="iTunes VIPs" border="0"></p></h2>
239
+ <font size = 2 color = "red"><p align = "center"> </p></font><br>
240
+ <div id="main"><table cellspacing="0" cellpadding="1" width="100%" border="0">
241
+ <tr>
242
+ <td></td>
243
+ <td align="rignt">
244
+ <div id="jobHeader">Sign In</div></td></tr><tr><td valign="top" width="50%"><hr><br><font size=2.5><b>Welcome!</b></font><font size=2>This site will allow you to view and/or download daily and weekly iTunes sales data. <br><br>If you have any issues, please contact us by email at &nbsp;</font><font size=2.5><b><a href= mailto:itunesondemand@apple.com>itunesondemand@apple.com<br><br></a></b><p>Please note: The information you are accessing on this site is market trend reports, and should not be considered to be your monthly royalty reports.<br></p><font><p></p><hr></td><td valign="top">
245
+ <div id="DSSecurityList">
246
+ <table>
247
+ <tr>
248
+ <td colspan="3">
249
+ <font size="1" color=red><a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.4.18"></a></font>
250
+ </td>
251
+ </tr>
252
+ <tr>
253
+ <td colspan="3">
254
+
255
+ <script>
256
+ function dsfocus(){ document.appleConnectForm.theAccountName.focus(); }
257
+ </script>
258
+
259
+
260
+
261
+
262
+ <!-- This FORM is embedded inside another FORM. Omitting Open Tag -->
263
+
264
+ <div id="ds_container">
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+ <font face="Lucida Grande, Geneva, Verdana, Arial, Helvetica, sans-serif" CLASS="L12" />
275
+ <font size=2>
276
+ <span class="dstext">Please enter your Apple ID and password.</span>
277
+ </font>
278
+ </font /><br><br>
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+ <table cellpadding=0 cellspacing=0 border=0 width=273>
290
+ <tr valign=top>
291
+ <td align=left><font size=2><font face="Geneva, Verdana, Arial, Helvetica, sans-serif" SIZE=1 CLASS="G10" />
292
+ <label for="accountname"><span class="dslabel">Apple ID</span></label>
293
+ <font color="#ff1102"></font></font /><br>
294
+ <font size='2'><input size="30" maxlength="128" id="accountname" type="text" name="theAccountName" /></font></font>
295
+
296
+ </td>
297
+ </tr>
298
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
299
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
300
+ <tr>
301
+ <td align='left'>
302
+ <font face="Geneva, Verdana, Arial, Helvetica, sans-serif" SIZE=1 CLASS="G10" />
303
+ <label for="accountpassword"><span class="dslabel">Password</span></label>
304
+ </font />
305
+ <br>
306
+ <input size="30" maxlength="32" id="accountpassword" type="password" name="theAccountPW" /><input border="0" width="0" height="0" type="image" name="1.Continue" src="/AppleConnect/US-EN/spacer.gif" />
307
+ </td>
308
+ </tr>
309
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
310
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
311
+ <tr>
312
+ <td>
313
+ <table cellspacing='0' border='0' width='273' cellpadding='0'>
314
+ <tr align='left'>
315
+ <td align='left' width='170'>
316
+
317
+
318
+
319
+ <font size='1'>
320
+ <span class="dstext">
321
+
322
+
323
+
324
+
325
+
326
+
327
+ <input border="0" alt="Did you forget your password?" type="image" name="1.Forgot" src="/AppleConnect/US-EN/forgotpasswdlink.gif" />
328
+
329
+
330
+ </span>
331
+ </font>
332
+
333
+ </td>
334
+ <td width='15'><img alt="" width="15" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td>
335
+ <td align='left' width='88'><input border="0" alt="Sign In" type="image" name="1.Continue" src="/AppleConnect/US-EN/buttonloginlime.gif" /></td>
336
+ </tr>
337
+ </table>
338
+ </td>
339
+ </tr>
340
+ </table>
341
+
342
+
343
+
344
+
345
+
346
+ <input type="hidden" name="theAuxValue" />
347
+ </div>
348
+ <input type="hidden" name="wosid" value="9tkH3ucxG921Y1uBDoPAIM" />
349
+ <!-- This FORM is embedded inside another FORM. Omitting Close Tag -->
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+
359
+ <p></p><br><br></td>
360
+ </tr>
361
+ </table>
362
+ </td></tr></table></div><div class="sidebar"></div><div class="clear"></div><input type="hidden" name="wosid" value="9tkH3ucxG921Y1uBDoPAIM" /></form><br> <div class="footerLinks" align="center"><a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.6.1">Home</a> <!--| <a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.6.3">FAQs</a> -->| <a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.6.5">Sign Out</a></div>
363
+ <div class="footerCopyright" align="center">Copyright &#169; 2008 Apple Inc. All rights reserved. </div><br/>
364
+ </body>
365
+ </html>