Greasy Fork 支持简体中文。

WM Graph API Interface (Beta Branch)

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

目前為 2014-09-04 提交的版本,檢視 最新版本

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