WM Graph API Interface (Beta Branch)

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

As of 2014-07-27. See the latest version.

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