-rw-r--r-- | content/flickr.js | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/content/flickr.js b/content/flickr.js new file mode 100644 index 0000000..3554796 --- a/dev/null +++ b/content/flickr.js | |||
@@ -0,0 +1,403 @@ | |||
1 | /* | ||
2 | * Photoset | ||
3 | */ | ||
4 | |||
5 | function Photoset(s) { | ||
6 | if(s instanceof Photoset) { | ||
7 | for(var p in s) this[p]=s[p]; | ||
8 | }else | ||
9 | this.fromNode(s); | ||
10 | } | ||
11 | Photoset.prototype = { | ||
12 | id: null, | ||
13 | primary: null, | ||
14 | secret: null, | ||
15 | server: null, | ||
16 | photos: null, | ||
17 | title: null, | ||
18 | description: null, | ||
19 | fromNode: function(n) { | ||
20 | this.id = n.getAttribute('id'); | ||
21 | this.primary = n.getAttribute('primary'); | ||
22 | this.secret = n.getAttribute('secret'); | ||
23 | this.server = n.getAttribute('server'); | ||
24 | this.photos = n.getAttribute('photos'); | ||
25 | this.title = n.getElementsByTagName('title').item(0).firstChild.nodeValue; | ||
26 | this.description = n.getElementsByTagName('description').item(0).firstChild; | ||
27 | if(this.description) this.description = this.description.nodeValue; | ||
28 | } | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * Photo | ||
33 | */ | ||
34 | function Photo(s) { | ||
35 | if(s instanceof Photo) { | ||
36 | for(var p in s) this[p]=s[p]; | ||
37 | }else | ||
38 | this.fromNode(s); | ||
39 | } | ||
40 | Photo.prototype = { | ||
41 | id: null, secret: null, | ||
42 | server: null, | ||
43 | title: null, | ||
44 | isprimary: null, | ||
45 | license: null, | ||
46 | dateupload: null, datetaken: null, datetakengranularity: null, | ||
47 | ownername: null, | ||
48 | iconserver: null, | ||
49 | originalformat: null, | ||
50 | lastupdate: null, | ||
51 | fromNode: function(n) { | ||
52 | this.id = n.getAttribute('id'); this.secret = n.getAttribute('secret'); | ||
53 | this.server = n.getAttribute('server'); | ||
54 | this.title = n.getAttribute('title'); | ||
55 | this.isprimary = n.getAttribute('isprimary'); | ||
56 | this.license = n.getAttribute('license'); | ||
57 | this.dateupload = n.getAttribute('dateupload'); | ||
58 | this.datetaken = n.getAttribute('datetaken'); this.datetakengranularity = n.getAttribute('datetakengranularity'); | ||
59 | this.ownername = n.getAttribute('ownername'); | ||
60 | this.iconserver = n.getAttribute('iconserver'); | ||
61 | this.originalformat = n.getAttribute('originalformat'); | ||
62 | this.lastupdate = n.getAttribute('lastupdate'); | ||
63 | }, | ||
64 | fromNode_: function(n) { | ||
65 | var t; | ||
66 | // TODO: @rotation @isfavorite | ||
67 | this.owner = {}; | ||
68 | t = n.getElementsByTagName('owner').item(0); | ||
69 | if(t) { | ||
70 | this.owner.nsid=t.getAttribute('nsid'); | ||
71 | this.owner.username=t.getAttribute('username'); | ||
72 | this.owner.realname=t.getAttribute('realname'); | ||
73 | this.owner.location=t.getAttribute.location; | ||
74 | } | ||
75 | t = n.getElementsByTagName('description').item(0); | ||
76 | if(t && t.firstChild) { | ||
77 | this.description = t.firstChild.nodeValue; | ||
78 | } | ||
79 | // TODO: visibility/@ispublic visibility/@isfriend visibility/@isfamily | ||
80 | // TODO: dates/@posted dates/@taken dates/@takengranularity dates/@lastupdate | ||
81 | // TODO: permissions/@permcomment permsiions/@permaddmeta | ||
82 | // TODO: editability/@canaddcomment editability/@canaddmeta | ||
83 | // TODO: comments | ||
84 | // TODO: notes/note/@id notes/note/@author notes/note/@authorname | ||
85 | // TODO: notes/note/@x notes/note/@y notes/note/@w notes/note/@h | ||
86 | // TODO: notes/note | ||
87 | // TODO: tags/tag/@id tags/tag/@author tags/tag/@raw tags/tag | ||
88 | // TODO: urls/url/@type urls/url | ||
89 | } | ||
90 | }; | ||
91 | |||
92 | function toutf8(ucode) { | ||
93 | var rv = ''; | ||
94 | for(var i=0;i<ucode.length;++i) { | ||
95 | var cc = ucode.charCodeAt(i); | ||
96 | if(cc<=0x7F) | ||
97 | rv += ucode.charAt(i); | ||
98 | else if(cc<=0x7ff) | ||
99 | rv += String.fromCharCode( | ||
100 | 0xc0|((cc>> 6)&0x1f), | ||
101 | 0x80|( cc &0x3f) ); | ||
102 | else if(cc<=0xffff) | ||
103 | rv += String.fromCharCode( | ||
104 | 0xe0|((cc>>12)&0x0f), | ||
105 | 0x80|((cc>> 6)&0x3f), | ||
106 | 0x80|( cc &0x3f) ); | ||
107 | else if(cc<=0x1fffff) | ||
108 | rv += String.fromCharCode( | ||
109 | 0xf0|((cc>>18)&0x07), | ||
110 | 0x80|((cc>>12)&0x3f), | ||
111 | 0x80|((cc>> 6)&0x3f), | ||
112 | 0x80|( cc &0x3f) ); | ||
113 | else if(cc<=0x03ffffff) | ||
114 | rv += String.fromCharCode( | ||
115 | 0xf8|((cc>>24)&0x03), | ||
116 | 0x80|((cc>>18)&0x3f), | ||
117 | 0x80|((cc>>12)&0x3f), | ||
118 | 0x80|((cc>> 6)&0x3f), | ||
119 | 0x80|( cc &0x3f) ); | ||
120 | else if(cc<=0x7fffffff) | ||
121 | rv += String.fromCharCode( | ||
122 | 0xfc|((cc>>30)&0x01), | ||
123 | 0x80|((cc>>24)&0x3f), | ||
124 | 0x80|((cc>>18)&0x3f), | ||
125 | 0x80|((cc>>12)&0x3f), | ||
126 | 0x80|((cc>> 6)&0x3f), | ||
127 | 0x80|( cc &0x3f) ); | ||
128 | } | ||
129 | return rv; | ||
130 | } | ||
131 | function xp_str(xp,x) { | ||
132 | var rv = x.evaluate( | ||
133 | xp, x, null, XPathResult.STRING_TYPE, null ); | ||
134 | return rv.stringValue; | ||
135 | } | ||
136 | function xp_node(xp,x) { | ||
137 | var rv = x.evaluate( | ||
138 | xp, x, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); | ||
139 | return rv.singleNodeValue; | ||
140 | } | ||
141 | |||
142 | function Flickr() { } | ||
143 | Flickr.prototype = { | ||
144 | |||
145 | rest_url: 'http://www.flickr.com/services/rest/', | ||
146 | auth_url: 'http://flickr.com/services/auth/', | ||
147 | photo_url: 'http://static.flickr.com/', | ||
148 | photos_url: 'http://www.flickr.com/photos/', | ||
149 | upload_url: 'http://www.flickr.com/services/upload/', | ||
150 | |||
151 | api_sig: function(paramstr) { | ||
152 | return MD5(toutf8(this.api_shs+paramstr)); | ||
153 | }, | ||
154 | api_call_url: function(params,url) { | ||
155 | params.api_key = this.api_key; | ||
156 | var pp = new Array(); | ||
157 | for(var p in params) { | ||
158 | pp.push(p); | ||
159 | } | ||
160 | var pstr = ''; | ||
161 | var rv = (url?url:this.rest_url)+'?'; | ||
162 | for(var p in pp.sort()) { | ||
163 | var pn = pp[p]; | ||
164 | pstr += pn+params[pn]; | ||
165 | rv += pn+'='+params[pn]+'&'; | ||
166 | } | ||
167 | rv += 'api_sig='+this.api_sig(pstr); | ||
168 | return rv; | ||
169 | }, | ||
170 | api_call: function(params, on_success, on_failure) { | ||
171 | if(params.auth_token == 'default') | ||
172 | params.auth_token = this.token; | ||
173 | var x = new XMLHttpRequest(); | ||
174 | x.open("GET",this.api_call_url(params)); | ||
175 | x.onreadystatechange=function() { | ||
176 | if(x.readyState!=4) return false; | ||
177 | if(x.status==200) { | ||
178 | var stat = x.responseXML.firstChild.getAttribute('stat'); | ||
179 | if(stat=='ok') { | ||
180 | if(on_success) on_success(x); | ||
181 | }else{ | ||
182 | var e = x.responseXML.getElementsByTagName('err').item(0); | ||
183 | var ecode = e.getAttribute('code'); | ||
184 | var emsg = e.getAttribute('msg'); | ||
185 | dump(params.method+' failed: '+ecode+' '+emsg+'\n'); | ||
186 | if(on_failure) on_failure(x,stat,ecode,emsg); | ||
187 | } | ||
188 | }else{ | ||
189 | if(on_failure) on_failure(x); | ||
190 | } | ||
191 | return true; | ||
192 | } | ||
193 | x.send(null); | ||
194 | return true; | ||
195 | }, | ||
196 | |||
197 | frob: null, | ||
198 | authorize_0: function(on_s, on_f) { | ||
199 | var _this = this; | ||
200 | this.api_call( | ||
201 | { method: 'flickr.auth.getFrob' }, | ||
202 | function(x) { | ||
203 | _this.frob = xp_str('/rsp/frob',x.responseXML); | ||
204 | var u = _this.api_call_url( | ||
205 | { frob: _this.frob, perms: 'delete' }, _this.auth_url ); | ||
206 | var wm = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService( | ||
207 | Components.interfaces.nsIWindowMediator ); | ||
208 | var bw = wm.getMostRecentWindow('navigator:browser'); | ||
209 | var b = bw.getBrowser(); | ||
210 | var t = b.addTab(u); | ||
211 | b.selectedTab = t; | ||
212 | if(on_s) on_s(); | ||
213 | }, function(x,s,c,m) { | ||
214 | if(on_f) on_f(x,s,c,m); | ||
215 | } | ||
216 | ); | ||
217 | }, | ||
218 | token: null, | ||
219 | perms: null, | ||
220 | user: null, | ||
221 | authorize_1: function(on_s, on_f) { | ||
222 | var _this = this; | ||
223 | this.api_call( | ||
224 | { method: 'flickr.auth.getToken', frob: this.frob }, | ||
225 | function(x) { | ||
226 | _this.token = xp_str('/rsp/auth/token',x.responseXML); | ||
227 | _this.perms = xp_str('/rsp/auth/perms',x.responseXML); | ||
228 | var u = xp_node('/rsp/auth/user',x.responseXML); | ||
229 | _this.user = { | ||
230 | nsid: u.getAttribute('nsid'), | ||
231 | username: u.getAttribute('username'), | ||
232 | fullname: u.getAttribute('fullname') | ||
233 | }; | ||
234 | if(on_s) on_s(x); | ||
235 | }, function(x,s,c,m) { | ||
236 | if(on_f) on_f(x,s,c,m); | ||
237 | } | ||
238 | ); | ||
239 | }, | ||
240 | |||
241 | prefs: Components.classes['@mozilla.org/preferences-service;1'].getService( | ||
242 | Components.interfaces.nsIPrefBranch | ||
243 | ), | ||
244 | prefs_root: 'net.klever.kin.flickr', | ||
245 | save_token: function() { | ||
246 | // TODO: don't clear when there's nothing to clear or catch exceptions | ||
247 | if(this.token) | ||
248 | this.prefs.setCharPref(this.prefs_root+'.auth_token',this.token); | ||
249 | else | ||
250 | this.prefs.clearUserPref(this.prefs_root+'.auth_token'); | ||
251 | if(this.perms) | ||
252 | this.prefs.setCharPref(this.prefs_root+'.auth_perms',this.perms); | ||
253 | else | ||
254 | this.prefs.clearUserPref(this.prefs_root+'.auth_perms'); | ||
255 | if(this.user && this.user.nsid!=null && this.user.nsid!=undefined) | ||
256 | this.prefs.setCharPref(this.prefs_root+'.auth_user.nsid',this.user.nsid); | ||
257 | else | ||
258 | this.prefs.clearUserPref(this.prefs_root+'.auth_user.nsid'); | ||
259 | if(this.user && this.user.username!=null && this.user.username!=undefined) | ||
260 | this.prefs.setCharPref(this.prefs_root+'.auth_user.username',this.user.username); | ||
261 | else | ||
262 | this.prefs.clearUserPref(this.prefs_root+'.auth_user.username'); | ||
263 | if(this.user && this.user.fullname!=null && this.user.fullname!=undefined) | ||
264 | this.prefs.setCharPref(this.prefs_root+'.auth_user.fullname',this.user.fullname); | ||
265 | else | ||
266 | this.prefs.clearUserPref(this.prefs_root+'.auth_user.fullname'); | ||
267 | }, | ||
268 | _reset_token: function() { | ||
269 | this.token = null; this.perms = null; this.user = null; | ||
270 | return false; | ||
271 | }, | ||
272 | load_token: function() { | ||
273 | try { | ||
274 | if(this.prefs.getPrefType(this.prefs_root+'.auth_token')!=this.prefs.PREF_STRING) | ||
275 | return this._reset_token(); | ||
276 | this.token = this.prefs.getCharPref(this.prefs_root+'.auth_token'); | ||
277 | if(this.prefs.getPrefType(this.prefs_root+'.auth_perms')!=this.prefs.PREF_STRING) | ||
278 | return this._reset_token(); | ||
279 | this.perms = this.prefs.getCharPref(this.prefs_root+'.auth_perms'); | ||
280 | if(this.prefs.getPrefType(this.prefs_root+'.auth_user.nsid')!=this.prefs.PREF_STRING) | ||
281 | return this._reset_token(); | ||
282 | this.user = new Object(); | ||
283 | this.user.nsid = this.prefs.getCharPref(this.prefs_root+'.auth_user.nsid'); | ||
284 | if(this.prefs.getPrefType(this.prefs_root+'.auth_user.username')!=this.prefs.PREF_STRING) | ||
285 | return this._reset_token(); | ||
286 | this.user.username = this.prefs.getCharPref(this.prefs_root+'.auth_user.username'); | ||
287 | if(this.prefs.getPrefType(this.prefs_root+'.auth_user.fullname')!=this.prefs.PREF_STRING) | ||
288 | return this._reset_token(); | ||
289 | this.user.fullname = this.prefs.getCharPref(this.prefs_root+'.auth_user.fullname'); | ||
290 | }catch(e) { return this._reset_token(); } | ||
291 | return true; | ||
292 | }, | ||
293 | reset_token: function() { | ||
294 | this._reset_token(); | ||
295 | this.save_token(); | ||
296 | }, | ||
297 | |||
298 | get_photo_url: function(ser,id,sec,sfx,ext) { | ||
299 | var rv = this.photo_url + ser + '/' + id + '_' + sec; | ||
300 | if(sfx && sfx!='_') rv += '_'+sfx; | ||
301 | rv += ext?'.'+ext:'.jpg'; | ||
302 | return rv; | ||
303 | }, | ||
304 | get_image_url: function(o,sfx) { | ||
305 | return this.get_photo_url( | ||
306 | o.server, | ||
307 | (o instanceof Photoset)? o.primary : o.id, | ||
308 | o.secret, | ||
309 | sfx, | ||
310 | (sfx=='o')?o.originalformat:null | ||
311 | ); | ||
312 | }, | ||
313 | get_photo_page_url: function(p) { | ||
314 | if(p instanceof Photo) // TODO: half wrong, what if no owner? | ||
315 | return this.photos_url + (p.owner.nsid?p.owner.nsid:this.user.nsid) + '/' + p.id; | ||
316 | else // TODO: take owner into account? | ||
317 | return this.photos_url + this.user.nsid + '/' + p; | ||
318 | }, | ||
319 | make_photo_url: function(p,sfx) { | ||
320 | if(sfx=='p') | ||
321 | return this.get_photo_page_url(p); | ||
322 | else | ||
323 | return this.get_image_url(p,sfx); | ||
324 | }, | ||
325 | |||
326 | upload_file: function(f,fa,on_success,on_failure) { | ||
327 | try { | ||
328 | var fi = Components.classes["@mozilla.org/file/local;1"] | ||
329 | .createInstance(Components.interfaces.nsILocalFile); | ||
330 | fi.initWithPath( f ); | ||
331 | var st = Components.classes["@mozilla.org/network/file-input-stream;1"] | ||
332 | .createInstance(Components.interfaces.nsIFileInputStream); | ||
333 | st.init(fi,0x01,00004,null); | ||
334 | var bis = Components.classes["@mozilla.org/binaryinputstream;1"] | ||
335 | .createInstance(Components.interfaces.nsIBinaryInputStream); | ||
336 | bis.setInputStream(st); | ||
337 | |||
338 | // allocate and initialize temp storage string | ||
339 | var pbs = Components.classes["@mozilla.org/storagestream;1"] | ||
340 | .createInstance(Components.interfaces.nsIStorageStream); | ||
341 | pbs.init(1024,10000000,null); | ||
342 | // create output stream | ||
343 | var pbos = pbs.getOutputStream(0); | ||
344 | // and a binaryoutputstream interface | ||
345 | var pbbos = Components.classes["@mozilla.org/binaryoutputstream;1"] | ||
346 | .createInstance(Components.interfaces.nsIBinaryOutputStream); | ||
347 | pbbos.setOutputStream(pbos); | ||
348 | |||
349 | /* create POST body */ | ||
350 | var boundarytoken = 'kadaroloongazaduviaxamma'; | ||
351 | var boundary = '--'+boundarytoken; | ||
352 | var b = ''; | ||
353 | |||
354 | var parms = { api_key: this.api_key, auth_token: this.token }; | ||
355 | for(var p in fa) parms[p] = fa[p]; | ||
356 | var pns = new Array(); | ||
357 | for(var p in parms) pns.push(p); | ||
358 | var pstr = ''; | ||
359 | for(var p in pns.sort()) { | ||
360 | var pn = pns[p]; | ||
361 | pstr += pn+parms[pn]; | ||
362 | b += boundary+'\nContent-Disposition: form-data; name="'+pn+'"\n\n'+toutf8(parms[pn])+'\n'; | ||
363 | } | ||
364 | b += boundary+'\nContent-Disposition: form-data; name="api_sig"\n\n'+this.api_sig(pstr)+'\n'; | ||
365 | b += boundary+'\nContent-Disposition: form-data; name="photo"; filename="'+f+'"\nContent-Type: image/jpeg\nContent-Transfer-Encoding: binary\n\n'; | ||
366 | pbbos.writeBytes(b,b.length); | ||
367 | var bisbytes = bis.available(); | ||
368 | pbbos.writeBytes(bis.readBytes(bisbytes),bisbytes); | ||
369 | pbbos.writeBytes('\n'+boundary+'--',3+boundary.length); bis.close(); st.close(); | ||
370 | |||
371 | pbbos.close(); pbos.close(); | ||
372 | |||
373 | var x = new XMLHttpRequest(); | ||
374 | x.open("POST",this.upload_url); | ||
375 | x.setRequestHeader('Content-Type', 'multipart/form-data; boundary="'+boundarytoken+'"'); | ||
376 | x.setRequestHeader('Connection','close'); | ||
377 | x.setRequestHeader('Content-Length',b.length); | ||
378 | x.onreadystatechange=function() { | ||
379 | if(x.readyState!=4) return false; | ||
380 | if(x.status==200) { | ||
381 | var stat = x.responseXML.firstChild.getAttribute('stat'); | ||
382 | if(stat=='ok') { | ||
383 | var pid = xp_str('/rsp/photoid',x.responseXML); | ||
384 | if(on_success) on_success(x,pid); | ||
385 | }else{ | ||
386 | var e = x.responseXML.getElementsByTagName('err').item(0); | ||
387 | var ecode = e.getAttribute('code'); | ||
388 | var emsg = e.getAttribute('msg'); | ||
389 | dump('upload failed: '+ecode+' '+emsg+'\n'); | ||
390 | if(on_failure) on_failure(x,stat,ecode,emsg); | ||
391 | } | ||
392 | }else{ | ||
393 | if(on_failure) on_failure(x); | ||
394 | } | ||
395 | return true; | ||
396 | }; | ||
397 | x.send(pbs.newInputStream(0)); | ||
398 | }catch(e) { | ||
399 | if(on_failure) on_failure(e,null,-1,e.message); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | }; | ||