WM Graph API Interface (Beta Branch)

Creates a low-access, read-only interface to the FB Graph API.

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greatest.deepsurf.us/scripts/420/26698/WM%20Graph%20API%20Interface%20%28Beta%20Branch%29.js

  1. // ==UserScript==
  2. // @name WM Graph API Interface (Beta Branch)
  3. // @namespace MerricksdadGraphInterface
  4. // @description Creates a low-access, read-only interface to the FB Graph API.
  5. // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
  6. // @version 3.2.3
  7. // @copyright Charlie Ewing except where noted
  8. // ==/UserScript==
  9.  
  10. //this script requires some functions in the WM Common Library
  11. //this script needs access to a pre-defined JSON object
  12.  
  13. var workOffline=false;
  14.  
  15. (function(){
  16. this.Graph = {
  17. posts: {}, //location to store adjusted post data
  18. authRequestOut: false, //dont ask for token while asking for token
  19. authToken: (isChrome||getOpt("disableSaveAuthToken"))?null:getOpt("lastAuthToken"), //use stored fb token
  20. userID: null,
  21. userAlias: null,
  22. userName: null,
  23. fetchTimeout: 30,
  24.  
  25. requests: [],
  26. likePost: function(postID,params){try{
  27. //https://graph.facebook.com/v1.0/POST_ID/likes?access_token=
  28. var req; req=GM_xmlhttpRequest({
  29. method: "POST",
  30. url: "https://graph.facebook.com/v1.0/"+postID+"/likes?access_token="+Graph.authToken,
  31. timeout: Graph.fetchTimeout*1000,
  32. onload: function(response) {try{
  33. if (response.responseText=="true") {
  34. if (params.callback) params.callback(params.post);
  35. } else {
  36. log(response.responseText);
  37. }
  38. }catch(e){log("Graph.likePost.onload: "+e);}},
  39. onerror: function(response) {try{
  40. }catch(e){log("Graph.likePost.onerror: "+e);}},
  41. onabort: function(response) {try{
  42. }catch(e){log("Graph.likePost.onabort: "+e);}},
  43. ontimeout: function(response) {try{
  44. }catch(e){log("Graph.likePost.ontimeout: "+e);}}
  45. });
  46. }catch(e){log("Graph.likePost: "+e);}},
  47.  
  48. unlikePost: function(postID){try{
  49. //https://graph.facebook.com/POST_ID/likes?access_token=
  50. var req; req=GM_xmlhttpRequest({
  51. method: "DELETE",
  52. url: "https://graph.facebook.com/v1.0/"+postID+"/likes?access_token="+Graph.authToken,
  53. timeout: Graph.fetchTimeout*1000,
  54. onload: function(response) {try{
  55. }catch(e){log("Graph.unlikePost.onload: "+e);}},
  56. onerror: function(response) {try{
  57. }catch(e){log("Graph.unlikePost.onerror: "+e);}},
  58. onabort: function(response) {try{
  59. }catch(e){log("Graph.unlikePost.onabort: "+e);}},
  60. ontimeout: function(response) {try{
  61. }catch(e){log("Graph.unlikePost.ontimeout: "+e);}}
  62. });
  63. }catch(e){log("Graph.unlikePost: "+e);}},
  64.  
  65. commentPost: function(postID,comment){try{
  66. //https://graph.facebook.com/POST_ID/comments?message=&access_token=
  67. var req; req=GM_xmlhttpRequest({
  68. method: "POST",
  69. url: "https://graph.facebook.com/v1.0/"+postID+"/comments?access_token="+Graph.authToken+"&message="+comment,
  70. timeout: Graph.fetchTimeout*1000,
  71. onload: function(response) {try{
  72. if (response.responseText=="true") {
  73. //comment successful
  74. } else {
  75. log(response.responseText);
  76. }
  77. }catch(e){log("Graph.commentPost.onload: "+e);}},
  78. onerror: function(response) {try{
  79. }catch(e){log("Graph.commentPost.onerror: "+e);}},
  80. onabort: function(response) {try{
  81. }catch(e){log("Graph.commentPost.onabort: "+e);}},
  82. ontimeout: function(response) {try{
  83. }catch(e){log("Graph.commentPost.ontimeout: "+e);}}
  84. });
  85. }catch(e){log("Graph.commentPost: "+e);}},
  86. requestAuthCodeB: function(callback){try{
  87. log("Graph.requestAuthCodeB()");
  88. if (Graph.authRequestOut) return {requestAlreadyOut:true}; //dont ask again while asking
  89. Graph.authRequestOut = true;
  90. var req; req=GM_xmlhttpRequest({
  91. method: "GET",
  92. url: "http://developers.facebook.com/tools/explorer?method=GET&path=me%3Ffields%3Did%2Cname&version=v1.0",
  93. timeout: Graph.fetchTimeout*1000,
  94. onload: function(response) {try{
  95. var test=response.responseText;
  96. var auth=test.longestQuoteWithin(matchFunc_OnlyAlphaNumeric);
  97. if (auth!="") {
  98. Graph.authToken = auth;
  99. log("Graph.requestAuthCodeB: got token");
  100. //log("TOKEN: " + auth);
  101. setOpt("lastAuthToken",auth);
  102. } else {
  103. log("Graph.requestAuthCodeB: "+response.responseText,{level:3});
  104. }
  105. Graph.authRequestOut=false;
  106. if (callback) setTimeout(callback,0);
  107. if(req)req=null;
  108. }catch(e){log("Graph.requestAuthCodeB.onload: "+e);}},
  109. onerror: function(response) {try{
  110. Graph.authToken="";
  111. Graph.authRequestOut=false;
  112. log("Graph.requestAuthCodeB: error:"+response.responseText+"\n Trying again in 30 seconds.",{level:3});
  113. setTimeout(function(){Graph.requestAuthCodeB(callback);},30000);
  114. if(req)req=null;
  115. }catch(e){log("Graph.requestAuthCodeB.onerror: "+e);}},
  116. onabort: function(response) {try{
  117. Graph.authToken="";
  118. Graph.authRequestOut=false;
  119. log("Graph.requestAuthCodeB: Request aborted, trying again in 30 seconds",{level:3});
  120. if(req)req=null;
  121. setTimeout(function(){Graph.requestAuthCodeB(callback);},30000);
  122. }catch(e){log("Graph.requestAuthCodeB.onabort: "+e);}},
  123. ontimeout: function(response) {try{
  124. Graph.authToken="";
  125. Graph.authRequestOut=false;
  126. log("Graph.requestAuthCodeB: Request timeout, trying again in 30 seconds",{level:3});
  127. if(req)req=null;
  128. setTimeout(function(){Graph.requestAuthCodeB(callback);},30000);
  129. }catch(e){log("Graph.requestAuthCodeB.ontimeout: "+e);}}
  130. });
  131. }catch(e){log("Graph.requestAuthCodeB: "+e);}},
  132. requestAuthCode: function(callback){try{
  133. log("Graph.requestAuthCode()");
  134. if (Graph.authRequestOut) return {requestAlreadyOut:true}; //dont ask again while asking
  135. Graph.authRequestOut = true;
  136. var req; req=GM_xmlhttpRequest({
  137. method: "GET",
  138. url: "http://developers.facebook.com/docs/reference/api/examples/",
  139. timeout: Graph.fetchTimeout*1000,
  140. onload: function(response) {try{
  141. var test=response.responseText;
  142. var searchString='<a href="https://graph.facebook.com/me/home?access_token=';
  143. var auth = test.indexOf(searchString),authEnd;
  144. if (auth!=-1) {
  145. authEnd = test.indexOf('">',auth);
  146. var authCode = (test.substring(auth+(searchString.length), authEnd));
  147. Graph.authToken = authCode;
  148. setOpt("lastAuthToken",authCode);
  149. log("Graph.requestAuthCode: got token");
  150. } else {
  151. log("Graph.requestAuthCode: "+response.responseText,{level:3});
  152. }
  153. Graph.authRequestOut=false;
  154. if (callback) setTimeout(callback,0);
  155. if(req)req=null;
  156. }catch(e){log("Graph.requestAuthCode.onload: "+e);}},
  157. onerror: function(response) {try{
  158. Graph.authToken="";
  159. Graph.authRequestOut=false;
  160. log("Graph.requestAuthCode: error:"+response.responseText+"\n Trying again in 30 seconds.",{level:3});
  161. setTimeout(function(){Graph.requestAuthCode(callback);},30000);
  162. if(req)req=null;
  163. }catch(e){log("Graph.requestAuthCode.onerror: "+e);}},
  164. onabort: function(response) {try{
  165. Graph.authToken="";
  166. Graph.authRequestOut=false;
  167. log("Graph.requestAuthCode: Request aborted, trying again in 30 seconds",{level:3});
  168. if(req)req=null;
  169. setTimeout(function(){Graph.requestAuthCode(callback);},30000);
  170. }catch(e){log("Graph.requestAuthCode.onabort: "+e);}},
  171. ontimeout: function(response) {try{
  172. Graph.authToken="";
  173. Graph.authRequestOut=false;
  174. log("Graph.requestAuthCode: Request timeout, trying again in 30 seconds",{level:3});
  175. if(req)req=null;
  176. setTimeout(function(){Graph.requestAuthCode(callback);},30000);
  177. }catch(e){log("Graph.requestAuthCode.ontimeout: "+e);}}
  178. });
  179. }catch(e){log("Graph.requestAuthCode: "+e);}},
  180.  
  181. fetchUser: function(params){try{
  182. log("Graph.fetchUser()");
  183. params=params || {};
  184. if (!Graph.authToken) {
  185. log("Graph.fetchUser: no authToken, get one");
  186. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  187. if (params["retries_noToken"]<3) {
  188. Graph.requestAuthCodeB(function(){Graph.fetchUser(params);} );
  189. } else {
  190. log("Graph.fetchUser: cannot get new fb auth token",{level:3});
  191. return {getAuthTokenFailed:true}
  192. }
  193. return;
  194. }
  195. var URL="https://graph.facebook.com/v1.0/me?access_token="+Graph.authToken;
  196. var req; req=GM_xmlhttpRequest({
  197. method: "GET",
  198. url: URL,
  199. timeout: Graph.fetchTimeout*1000,
  200. onload: function(response) {try{
  201. if (response){
  202. //convert to JSON
  203. try{
  204. var data = JSON.parse(response.responseText);
  205. if (data["id"]||null){
  206. //expected data exists
  207. Graph.userID=data["id"||null];
  208. Graph.userAlias=(data["username"]||null);
  209. Graph.userName=(data["name"]||null);
  210.  
  211. if (params["callback"]) {
  212. var fx=params["callback"];
  213. delete params["callback"];
  214. setTimeout(fx,0);
  215. }
  216. } else if (data["error"]||null) {
  217. var emsg=data.error.message||null;
  218. //check for session expired
  219. if (emsg.find("Session has expired")||emsg.find("session is invalid")){
  220. //session expired or logged out, get a new token
  221. Graph.authToken="";
  222. params["retries_expToken"]=(params["retries_expToken"])?params["retries_expToken"]+1:1; //count retries
  223. if (params["retries_expToken"]<3) {
  224. Graph.requestAuthCodeB(function(){Graph.fetchUser(params);} );
  225. } else log("Graph.fetchUser: cannot refresh expired fb auth token",{level:3});
  226. } else if (emsg) log("Graph.fetchUser: "+emsg,{level:3});
  227.  
  228. } else log("Graph.fetchUser: response was unrecognized",{level:3});
  229. } catch (e){log("Graph.fetchUser: response error: "+e+": "+response);}
  230.  
  231. } else log("Graph.fetchUser: response was empty",{level:3});
  232. if(req)req=null;
  233. }catch(e){log("Graph.fetchUser.onload: "+e);}},
  234.  
  235. onabort: function(response) {try{
  236. log("Graph.fetchUser: Request aborted, trying again in 30 seconds.");
  237. setTimeout(function(){Graph.fetchUser(params);},30000);
  238. if(req)req=null;
  239. }catch(e){log("Graph.fetchUser.onabort: "+e);}},
  240. ontimeout: function(response) {try{
  241. log("Graph.fetchUser: Request timeout, trying again in 30 seconds.");
  242. setTimeout(function(){Graph.fetchUser(params);},30000);
  243. if(req)req=null;
  244. }catch(e){log("Graph.fetchUser.ontimeout: "+e);}},
  245.  
  246. onerror: function(response) {try{
  247. if (response.responseText=="") {
  248. log(JSON.stringify(response));
  249. log("Graph.fetchUser: responseText was empty. Check to make sure your browser is online.", {level:5});
  250. }
  251. var data = JSON.parse(response.responseText);
  252. if (data) {if (data.error||null) {
  253. var emsg=data.error.message||null;
  254. //check for session expired
  255. if (emsg.find("Session has expired")||emsg.find("session is invalid")){
  256. //session expired or logged out, get a new token
  257. Graph.authToken="";
  258. params["retries_expToken"]=(params["retries_expToken"])?params["retries_expToken"]+1:1; //count retries
  259. if (params["retries_expToken"]<3) {
  260. Graph.requestAuthCodeB(function(){Graph.fetchUser(params);} );
  261. } else log("Graph.fetchUser: cannot refresh expired fb auth token",{level:3});
  262. } else if (emsg) log("Graph.fetchUser.onerror: "+emsg,{level:3});
  263. }} else {
  264. log("Graph.fetchUser.onerror: "+response+"\n Trying again in 30 seconds.");
  265. setTimeout(function(){Graph.fetchUser(params);},30000);
  266. if(req)req=null;
  267. }
  268. }catch(e){log("Graph.fetchUser.onerror: "+e);}}
  269. });
  270. }catch(e){log("Graph.fetchUser: "+e);}},
  271.  
  272. matchRequest: function(params){try{
  273. for (var r in Graph.requests) {
  274. var req = Graph.requests[r];
  275.  
  276. //match the feed
  277. if (JSON.stringify(req.friends) == JSON.stringify(params.friends)){
  278. //match the app filters
  279. if (JSON.stringify(req.apps) == JSON.stringify(params.apps)) {
  280. //match direction of request
  281. if (req.direction==params.direction) {
  282. return r;
  283. }
  284. }
  285. }
  286. }
  287. return -1;
  288. }catch(e){log("Graph.matchRequest: "+e);}},
  289.  
  290. validatePost: function(params){try{
  291. var post=params.post;
  292. var callback=params.callback;
  293. var isOlder=params.next;
  294.  
  295. //log("Graph.validatePost()",{level:1});
  296.  
  297. //exclude non-app posts and posts with no action links
  298. //if (!exists(post.actions||null) || !exists(post.application)) return;
  299.  
  300. //exclude posts with less than like and comment and which have no link
  301. //if (!(post.actions.length>=2) || !exists(post.link)) return;
  302. var postID=post["post_id"]||post["id"];
  303.  
  304. //exclude posts already in our repository
  305. if (exists(Graph.posts[postID])) return;
  306.  
  307. //store a reference to this post
  308. Graph.posts[postID]=1;
  309.  
  310. //send the post back to the callback function here
  311. if (callback) setTimeout(function(){callback(post,isOlder);},0);
  312. }catch(e){log("Graph.validatePost: "+e);}},
  313. fetchPostsFQL_B: function(params){try{
  314. console.log(JSON.stringify(params));
  315. if (arguments.length==0) {
  316. log("Graph.fetchPostsFQL: no parameters passed");
  317. return;
  318. }
  319. /*
  320. direction: 0=until now | 1=forward from front edge | -1=backward from back edge
  321. apps = array of app id's to fetch posts for, error on no apps passed
  322. friends = array of friend id's to fetch posts for, default all friends
  323. limit = number to fetch
  324. timeouts = number of timeouts so far when performing retry looping
  325. targetEdge = unix time to continue fetching until
  326. currentEdge = current remembered edge of feed
  327. retries_noToken = number of times this function has called getAuthToken and failed
  328. callback = function to enact on each post
  329. edgeHandler = function to keep track of edges
  330. */
  331. if (!(params.apps||null)) {
  332. log("Graph.fetchPostsFQL: no apps requested");
  333. return;
  334. }
  335. var bypassMatchRequest = (params.targetEdge||null)?true:false;
  336.  
  337. //validate current auth token
  338. if (!Graph.authToken) {
  339. log("Graph.fetchPostsFQL: no authToken, get one");
  340. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  341. if (params["retries_noToken"]<3) {
  342. Graph.requestAuthCodeB(function(){Graph.fetchPostsFQL_B(params);} );
  343. } else {
  344. log("Graph.fetchPostsFQL: cannot get new fb auth token",{level:3});
  345. return {getAuthTokenFailed:true};
  346. }
  347. return;
  348. }
  349.  
  350. //check if there is a request already out with this feed id and matches the direction
  351. if (!bypassMatchRequest) {
  352. var r=Graph.matchRequest(params);
  353. if (r!=-1){
  354. log("Graph.fetchPostsFQL: a request is already out for posts in that direction and has not returned",{level:3});
  355. return {requestAlreadyOut:true};
  356. }
  357. }
  358. //compile feed request strings
  359. var URL_prefix="https://graph.facebook.com/v1.0/fql?access_token={0}&q=";
  360. var URL="{\"query1\":\"SELECT post_id,target_id,message,app_id,action_links,created_time,attachment,app_data,like_info,source_id FROM stream WHERE source_id IN ({3}){1}{2} ORDER BY created_time DESC{4}\",\"query2\":\"SELECT uid,name FROM user WHERE uid IN (SELECT source_id FROM #query1)\"}";
  361. URL_prefix=URL_prefix.replace("{0}",Graph.authToken);
  362. //specialize call for specific friend post requests
  363. if (params.friends||null) {
  364. //URL=URL.replace("{3}",params.friends.join(",")); //use single friend passes, not multi friend queries
  365. URL=URL.replace("{3}",params.friends);
  366. } else {
  367. URL=URL.replace("{3}","SELECT uid2 FROM friend WHERE uid1=me() LIMIT 5000");
  368. }
  369.  
  370. //as of FQL 2.1, WM can no longer request data by specific friend UID
  371. //so by default we now ALWAYS just use a list of all of our friends
  372. //URL=URL.replace("{3}","SELECT uid2 FROM friend WHERE uid1=me() LIMIT 5000");
  373. //reinstated above params.friends parameter
  374. //get older posts
  375. //verify that the feed "until" time does not violate olderLimit set by user
  376. if (params.direction<0){
  377. URL=URL.replace("{2}"," AND created_time < "+params.currentEdge);
  378. //get newer posts
  379. } else if (params.direction>0){
  380. URL=URL.replace("{2}"," AND created_time > "+params.currentEdge);
  381. //fetch at current time
  382. } else {
  383. URL=URL.replace("{2}","");
  384. }
  385.  
  386. //filters by apps requested
  387. //unless no apps were passed or we specified no app filtering
  388. URL=URL.replace("{1}",""); //temp fix, no app filtering ever
  389. /*if ((params.apps||null) && !(params.noAppFiltering||null)){
  390. URL=URL.replace("{1}"," AND app_id IN ("+params.apps.join(",")+")");
  391. } else {
  392. //no app filtering, let WM do this internally
  393. URL=URL.replace("{1}","");
  394. }*/
  395. //add the user defined fetchQty
  396. URL=URL.replace("{4}","+LIMIT+"+(params.limit||5000));
  397. //encode the url
  398. URL=URL_prefix+encodeURI(URL).replace(/\:/,"%3A").replace(/\#/,"%23");
  399.  
  400. log("Graph.fetchPostsFQL: processing feed <a target='_blank' href='"+URL+"'>"+URL+"</a>");
  401. //remember this request
  402. Graph.requests.push(mergeJSON(params));
  403.  
  404. //make the request
  405. var req; req=GM_xmlhttpRequest({
  406. method: "GET",
  407. url: URL,
  408. timeout: Graph.fetchTimeout*1000,
  409. onload: function(response) {try{
  410. //show dev tools
  411. if (opts && debug && !isChrome) if (opts.devDebugGraphData) {
  412. var pkg=debug.print("Graph.fetchPostsFQL.onload.devDebugGraphData: ");
  413. pkg.msg.appendChild(createElement("button",{type:"button",onclick:function(){
  414. promptText(response.responseText);
  415. }},[
  416. createElement("img",{src:"http://i1181.photobucket.com/albums/x430/merricksdad/array.png",title:"Show Data",style:"width:16px;height:16px; vertical-align:bottom;"})
  417. ]));
  418. }
  419. //remove the memory that a request is out
  420. var r = Graph.matchRequest(params);
  421. if (r!=-1) Graph.requests.remove(r);
  422.  
  423. if (response){
  424. try{
  425. //convert to JSON
  426. var data = JSON.parse(response.responseText);
  427. //manage the return object
  428. if (exists(data.data)) {
  429. //there should be two return queries under data
  430. //zip the two together by matching the name to the source_id in the post
  431. var realData = data.data[0].fql_result_set;
  432. var nameData = data.data[1].fql_result_set;
  433. var uidKeys = {};
  434. for (var n=0,l=nameData.length;n<l;n++){
  435. uidKeys[nameData[n].uid.toString()]=nameData[n].name;
  436. }
  437.  
  438. for (var p=0,l=realData.length;p<l;p++){
  439. realData[p].fromName = (uidKeys[realData[p].source_id.toString()]||"undefined");
  440. }
  441. data.data=realData;
  442. //store posts
  443. if (data.data.length) log("Graph.fetchPostsFQL.onLoad: "+data.data.length+" posts received. Validating data...");
  444. else log("Graph.fetchPostsFQL.onLoad: facebook returned an empty data set.");
  445. //no paging exists in the FQL system, we make our own
  446. var gotMoreToDo=false;
  447. var lastPullOldestPost=null;
  448. var lastPullNewestPost=null;
  449. if (data.data.length) {
  450. lastPullOldestPost=data.data.last().created_time;
  451. lastPullNewestPost=data.data[0].created_time;
  452. }
  453. if ((params.targetEdge||null) && (data.data.length)) {
  454. gotMoreToDo = (params.direction<0)?
  455. (lastPullOldestPost > params.targetEdge): //keep fetching older
  456. (lastPullNewestPost < params.targetEdge); //keep fetching newer
  457. }
  458. //read them in backward
  459. if (data.data.length) for (var i=data.data.length-1;i>=0;i--) {
  460. var post=data.data[i];
  461.  
  462. //exclude posts already in our repository
  463. if (Graph.posts[post["post_id"]]!=1) {
  464.  
  465. //store a reference to this post
  466. Graph.posts[post["post_id"]]=1;
  467.  
  468. //send the post back to the callback function here
  469. params.callback(post);
  470. }
  471. }
  472. //process the edge handler for any request that returned posts
  473. var edgeMsg = {friends:params.friends,apps:params.apps,edge:{}};
  474. if (params.direction>=0) edgeMsg.edge.newer=lastPullNewestPost;
  475. if (params.direction<=0) edgeMsg.edge.older=lastPullOldestPost;
  476. if (data.data.length) if (params.edgeHandler||null) params.edgeHandler(edgeMsg);
  477. //go back and do another request if we have specified we have more to do
  478. //this is for use with fetchHours and similar functions
  479. if (gotMoreToDo) {
  480. log("Graph.fetchPostsFQL.onload: was not able to get enough in one return, going back for more...");
  481. var newParams = mergeJSON(params);
  482. newParams.currentEdge=(params.direction<0)?lastPullOldestPost:lastPullNewestPost;
  483. Graph.fetchPostsFQL_B(newParams);
  484. }
  485.  
  486.  
  487. } else if (data.error||null) {
  488. //check for session expired
  489. if ((data.error.message||"").find("Session has expired")){
  490. //session expired, get a new token
  491. Graph.authToken="";
  492. params["retries_expToken"]=(params["retries_expToken"])?params["retries_expToken"]+1:1; //count retries
  493. if (params["retries_expToken"]<3) {
  494. Graph.requestAuthCodeB(function(){Graph.fetchPosts(params);} );
  495. } else log("Graph.fetchPostsFQL: cannot refresh expired fb auth token",{level:3});
  496. }
  497. else if (data.error.message||null) log("Graph.fetchPostsFQL: "+data.error.message,{level:3});
  498.  
  499. } else log("Graph.fetchPostsFQL: response was unrecognized",{level:3});
  500. data=null;
  501. } catch (e){log("Graph.fetchPostsFQL: response error: "+e+": "+response);}
  502. } else log("Graph.fetchPostsFQL: response was empty",{level:3});
  503. if(req)req=null;
  504. }catch(e){log("Graph.fetchPostsFQL.onload: "+e);}},
  505.  
  506. onabort: function(response) {try{
  507. //remove the memory that a request is out
  508. var r = Graph.matchRequest(params);
  509. if (r!=-1) Graph.requests.remove(r);
  510. log("Graph.fetchPostsFQL: aborted: "+response.responseText);
  511. if(req)req=null;
  512. }catch(e){log("Graph.fetchPostsFQL.onabort: "+e);}},
  513.  
  514. ontimeout: function(response) {try{
  515. //remove the memory that a request is out
  516. params.timeouts++;
  517. var r = Graph.matchRequest(params);
  518. if (r!=-1) Graph.requests.remove(r);
  519. log("Graph.fetchPostsFQL: timeout: retry="+(params.timeouts<3)+", "+response.responseText);
  520. if(req)req=null;
  521. if (params.timeouts<3) Graph.fetchPostsFQL_B(params);
  522. }catch(e){log("Graph.fetchPostsFQL.ontimeout: "+e);}},
  523.  
  524. onerror: function(response) {try{
  525. //remove the memory that a request is out
  526. var r = Graph.matchRequest(params);
  527. if (r!=-1) Graph.requests.remove(r);
  528. log("Graph.fetchPostsFQL: error: "+response.responseText);
  529. if(req)req=null;
  530. }catch(e){log("Graph.fetchPostsFQL.onerror: "+e);}}
  531. });
  532. }catch(e){log("Graph.fetchPostsFQL_B: "+e);}},
  533. fetchPostsFQL: function(params){try{
  534. log("Graph.fetchPostsFQL("+((params.newer)?"newer":(params.older)?"older":"")+")",{level:1});
  535. params=params || {};
  536. params.timeouts=params.timeouts||0;
  537. var bypassMatchRequest = (params.range||null)?true:false;
  538. //remember the target position if this is a ranged search
  539. //we'll pass targetrange back to this function later if we need to fetch more
  540. if (params.range||null){
  541. if (params.range.oldedge||null) params.targetedge = params.range.oldedge;
  542. }
  543.  
  544. //validate auth token
  545. if (!Graph.authToken) {
  546. log("Graph.fetchPostsFQL: no authToken, get one");
  547. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  548. if (params["retries_noToken"]<3) {
  549. Graph.requestAuthCodeB(function(){Graph.fetchPostsFQL(params);} );
  550. } else {
  551. log("Graph.fetchPostsFQL: cannot get new fb auth token",{level:3});
  552. return {getAuthTokenFailed:true};
  553. }
  554. return;
  555. }
  556.  
  557. //check if there is a request already out with this fb id and matches the direction
  558. var r=Graph.matchRequest(params);
  559. if (!bypassMatchRequest) if (r!=-1){
  560. if (Graph.requests[r].older==null && Graph.requests[r].newer==null) {
  561. log("Graph.fetchPostsFQL: the initial request for data has not been returned yet",{level:3});
  562. return {initRequestSlow:true};
  563. } else {
  564. log("Graph.fetchPostsFQL: a request is already out for posts in that direction and has not returned",{level:3});
  565. return {requestAlreadyOut:true};
  566. }
  567. }
  568.  
  569. var feed=params.feed||null;
  570. var filter = (params.filter||"default");
  571. //create default filter instances when they do not exist
  572. if (params.groupApps||null) {
  573. //set our filter to our first app in the groupApps
  574. //this will be used below various times
  575. filter = "app_"+params.groupApps[0];
  576. if (feed||null) for (var a=0,l=params.groupApps.length;a<l;a++) {
  577. var filtName = "app_"+params.groupApps[a];
  578. if (!(feed.filters[filtName]||null)) feed.addFilter({id:filtName}); //create filter instance if needed
  579. }
  580. } else {
  581. if (feed||null) if (!(feed.filters[filter]||null)) feed.addFilter({id:filter}); //create filter instance if needed
  582. }
  583. //compile feed request strings
  584. var URL_prefix="https://graph.facebook.com/v1.0/fql?access_token={0}&q=";
  585. var URL="{\"query1\":\"SELECT post_id,target_id,message,app_id,action_links,created_time,attachment,app_data,like_info,source_id FROM stream WHERE source_id IN ({3}){1}{2} ORDER BY created_time DESC{4}\",\"query2\":\"SELECT uid,name FROM user WHERE uid IN (SELECT source_id FROM #query1)\"}";
  586. URL_prefix=URL_prefix.replace("{0}",Graph.authToken);
  587. //specialize call for specific friend post requests
  588. if (params.specificFriend||null) {
  589. URL=URL.replace("{3}",feed.id);
  590. } else {
  591. URL=URL.replace("{3}","SELECT uid2 FROM friend WHERE uid1=me() LIMIT 5000");
  592. }
  593. //get older posts
  594. //verify that the feed "until" time does not violate olderLimit set by user
  595. if (params.older){
  596. var edge=(params.range||null)?params.range.oldedge:feed.filters[filter].oldedge;
  597. if (edge||null){
  598. var limit=(params.limit||null); //this is not FB search limit keyword, this is a WM timelimit
  599. var timeNow=timeStamp();
  600. //no oldest post limit on range fetches
  601. if (params.range||null) limit=null;
  602. if (limit) {
  603. if ((timeNow-(edge*1000)) > limit) {
  604. log("Graph.fetchPostsFQL("+params.feed.url+"): the user-set older limit of this feed has been reached",{level:2});
  605. return {olderLimitReached:true};
  606. }
  607. }
  608. URL=URL.replace("{2}"," AND created_time < "+edge);
  609.  
  610. } else {
  611. log("Graph.fetchPostsFQL("+params.feed.url+"): The previous result did not return pagination. Restarting fetching from current time.");
  612. URL=URL.replace("{2}","");
  613. }
  614. //get newer posts
  615. } else if (params.newer){
  616. var edge=(params.range||null)?params.range.newedge:feed.filters[filter].newedge;
  617. if (exists(edge)) {
  618. URL=URL.replace("{2}"," AND created_time > "+edge);
  619. }
  620. //fetch at current time
  621. } else {
  622. URL=URL.replace("{2}","");
  623. }
  624.  
  625. //filters come in the form of "app_123456789012"
  626. URL=URL.replace("{1}","");//temp fix
  627. /*if (params.groupApps||null) {
  628. //fetch posts for multiple apps at once
  629. URL=URL.replace("{1}"," AND app_id IN ("+params.groupApps.join(",")+")");
  630. } else if (filter!=undefined && filter!=null){
  631. URL=URL.replace("{1}"," AND app_id IN ("+filter.split("app_")[1]+")");
  632. } else {
  633. //no filter, nothing passed
  634. //this should never happen
  635. URL=URL.replace("{1}","");
  636. }*/
  637. //add the user defined fetchQty
  638. URL=URL.replace("{4}","+LIMIT+"+(params.fetchQty||25));
  639. //encode the url
  640. URL=URL_prefix+encodeURI(URL).replace(/\:/,"%3A").replace(/\#/,"%23");
  641.  
  642. log("Graph.fetchPostsFQL: processing feed <a target='_blank' href='"+URL+"'>"+URL+"</a>");
  643. //console.log(URL);
  644. //return;
  645. //remember this request
  646. Graph.requests.push({feed:params.feed, older:params.older, newer:params.newer, filter:filter, groupApps:params.groupApps, specificFriend:params.specificFriend});
  647. //console.log("request pushed");
  648.  
  649. //return;
  650. var req; req=GM_xmlhttpRequest({
  651. method: "GET",
  652. url: URL,
  653. timeout: Graph.fetchTimeout*1000,
  654. onload: function(response) {try{
  655. //show dev tools
  656. if (opts && debug && !isChrome) if (opts.devDebugGraphData) {
  657. var pkg=debug.print("Graph.fetchPostsFQL.onload.devDebugGraphData: ");
  658. pkg.msg.appendChild(createElement("button",{type:"button",onclick:function(){
  659. //response.responseText.toClipboard();
  660. promptText(response.responseText);
  661. }},[
  662. createElement("img",{src:"http://i1181.photobucket.com/albums/x430/merricksdad/array.png",title:"Show Data",style:"width:16px;height:16px; vertical-align:bottom;"})
  663. ]));
  664. }
  665. //remove the memory that a request is out
  666. var r = Graph.matchRequest(params);
  667. if (r!=-1) Graph.requests.remove(r);
  668.  
  669. if (response){
  670. try{
  671. //convert to JSON
  672. var data = JSON.parse(response.responseText);
  673. //manage the return object
  674. if (exists(data.data)) {
  675. //log("response contains data");
  676. //there should be two return queries under data
  677. //zip the two together by matching the name to the source_id in the post
  678. var realData = data.data[0].fql_result_set;
  679. var nameData = data.data[1].fql_result_set;
  680. var uidKeys = {};
  681. for (var n=0,l=nameData.length;n<l;n++){
  682. uidKeys[nameData[n].uid.toString()]=nameData[n].name;
  683. }
  684.  
  685. for (var p=0,l=realData.length;p<l;p++){
  686. realData[p].fromName = (uidKeys[realData[p].source_id.toString()]||"undefined");
  687. }
  688. data.data=realData;
  689. //store posts
  690. if (data.data.length) log("Graph.fetchPostsFQL.onLoad: "+data.data.length+" posts received. Validating data...");
  691. else log("Graph.fetchPostsFQL.onLoad: facebook returned an empty data set.");
  692. //no paging exists in the FQL system, we make our own
  693. var gotMoreToDo=false;
  694. var lastPullOldestPost=null;
  695. var lastPullNewestPost=null;
  696. if (data.data.length) {
  697. lastPullOldestPost=data.data.last().created_time;
  698. lastPullNewestPost=data.data[0].created_time;
  699. }
  700. if ((params.targetedge||null) && (data.data.length)) {
  701. gotMoreToDo = (lastPullOldestPost > params.targetedge);
  702. }
  703. //read them in backward
  704. if (data.data.length) for (var i=data.data.length-1;i>=0;i--) {
  705. var post=data.data[i];
  706.  
  707. Graph.validatePost({
  708. post:post,
  709. callback:params.callback||null,
  710. older:params.older,
  711. newer:params.newer
  712. });
  713.  
  714. }
  715. //go back and do another request if we have specified we have more to do
  716. //this is for use with fetchHours and similar functions
  717. if (gotMoreToDo) {
  718. log("Graph.fetchPostsFQL.onload: was not able to get enough in one return, going back for more...");
  719. //clone the last set of params
  720. var newParams = mergeJSON(params);
  721. newParams.range={oldedge:0,newedge:0}; //new instance to prevent byRef errors
  722. //update the range settings
  723. newParams.range.newedge=lastPullOldestPost;
  724. newParams.range.oldedge=params.targetedge; //remember the original passed oldest data to target
  725. //make the next request
  726. Graph.fetchPostsFQL(newParams);
  727. }
  728. //start cleanup
  729. if (params.callback) delete params.callback;
  730.  
  731.  
  732. //capture the next and prev urls, but dont overwrite current known time boundaries
  733. if (data.data.length){
  734. if (params.groupApps||null){
  735. //manage all filters at once from the group-fetch apps array
  736. for (var n=0,l=params.groupApps.length;n<l;n++){
  737. var filtName = "app_"+params.groupApps[n];
  738. if (!params.newer && !params.older) {
  739. feed.filters[filtName].newedge = lastPullNewestPost;
  740. feed.filters[filtName].oldedge = lastPullOldestPost;
  741. }
  742. if (params.newer) feed.filters[filtName].newedge = lastPullNewestPost;
  743. if (params.older) feed.filters[filtName].oldedge = lastPullOldestPost;
  744. }
  745. } else if (filter!=undefined && filter!=null) {
  746. //if the current request was a recent posts pull, set both edges
  747. if (!params.newer && !params.older) {
  748. feed.filters[filter].newedge = lastPullNewestPost;
  749. feed.filters[filter].oldedge = lastPullOldestPost;
  750. }
  751.  
  752. //if the current request got newer posts, push the newer post edge
  753. if (params.newer) feed.filters[filter].newedge = lastPullNewestPost;
  754. //if the current request got older posts, push the older post edge
  755. if (params.older) feed.filters[filter].oldedge = lastPullOldestPost;
  756. }
  757. }
  758.  
  759. } else if (data.error||null) {
  760. //check for session expired
  761. if ((data.error.message||"").find("Session has expired")){
  762. //session expired, get a new token
  763. Graph.authToken="";
  764. params["retries_expToken"]=(params["retries_expToken"])?params["retries_expToken"]+1:1; //count retries
  765. if (params["retries_expToken"]<3) {
  766. Graph.requestAuthCodeB(function(){Graph.fetchPostsFQL(params);} );
  767. } else log("Graph.fetchPostsFQL: cannot refresh expired fb auth token",{level:3});
  768. }
  769. else if (data.error.message||null) log("Graph.fetchPostsFQL: "+data.error.message,{level:3});
  770.  
  771. } else log("Graph.fetchPostsFQL: response was unrecognized",{level:3});
  772. data=null;
  773. } catch (e){log("Graph.fetchPostsFQL: response error: "+e+": "+response);}
  774. } else log("Graph.fetchPostsFQL: response was empty",{level:3});
  775. if(req)req=null;
  776. }catch(e){log("Graph.fetchPostsFQL.onload: "+e);}},
  777.  
  778. onabort: function(response) {try{
  779. //remove the memory that a request is out
  780. var r = Graph.matchRequest(params);
  781. if (r!=-1) Graph.requests.remove(r);
  782. log("Graph.fetchPostsFQL: aborted: "+response.responseText);
  783. if(req)req=null;
  784. }catch(e){log("Graph.fetchPostsFQL.onabort: "+e);}},
  785.  
  786. ontimeout: function(response) {try{
  787. //remove the memory that a request is out
  788. params.timeouts++;
  789. var r = Graph.matchRequest(params);
  790. if (r!=-1) Graph.requests.remove(r);
  791. log("Graph.fetchPostsFQL: timeout: retry="+(params.timeouts<3)+", "+response.responseText);
  792. if(req)req=null;
  793. if (params.timeouts<3) Graph.fetchPostsFQL(params);
  794. }catch(e){log("Graph.fetchPostsFQL.ontimeout: "+e);}},
  795.  
  796. onerror: function(response) {try{
  797. //remove the memory that a request is out
  798. var r = Graph.matchRequest(params);
  799. if (r!=-1) Graph.requests.remove(r);
  800. log("Graph.fetchPostsFQL: error: "+response.responseText);
  801. if(req)req=null;
  802. }catch(e){log("Graph.fetchPostsFQL.onerror: "+e);}}
  803. });
  804. }catch(e){log("Graph.fetchPostsFQL: "+e);}},
  805. /* fetchPosts details:
  806. params = {
  807. feed:<feed reference>,
  808. filter:<appID>,
  809. next:<url containing 'until'>,
  810. prev:<url containing 'since'>,
  811. callback:<where to ship the return data>,
  812. retries_noToken:<counter>,
  813. fetchQty:<number>,
  814. specific:<specific range object>
  815. }
  816. */
  817. fetchPosts: function(params){try{
  818. log("Graph.fetchPosts is deprecated. Use Graph.fetchPostsFQL.");
  819. return Graph.fetchPostsFQL(params);
  820. log("Graph.fetchPosts()",{level:1});
  821. params=params || {};
  822. params.timeouts=params.timeouts||0;
  823. var bypassMatchRequest = (params.range||null)?true:false;
  824. //remember the target position if this is a ranged search
  825. //the very first call we make is a "since" call, but all sequential calls are "until" calls due to FB's stupid pagination methods
  826. if (params.range||null){
  827. //log(params.range.since);
  828. if (params.range.since||null) params.targetUntil = params.range.since;
  829. }
  830. if (!Graph.authToken) {
  831. log("Graph.fetchPosts: no authToken, get one");
  832. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  833. if (params["retries_noToken"]<3) {
  834. Graph.requestAuthCodeB(function(){Graph.fetchPosts(params);} );
  835. } else {
  836. log("Graph.fetchPosts: cannot get new fb auth token",{level:3});
  837. return {getAuthTokenFailed:true};
  838. }
  839. return;
  840. }
  841.  
  842. //check if there is a request already out with this fb id and matches the direction
  843. var r=Graph.matchRequest(params);
  844. if (!bypassMatchRequest) if (r!=-1){
  845. if (Graph.requests[r].next==null && Graph.requests[r].prev==null) {
  846. log("Graph.fetchPosts: the initial request for data has not been returned yet",{level:3});
  847. return {initRequestSlow:true};
  848. } else {
  849. log("Graph.fetchPosts: a request is already out for posts in that direction and has not returned",{level:3});
  850. return {requestAlreadyOut:true};
  851. }
  852. }
  853.  
  854. //for each user specified feed source, get posts
  855. var feed=params.feed||null;
  856. var filter = (params.filter||"default");
  857. if (!(feed.filters[filter]||null)) feed.addFilter({id:filter}); //create filter instance if needed
  858.  
  859. var URL=feed.url+"?date_format=U&limit="+((params.range||null)?250:params.fetchQty)+"&access_token="+Graph.authToken;
  860. //get older posts
  861. //verify that the feed "until" time does not violate olderLimit set by user
  862. if (params.next || ((params.range||null)?params.range.until||null:null) ){
  863. var until=(params.range||null)?params.range.until:feed.filters[filter].next.getUrlParam("until");
  864. //debug.print(["var until",until]);
  865. if (until||null){
  866. var limit=(params.limit||null); //this is not FB search limit keyword, this is a WM timelimit
  867. var timeNow=timeStamp();
  868. //no oldest post limit on range fetches
  869. if (params.range||null) limit=null;
  870. var fixTime = (until.length < 10)?(until+"000"):until;
  871.  
  872. //debug.print(["var until:",until, until.length, fixTime])
  873. if (limit) {
  874. if ((timeNow-(fixTime)) > limit) {
  875. //log("Graph.fetchPosts("+params.feed.url+"): the user-set older limit of this feed has been reached",{level:2});
  876. return {olderLimitReached:true};
  877. }
  878. }
  879. URL+="&until="+fixTime;
  880. } else {
  881. log("Graph.fetchPosts("+params.feed.url+"): The previous result did not return pagination. Restarting fetching from current time.");
  882. }
  883. }
  884. //get newer posts
  885. //rules manager action fetchHours will be asking for a range staring at time X, so use range.since
  886. else if (params.prev || ((params.range||null)?params.range.since||null:null) ) {
  887. var since=(params.range||null)?params.range.since:feed.filters[filter].prev.getUrlParam("since");
  888. if (exists(since)) {
  889. URL+="&since="+since;
  890. }
  891. }
  892.  
  893. //add a filter if there is one
  894. if (exists(params.filter)) URL+="&filter="+filter; //check using params.filter, do not use filter here or it may inject "default"
  895.  
  896. log("Graph.fetchPosts: processing feed <a target='_blank' href='"+URL+"'>"+URL+"</a>");
  897. //remember this request
  898. Graph.requests.push({feed:params.feed, next:params.next, prev:params.prev, filter:filter});
  899.  
  900. var req; req=GM_xmlhttpRequest({
  901. method: "GET",
  902. url: URL,
  903. timeout: Graph.fetchTimeout*1000,
  904. onload: function(response) {try{
  905. //show dev tools
  906. if (opts && debug && !isChrome) if (opts.devDebugGraphData) {
  907. var pkg=debug.print("Graph.fetchPosts.onload.devDebugGraphData: ");
  908. pkg.msg.appendChild(createElement("button",{type:"button",onclick:function(){
  909. //response.responseText.toClipboard();
  910. promptText(response.responseText);
  911. }},[
  912. createElement("img",{src:"http://i1181.photobucket.com/albums/x430/merricksdad/array.png",title:"Show Data",style:"width:16px;height:16px; vertical-align:bottom;"})
  913. ]));
  914. }
  915. //remove the memory that a request is out
  916. var r = Graph.matchRequest(params);
  917. if (r!=-1) Graph.requests.remove(r);
  918.  
  919. if (response){
  920. try{
  921. //convert to JSON
  922. var data = JSON.parse(response.responseText);
  923. //add new posts to graph.posts
  924. if (exists(data.data)) {
  925. //log("response contains data");
  926.  
  927. //alert(JSON.stringify(data.data));
  928. //store posts
  929. if (data.data.length) log("Graph.fetchPosts.onLoad: "+data.data.length+" posts received. Validating data...");
  930. else log("Graph.fetchPosts.onLoad: facebook returned an empty data set.");
  931. var gotMoreToDo=false;
  932. if ((params.targetUntil||null) && (data.data.length) && (data.paging.next)) {
  933. var lastPullOldestPost=data.paging.next.getUrlParam("until");
  934. //2013/9/7: known facebook limit maximum is 500, but we are fetching in 250's
  935. //have we maxed out AND is oldest returned post newer than what we asked for
  936. gotMoreToDo = (data.data.length>=250) && (lastPullOldestPost > params.targetUntil);
  937. }
  938. if (data.data.length) for (var i=data.data.length-1;i>=0;i--) {
  939. var post=data.data[i];
  940.  
  941. Graph.validatePost({
  942. post:post,
  943. callback:params.callback||null,
  944. next:params.next
  945. });
  946.  
  947. }
  948. if (gotMoreToDo) {
  949. log("Graph.fetchPosts.onload: was not able to get enough in one return, going back for more...");
  950. //clone the last set of params
  951. var newParams = mergeJSON(params);
  952. newParams.range={since:0,until:0}; //new instance to prevent byRef errors
  953. //update the range settings
  954. //newParams.range.since=data.paging.previous.getUrlParam("since");
  955. newParams.range.until=data.paging.next.getUrlParam("until");
  956. //log([params.range.since,newParams.range.since,newParams.range.until,timeStampNoMS()]);
  957. newParams.targetUntil = params.range.since; //remember the original passed oldest data to target
  958. //make the next request
  959. Graph.fetchPosts(newParams);
  960. }
  961. //start cleanup
  962. if (params.callback) delete params.callback;
  963.  
  964.  
  965. //capture the next and prev urls, but dont overwrite current known time boundaries
  966. if (data.paging||null){
  967. //if this is the first time we've used this object, remember its locations
  968. if (!feed.filters[filter].next) feed.filters[filter].next = data.paging.next;
  969. if (!feed.filters[filter].prev) feed.filters[filter].prev = data.paging.prev;
  970.  
  971. //if the current request did not get older posts, push the newer post bracket
  972. if (!params.prev) feed.filters[filter].next = data.paging.next;
  973. //if the current request did not get newer posts, push the older post bracket
  974. if (!params.next) feed.filters[filter].prev = data.paging.previous;
  975. } else {
  976. log("Graph.fetchPosts.onLoad: facebook failed to return pagination data.")
  977. }
  978.  
  979. } else if (data.error||null) {
  980. //check for session expired
  981. if ((data.error.message||"").find("Session has expired")){
  982. //session expired, get a new token
  983. Graph.authToken="";
  984. params["retries_expToken"]=(params["retries_expToken"])?params["retries_expToken"]+1:1; //count retries
  985. if (params["retries_expToken"]<3) {
  986. Graph.requestAuthCodeB(function(){Graph.fetchPosts(params);} );
  987. } else log("Graph.fetchPosts: cannot refresh expired fb auth token",{level:3});
  988. }
  989. else if (data.error.message||null) log("Graph.fetchPosts: "+data.error.message,{level:3});
  990.  
  991. } else log("Graph.fetchPosts: response was unrecognized",{level:3});
  992. data=null;
  993. } catch (e){log("Graph.fetchPosts: response error: "+e+": "+response);}
  994. } else log("Graph.fetchPosts: response was empty",{level:3});
  995. if(req)req=null;
  996. }catch(e){log("Graph.fetchPosts.onload: "+e);}},
  997.  
  998. onabort: function(response) {try{
  999. //remove the memory that a request is out
  1000. var r = Graph.matchRequest(params);
  1001. if (r!=-1) Graph.requests.remove(r);
  1002. log("Graph.fetchPosts: aborted: "+response.responseText);
  1003. if(req)req=null;
  1004. }catch(e){log("Graph.fetchPosts.onabort: "+e);}},
  1005.  
  1006. ontimeout: function(response) {try{
  1007. //remove the memory that a request is out
  1008. params.timeouts++;
  1009. var r = Graph.matchRequest(params);
  1010. if (r!=-1) Graph.requests.remove(r);
  1011. log("Graph.fetchPosts: timeout: retry="+(params.timeouts<3)+", "+response.responseText);
  1012. if(req)req=null;
  1013. if (params.timeouts<3) Graph.fetchPosts(params);
  1014. }catch(e){log("Graph.fetchPosts.ontimeout: "+e);}},
  1015.  
  1016. onerror: function(response) {try{
  1017. //remove the memory that a request is out
  1018. var r = Graph.matchRequest(params);
  1019. if (r!=-1) Graph.requests.remove(r);
  1020. log("Graph.fetchPosts: error: "+response.responseText);
  1021. if(req)req=null;
  1022. }catch(e){log("Graph.fetchPosts.onerror: "+e);}}
  1023. });
  1024. }catch(e){log("Graph.fetchPosts: "+e);}},
  1025. };
  1026.  
  1027. this.Graph2 = {
  1028. postData : {},
  1029. currentUser : {
  1030. authToken : null,
  1031. name : null,
  1032. id : null,
  1033. alias : null
  1034. },
  1035. friends : {},
  1036. friendLists : {},
  1037. groups : [],
  1038.  
  1039. fetchTimeout: 30,
  1040. getGraphExplorerAuthToken : function(callback){try{
  1041. //log("Graph.requestAuthCode()");
  1042. var req; req=GM_xmlhttpRequest({
  1043. method: "GET",
  1044. url: "http://developers.facebook.com/docs/reference/api/examples/",
  1045. timeout: Graph2.fetchTimeout*1000,
  1046. onload: function(response) {try{
  1047. var test=response.responseText;
  1048. var searchString='<a href="https://graph.facebook.com/v1.0/me/home?access_token=';
  1049. var auth = test.indexOf(searchString),authEnd;
  1050. if (auth!=-1) {
  1051. authEnd = test.indexOf('">',auth);
  1052. var authCode = (test.substring(auth+(searchString.length), authEnd));
  1053. Graph2.currentUser.authToken = authCode;
  1054. log("Graph2.getGraphExplorerAuthToken: got token");
  1055. } else {
  1056. log("Graph2.getGraphExplorerAuthToken: "+response.responseText,{level:3});
  1057. }
  1058. if (callback) setTimeout(callback,0);
  1059. }catch(e){log("Graph2.getGraphExplorerAuthToken.onload: "+e);}},
  1060. onerror: function(response) {try{
  1061. Graph2.currentUser.authToken="";
  1062. log("Graph2.getGraphExplorerAuthToken: error:"+response.responseText+"\n.",{level:3});
  1063. }catch(e){log("Graph2.getGraphExplorerAuthToken.onerror: "+e);}},
  1064. onabort: function(response) {try{
  1065. Graph2.currentUser.authToken="";
  1066. log("Graph2.getGraphExplorerAuthToken: Request aborted",{level:3});
  1067. }catch(e){log("Graph2.getGraphExplorerAuthToken.onabort: "+e);}},
  1068. ontimeout: function(response) {try{
  1069. Graph2.currentUser.authToken="";
  1070. log("Graph.getGraphExplorerAuthToken: Request timeout",{level:3});
  1071. }catch(e){log("Graph2.getGraphExplorerAuthToken.ontimeout: "+e);}}
  1072. });
  1073. }catch(e){log("Graph2.getGraphExplorerAuthToken: "+e);}},
  1074. getAuthToken: function (callback){try{
  1075. getGraphExplorerAuthToken(callback);
  1076. }catch(e){log("Graph.getAuthToken: "+e);}},
  1077.  
  1078. likePost: function(params){try{
  1079. /*
  1080. postID
  1081. callback
  1082. post
  1083. responseText <--passback
  1084. */
  1085. var req; req=GM_xmlhttpRequest({
  1086. method: "POST",
  1087. url: "https://graph.facebook.com/v1.0/"+params.postID+"/likes?access_token="+Graph2.currentUser.authToken,
  1088. timeout: Graph2.fetchTimeout*1000,
  1089. onload: function(response) {try{
  1090. params.responseText = response.responseText
  1091. if (response.responseText=="true") {
  1092. if (params.callback) params.callback(params.post);
  1093. } else {
  1094. //log(response.responseText);
  1095. //hande in whatever called this function
  1096. }
  1097. }catch(e){log("Graph2.likePost.onload: "+e);}},
  1098. onerror: function(response) {try{
  1099. params.responseText = response.responseText
  1100. }catch(e){log("Graph2.likePost.onerror: "+e);}},
  1101. onabort: function(response) {try{
  1102. params.responseText = response.responseText
  1103. }catch(e){log("Graph2.likePost.onabort: "+e);}},
  1104. ontimeout: function(response) {try{
  1105. params.responseText = response.responseText
  1106. }catch(e){log("Graph2.likePost.ontimeout: "+e);}}
  1107. });
  1108. }catch(e){log("Graph2.likePost: "+e);}},
  1109.  
  1110. unlikePost: function(params){try{
  1111. /*
  1112. postID
  1113. callback
  1114. post
  1115. responseText <--passback
  1116. */
  1117. var req; req=GM_xmlhttpRequest({
  1118. method: "DELETE",
  1119. url: "https://graph.facebook.com/v1.0/"+params.postID+"/likes?access_token="+Graph2.currentUser.authToken,
  1120. timeout: Graph2.fetchTimeout*1000,
  1121. onload: function(response) {try{
  1122. params.responseText = response.responseText
  1123. if (params.callback) params.callback(params.post);
  1124. }catch(e){log("Graph2.unlikePost.onload: "+e);}},
  1125. onerror: function(response) {try{
  1126. params.responseText = response.responseText
  1127. }catch(e){log("Graph2.unlikePost.onerror: "+e);}},
  1128. onabort: function(response) {try{
  1129. params.responseText = response.responseText
  1130. }catch(e){log("Graph2.unlikePost.onabort: "+e);}},
  1131. ontimeout: function(response) {try{
  1132. params.responseText = response.responseText
  1133. }catch(e){log("Graph2.unlikePost.ontimeout: "+e);}}
  1134. });
  1135. }catch(e){log("Graph2.unlikePost: "+e);}},
  1136.  
  1137. commentPost: function(params){try{
  1138. /*
  1139. postID
  1140. callback
  1141. post
  1142. comment
  1143. responseText <--passback
  1144. */
  1145. var req; req=GM_xmlhttpRequest({
  1146. method: "POST",
  1147. url: "https://graph.facebook.com/v1.0/"+params.postID+"/comments?access_token="+Graph2.currentUser.authToken+"&message="+params.comment,
  1148. timeout: Graph2.fetchTimeout*1000,
  1149. onload: function(response) {try{
  1150. params.responseText = response.responseText
  1151. if (response.responseText=="true") {
  1152. if (params.callback) params.callback(params.post);
  1153. } else {
  1154. //log(response.responseText);
  1155. }
  1156. }catch(e){log("Graph2.commentPost.onload: "+e);}},
  1157. onerror: function(response) {try{
  1158. params.responseText = response.responseText
  1159. }catch(e){log("Graph2.commentPost.onerror: "+e);}},
  1160. onabort: function(response) {try{
  1161. params.responseText = response.responseText
  1162. }catch(e){log("Graph2.commentPost.onabort: "+e);}},
  1163. ontimeout: function(response) {try{
  1164. params.responseText = response.responseText
  1165. }catch(e){log("Graph2.commentPost.ontimeout: "+e);}}
  1166. });
  1167. }catch(e){log("Graph2.commentPost: "+e);}},
  1168. getCurrentUser : function(params){try{
  1169. /*
  1170. retires_noToken <-- counter for retries without successfully finding the token
  1171. responseText <--passback
  1172. */
  1173. Graph2.getUser({
  1174. userID:"me",
  1175. callback:function(){Graph2.procCurrentUser(params);}
  1176. });
  1177. }catch(e){log("Graph2.getCurrentUser: "+e);}},
  1178. procCurrentUser : function(params){try{
  1179. /*
  1180. responseText
  1181. */
  1182. var data = JSON.parse(params.responseText);
  1183. if (data["id"]||null){
  1184. //expected data exists
  1185. Graph2.currentUser.id=data["id"||null];
  1186. //Graph2.currentUser.alias=(data["username"]||null);
  1187. Graph2.currentUser.name=(data["name"]||null);
  1188. Graph2.currentUser.timezone=(data["timezone"]||null);
  1189. Graph2.currentUser.locale=(data["locale"]||null);
  1190.  
  1191. if (params.callback) setTimeout(function(){params.callback(Graph2.currentUser);},0);
  1192. } else if (data["error"]||null) {
  1193. var emsg=data.error.message||null;
  1194. //check for session expired
  1195. if (emsg.find("Session has expired")||emsg.find("session is invalid")){
  1196. //session expired or logged out, get a new token
  1197. Graph2.currentUser.authToken="";
  1198. params["retries_expToken"]=(params["retries_expToken"])?params["retries_expToken"]+1:1; //count retries
  1199. if (params["retries_expToken"]<3) {
  1200. setTimeout(function(){Graph2.getAuthToken({callback:Graph2.getCurrentUser});},0);
  1201. } else log("Graph2.procCurrentUser: cannot refresh expired fb auth token",{level:3});
  1202. } else if (emsg) log("Graph2.procCurrentUser: "+emsg,{level:3});
  1203.  
  1204. } else log("Graph2.procCurrentUser: response was unrecognized",{level:3});
  1205. }catch(e){log("Graph2.procCurrentUser: "+e);}},
  1206. getUser : function(params){try{
  1207. /*
  1208. userID <-- "me" for current user
  1209. callback
  1210. retires_noToken <-- counter for retries without successfully finding the token
  1211. responseText <--passback
  1212. */
  1213. //log("Graph.getUser()");
  1214. params=params || {};
  1215. if (!Graph2.currentUser.authToken) {
  1216. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  1217. if (params["retries_noToken"]<3) {
  1218. Graph2.getAuthToken(function(){Graph2.getUser(params);} );
  1219. } else {
  1220. log("Graph2.getUser: cannot get new fb auth token",{level:3});
  1221. return;
  1222. }
  1223. return;
  1224. }
  1225. var URL="https://graph.facebook.com/v1.0/"+params.userID+"?access_token="+Graph2.currentUser.authToken;
  1226. var req; req=GM_xmlhttpRequest({
  1227. method: "GET",
  1228. url: URL,
  1229. timeout: Graph2.fetchTimeout*1000,
  1230. onload: function(response) {try{
  1231. if (response){
  1232. params.responseText = response.responseText;
  1233. } else log("Graph2.getUser: response was empty",{level:3});
  1234. }catch(e){log("Graph2.getUser.onload: "+e);}},
  1235.  
  1236. onabort: function(response) {try{
  1237. params.responseText = response.responseText;
  1238. log("Graph2.getUser: Request aborted");
  1239. }catch(e){log("Graph2.getUser.onabort: "+e);}},
  1240. ontimeout: function(response) {try{
  1241. params.responseText = response.responseText;
  1242. log("Graph2.getUser: Request timeout");
  1243. }catch(e){log("Graph2.getUser.ontimeout: "+e);}},
  1244.  
  1245. onerror: function(response) {try{
  1246. params.responseText = response.responseText;
  1247. if (response.responseText=="") {
  1248. log("Graph2.getUser: responseText was empty. Check to make sure your browser is online.", {level:5});
  1249. } else {
  1250. log("Graph2.getUser: Request error");
  1251. }
  1252. }catch(e){log("Graph2.getUser.onerror: "+e);}}
  1253. });
  1254. }catch(e){log("Graph2.getUser: "+e);}},
  1255. getPost : function() {
  1256. },
  1257. getPosts : function() {
  1258. },
  1259. getIsFriend : function(userId) {
  1260. return ((Graph2.friends[userId]||null)!=null);
  1261. },
  1262. getFriends : function(params) {try{
  1263. params=params || {};
  1264. if (!Graph2.currentUser.authToken) {
  1265. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  1266. if (params["retries_noToken"]<3) {
  1267. Graph2.getAuthToken(function(){Graph2.getFriends(params);} );
  1268. } else {
  1269. log("Graph2.getFriends: cannot get new fb auth token",{level:3});
  1270. return;
  1271. }
  1272. return;
  1273. }
  1274. var URL="https://graph.facebook.com/v1.0/me/friends?limit=5000&access_token="+Graph2.currentUser.authToken;
  1275. var req; req=GM_xmlhttpRequest({
  1276. method: "GET",
  1277. url: URL,
  1278. timeout: Graph2.fetchTimeout*1000,
  1279. onload: function(response) {try{
  1280. if (response){
  1281. params.responseText = response.responseText;
  1282. setTimeout(function(){Graph2.procFriends(params);},0);
  1283. } else log("Graph2.getFriends: response was empty",{level:3});
  1284. }catch(e){log("Graph2.getFriends.onload: "+e);}},
  1285.  
  1286. onabort: function(response) {try{
  1287. params.responseText = response.responseText;
  1288. log("Graph2.getFriends: Request aborted");
  1289. }catch(e){log("Graph2.getFriends.onabort: "+e);}},
  1290. ontimeout: function(response) {try{
  1291. params.responseText = response.responseText;
  1292. log("Graph2.getFriends: Request timeout");
  1293. }catch(e){log("Graph2.getFriends.ontimeout: "+e);}},
  1294.  
  1295. onerror: function(response) {try{
  1296. params.responseText = response.responseText;
  1297. log("Graph2.getFriends: Request error");
  1298. }catch(e){log("Graph2.getFriends.onerror: "+e);}}
  1299. });
  1300. }catch(e){log("Graph2.getFriends: "+e);}},
  1301.  
  1302. procFriends : function(params){try{
  1303. /*
  1304. responseText
  1305. */
  1306. var data = JSON.parse(params.responseText);
  1307. if (data["data"]||null){
  1308.  
  1309. //index friend user data
  1310. for (var user in data.data) {
  1311. Graph2.friends[user.id] = user;
  1312. }
  1313. if (params.callback) setTimeout(function(){params.callback(Graph2.friends);},0);
  1314. } else log("Graph2.procFriends: response was unrecognized",{level:3});
  1315. }catch(e){log("Graph2.procFriends: "+e);}},
  1316. getIsUserInFriendList : function() {
  1317. },
  1318. getFriendLists : function(params) {try{
  1319. params=params || {};
  1320. if (!Graph2.currentUser.authToken) {
  1321. params["retries_noToken"]=(params["retries_noToken"])?params["retries_noToken"]+1:1; //count retries
  1322. if (params["retries_noToken"]<3) {
  1323. Graph2.getAuthToken(function(){Graph2.getFriendLists(params);} );
  1324. } else {
  1325. log("Graph2.getFriendLists: cannot get new fb auth token",{level:3});
  1326. return;
  1327. }
  1328. return;
  1329. }
  1330. var URL="https://graph.facebook.com/v1.0/me/friendlists?limit=5000&access_token="+Graph2.currentUser.authToken;
  1331. var req; req=GM_xmlhttpRequest({
  1332. method: "GET",
  1333. url: URL,
  1334. timeout: Graph2.fetchTimeout*1000,
  1335. onload: function(response) {try{
  1336. if (response){
  1337. params.responseText = response.responseText;
  1338. setTimeout(function(){Graph2.procFriend=Lists(params);},0);
  1339. } else log("Graph2.getFriendLists: response was empty",{level:3});
  1340. }catch(e){log("Graph2.getFriendLists.onload: "+e);}},
  1341.  
  1342. onabort: function(response) {try{
  1343. params.responseText = response.responseText;
  1344. log("Graph2.getFriendLists: Request aborted");
  1345. }catch(e){log("Graph2.getFriendLists.onabort: "+e);}},
  1346. ontimeout: function(response) {try{
  1347. params.responseText = response.responseText;
  1348. log("Graph2.getFriendLists: Request timeout");
  1349. }catch(e){log("Graph2.getFriendLists.ontimeout: "+e);}},
  1350.  
  1351. onerror: function(response) {try{
  1352. params.responseText = response.responseText;
  1353. log("Graph2.getFriendLists: Request error");
  1354. }catch(e){log("Graph2.getFriendLists.onerror: "+e);}}
  1355. });
  1356. }catch(e){log("Graph2.getFriendLists: "+e);}},
  1357. procFriendLists : function(params){try{
  1358. /*
  1359. responseText
  1360. */
  1361. var data = JSON.parse(params.responseText);
  1362. if (data["data"]||null){
  1363.  
  1364. //index friend user data
  1365. for (var list in data.data) {
  1366. Graph2.friendLists[list.id] = list;
  1367. }
  1368. if (params.callback) setTimeout(function(){params.callback(Graph2.friendLists);},0);
  1369. } else log("Graph2.procFriendLists: response was unrecognized",{level:3});
  1370. }catch(e){log("Graph2.procFriendLists: "+e);}}
  1371. }
  1372. log("Graph initialized");
  1373. })();