summaryrefslogtreecommitdiffabout
path: root/content/flickr.js
Unidiff
Diffstat (limited to 'content/flickr.js') (more/less context) (ignore whitespace changes)
-rw-r--r--content/flickr.js403
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
5function Photoset(s) {
6 if(s instanceof Photoset) {
7 for(var p in s) this[p]=s[p];
8 }else
9 this.fromNode(s);
10}
11Photoset.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 */
34function Photo(s) {
35 if(s instanceof Photo) {
36 for(var p in s) this[p]=s[p];
37 }else
38 this.fromNode(s);
39}
40Photo.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
92function 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}
131function xp_str(xp,x) {
132 var rv = x.evaluate(
133 xp, x, null, XPathResult.STRING_TYPE, null );
134 return rv.stringValue;
135}
136function 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
142function Flickr() { }
143Flickr.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};