Greasy Fork is available in English.

WM Host Object

This is the host object which is created under the WM version 4.x script

As of 09.12.2014. See ბოლო ვერსია.

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greatest.deepsurf.us/scripts/6908/27576/WM%20Host%20Object.js.

  1. // ==UserScript==
  2. // @name WM Host Object
  3. // @namespace MerricksdadWMHostObject
  4. // @description This is the host object which is created under the WM version 4.x script
  5. // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
  6. // @version 4.0.0.0
  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.  
  14. (function(){
  15.  
  16. this.WallManager={
  17. paused : false,
  18. fetchPaused : false,
  19. requestsOpen : 0,
  20. reqTO : 30000, //request timeout default
  21. newSidekicks : [],
  22.  
  23. accDefaultText : "Got this!",
  24. failText : "Oh no! Sorry pardner!",
  25. overLimitText : "Limit reached!",
  26.  
  27. version:"4.0.0.0",
  28. currentUser:{
  29. id:"",
  30. profile:"",
  31. alias:""
  32. },
  33. resources:{
  34. iconsURL:GM_getResourceURL("IconSheet")
  35. },
  36. apps:{},
  37. posts:{},
  38. history:{},
  39. config:null,
  40. opts:{},
  41. quickOpts:{},
  42. displayGroups:{},
  43. likeQueue:[],
  44. switches:{
  45. manualAuthToken:true
  46. },
  47.  
  48. statusText : {
  49. "20":"Sidekick returned force accept",
  50. "3":"Marked as accepted by user",
  51. "2":"Responseless Collection",
  52. "1":"Accepted",
  53. "0":"Unknown",
  54. "-1":"Failed",
  55. "-2":"None Left",
  56. "-3":"Over Limit (App)",
  57. "-4":"Over Limit, Sent One Anyway",
  58. "-5":"Server Error",
  59. "-6":"Already Got",
  60. "-7":"Server Down For Repairs",
  61. "-8":"Problem Getting Passback Link",
  62. "-9":"Final Request Returned Null Page",
  63. "-10":"Final Request Failure",
  64. "-11":"Expired",
  65. "-12":"Not a Neighbor",
  66. "-13":"Requirements not met",
  67. "-14":"Timeout",
  68. "-15":"Unrecognized Response",
  69. "-16":"Passback Link is missing",
  70. "-17":"Window Missing",
  71. "-18":"Marked as failed by user",
  72. "-20":"Sidekick returned force fail",
  73. "-19":"Over Limit (Bonus Type)",
  74. "-21":"Cancelled mid-process by user",
  75. },
  76. sortGroups : function(params){
  77. params=params||{};
  78. params.direction=(WM.quickOpts.groupDirection=(params.direction||WM.quickOpts.groupDirection||"desc")); //default descending to keep time ordered posts in order newest to oldest
  79. WM.saveQuickOpts();
  80. //reorder the groups
  81. var groupsArray=[];
  82. for (var g in WM.displayGroups) {
  83. groupsArray.push({id:g,node:WM.displayGroups[g].parentNode,box:WM.displayGroups[g]});
  84. }
  85. if (["asc","ascending"].inArray(params.direction.toLowerCase())) groupsArray.sort(function(a,b){return a.id>b.id;});
  86. else if (["desc","descending"].inArray(params.direction.toLowerCase())) groupsArray.sort(function(a,b){return a.id<b.id;});
  87. WM.displayGroups={};
  88. for (var g=0; g<groupsArray.length; g++) {
  89. WM.displayGroups[groupsArray[g].id]=groupsArray[g].box;
  90. WM.console.feedNode.appendChild(groupsArray[g].node);
  91. }
  92. },
  93.  
  94. newGroup : function(params){
  95. params=params||{};
  96.  
  97. //prevent duplicates
  98. if (WM.displayGroups[params.by]||null) return WM.displayGroups[params.by];
  99. //create the nodes
  100. var box;
  101. var group=createElement("div",{className:"listItem"},[
  102. createElement("div",{className:"line", onclick:function(){
  103. //toggle rollout
  104. with (this.nextSibling) className=className.swapWordB((className.containsWord("collapsed")),"expanded","collapsed");
  105. with (this.firstChild.firstChild) className=className.swapWordB((className.containsWord("treeCollapse"+WM.opts.littleButtonSize)),"treeExpand"+WM.opts.littleButtonSize,"treeCollapse"+WM.opts.littleButtonSize);
  106. }},[
  107. createElement("div",{className:"littleButton",title:"Toggle Content"},[
  108. createElement("img",{className:"resourceIcon treeCollapse"+WM.opts.littleButtonSize}),
  109. ]),
  110. createElement("label",{textContent:params.label||params.by})
  111. ]),
  112. box=createElement("div",{className:"subsection rollout expanded"}),
  113. ]);
  114. //add it to our group list
  115. WM.displayGroups[params.by]=box;
  116. WM.sortGroups();
  117. return box;
  118. },
  119. pauseCollecting : function(doPause){
  120. var isPaused;
  121. if (exists(doPause)) isPaused = (WM.paused = doPause);
  122. else isPaused=(WM.paused = !WM.paused);
  123. var btn=WM.console.pauseCollectButton;
  124. btn.className = btn.className.swapWordB(isPaused,"oddGreen","oddOrange");
  125. btn.title = (isPaused)?"Start Automatic Collection":"Pause Automatic Collection";
  126. var img = btn.childNodes[0];
  127. img.className = img.className.swapWordB(isPaused,"playRight24","stop24");
  128. },
  129.  
  130. pauseFetching : function(doPause){
  131. var isPaused;
  132. if (exists(doPause)) isPaused = (WM.fetchPaused = doPause);
  133. else isPaused=(WM.fetchPaused = !WM.fetchPaused);
  134. var btn=WM.console.pauseFetchButton;
  135. btn.className = btn.className.swapWordB(isPaused,"oddGreen","oddOrange");
  136. btn.title = (isPaused)?"Start Automatic Fetching":"Pause Automatic Fetching";
  137. },
  138.  
  139. clearGroups : function(params){
  140. //destroy previous groups
  141. for (var g in WM.displayGroups){
  142. remove(WM.displayGroups[g].parentNode); //kill the node
  143. delete WM.displayGroups[g]; //remove from list
  144. }
  145. },
  146.  
  147. clearPosts : function(){
  148. //remove all post nodes from the collector panel
  149. for (var p in WM.posts){
  150. if (WM.posts[p].node) remove(WM.posts[p].node);
  151. }
  152. },
  153. constructGroups : function(params){
  154. params=params||{};
  155. //this specifically allows a null so we can remove grouping
  156. var by=exists(params.by)?params.by:WM.quickOpts.groupBy;
  157. //if nothing changed, just cancel
  158. if (by==WM.quickOpts.groupBy) return;
  159. //set the new group order
  160. WM.quickOpts.groupBy=by;
  161. WM.saveQuickOpts();
  162. WM.clearGroups();
  163. },
  164. sortPosts : function(params){
  165. params=params||{};
  166. params.direction=(WM.quickOpts.sortDirection=(params.direction||WM.quickOpts.sortDirection||"desc")); //default descending to keep time ordered posts in order newest to oldest
  167. params.by=(WM.quickOpts.sortBy=(exists(params.by)?params.by:(WM.quickOpts.sortBy||"created_time"))); //default by date
  168. WM.saveQuickOpts();
  169.  
  170. //convert to array
  171. var postsArray=methodsToArray(WM.posts);
  172.  
  173. //sort
  174. postsArray.sort(function(a,b){
  175. if (["ascending","asc"].inArray(params.direction.toLowerCase())) return a[params.by]>b[params.by];
  176. if (["descending","desc"].inArray(params.direction.toLowerCase())) return a[params.by]<b[params.by];
  177. });
  178.  
  179. //convert back to object
  180. WM.posts=arrayToMethods(postsArray);
  181. },
  182.  
  183. doWhichTestTree : function(post, testList, testData, custom) {try{
  184. //match post to an app
  185. var app=post.app;
  186. var synApp=app.synApp, w=null;
  187.  
  188. for (var i=0,test;((test=testList[i]) && (w===null));i++) {
  189.  
  190. //run only for tests that are not specifically disabled
  191. if (test.enabled===false) continue;
  192. //set find mode
  193. var findMode="auto";
  194. //finish constructing dynamic collection tests
  195. var ret = test.ret;
  196. if (custom) {
  197. if (!ret) ret = "dynamic"; //default to dynamic
  198. if (ret!="dynamic" && ret!="none" && ret!="exclude" && !ret.startsWith(synApp.appID)) ret=synApp.appID+ret; //add appID except to magic words
  199. findMode=test.findMode;
  200. }
  201.  
  202. //part to make dynamic collection tests work only if they are the correct appID
  203. //also do not process disabled tests
  204. if (!custom || (custom && (!test.appID || (app.appID==test.appID)))){
  205.  
  206. //if the test is not disabled (by test enabled both existing and being false)
  207. //OR if the test IS a dynamic test and the appID matches
  208. //OR if the test IS a dynamic test and no appID was supplied
  209. //then run the test
  210.  
  211. //detect test type
  212. var testType=(test.search||null);
  213. var types=WM.grabber.methods;
  214. if (!testType) for (var tt=0,len=types.length; tt<len; tt++) {if (test[types[tt]]||"") {testType=types[tt];break;}}
  215.  
  216. //select the type of data to use
  217. var src="";
  218. if (isArray(testType)){ //new search array format
  219. for (var t=0,tlen=testType.length;t<tlen;t++) src+=(testData[testType[t]]||"");
  220. }
  221. else src = (testData[testType]||""); //old test method like testType:text
  222.  
  223. if (src){
  224. //begin processing this test
  225. var subTests=test.subTests, kids=test.kids, allowNone=false, subNumRange=test.subNumRange,text=(test.find||test[testType]||"");
  226.  
  227. //process subtests array
  228. if (subTests && (findMode=="auto" || findMode=="subtests") && text) {
  229. for (var i2=0,subTest,found=false;((subTest=subTests[i2]) && (!found));i2++) {
  230. var testX = text.replace('{%1}',subTest).toLowerCase();
  231.  
  232. //do a standard test with the replaced search string
  233. found=src.find(testX);
  234.  
  235. //return a found value, replacing %1 with a lowercase no-space text equal to the subtest string
  236. w=(found)?ret.replace('{%1}',subTest.noSpaces().toLowerCase()):w;
  237.  
  238. testX=null;
  239. }
  240.  
  241. //process number array
  242. } else if (subNumRange && (findMode=="auto" || findMode=="subnumrange") && text){
  243. var start=parseInt(subNumRange.split(",")[0]), end=parseInt(subNumRange.split(",")[1]);
  244. for (var i2=start,found=false;((!found) && i2<=end);i2++) {
  245. var testX = text.replace('{%1}',i2).toLowerCase();
  246.  
  247. //do a standard test with the replaced search string
  248. found=src.find(testX);
  249.  
  250. //return a found value, replacing %1 with a lowercase no-space text equal to the subtest string
  251. w=(found)?ret.replace('{%1}',i2):w;
  252.  
  253. testX=null;
  254. }
  255.  
  256. //process text array, process similar to subtests
  257. } else if (text && (findMode=="auto" || findMode=="basic") && (isArray(text))) {
  258. for (var i2=0,subTest,found=false;((subTest=text[i2]) && (!found));i2++) {
  259. var testX = subTest.toLowerCase();
  260.  
  261. //do a standard test with the replaced search string
  262. found=src.find(testX);
  263.  
  264. //return the same value no matter which element from the array is found
  265. w=(found)?ret:w;
  266.  
  267. testX=null;
  268. }
  269.  
  270.  
  271. //process regex
  272. } else if (text && (test.regex||test.isRegex||null) ) {
  273. var mods = (test.mods||"gi");
  274. var testRegex = new RegExp(text,mods);
  275. var match=src.match(testRegex);
  276. if (match) match=match[0]; //always take the first match
  277. w=ret||match||w;
  278.  
  279.  
  280.  
  281. //process single text
  282. } else if (text) {
  283. try{
  284. w=(src.find(text.toLowerCase() ))?ret:w;
  285. } catch(e){
  286. log("WM.doWhichTestTree:"+e);
  287. log("--app:"+app.appID);
  288. log("--test:"+JSON.stringify(test));
  289. }
  290. }
  291.  
  292. }
  293.  
  294. //see if test has type 2 subtests (child node tests based on parent test)
  295. w = ((kids && w)?WM.doWhichTestTree(post, kids, testData, custom):w) || w; //if kids return null, default to key found above
  296.  
  297. //if this test tree returned "none", start over with next tree by replacing "none" with null
  298. //true "none" is handled in the which() function below
  299. if (w==="none") w=null;
  300.  
  301.  
  302. }//end custom checker
  303. }
  304.  
  305. return w;
  306. }catch(e){log("WM.doWhichTestTree: "+e);}},
  307.  
  308. which : function(post,params) {try{
  309. //prevent the rules manager from mistaking main as a post object
  310. if (!post) return;
  311. params=params||{};
  312. //match post to an app
  313. var w, app=post.app, synApp=app.synApp;
  314.  
  315. //create various data for the tests to use
  316. if (!params.reid) post.testData = {
  317. title: (post.name||"undefined").toLowerCase(),
  318. msg: (post.message||"undefined").toLowerCase(),
  319. caption: (post.caption||"undefined").toLowerCase(),
  320. desc: (post.description||"undefined").toLowerCase(),
  321. link: (post.linkText||"undefined").toLowerCase(),
  322. url: Url.decode(post.linkHref).toLowerCase(),
  323. img: (post.picture||"undefined").toLowerCase(),
  324. fromName: post.fromName.toLowerCase(),
  325. fromID: post.fromID.toLowerCase(),
  326. targetName: "undefined", //","+post.getTargets("name").join(",").toLowerCase(),
  327. //targetID: "undefined", //","+post.getTargets("id").join(",").toLowerCase(),
  328. canvas: "undefined", //app.namespace.toLowerCase(),
  329. likeName: "undefined", //","+post.getLikes("name").join(",").toLowerCase(),
  330. likeID: "undefined", //","+post.getLikes("id").join(",").toLowerCase(),
  331. comments: "undefined", //post.getComments("message").join(" \n").toLowerCase(),
  332. commentorName: "undefined", //","+post.getComments("name").join(",").toLowerCase(),
  333. commentorID: "undefined", //","+post.getComments("id").join(",").toLowerCase(),
  334. };
  335. var testData=post.testData;
  336.  
  337. //replacement for old options like body, either and html
  338. testData.body = testData.title+testData.caption+testData.desc;
  339. testData.either = testData.link+testData.body;
  340. testData.html = testData.fromID + testData.fromName + testData.targetID + testData.targetName + testData.message
  341. + testData.href + testData.either + testData.img + testData.canvas + testData.likeID + testData.likeName
  342. + testData.commentorID + testData.commentorName + testData.comments;
  343.  
  344. var dynamicTests = WM.grabber.tests;
  345.  
  346. //check user built dynamic tests first if enabled and told to run first
  347. if (WM.opts["dynamic"+app.appID] && WM.opts.dynamicFirst && dynamicTests) {
  348. w=WM.doWhichTestTree(post,dynamicTests, testData, true)||"none";
  349. }
  350.  
  351. //process this game's tests if dynamic didn't already get one
  352. if (w=="none" || !w || w=="") {
  353. w=((tests=synApp.tests)?WM.doWhichTestTree(post,tests, testData):"none")||"none";
  354. }
  355.  
  356. //check user built dynamic tests last if enabled and not told to run first
  357. if (w=="none" || !w || w=="") {
  358. if (WM.opts["dynamic"+app.appID] && !WM.opts.dynamicFirst && dynamicTests) {
  359. w=WM.doWhichTestTree(post,dynamicTests,testData, true)||"none";
  360. }
  361. }
  362.  
  363. //switch to undefined collection if enabled
  364. w=(w==="none" && app.opts["doUnknown"])?"doUnknown":w;
  365.  
  366. return w;
  367. }catch(e){log("WM.which: "+e);}},
  368.  
  369. resetAccepted : function(params) {
  370. params=params||{};
  371. var ask=WM.opts.historyConfirmClear;
  372. if (params.noConfirm || !ask || (ask && confirm("Delete all history for this profile?"))){
  373. doAction(function(){
  374. WM.history={};
  375. setOpt('history_'+WM.currentUser.profile,'{}');
  376. });
  377. }
  378. },
  379.  
  380. onWindowResize : function(){
  381. WM.resizeConsole();
  382. },
  383. onHeartbeat : function(){
  384. if (WM.rulesManager.enabled) {
  385.  
  386. //affect rules at the base level
  387. WM.rulesManager.doEvent("onHeartbeat",{});
  388. //affect rules at the app level
  389. if (WM.opts.heartbeatAffectsApps) {
  390. for (var a in WM.apps) {
  391. (function(){
  392. WM.rulesManager.doEvent("onHeartbeat",WM.apps[a]);
  393. })();
  394. }
  395. }
  396.  
  397. //affect rules at the post level
  398. if (WM.opts.heartbeatAffectsPosts) {
  399. for (var p in WM.posts) if (!WM.posts[p].isGhost) {
  400. (function(){
  401. WM.rulesManager.doEvent("onHeartbeat",WM.posts[p]);
  402. })();
  403. }
  404. }
  405.  
  406. //affect rules at the rule level
  407. if (WM.opts.heartbeatAffectsRules) {
  408. for (var r=0; r<WM.rulesManager.rules.length; r++) {
  409. (function(){
  410. WM.rulesManager.doEvent("onHeartbeat",WM.rulesManager.rules[r]);
  411. })();
  412. }
  413. }
  414.  
  415. //affect rules at the feed and feed filter levels
  416. if (WM.opts.heartbeatAffectsFeeds || WM.opts.heartbeatAffectsFeedFilters) {
  417. var feeds=WM.feedManager.feeds;
  418. for (var f=0,len=feeds.length; f<len; f++) {
  419. //do the feed
  420. if (WM.opts.heartbeatAffectsFeeds) (function(){
  421. WM.rulesManager.doEvent("onHeartbeat",feeds[f]);
  422. })();
  423. //do the feed filters
  424. if (WM.opts.heartbeatAffectsFeedFilters) {
  425. for (var ff in feeds[f].filters){
  426. (function(){
  427. WM.rulesManager.doEvent("onHeartbeat",feeds[f].filters[ff]);
  428. })();
  429. }
  430. }
  431. }
  432. }
  433. }
  434. //check for new sidekick arrivals
  435. if (isArrayAndNotEmpty(WM.newSidekicks)) {
  436. while (WM.newSidekicks.length>0) {
  437. var app=WM.newSidekicks.shift();
  438. app.fetchPosts();
  439. }
  440. }
  441. //check for autolike queue contents
  442. var quePost = WM.checkAutoLikeQue();
  443. if (quePost) {
  444. //log([quePost.fn,quePost.post.id]);
  445. switch (quePost.fn) {
  446. case "like":quePost.post.like();break;
  447. case "comment":quePost.post.comment(quePost.say);break;
  448. }
  449. }
  450. },
  451.  
  452. //this is for when the WM.config and globalConfig settings change
  453. onSave : function() {
  454. //recopy the settings array from WM.config
  455. WM.updateSettingsValues();
  456.  
  457. //hide or show counters
  458. if (WM.opts.showcounters) WM.showCounters(); else WM.hideCounters();
  459.  
  460. //update intervals
  461. WM.setIntervals();
  462.  
  463. //set new user colors
  464. WM.setColors();
  465. //update config settings
  466. WM.changeConfigSettings();
  467.  
  468. //update those settings we use as global variables
  469. WM.changeDebugSettings();
  470. //set console heights
  471. //WM.resizeConsole();
  472. },
  473.  
  474. updateSettingsValues : function(){try{
  475. WM.opts = WM.config.values;
  476. //new: do this for each of the apps too
  477. for (var a in WM.apps) WM.apps[a].opts=WM.apps[a].config.values;
  478. }catch(e){"WM.updateSettingsValues: "+e}},
  479.  
  480. getAccText: function(appID,w,past,status){
  481. var app=WM.apps[appID].synApp;
  482. //detect and use a status code message
  483. if (!(status>-1 || status==-4 || status==-6)) return WM.statusText[status];
  484. //or return a generic message based on post type
  485. else return (w=="dynamic")?"Dynamic Grab"+((past)?"bed":""):(((w.find("send")?"Sen"+((past)?"t":"d")+" ":w.find("wishlist")?"":"G"+((past?"o":"e"))+"t ") + (app.userDefinedTypes[w]||app.accText[w])) || ((past)?WM.accDefaultText:"Get Unknown") || ((w.startsWith(app.appID+"doUnknown"))?"Unknown":"") );
  486. },
  487.  
  488. stopCollectionOf : function(w){
  489. for (var p in WM.posts) if (!WM.posts[p].isGhost && WM.posts[p].which==w) WM.posts[p].stopCollect();
  490. },
  491.  
  492. startCollectionOf : function(w){
  493. for (var p in WM.posts) if (!WM.posts[p].isGhost && WM.posts[p].which==w) WM.posts[p].collect();
  494. },
  495.  
  496. pauseByType : function(app,w){
  497. if (!isArray(w)) w=[w];
  498. //mark as paused all those posts not yet done
  499. for (var p in WM.posts) if (!WM.posts[p].isGhost && w.inArray(WM.posts[p].which)) WM.posts[p].pause();
  500. //store the paused type but dont save it
  501. var a=(app.parent||app);
  502. for (var i=0; i<w.length; i++) {
  503. var t=w[i];
  504. //add it to the array without making a duplicate
  505. if (!a.typesPaused.inArray(t)) {
  506. a.typesPaused.push(t);
  507. //add a visible node
  508. a.typesPausedNode.appendChild(
  509. a.pausedTypesListNodes[t]=createElement("div",{className:"line"},[
  510. createElement("span",{textContent:(a.userDefinedTypes[t]||a.accText[t])+" ("+t+") "}),
  511. createElement("div",{className:"littleButton oddGreen", title:"Unpause Type"},[
  512. createElement("img",{className:"resourceIcon playRight"+WM.opts.littleButtonSize,onclick:function(){
  513. WM.unPauseByType(a,t);
  514. }})
  515. ])
  516. ])
  517. );
  518. }
  519. }
  520. },
  521.  
  522. unPauseByType : function(app,w){
  523. if (!isArray(w)) w=[w];
  524. //unpause all those posts not yet done
  525. for (var p in WM.posts) if (!WM.posts[p].isGhost && w.inArray(WM.posts[p].which)) WM.posts[p].unPause();
  526. //remove paused type from list but dont save it
  527. var a=(app.parent||app);
  528. for (var i=0; i<w.length; i++) {
  529. //remove the visible node
  530. remove (a.pausedTypesListNodes[w[i]]);
  531. //delete the visible node entry
  532. delete a.pausedTypesListNodes[w[i]];
  533. //remove it from the array
  534. a.typesPaused.removeByValue(w[i]);
  535. }
  536. },
  537.  
  538. setAsAccepted : function(comment,status, post) {try{
  539. var app=post.app;
  540. var synApp=app.synApp;
  541. post.state="accepted";
  542. post.status=status;
  543. post.accept();
  544.  
  545. WM.history[post.id]={status:status, date:timeStamp(), which:(post.which||"undefined").removePrefix(synApp.appID), appID:app.appID};
  546. setOptJSON('history_'+WM.currentUser.profile,WM.history);
  547.  
  548. //do friend tracking
  549. if (WM.opts.useFriendTracker && WM.opts.trackAccepted){
  550. WM.friendTracker.trackStatus(post,true);
  551. }
  552.  
  553. var postNode=post.node||$("post_"+post.id);
  554. if (postNode){
  555. var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode});
  556.  
  557. var text=WM.getAccText(synApp.appID,post.which,true,status);
  558.  
  559. link.textContent = (comment || text || WM.statusText[status] || WM.accDefaultText);
  560. WM.updatePostStatus(post.id);
  561. }
  562.  
  563. app.acceptCount++;
  564.  
  565. //perform the onAccepted event
  566. WM.rulesManager.doEvent("onAccepted",post);
  567.  
  568. //try autolike
  569. try{
  570. if (WM.opts.useautolike && (WM.opts.autolikeall || WM.opts.autolikeaccepted || (WM.opts.autolikesent && (post.which||"undefined").startsWith("send")) )) {
  571. if (!WM.opts["nolike"+app.appID]){
  572. WM.queAutoLike(post);
  573. //post.like();
  574. }
  575. }
  576. } catch(e){log("setAsAccepted: autolike failed: "+e,{level:3});}
  577. //try autocomment
  578. try{
  579. if (WM.opts.useautocomment && (WM.opts.autolikeall || WM.opts.autolikeaccepted || (WM.opts.autolikesent && (post.which||"undefined").startsWith(synApp.appID+"send")) )) {
  580. if (!WM.opts["nolike"+app.appID]){
  581. //setTimeout(function(){post.comment();},100+(WM.opts.autolikedelay*1000));
  582. WM.queAutoComment(post,null);
  583. }
  584. }
  585. } catch(e){log("setAsAccepted: autocomment failed: "+e,{level:3});}
  586. }catch(e){log("WM.setAsAccepted: "+e);}},
  587.  
  588. disableOpt : function(w,app){try{
  589. var targetConfig=(app||null)?app.config:WM.config;
  590. ((app||null)?app.opts:WM.opts)[w]=false;
  591. targetConfig.set(w,false);
  592. targetConfig.save();
  593. debug.print([w,app,false]);
  594. }catch(e){log("WM.disableOpt: "+e);}},
  595.  
  596. enableOpt : function(w,app){try{
  597. var targetConfig=(app||null)?app.config:WM.config;
  598. ((app||null)?app.opts:WM.opts)[w]=true;
  599. targetConfig.set(w,true);
  600. targetConfig.save();
  601. debug.print([w,app,true]);
  602. }catch(e){log("WM.enableOpt: "+e);}},
  603.  
  604. setOpt : function(w,v,app){try{
  605. var targetConfig=(app||null)?app.config:WM.config;
  606. ((app||null)?app.opts:WM.opts)[w]=v;
  607. targetConfig.set(w,v);
  608. targetConfig.save();
  609. debug.print([w,app,v]);
  610. }catch(e){log("WM.setOpt: "+e);}},
  611.  
  612. resetCounters : function(){try{
  613. for (var a in WM.apps) WM.apps[a].resetCounter();
  614. }catch(e){log("WM.resetCounters: "+e);}},
  615.  
  616. setAsFailed : function(comment, status, post){try{
  617. var app=post.app;
  618. var synApp=app.synApp;
  619. var postNode=post.node||$("post_"+post.id);
  620.  
  621. //special effects for timeout and cancelProcess
  622. if ((!WM.opts.failontimeout && status==-14) || status==-21) {
  623. post.state="timeout";
  624. post.timeout();
  625. if (status==-14) WM.rulesManager.doEvent("onTimeout",post);
  626.  
  627. } else {
  628. post.state="failed";
  629. post.fail(); // don't pass true or it will loop here
  630.  
  631. WM.history[post.id]={status:status, date:timeStamp(), which:(post.which||"undefined").removePrefix(synApp.appID), appID:app.appID};
  632. setOptJSON('history_'+WM.currentUser.profile,WM.history);
  633. WM.rulesManager.doEvent("onFailed",post);
  634. }
  635. post.status=status;
  636.  
  637. //do friend tracking
  638. if (WM.opts.useFriendTracker && WM.opts.trackFailed){
  639. WM.friendTracker.trackStatus(post,false);
  640. }
  641. if (postNode) {
  642. var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode});
  643. if (link) {
  644. //I can see no reason the link should be missing, but since its been proven to fail, here is a patch
  645. link.textContent = (comment || WM.statusText[status] || WM.failText);
  646. }
  647. WM.updatePostStatus(post.id);
  648. }
  649.  
  650. app.failCount++;
  651. //try autolike
  652. try{
  653. if (WM.opts.useautolike && WM.opts.autolikeall) {
  654. if (!WM.opts["nolike"+app.appID]){
  655. WM.queAutoLike(post);
  656. //post.like();
  657. //setTimeout(function(){post.like();},100+(WM.opts.autolikedelay*1000));
  658. }
  659. }
  660. } catch(e){log("setAsFailed: autolike failed: "+e,{level:3});}
  661. //try autocomment
  662. try{
  663. if (WM.opts.useautocomment && WM.opts.autolikeall) {
  664. if (!WM.opts["nolike"+app.appID]){
  665. //setTimeout(function(){post.comment();},100+(WM.opts.autolikedelay*1000));
  666. WM.queAutoComment(post,null);
  667. }
  668. }
  669. } catch(e){log("setAsFailed: autocomment failed: "+e,{level:3});}
  670. }catch(e){log("WM.setAsFailed: "+e);}},
  671.  
  672. setPriority : function(){
  673. var postNode=selectSingleNode(".//ancestor::*[starts-with(@id,'post_')]",{node:this});
  674. var id=postNode.id.replace("post_","");
  675. WM.posts[id]["priority"]=this.getAttribute("name");
  676. remove(postNode);
  677. WM.posts[id].draw();
  678. },
  679.  
  680. clearURL : function(tab){
  681. WM.collector.close(tab);
  682. WM.requestsOpen--;
  683. },
  684. //constantly update sidekick channel data
  685. skChannel : {},
  686. fetchSidekickData : function(){try{
  687. if (WM) {
  688. var node=selectSingleNode("./div",{node:$("wmDataDump")});
  689. while (node){
  690. log("WM.fetchSidekickData: found "+JSON.parse(node.getAttribute("data-ft")));
  691. WM.skChannel=mergeJSON(WM.skChannel,JSON.parse(node.getAttribute("data-ft")));
  692. remove(node);
  693. node=selectSingleNode("./div",{node:$("wmDataDump")});
  694. }
  695. setTimeout(WM.fetchSidekickData,1000);
  696. }
  697. }catch(e){log("WM.fetchSidekickData: "+e);}},
  698. //this is WM3's method of handling conversations with sidekicks
  699. onFrameLoad3 : function(tab){try{
  700. log("onFrameLoad3(): tab="+tab.id);
  701. var postID=tab.postID||tab.id;
  702. var post=tab.post||WM.posts[postID];
  703.  
  704. //detect if post process was cancelled by user
  705. if (post.processCancelled){
  706. //reset the cancel memory
  707. post.processCancelled = false;
  708. log("onFrameLoad3: process cancelled by user");
  709. //set the timeout flag even though its not timed out
  710. WM.setAsFailed(null,-21,post);
  711. WM.clearURL(tab);
  712. return;
  713. }
  714.  
  715. //detect if valid WM.collector window still exists
  716. var windowExists=(tab.hwnd && !tab.hwnd.closed);
  717. /*try{
  718. var testUrl=tab.hwnd.location.toString();
  719. } catch(e) {
  720. windowExists=false;
  721. }*/
  722. //make sure the post object still exists
  723. if (!(post||null)){
  724. log("onFrameLoad3: post is null");
  725. WM.clearURL(tab);
  726. return;
  727. }
  728.  
  729. //check if window object is missing
  730. if (!windowExists) {
  731. log("windowExists = false");
  732. if (!tab.hwnd) log("onFrameLoad3: tab.hwnd is null");
  733. if (tab.hwnd.closed) log("onFrameLoad3: tab.hwnd is closed");
  734. WM.setAsFailed(null,-17,post);
  735. WM.clearURL(tab);
  736. return;
  737. }
  738. //check timer on this open post
  739. var openTime=tab.openTime;
  740. var nowTime=timeStamp();
  741. if ((WM.opts.reqtimeout*1000)<(nowTime-openTime)){
  742. log("onFrameLoad3: post timed out");
  743. WM.setAsFailed(null,-14,post);
  744. WM.clearURL(tab);
  745. return;
  746. }
  747. //create the retry function
  748. var retry=function(){setTimeout(function(){WM.onFrameLoad3(tab); return;},1000); return;};
  749. //look for status data
  750. var tabID = tab.id;
  751. var skData = WM.skChannel[tabID]||null;
  752. if (skData) {
  753. //data exists for this post
  754. if (skData.status) {
  755. //status is available
  756. delete WM.skChannel[tabID];
  757. //get useful post data
  758. var app=post.app; var synApp=app.parent||app;
  759. var postNode=post.node||$("post_"+post.id);
  760. var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode});
  761. var w=post.which||"undefined";
  762. //confirm status
  763. var gotItem=((skData.status>0) || (skData.status==-6) || (skData.status==-4) || (skData.status==-15 && WM.opts.accepton15));
  764. var failedItem=(skData.status<0);
  765. if (gotItem){
  766. //build debug block
  767. switch(skData.status){
  768. case -6: case -4: case 1:
  769. // this bonus is available or we still have the ability to send something for no return
  770. //dont break before next
  771. case -15: case 2:
  772. if (!synApp.flags.requiresTwo){
  773. WM.setAsAccepted(null, skData.status, post);
  774. }
  775. break;
  776.  
  777. default:
  778. //should not have come here for any reason, but if we did assume its a status code I didnt script for
  779. WM.setAsFailed(null, skData.status, post);
  780. log("onFrameLoad3: unexpected status code: "+skData.status,{level:2});
  781. break;
  782. }
  783. } else {
  784. WM.setAsFailed(null,skData.status,post);
  785. }
  786. // click "yes" to accept it, if we got this far we actually found an accept button
  787. if(synApp.flags.requiresTwo && gotItem) {
  788. if (skData.nopopLink) {
  789. var req; req=GM_xmlhttpRequest({
  790. method: "GET",
  791. url: skData.nopopLink,
  792. timeout: WM.opts.reqtimeout*1000,
  793. onload: function(response) {
  794. //search for error messages
  795. var test=response.responseText;
  796. if (test==""){
  797. //no text was found at requested href
  798. log("onFrameLoad3: final stage: null response",{level:2});
  799. WM.setAsFailed(null, -9,post);
  800. } else {
  801. //if no errors then we got it
  802. WM.setAsAccepted(null, skData.status,post);
  803. }
  804. WM.clearURL(tab);
  805. if(req)req=null;
  806. },
  807.  
  808. onerror: function(response) {
  809. log("onFrameLoad3: final stage: error returned",{level:2});
  810. //if final request fails, drop the request for now
  811. WM.setAsFailed(null, -10,post);
  812. WM.clearURL(tab);
  813. if(req)req=null;
  814. },
  815.  
  816. onabort: function(response) {
  817. log("onFrameLoad3: final stage: request aborted",{level:2});
  818. WM.setAsFailed(null, -10,post);
  819. WM.clearURL(tab);
  820. if(req)req=null;
  821. },
  822. ontimeout: function(response) {
  823. log("onFrameLoad3: final stage: request timeout",{level:2});
  824. WM.setAsFailed(null, -10,post);
  825. WM.clearURL(tab);
  826. if(req)req=null;
  827. },
  828. });
  829.  
  830. } else {
  831. log("onFrameLoad3: skData.nopopLink is null and a string was expected",{level:3});
  832. WM.setAsFailed(null, -16,post);
  833. WM.clearURL(tab);
  834. return;
  835. }
  836. } else WM.clearURL(tab); //<- default page clearer, do not remove
  837. } else retry();
  838. } else {
  839. retry();
  840. //send the tab its init message (again)
  841. tab.hwnd.postMessage({
  842. channel:"WallManager",
  843. msg:1,
  844. tabID:tab.id,
  845. },"*");
  846. //log("useGM_openInTab: "+WM.opts.useGM_openInTab);
  847. }
  848. }catch(e){log("WM.onFrameLoad3: "+e);}},
  849. //this is WM1-2's method of handling conversation with sidekicks
  850. //WM3 defaults to this if sidekick is not WM3 compatible
  851. onFrameLoad : function(tab,noDebug){try{
  852. //tab object contains {id,post,url}
  853. if (!noDebug) log("onFrameLoad()",{level:0});
  854.  
  855. var id=tab.id; var post=tab.post||WM.posts[id];
  856. if (!(post||null)) {
  857. //resource deleted while post was out
  858. WM.clearURL(tab);
  859. return;
  860. }
  861.  
  862. //detect if post process was cancelled by user
  863. if (post.processCancelled){
  864. //reset the cancel memory
  865. post.processCancelled = false;
  866. log("onFrameLoad3: process cancelled by user");
  867. //set the timeout flag even though its not timed out
  868. WM.setAsFailed(null,-21,post);
  869. WM.clearURL(tab);
  870. return;
  871. }
  872.  
  873. var app=post.app;
  874. var synApp=app.parent||app;
  875.  
  876. var httpsTrouble=synApp.flags.httpsTrouble;
  877. var responseLess=synApp.flags.skipResponse;
  878.  
  879. var postNode=post.node||$("post_"+post.id);
  880. var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode});
  881. var w=post.which||"undefined";
  882.  
  883. tab.tries=(tab.tries||0)+1;
  884. if (tab.tries>WM.opts.reqtimeout) {
  885. log("onFrameLoad: request timeout",{level:3});
  886. WM.setAsFailed(null, -14, post);
  887. WM.clearURL(tab);
  888. return;
  889. }
  890.  
  891. var retry=function(){setTimeout(function(e){WM.onFrameLoad(tab, true);}, 1000);};
  892.  
  893. var failedItem=false, gotItem=false, nopopLink;
  894.  
  895. //check if window object is missing
  896. var windowExists=(tab.hwnd && !tab.hwnd.closed);
  897. if (!windowExists) {WM.setAsFailed(null,-17,post); WM.clearURL(tab); return;}
  898. //check if window document does not yet exist
  899. //if (!(tab.hwnd.document||null)) {retry(); return;}
  900.  
  901. //get sidekick return value
  902. var hashMsg="",hashStatus=0;
  903. try{
  904. //if error encountered, reload the page
  905. if (tab.hwnd.document.title==="Problem loading page"){
  906. log("processPosts: problem loading page",{level:1});
  907. tab.hwnd.location.reload();
  908. retry();
  909. return;
  910. }
  911.  
  912. var temphash = tab.hwnd.location.hash; //capture a hash if we can
  913. hashMsg = ((temphash)?temphash.removePrefix("#"):null) || tab.hwnd.location.href.split("#")[1];
  914. hashStatus=(responseLess)?2:(hashMsg||null)?parseInt(hashMsg.split('status=')[1].split("&")[0]):0;
  915. gotItem=((hashStatus>0) || (hashStatus==-6) || (hashStatus==-4) || (hashStatus==-15 && WM.opts.accepton15));
  916. failedItem=(hashStatus<0);
  917. if (!gotItem && !failedItem) {retry(); return;}
  918.  
  919. } catch(e){
  920. var errText=""+e;
  921. if (errText.contains("hashMsg is undefined")) {
  922. //this known issue occurs when a page is not yet fully loaded and the
  923. //WM script tries to read the page content
  924. retry();
  925. return;
  926. }
  927. else if (errText.contains("Permission denied to access property")) {
  928. //we've reached some known cross domain issue
  929. if (responseLess) {
  930. //if the sidekick creator has chosen to use responseless collection
  931. //simply assume the page has loaded and mark the item as collected
  932.  
  933. gotItem=true;failedItem=false;hashStatus=2;
  934. } else {
  935. console.log("WM.onFrameLoad(before retry): "+e);
  936. retry();
  937. return;
  938. }
  939. }
  940. else if (errText.contains("NS_ERROR_INVALID_POINTER")
  941. || errText.contains("tab.hwnd.document is null") ) {
  942.  
  943. WM.setAsFailed(null,-17,post);
  944. WM.clearURL(tab);
  945. return;
  946. }
  947. else {
  948. log("onFrameLoad: "+e,{level:3});
  949. retry();
  950. return;
  951. }
  952. }
  953.  
  954. //if gotItem then we have been offered the item so far
  955. if (gotItem){
  956. //build debug block
  957. switch(hashStatus){
  958. case -6: case -4: case 1:
  959. // this bonus is available or we still have the ability to send something for no return
  960. if (synApp.flags.requiresTwo){
  961. try{
  962. nopopLink=hashMsg.split("&link=[")[1].split("]")[0];
  963. }catch(e){
  964. //known rare issue where no link is passed back by pioneer trail
  965. }
  966. }
  967. //dont break before next
  968. case -15: case 2:
  969. if (!synApp.flags.requiresTwo){
  970. WM.setAsAccepted(null, hashStatus,post);
  971. }
  972. break;
  973.  
  974. default:
  975. //should not have come here for any reason, but if we did assume its a status code I didnt script for
  976. WM.setAsFailed(null, hashStatus,post);
  977. log("onFrameLoad: unexpected status code: "+hashStatus,{level:2});
  978. break;
  979. }
  980. } else {
  981. WM.setAsFailed(null,hashStatus,post);
  982. }
  983.  
  984.  
  985. // click "yes" to accept it, if we got this far we actually found an accept button
  986. if(synApp.flags.requiresTwo && gotItem) {
  987. if (nopopLink) {
  988. var req; req=GM_xmlhttpRequest({
  989. method: "GET",
  990. url: nopopLink,
  991. timeout: WM.opts.reqtimeout*1000,
  992. onload: function(response) {
  993. //search for error messages
  994. var test=response.responseText;
  995. if (test==""){
  996. //no text was found at requested href
  997. log("onFrameLoad: final stage: null response",{level:2});
  998. WM.setAsFailed(null, -9,post);
  999. } else {
  1000. //if no errors then we got it
  1001. WM.setAsAccepted(null, hashStatus,post);
  1002. }
  1003. WM.clearURL(tab);
  1004. if(req)req=null;
  1005. },
  1006.  
  1007. onerror: function(response) {
  1008. log("onFrameLoad: final stage: error returned",{level:2});
  1009. //if final request fails, drop the request for now
  1010. WM.setAsFailed(null, -10,post);
  1011. WM.clearURL(tab);
  1012. if(req)req=null;
  1013. },
  1014.  
  1015. onabort: function(response) {
  1016. log("onFrameLoad: final stage: request aborted",{level:2});
  1017. WM.setAsFailed(null, -10,post);
  1018. WM.clearURL(tab);
  1019. if(req)req=null;
  1020. },
  1021. ontimeout: function(response) {
  1022. log("onFrameLoad: final stage: request timeout",{level:2});
  1023. WM.setAsFailed(null, -10,post);
  1024. WM.clearURL(tab);
  1025. if(req)req=null;
  1026. },
  1027. });
  1028.  
  1029. } else {
  1030. log("onFrameLoad: nopopLink is null and a string was expected",{level:3});
  1031. WM.setAsFailed(null, -16,post);
  1032. WM.clearURL(tab);
  1033. return;
  1034. }
  1035. } else WM.clearURL(tab);
  1036.  
  1037. }catch(e){log("WM.onFrameLoad: "+e);}},
  1038.  
  1039. toggle : function(opt,app){
  1040. var targetConfig=(app||null)?app.config:WM.config;
  1041. var targetOpts=(app||null)?app.opts:WM.opts;
  1042. if (targetOpts[opt]){
  1043. targetConfig.set(opt, false);
  1044. targetOpts[opt] = false;
  1045. } else {
  1046. targetConfig.set(opt, true);
  1047. targetOpts[opt] = true;
  1048. }
  1049. targetConfig.save();
  1050. },
  1051.  
  1052. getAppDropDownList : function(selectedIndex,allowBlank){
  1053. var retApps=[];
  1054. //add the fake initial option
  1055. retApps.push(createElement("option",{textContent:"select an app",value:""}));
  1056. retApps.push(createElement("option",{textContent:"* All",value:""}));
  1057. if (allowBlank) retApps.push(createElement("option",{textContent:"all apps",value:""}));
  1058.  
  1059. for(var i in WM.apps){
  1060. if (!WM.apps[i].parent) {
  1061. var elem = createElement("option",{textContent:WM.apps[i].name,value:i});
  1062. if ((selectedIndex||null) == i) elem.selected = true;
  1063. retApps.push(elem);
  1064. }
  1065. }
  1066. return retApps;
  1067. },
  1068.  
  1069. getBonusDropDownList : function(params){
  1070. params=params||{};
  1071. var selected = params.selected||"";
  1072. var appID = params.appID||null;
  1073. var dropID = params.dropID||false; //force the element value to drop its appID prefix
  1074.  
  1075. var optsret=[], bonuses={};
  1076. if (appID) bonuses = mergeJSON(WM.apps[appID].accText,WM.apps[appID].userDefinedTypes);
  1077. bonuses["dynamic"]="* Dynamic: Just Grab It";
  1078. bonuses["none"]="* None: Break Identification Circuit";
  1079. bonuses["wishlist"]="* Flag as Wishlist";
  1080. bonuses["exclude"]="* Exclude: Prevent Collection";
  1081. bonuses["send"]="* Send Unknown";
  1082. bonuses["doUnknown"]="* Get Unknown";
  1083.  
  1084. //create option values and names;
  1085. for (var i in bonuses) {
  1086. var elem
  1087. if (appID) elem = createElement("option",{textContent:((i.startsWith(appID+"send"))?"Send ":((bonuses[i].substring(0,1)=="*")?"":"Get "))+bonuses[i],value:((dropID)?i.removePrefix(appID):i)});
  1088. else elem = createElement("option",{textContent:bonuses[i],value:i});
  1089.  
  1090. if (appID) {if (selected==((dropID)?i.removePrefix(appID):i) ) elem.selected = true;}
  1091. else {if (selected==i) elem.selected=true;}
  1092.  
  1093. optsret.push(elem);
  1094. }
  1095.  
  1096. return optsret;
  1097. },
  1098.  
  1099. reIDAll : function(){
  1100. for (var p in WM.posts) {
  1101. if (!WM.posts[p].isGhost && WM.posts[p].identify({reid:true}))
  1102. WM.rulesManager.doEvent("onIdentify",WM.posts[p]);
  1103. }
  1104. WM.sortPosts(); //in this case sorting may cancel movetotop and movetobottom
  1105. WM.clearGroups();
  1106. WM.redrawPosts({postRedraw:true});
  1107. },
  1108.  
  1109. updatePostStatus : function(id){
  1110. var status = WM.posts[id].status;
  1111. var statusNode = selectSingleNode(".//*[contains(@class,'status')]",{node:$("post_"+id)});
  1112. if (statusNode) statusNode.textContent="Status: "+(status||"0") + " " + (WM.statusText[status||"0"]);
  1113. status=null; statusNode=null;
  1114. },
  1115.  
  1116. onLikePost : function(post){
  1117. post.isLiked=true;
  1118. return;
  1119. //pre beta 40 stuff
  1120. var postID=tab.id;
  1121. var post=tab.post||WM.posts[postID];
  1122.  
  1123. //detect if post process was cancelled by user
  1124. if (post.processCancelled){
  1125. //reset the cancel memory
  1126. post.processCancelled = false;
  1127. log("onLikePost: feedback cancelled by user");
  1128. WM.collector.close(tab);
  1129. return;
  1130. }
  1131.  
  1132. //detect if valid WM.collector window still exists
  1133. var windowExists=(tab.hwnd && !tab.hwnd.closed);
  1134.  
  1135. //check if window object is missing
  1136. if (!windowExists) {
  1137. log("onLikePost: tab.hwnd is null or closed");
  1138. WM.collector.close(tab);
  1139. return;
  1140. }
  1141. try{
  1142. var like=tab.hwnd.location.hash.removePrefix("#").getUrlParam("status")=="1";
  1143. if (like) {
  1144. if (tab.post) {
  1145. //tell the post it is liked
  1146. tab.post.isLiked = true;
  1147. //delete the post reference from the tab
  1148. delete tab.post;
  1149. }
  1150. WM.collector.close(tab);
  1151. return;
  1152. }
  1153. } catch (e){
  1154. //log(""+e);
  1155. }
  1156.  
  1157. tab.tries=(tab.tries||0)+1;
  1158. if (tab.tries<WM.opts.autoliketimeout) setTimeout(function(){WM.onLikePost(tab);}, 1000);
  1159. else {
  1160. log("onLikePost: unable to finish feedback",{level:3});
  1161. doAction(function(){WM.collector.close(tab);});
  1162. }
  1163. },
  1164.  
  1165. toggleSidekick : function(){
  1166. var appID = this.id.split("master_")[1];
  1167. var opt = !(WM.quickOpts["masterSwitch"][appID]||false); //toggle
  1168. WM.quickOpts["masterSwitch"][appID]=opt;
  1169. var className = this.parentNode.className;
  1170. this.parentNode.className = ((opt)?className.removeWord("disabled"):className.addWord("disabled"));
  1171. this.textContent=((opt)?"Disable":"Enable");
  1172. WM.saveQuickOpts();
  1173. },
  1174.  
  1175. saveQuickOpts : function(){
  1176. setOptJSON('quickopts_'+WM.currentUser.profile, WM.quickOpts);
  1177. },
  1178.  
  1179. setAppFilter : function(tab){
  1180. WM.quickOpts.filterApp=tab.appFilter;
  1181. WM.saveQuickOpts();
  1182. WM.clearGroups();
  1183. WM.redrawPosts({postRedraw:false,reorder:true});
  1184. WM.rulesManager.doEvent("onSetAppFilter",WM.apps[tab.appFilter]);
  1185. //debug.print(["Collection Tab Selected",WM.currentAppTab,WM.apps[tab.appFilter]]);
  1186. },
  1187. setDisplay : function(){
  1188. var x=this.getAttribute("name");
  1189. WM.quickOpts.displayMode=x;
  1190. WM.saveQuickOpts();
  1191. WM.redrawPosts({postRedraw:true,reorder:true});
  1192. WM.setDisplayCols();
  1193. },
  1194. setDisplayCols : function(params){
  1195. params=params||{};
  1196. params.cols=params.cols||WM.quickOpts.displayCols;
  1197. WM.quickOpts.displayCols=params.cols||1;
  1198. WM.saveQuickOpts();
  1199. with (WM.console.feedNode) {
  1200. className=className
  1201. .toggleWordB(params.cols==1,"singleCol")
  1202. .toggleWordB(params.cols==2,"twoCol")
  1203. .toggleWordB(params.cols==3,"threeCol")
  1204. .toggleWordB(params.cols==4,"fourCol");
  1205. }
  1206. },
  1207.  
  1208. redrawPosts : function(params){
  1209. params=params||{};
  1210. var feedNode=WM.console.feedNode;
  1211. //set the proper display mode
  1212. feedNode.className=feedNode.className
  1213. .toggleWordB((WM.quickOpts.displayMode=="1" || WM.quickOpts.displayMode=="3"),"short");
  1214. //avoid order issues by removing the posts from the panel
  1215. WM.clearPosts();
  1216.  
  1217. //redraw||reorder
  1218. for (var p in WM.posts) {
  1219. var post=WM.posts[p];
  1220. if (!post.isGhost) {
  1221. post.draw(params.postRedraw,params.reorder);
  1222. }
  1223. }
  1224. },
  1225.  
  1226. moveFloater : function(ev){
  1227. if (isChrome) return;
  1228. var img=this, offset=trueOffset(img), scrolled=trueScrollOffset(img),
  1229. post=selectSingleNode(".//ancestor::div[starts-with(@id,'post')]",{node:img}),
  1230. floater=$(post.id.replace("post","floater")), special={};
  1231.  
  1232. //log( (scrolled.left) +","+ (scrolled.top) );
  1233.  
  1234. special.x=(ev.clientX > (document.documentElement.clientWidth/2))?-(240+4+22):0; //width+overshot+BorderAndPadding
  1235. special.y=(ev.clientY > (document.documentElement.clientHeight/2))?-(120+4+12):0;
  1236.  
  1237. floater.style.left=(ev.clientX-(offset.left-scrolled.left))+(2+special.x)+"px";
  1238. floater.style.top=(ev.clientY-(offset.top-scrolled.top))+(2+special.y)+"px";
  1239. },
  1240. //create a drip system for autolike, instead of an offset
  1241. queAutoLike : function(post){
  1242. var nowTime = timeStamp();
  1243. var lastInQue = WM.likeQueue.last();
  1244. var targetTime = nowTime + (1000*WM.opts.autolikedelay);
  1245. if (lastInQue||null) {
  1246. if (lastInQue.timer>nowTime) {
  1247. targetTime = lastInQue.timer + (1000*WM.opts.autolikedelay);
  1248. }
  1249. }
  1250. WM.likeQueue.push({post:post, timer:targetTime, fn:"like"});
  1251. WM.console.likeQueueCounterNode.textContent = WM.likeQueue.length;
  1252. },
  1253. //create a drip system for autolike, instead of an offset
  1254. queAutoComment : function(post,say){
  1255. var nowTime = timeStamp();
  1256. var lastInQue = WM.likeQueue.last();
  1257. var targetTime = nowTime + (1000*WM.opts.autolikedelay);
  1258. if (lastInQue||null) {
  1259. if (lastInQue.timer>nowTime) {
  1260. targetTime = lastInQue.timer + (1000*WM.opts.autolikedelay);
  1261. }
  1262. }
  1263. WM.likeQueue.push({post:post, timer:targetTime, say:say, fn:"comment"});
  1264. WM.console.likeQueueCounterNode.textContent = WM.likeQueue.length;
  1265. //log(["autocomment added",say]);
  1266. },
  1267.  
  1268. //dump the autolike queue
  1269. emptyAutoLikeQue : function() {
  1270. WM.likeQueue=[];
  1271. WM.console.likeQueueCounterNode.textContent = 0;
  1272. },
  1273. //get the next ready autolike target
  1274. checkAutoLikeQue : function() {
  1275. if (WM.likeQueue.length<1) return null;
  1276. var nowTime = timeStamp();
  1277. if (WM.likeQueue[0].timer<=nowTime) {
  1278. WM.console.likeQueueCounterNode.textContent = (WM.likeQueue.length-1);
  1279. var t=nowTime;
  1280. for (var i in WM.likeQueue) {
  1281. i.timer = t;
  1282. t+=(1000*WM.opts.autolikedelay);
  1283. }
  1284. return WM.likeQueue.shift(); // no longer returns the post, but the block of what to do with what post
  1285. }
  1286. return null;
  1287. },
  1288.  
  1289. processPosts : function(){
  1290. //dont run if menu is open or if requests are still out or if the console is paused
  1291. if($("Config") || (WM.requestsOpen >= WM.opts.maxrequests) || WM.paused) return;
  1292.  
  1293. var postNode=selectSingleNode(".//div[starts-with(@id,'post_') and contains(@class,'collect') and not(contains(@class,'paused') or contains(@class,'working'))]",{node:WM.console.feedNode});
  1294. if (postNode) {
  1295. var post = WM.posts[postNode.id.replace('post_','')];
  1296. if (post) post.open();
  1297. }
  1298. },
  1299.  
  1300. olderPosts : function (params) {
  1301. WM.fetch({older:true});
  1302. },
  1303.  
  1304. newerPosts : function (params) {
  1305. WM.fetch({newer:true});
  1306. },
  1307. fetchRange : function (params) {
  1308. WM.fetch({bypassPause:true, older:true, targetEdge:params.oldedge, currentEdge:params.newedge});
  1309. },
  1310.  
  1311. cleanPosts : function () {try{
  1312. for (var p in WM.posts) if (!WM.posts[p].isGhost) {
  1313. var post = WM.posts[p];
  1314. with (post) if (!(
  1315. isPinned || isCollect || isWorking ||
  1316. (isTimeout && !WM.opts.cleanTimedOut)
  1317. )) post.remove();
  1318. }
  1319. }catch(e){log("WM.cleanPosts(): "+e);}},
  1320. setIntervals : function() {try{
  1321. //setup the timer to try post collection
  1322. if (procIntv) window.clearInterval(procIntv);
  1323. procIntv=window.setInterval(WM.processPosts, 2000);
  1324.  
  1325. //setup the timer to get new posts
  1326. if (newIntv) window.clearInterval(newIntv);
  1327. if(calcTime(WM.opts.newinterval)>0) newIntv=window.setInterval(WM.newerPosts, calcTime(WM.opts.newinterval));
  1328.  
  1329. //setup the timer to get older posts
  1330. if (oldIntv) window.clearInterval(oldIntv);
  1331. if(calcTime(WM.opts.oldinterval)>0) oldIntv=window.setInterval(WM.olderPosts, calcTime(WM.opts.oldinterval)+2000);
  1332. olderLimit=calcTime(WM.opts.maxinterval)||0;
  1333.  
  1334. //setup the timer to clean up old posts from the feed
  1335. if (cleanIntv) window.clearInterval(cleanIntv);
  1336. if(calcTime(WM.opts.cleaninterval)>0) cleanIntv=window.setInterval(WM.cleanPosts, calcTime(WM.opts.cleaninterval)+250);
  1337.  
  1338. //setup global heartbeat
  1339. if (hbIntv) window.clearInterval(hbIntv);
  1340. hbIntv=window.setInterval(WM.onHeartbeat, WM.opts.heartRate);
  1341. }catch(e){log("WM.setIntervals: "+e);}},
  1342.  
  1343. hideCounters : function(){try{
  1344. hideNodes("//*[contains(@class,'accFailBlock')]");
  1345. }catch(e){log("WM.hideCounters: "+e);}},
  1346.  
  1347. showCounters : function(){try{
  1348. showNodes("//*[contains(@class,'accFailBlock')]");
  1349. }catch(e){log("WM.showCounters: "+e);}},
  1350.  
  1351. validatePost : function(fbPost){try{
  1352. //validate required post fields
  1353. /*if (!( exists(fbPost.application) && exists(fbPost.link) && fbPost.type=="link")) {
  1354. return;
  1355. }*/
  1356.  
  1357. //accept only posts we have sidekicks for
  1358. var app;
  1359. if (!exists(app=WM.apps[fbPost.app_id])) return;
  1360. //prevent redrawing same post in case one slips past the graph validator
  1361. var postID=fbPost.post_id;
  1362. if (WM.posts[postID]||null) return;
  1363.  
  1364. //accept only posts for which a sidekick is enabled
  1365. if (!WM.quickOpts.masterSwitch[app.appID]) return;
  1366.  
  1367. //create a Post object from the post data
  1368. var post=(WM.posts[fbPost]=new WM.Post(fbPost));
  1369. if (post) {
  1370. var hasID=post.identify();
  1371. WM.sortPosts(); //make sure new posts fit the current sort order and direction
  1372. if (hasID) {
  1373. WM.rulesManager.doEvent("onValidate",post);
  1374. WM.rulesManager.doEvent("onIdentify",post);
  1375. post.draw(true,true);
  1376. //track the post
  1377. if (WM.opts.useFriendTracker && !post.isMyPost){
  1378. WM.friendTracker.track(post);
  1379. }
  1380. }
  1381. } else {
  1382. log("WM.validatePost: Unable to transform post data into a useful post object. (id:"+fbPost.post_id+")");
  1383. }
  1384. }catch(e){log("WM.validatePost: "+e);}},
  1385.  
  1386. handleEdges : function(params){
  1387. /*
  1388. apps
  1389. friends
  1390. edge:{newer,older}
  1391. */
  1392. //console.log("handleEdges: "+JSON.stringify(params));
  1393. if (params.friends||null) {
  1394. //update user created feeds
  1395. for (var f=0,l=WM.feedManager.feeds.length;f<l;f++){
  1396. var feed = WM.feedManager.feeds[f];
  1397. //if this feed is listed in those passed back...
  1398. if (params.friends.contains(feed.id)){
  1399. //update each app filter in this feed
  1400. for (var c=0,l=params.apps.length;c<l;c++) {
  1401. var appID=params.apps[c];
  1402. filter = feed.filters["app_"+appID];
  1403. if (!(filter||null)) {
  1404. //this filter does not exist, create one
  1405. filter=feed.addFilter({id:"app_"+appID});
  1406. }
  1407. if (params.edge.older) filter.oldedge = params.edge.older;
  1408. if (params.edge.newer) filter.newedge = params.edge.newer;
  1409. filter.oldedgeNode.textContent = filter.oldedge;
  1410. filter.newedgeNode.textContent = filter.newedge;
  1411. if (timeStamp()-(filter.oldedge*1000)>olderLimit) filter.olderLimitReached=true;
  1412. }
  1413. }
  1414. }
  1415. } else {
  1416. //update base feed
  1417. feed = WM.feedManager.feeds[0];
  1418. for (var c=0,l=params.apps.length;c<l;c++) {
  1419. var appID=params.apps[c];
  1420. //update each app filter in this feed
  1421. filter = feed.filters["app_"+appID];
  1422. if (!(filter||null)) {
  1423. //this filter does not exist, create one
  1424. filter=feed.addFilter({id:"app_"+appID});
  1425. }
  1426. if (params.edge.older) filter.oldedge = params.edge.older;
  1427. if (params.edge.newer) filter.newedge = params.edge.newer;
  1428. filter.oldedgeNode.textContent = filter.oldedge;
  1429. filter.newedgeNode.textContent = filter.newedge;
  1430. if (timeStamp()-(filter.oldedge*1000)>olderLimit) filter.olderLimitReached=true;
  1431. }
  1432. }
  1433. },
  1434. fetch : function(params) {try{
  1435. /*
  1436. older:bool
  1437. newer:bool
  1438. apps:[]
  1439. feed:[]
  1440. targetEdge:unixtime
  1441. currentEdge:unixtime
  1442. bypassPause:bool
  1443. bypassFeedDisabled:bool
  1444. bypassAppDisabled:bool
  1445. */
  1446. params=params||{};
  1447. if (WM.fetchPaused && !params.bypassPause) return;
  1448.  
  1449. //convert a single passed app to a single entry list
  1450. if (exists(params.apps) && ((params.apps.objType||null)=="app")) {
  1451. var ret={};
  1452. ret[params.apps.appID]=params.apps;
  1453. params.apps=ret;
  1454. }
  1455. var useApps = params.apps||WM.apps;
  1456.  
  1457. //convert a single passed feed to an array
  1458. if (exists(params.feeds) && ((params.feeds.objType||null)=="feed")) {
  1459. params.feeds=[params.feeds];
  1460. }
  1461. params.currentEdge = params.currentEdge||null; //nullify undefined edge
  1462. //for each feed individually
  1463. var feeds=params.feeds||WM.feedManager.feeds;
  1464. for (var f=0,len=feeds.length;f<len;f++) {
  1465. var feed=feeds[f];
  1466. var friend=(feed.url!="https://graph.facebook.com/me/home")?[feed.id]:null;
  1467. //ignore the old me feed because it is a duplicate of the wall feed
  1468. if (feed.url!="https://graph.facebook.com/me/feed") if (feed.enabled || params.bypassFeedDisabled) {
  1469. //for each app make a separate fetch call for the given feed
  1470. //override this: no more by-app fetching
  1471. if (false && !WM.opts.groupFetching && (useApps||null)) {
  1472. for (var a in useApps) {
  1473. var app=useApps[a];
  1474. //only fetch for enabled apps
  1475. //where we are fetching new
  1476. //or if we are fetching old we are not at our older limit
  1477. var feedFilter=feed.filters["app_"+a];
  1478. if ((app.enabled || params.bypassAppDisabled) && (feedFilter.enabled || params.bypassFilterDisabled) && !(
  1479. params.older && feedFilter.olderLimitReached
  1480. )
  1481. ){
  1482. var G=Graph.fetchPostsFQL_B({
  1483. callback:WM.validatePost,
  1484. direction:(params.newer?1:(params.older?-1:0)),
  1485. limit:WM.opts.fetchQty,
  1486. targetEdge:(params.targetEdge||null), //special for new rules manager actions
  1487. friends:friend,
  1488. apps:[app.appID],
  1489. currentEdge:params.currentEdge||(params.newer?feedFilter.newedge:(params.older?feedFilter.oldedge:null)),
  1490. edgeHandler:WM.handleEdges,
  1491. noAppFiltering:WM.opts.noAppFiltering
  1492. });
  1493. }
  1494. }
  1495. //join apps together before fetching a single time for the given feed
  1496. } else {
  1497. //get the keys of the apps collection
  1498. var keys=Object.keys(useApps);
  1499. //if any sidekicks are docked
  1500. if (keys.length) {
  1501. //get the values of the apps collection
  1502. var appsToProcess=keys.map(function (key) {
  1503. return useApps[key];
  1504. //filter out which apps are able to be fetched for
  1505. }).filter(function(o,i,p){
  1506. //get the feed filter text
  1507. var feedFilter=feed.filters["app_"+o.appID];
  1508. //get if the app is enabled
  1509. var isEnabled = (o.enabled || params.bypassAppDisabled);
  1510. var isFilterEnabled=true,isOlderLimitReached=false;
  1511. if (feedFilter||null) {
  1512. //get if the feed filter is enabled
  1513. isFilterEnabled = (feedFilter.enabled || params.bypassFilterDisabled);
  1514. //get if the feed filter has already reached its older edge limit
  1515. isOlderLimitReached = (params.older && feedFilter.olderLimitReached);
  1516. } else {
  1517. //feed filter does not exist for this app
  1518. //assume it was deleted by the user on purpose
  1519. //and don't fetch for this app on this feed
  1520. log("WM.fetch: could not find filter for " + o.appID + "in feed " + feed.id);
  1521. return false;
  1522. }
  1523. if (isEnabled && isFilterEnabled && !isOlderLimitReached) return true;
  1524. return false;
  1525. //simply the array
  1526. }).map(function(o,i,p){
  1527. //just get the id's of apps to do, not the entire app object
  1528. return o.appID;
  1529. });
  1530. //make sure we matched filters to process
  1531. if (appsToProcess.length){
  1532. //get the shared edges of the passed apps
  1533. var edges = feed.getMergedEdges({apps:appsToProcess});
  1534. //console.log("getMergedEdges returned: "+JSON.stringify(edges));
  1535. var G=Graph.fetchPostsFQL_B({
  1536. callback:WM.validatePost,
  1537. direction:(params.newer?1:(params.older?-1:0)),
  1538. limit:WM.opts.fetchQty,
  1539. targetEdge:(params.targetEdge||null), //special for new rules manager actions
  1540. friends:friend,
  1541. apps:appsToProcess,
  1542. currentEdge:params.currentEdge||(params.newer?edges.newedge:(params.older?edges.oldedge:null)),
  1543. edgeHandler:WM.handleEdges,
  1544. noAppFiltering:WM.opts.noAppFiltering
  1545. });
  1546. }
  1547. }
  1548. }
  1549. }
  1550. }
  1551. }catch(e){log("WM.fetch: "+e);}},
  1552. changeDebugSettings : function(){try{
  1553. if (debug && debug.initialized) {
  1554. debug.doDebug = WM.opts.debug;
  1555. debug.debugLevel = parseInt(WM.opts.debugLevel);
  1556. debug.debugMaxComments = WM.opts.debugMaxComments;
  1557. debug.useScrollIntoView = WM.opts.debugScrollIntoView;
  1558. debug.stackRepeats = WM.opts.debugStackRepeats;
  1559. } else {
  1560. if (debug) debug.init();
  1561. setTimeout(WM.changeDebugSettings,1000);
  1562. }
  1563. }catch(e){log("WM.changeDebugSettings: "+e);}},
  1564. changeConfigSettings : function(){try{
  1565. WM.config.sectionsAsTabs=WM.opts.configSectionsAsTabs;
  1566. WM.config.separatorsAsTabs=WM.opts.configSeparatorsAsTabs;
  1567. WM.config.useScrollIntoView=WM.opts.configScrollIntoView;
  1568. WM.config.confirms={
  1569. save:WM.opts.configConfirmSave,
  1570. cancel:WM.opts.configConfirmCancel,
  1571. "import":WM.opts.configConfirmImport,
  1572. restore:WM.opts.configConfirmRestore
  1573. };
  1574. }catch(e){log("WM.changeConfigSettings: "+e);}},
  1575.  
  1576. resizeConsole : function(){try{
  1577. //negotiate height with fb bluebar
  1578. var node=$("pagelet_bluebar");
  1579. var h=(node)?elementOuterHeight(node):0;
  1580. with($("wmContent")){
  1581. style.height=document.documentElement.offsetHeight-h+"px";
  1582. style.width=document.documentElement.offsetWidth+"px";
  1583. }
  1584. WM.console.tabContainer.redraw();
  1585. WM.console.collectTabControl.redraw();
  1586.  
  1587. }catch(e){log("WM.resizeConsole: "+e);}},
  1588.  
  1589. setColors : function(){try{
  1590. var colors=["excluded","working","timeout","paused","nodef","failed","accepted","scam","pinned"];
  1591. var css="";
  1592. for (var c=0, color; (color=colors[c]); c++) {
  1593. css+=("div."+color+"{background-color:"+WM.opts["colors"+color]+" !important;}\n");
  1594. }
  1595. //set the new transition delay timer
  1596. css+=(".wm.post.short:hover .floater {-moz-transition-property: padding,border,width,height;-moz-transition-delay:"+WM.opts["transitiondelay"]+"s; width:240px; padding:5px 10px;border:1px solid;}\n");
  1597. remove($("user_colors_css"));
  1598. addGlobalStyle(css,"user_colors_css");
  1599. }catch(e){log("WM.setColors: "+e);}},
  1600.  
  1601. initConsole : function(){try{
  1602. WM.console.loading=false;
  1603. if (WM.console.initialized) log("WM Console Initialized");
  1604.  
  1605. //show options menu button
  1606. with (WM.console.configButton) {
  1607. className = className.removeWord("jsfHidden");
  1608. }
  1609.  
  1610. //set console heights
  1611. WM.resizeConsole();
  1612.  
  1613. //load feed sources
  1614. WM.feedManager.init();
  1615. //import friend tracker data
  1616. //and delete posts out of bounds with our "track for how many days"
  1617. WM.friendTracker.init();
  1618. WM.friendTracker.clean();
  1619.  
  1620. //initialize user colors
  1621. WM.setColors();
  1622. //set up the priorities and limits object
  1623. //and new rules manager
  1624. WM.rulesManager.init();
  1625.  
  1626. //decipher the dynamic tests
  1627. WM.grabber.init();
  1628.  
  1629. //show counters
  1630. if (WM.opts.showcounters) WM.showCounters(); else WM.hideCounters();
  1631. //set intervals
  1632. WM.setIntervals();
  1633. //set autopause
  1634. if (WM.opts.autopausecollect) WM.pauseCollecting(true);
  1635. if (WM.opts.autopausefetch) WM.pauseFetching(true);
  1636. //open a channel for sidekick communication
  1637. WM.fetchSidekickData();
  1638.  
  1639. //add an entrypoint for sidekicks since we know FB gave us access
  1640. var createDock = function(){
  1641. document.body.appendChild(
  1642. createElement('div',{id:'wmDock',style:'display:none;',onclick:function(){
  1643. WM.dock.answerDockingDoor();
  1644. }})
  1645. );
  1646. document.body.appendChild(
  1647. createElement('div',{id:'wmDataDump',style:'display:none;'})
  1648. );
  1649. };
  1650. createDock();
  1651.  
  1652. }catch(e){log("WM.initConsole: "+e);}},
  1653.  
  1654. cleanHistory : function(params){try{
  1655. log("Cleaning History");
  1656. params=params||{};
  1657. var ask=WM.opts.historyConfirmClean;
  1658. if (params.noConfirm || !ask || (ask && confirm("Clean and pack history for this profile?"))){
  1659. //history = getOptJSON("history_"+WM.currentUser.profile)||{};
  1660. var ageDays=parseInt(WM.opts.itemage);
  1661. var timeNow=timeStamp();
  1662. for(var i in WM.history) {
  1663. if( ( (timeNow-WM.history[i].date) /day) > ageDays) {
  1664. delete WM.history[i];
  1665. }
  1666. }
  1667. setOptJSON("history_"+WM.currentUser.profile, WM.history);
  1668. }
  1669. }catch(e){log("WM.cleanHistory: "+e);}},
  1670.  
  1671. optionsSetup : function(){try{
  1672. debug.print("WM.optionsSetup:");
  1673.  
  1674. //create the settings tree
  1675. WM.config = new Config({
  1676. storageName:"settings_"+(WM.quickOpts.useGlobalSettings?"global":WM.currentUser.profile),
  1677. onSave:WM.onSave,
  1678. title:"FB Wall Manager "+WM.version+(WM.quickOpts.useGlobalSettings?" (!! Global Settings !!)":""),
  1679. logo:createElement("span",{}[
  1680. createElement("img",{className:"logo",src:"",textContent:"v"+WM.version}),
  1681. createElement("text","v"+WM.version)
  1682. ]),
  1683. css:(
  1684. WM.console.dynamicIcons()+
  1685. jsForms.globalStyle()
  1686. ),
  1687. settings:{
  1688. btn_useGlobal:{
  1689. type:"button",
  1690. label:"Use Global Settings",
  1691. title:"Switch to using a global storage for settings. Those settings can then be used by other accounts (not browser profiles).",
  1692. script:function(){
  1693. if (WM.quickOpts.useGlobalSettings||false) {
  1694. //already using global settings
  1695. return;
  1696. }
  1697. if (confirm("Switch to using global (shared) settings?")){
  1698. WM.quickOpts.useGlobalSettings=true;
  1699. WM.saveQuickOpts();
  1700. WM.config.title = "FB Wall Manager "+WM.version+" (!! Global Settings !!))";
  1701. WM.config.storageName = "settings_global";
  1702. WM.config.values=WM.config.read();
  1703. WM.config.configure();
  1704. WM.config.reload();
  1705. }
  1706. },
  1707. },
  1708. btn_useOwnProfile:{
  1709. type:"button",
  1710. label:"Use Profile Settings",
  1711. title:"Switch to using your own profile storage for settings.",
  1712. script:function(){
  1713. if (!(WM.quickOpts.useGlobalSettings||false)) {
  1714. //already using profile settings
  1715. return;
  1716. }
  1717. if (confirm("Switch to using your own profile settings?")){
  1718. WM.quickOpts.useGlobalSettings=false;
  1719. WM.saveQuickOpts();
  1720. WM.config.title = "FB Wall Manager "+WM.version;
  1721. WM.config.storageName = "settings_"+WM.currentUser.profile;
  1722. WM.config.values=WM.config.read();
  1723. WM.config.configure();
  1724. WM.config.reload();
  1725. }
  1726. },
  1727. },
  1728. wmtab_opts:tabSection("Host Options",{
  1729. section_basicopts:section("Basics",{
  1730. /*authTokenTools:optionBlock("Authorization",{
  1731. devAuthToken:checkBox("Automatically check my developer tool app for my Auth Token"),
  1732. },true),*/
  1733. intervals:optionBlock("Post Fetching",{
  1734. newinterval:{
  1735. label:"Get Newer Posts Interval",
  1736. type:"selecttime",
  1737. title:"Fetch new posts from facebook after a set time.",
  1738. options:{
  1739. "off":"Off",
  1740. "tenth":"6 seconds",
  1741. "sixth":"10 seconds",
  1742. "half":"30 seconds",
  1743. "one":"1 minute",
  1744. "two":"2 minutes",
  1745. "three":"3 minutes",
  1746. "four":"4 minutes",
  1747. "five":"5 minutes",
  1748. "ten":"10 minutes",
  1749. },
  1750. "default":"t:30s"
  1751. },
  1752.  
  1753. fetchQty:{
  1754. label:"Fetch how many? (subject to filtering)",
  1755. type:"select",
  1756. title:"Posts fetched per request. Higher numbers affect speed of fetching.",
  1757. options:{
  1758. "5":"5",
  1759. "10":"10",
  1760. "25":"25",
  1761. "50":"50",
  1762. "100":"100",
  1763. "250":"250",
  1764. "500":"500 (FB maximum)", //known maximum fetch as of 9/8/2013
  1765. },
  1766. "default":"25"
  1767. },
  1768.  
  1769. oldinterval:{
  1770. label:"Get Older Posts Interval",
  1771. type:"selecttime",
  1772. title:"Fetch previous posts from facebook after a set time.",
  1773. options:{
  1774. "off":"Off",
  1775. "tenth":"6 seconds",
  1776. "sixth":"10 seconds",
  1777. "half":"30 seconds",
  1778. "one":"1 minute",
  1779. "two":"2 minutes",
  1780. "three":"3 minutes",
  1781. "four":"4 minutes",
  1782. "five":"5 minutes",
  1783. "ten":"10 minutes",
  1784. },
  1785. "default":"off"
  1786. },
  1787.  
  1788. maxinterval:{
  1789. label:"How old is too old?",
  1790. type:"selecttime",
  1791. title:"Tell WM what you think is a good max post age to fetch. Also affects which posts are considered 'stale'.",
  1792. options:{
  1793. "off":"Off/Infinite",
  1794. "hour":"1",
  1795. "2hour":"2",
  1796. "3hour":"3",
  1797. "4hour":"4",
  1798. "8hour":"8",
  1799. "12hour":"12",
  1800. "18hour":"18",
  1801. "24hour":"24",
  1802. "32hour":"32",
  1803. "48hour":"48",
  1804. },
  1805. "default":"t:1d"
  1806. },
  1807.  
  1808. groupFetching:checkBox("All installed sidekicks in one request (default: one request per sidekick)",false,{},true),
  1809. noAppFiltering:checkBox("Have WM filter posts for you instead of having facebook do it (may prevent some empty data set issues)",false,{},true),
  1810. },true),
  1811.  
  1812. autoPauseBlock:optionBlock("Fetching/Collecting Autopause",{
  1813. autopausefetch:checkBox("Pause Fetching after First Fetch"),
  1814. autopausecollect:checkBox("Pause Collection on Startup"),
  1815. },true),
  1816.  
  1817. multiTaskBlock:optionBlock("Multi-task",{
  1818. maxrequests:inputBox("Max requests simultaneously",1),
  1819. recycletabs:inputBox("Recycle Windows/Tabs",1),
  1820. recycletabsall:checkBox("Recycle All",true),
  1821. },true),
  1822.  
  1823. queBlock:optionBlock("Task-Queue",{
  1824. queuetabs:checkBox("Force all posts and autolikes through one tab using a queue (overrides multi-task)",true),
  1825. },true),
  1826.  
  1827. timeoutBlock:optionBlock("Time-outs",{
  1828. reqtimeout:inputBox("Item Acceptance Page Timeout (seconds)",30),
  1829. failontimeout:checkBox("Mark Timeout as Failure (default: retry indefinitely)"),
  1830. },true),
  1831. }),
  1832.  
  1833. section_access:section("Accessibility",{
  1834. shortModeBlock:optionBlock("Short Mode",{
  1835. thumbsize:{
  1836. label:"Thumbnail Size",
  1837. type:"select",
  1838. title:"Size of bonus thumbnails in display mode: short and .",
  1839. options:{
  1840. "mosquito":"16px",
  1841. "tiny":"24px",
  1842. "small":"32px",
  1843. "medium":"48px",
  1844. "large":"64px",
  1845. "xlarge":"96px",
  1846. },
  1847. "default":"medium"
  1848. },
  1849. transitiondelay:inputBox("Hover Box Delay (s)",1),
  1850. },true),
  1851.  
  1852. accessTweaksBlock:optionBlock("Tweaks",{
  1853. debugrecog:checkBox("Show Identified Text (instead of original link text)",true),
  1854. showcounters:checkBox("Show Accept/Fail Counts",true),
  1855. showdynamictips:checkBox("Show Dynamic Console Tips",true),
  1856. appsConfirmDeleteUDT:checkBox("Confirm Delete User Defined Types",true),
  1857. },true),
  1858.  
  1859. toolBoxBlock:optionBlock("Customize Post Toolbox",{
  1860. showtoolbox:checkBox("Enable ToolBox", true),
  1861.  
  1862. showopen:checkBox("Open Post",true),
  1863. showmarkfailed:checkBox("Mark As Failed",true),
  1864. showmarkaccepted:checkBox("Mark As Accepted",true),
  1865. showlike:checkBox("Like Post",true),
  1866. showreid:checkBox("Re-ID Post",true),
  1867. showmovetop:checkBox("Move to Top",true),
  1868. showmovebottom:checkBox("Move to Bottom",true),
  1869. showpin:checkBox("Pin Post",true),
  1870. showclean:checkBox("Clean Post",true),
  1871. showpostsrc:checkBox("Show Post Source",true),
  1872.  
  1873. //new stuff
  1874. showcancelprocess:checkBox("Cancel Process or Like",true),
  1875. showrestartprocess:checkBox("Restart Process or Like",true),
  1876. showpausetype:checkBox("Pause Bonus Type",true),
  1877. showunpausetype:checkBox("Unpause Bonus Type",true),
  1878. showaddfeed:checkBox("Add To Feeds",true),
  1879.  
  1880. showmakerule:checkBox("Rule From Post",true),
  1881. showoriginaldata:checkBox("Show Original Data",true),
  1882. showautocomment:checkBox("Auto Comment",true),
  1883. },true),
  1884. littleToolBoxBlock:optionBlock("Customize Mini Toolbox",{
  1885. littleButtonSize:{
  1886. label:"Mini Toolbutton Size (requires refresh to redraw)",
  1887. type:"select",
  1888. title:"Size of buttons on mini toolbars",
  1889. options:{
  1890. "16":"16px",
  1891. "24":"24px",
  1892. "32":"32px",
  1893. },
  1894. "default":"24",
  1895. },
  1896. },true),
  1897.  
  1898. userColorsBlock:optionBlock("Colors",{
  1899. colorsaccepted:colorBox("Accepted","limegreen"),
  1900. colorsfailed:colorBox("Failed","red"),
  1901. colorsworking:colorBox("Working","yellow"),
  1902. colorsexcluded:colorBox("Excluded","gray"),
  1903. colorspaused:colorBox("Paused","silver"),
  1904. colorsnodef:colorBox("No Definition","deepskyblue"),
  1905. colorsscam:colorBox("Potential Scam","purple"),
  1906. colorspinned:colorBox("Pinned","black"),
  1907. colorstimeout:colorBox("Timeout","orange"),
  1908. },true),
  1909.  
  1910. }),
  1911.  
  1912. section_feedback:section("Feedback",{
  1913. publishwarning:{type:"message",title:"Autolike has changed",textContent:"As of WM beta 40 you must allow 'publish_actions' on the 'user data permissions' tab in your Graph API Explorer token builder.",newitem:true},
  1914. gotoapiexplorer:anchor("Visit API Explorer","http://developers.facebook.com/tools/explorer?&version=v1.0"),
  1915. autoSetup:optionBlock("Setup",{
  1916. useautocomment:checkBox("Use Auto-comment (experimental)"),
  1917. useautolike:checkBox("Use Auto-like"),
  1918. //autoliketimeout:inputBox("Timeout (seconds)",30),
  1919. autolikedelay:inputBox("Ban-Prevention Delay (seconds)",3),
  1920. },true),
  1921. autoLikeBlock:optionBlock("Perform Feedback For",{
  1922. autolikeall:checkBox("All Posts"),
  1923. autolikeaccepted:checkBox("Accepted Posts"),
  1924. autolikesent:checkBox("Sent Posts"),
  1925. },true),
  1926. autoCommentListBlock:optionBlock("Comments (experimental)",{
  1927. autocommentlist:textArea("Random Comments (One per line)","Thanks\nThank you\nthanks"),
  1928. },true),
  1929. blockautolikebygame:optionBlock("Block Feedback by Game",{},false),
  1930. }),
  1931.  
  1932. section_filters:section("Filters",{
  1933. displayfilters:optionBlock("Remove Feed Parts (Classic Mode Only)",{
  1934. hideimages:checkBox("Images (All)"),
  1935. hideimagesunwanted:checkBox("Images (Unwanted Posts)"),
  1936. hidebody:checkBox("Post Body Text"),
  1937. hidevia:checkBox("Via App"),
  1938. hidedate:checkBox("Date/Time"),
  1939. },true),
  1940.  
  1941. filters:optionBlock("Hide By Type",{
  1942. hidemyposts:checkBox("My Posts"),
  1943. hideunwanted:checkBox("Unwanted"),
  1944. hideaccepted:checkBox("Accepted"),
  1945. hidefailed:checkBox("Failed"),
  1946. hidescams:checkBox("Scams"),
  1947. hidestale:checkBox("Stale Posts"),
  1948. hideexcluded:checkBox("Excluded"),
  1949. hideliked:checkBox("Liked By Me"),
  1950. hideunsupported:checkBox("Unsupported Apps"),
  1951.  
  1952. donthidewishlists:checkBox("Don't Hide Known Wish Lists"),
  1953. }),
  1954.  
  1955. //allow hiding all posts by particular games
  1956. filterapps:optionBlock("Hide By App",{}),
  1957.  
  1958. //now added dynamically as appID+"dontsteal"
  1959. dontstealBlock:optionBlock("Don't take W2W posts not for me",{}),
  1960.  
  1961. skipopts:optionBlock("Skip By Type",{
  1962. skipliked:checkBox("Liked By Me"),
  1963. skipstale:checkBox("Day-Old Posts"),
  1964. }),
  1965.  
  1966. filterTweaksBlock:optionBlock("Tweaks",{
  1967. accepton15:checkBox("Mark 'Unrecognized Response' As Accepted"),
  1968. markliked:checkBox("Mark Liked As Accepted (must check Skip Liked)"),
  1969. },true),
  1970.  
  1971. filterCleanupBlock:optionBlock("Cleanup",{
  1972. cleaninterval:{
  1973. label:"Cleanup Interval",
  1974. type:"selecttime",
  1975. title:"Remove unwanted posts from collection console after a set time.",
  1976. options:{
  1977. "off":"Off",
  1978. "one":"1 minute",
  1979. "two":"2 minutes",
  1980. "five":"5 minutes",
  1981. "ten":"10 minutes",
  1982. "fifteen":"15 minutes",
  1983. "thirty":"30 minutes",
  1984. "hour":"1 hour",
  1985. },
  1986. "default":"off"
  1987. },
  1988. cleanTimedOut:checkBox("Clean timed out posts",true),
  1989. },true),
  1990. }),
  1991.  
  1992. section_history:section("History",{
  1993. itemage:inputBox("How long to keep tried items in memory (days)",2),
  1994. oblock_historyConfirms:optionBlock("Confirm (Changes available on next config open)",{
  1995. historyConfirmClear:{type:"checkbox",label:"Clear History",title:"Confirm before clearing history.","default":true},
  1996. },true),
  1997. reset:button("Clear History",
  1998. WM.resetAccepted
  1999. ),
  2000. }),
  2001.  
  2002. section_feedopts:section("Feeds Manager",{
  2003. oblock_feedsConfirms:optionBlock("Confirm",{
  2004. feedsConfirmDeleteFeed:{type:"checkbox",label:"Delete Rule",title:"Require confirmation to delete a feed.","default":true},
  2005. },true),
  2006. }),
  2007.  
  2008. section_dynamicopts:section("Dynamic Grabber",{
  2009. dynamicopts:optionBlock("Dynamic Collection",{
  2010. dynamicFirst:checkBox("Run Dynamics BEFORE Sidekicks",true),
  2011. },true),
  2012. enableDynamic:optionBlock("Enable Dynamics by Game",{}),
  2013. oblock_dynamicConfirms:optionBlock("Confirm",{
  2014. dynamicConfirmDeleteTest:{type:"checkbox",label:"Delete Rule",title:"Require confirmation to delete a test.","default":true},
  2015. },true),
  2016. }),
  2017.  
  2018. section_friendtrackopts:section("Friend Tracker",{
  2019. useFriendTracker:checkBox("Enable Friend Tracking",true),
  2020. trackTime:inputBox("Track For How Many Days",2),
  2021. trackeropts:optionBlock("Track Data",{
  2022. trackCreated:checkBox("Post Creation Counts",true),
  2023. trackLastKnownPost:checkBox("Last Known Post Time",true),
  2024. trackAccepted:checkBox("Bonuses Accepted",true),
  2025. trackFailed:checkBox("Bonuses Failed",true),
  2026. },true),
  2027. oblock_trackerConfirms:optionBlock("Confirm",{
  2028. trackConfirmClearUser:{type:"checkbox",label:"Clear User Data",title:"Require confirmation to clear user data.","default":true},
  2029. },true),
  2030. }),
  2031. section_rulesopts:section("Rules Manager",{
  2032. oblock_rulesHeartbeat:optionBlock("Heartbeat",{
  2033. heartRate:inputBox("Global Heartbeat Delay (ms)",1000),
  2034. heartbeatAffectsApps:{type:"checkbox",label:"Affect Apps",title:"Heartbeat can be heard at app level on every rule at once. This can slow down your system."},
  2035. heartbeatAffectsPosts:{type:"checkbox",label:"Affect Posts",title:"Heartbeat can be heard at post level on every rule at once. This can slow down your system."},
  2036. heartbeatAffectsRules:{type:"checkbox",label:"Affect Rules",title:"Heartbeat can be heard at rule level on every rule at once. This can slow down your system."},
  2037. heartbeatAffectsFeeds:{type:"checkbox",label:"Affect Feeds",title:"Heartbeat can be heard at feed level on every rule at once. This can slow down your system."},
  2038. heartbeatAffectsFeedFilters:{type:"checkbox",label:"Affect Feed Filters",title:"Heartbeat can be heard at feed filter level on every rule at once. This can slow down your system."},
  2039. },true),
  2040. oblock_rulesConfirms:optionBlock("Confirm",{
  2041. rulesConfirmDeleteValidator:{type:"checkbox",label:"Delete Validator",title:"Require confirmation to delete a rule's validator.","default":true},
  2042. rulesConfirmDeleteAction:{type:"checkbox",label:"Delete Action",title:"Require confirmation to delete a rule's action.","default":true},
  2043. rulesConfirmDeleteRule:{type:"checkbox",label:"Delete Rule",title:"Require confirmation to delete a rule.","default":true},
  2044. rulesConfirmResetLimit:{type:"checkbox",label:"Reset Limit",title:"Require confirmation to reset individual limits.","default":true},
  2045. rulesConfirmResetAllLimits:{type:"checkbox",label:"Reset All Limits",title:"Require confirmation to reset all limits.","default":true},
  2046. rulesConfirmHatch:{type:"checkbox",label:"Hatch Eggs",title:"Require confirmation to hatch eggs.","default":true},
  2047. },true),
  2048. rulesJumpToNewRule:{type:"checkbox",label:"Jump To New Rules",title:"When new rules are created from tests or posts, select the rules manager tab and scroll the new rule into view.","default":true},
  2049. }),
  2050.  
  2051. section_dev:section("Debug",{
  2052. oblock_debugTweaks:optionBlock("Tweaks",{
  2053. pinundefined:checkBox("Pin Undefined Bonus Types"),
  2054. },true),
  2055. debugOpts:optionBlock("Debug",{
  2056. debug:checkBox("Enable Debug",true),
  2057. debugLevel:{
  2058. label:"Debug Sensitivity",
  2059. title:"Sets the level of errors and warnings to report. 0 is all, 5 shows only the worst.",
  2060. type:"select",
  2061. options:{
  2062. "0":"Function calls",
  2063. "1":"Function subsections & debug notes",
  2064. "2":"Captured expected errors",
  2065. "3":"Known open errors",
  2066. "4":"Unexpected errors",
  2067. "5":"Fatal errors",
  2068. },
  2069. "default":"0"
  2070. },
  2071. debugMaxComments:inputBox("Max debug lines (0 for no limit)",100),
  2072. debugScrollIntoView:checkBox("Use scrollIntoView"),
  2073. debugStackRepeats:checkBox("Stack Immediate Repeats"),
  2074.  
  2075. },true),
  2076. advDebugOpts:optionBlock("Advanced Debug",{
  2077. devDebugFunctionSubsections:checkBox("Debug Function Subsections",false),
  2078. devDebugGraphData:checkBox("Debug Graph Packets (not available for Chrome)",false),
  2079. },true),
  2080. GM_special:optionBlock("Script-runner Options",{
  2081. useGM_openInTab:checkBox("Use GM_openInTab instead of window.open",false),
  2082. },true),
  2083.  
  2084. }),
  2085.  
  2086. section_configopts:section("Config",{
  2087. oblock_configConfirms:optionBlock("Confirm (Changes available on next config open)",{
  2088. configConfirmSave:{type:"checkbox",label:"Save",title:"Confirm before saving settings.","default":true},
  2089. configConfirmCancel:{type:"checkbox",label:"Cancel",title:"Confirm before closing settings without saving.","default":true},
  2090. configConfirmImport:{type:"checkbox",label:"Import",title:"Confirm before importing settings.","default":true},
  2091. configConfirmRestore:{type:"checkbox",label:"Restore Defaults",title:"Confirm before restoring defaults.","default":true},
  2092. },true),
  2093. oblock_configStyling:optionBlock("Styling (Changes available on next config open)",{
  2094. configSectionsAsTabs:{type:"checkbox",label:"Display Sections as Tabs",title:"Converts top level roll-outs only. Display those rollouts as tabs on next open of config."},
  2095. configSeparatorsAsTabs:{type:"checkbox",label:"Display Separators as Tabs",title:"Converts second level roll-outs only. Display those rollouts as tabs on next open of config. Removes select all/none buttons on top of the separator."},
  2096. },true),
  2097. oblock_configTweaks:optionBlock("Tweaks (Changes available on next config open)",{
  2098. configScrollIntoView:{type:"checkbox",label:"Use scrollIntoView",title:"When tabs and sections are opened, use the scrollIntoView function to bring them more fully into view. This is jerky at best."},
  2099. },true),
  2100. }),
  2101. }),
  2102.  
  2103. wmtab_games:tabSection("Sidekick Options",{
  2104. skmovedwarning:{type:"message",title:"Sidekick options have moved",textContent:"Sidekick options have been moved to separate config windows. Access them by using the 'Manage Sidekicks' tab, where you can find new 'Options' buttons for each sidekick."},
  2105. }),
  2106.  
  2107. wmtab_info:tabSection("Info",{
  2108. MainMessageCenter:separator("Documentation - Messages - Help",null,{
  2109. Mainupdate:anchor("Update Script","http://userscripts.org/scripts/source/86674.user.js"),
  2110. donateWM:{type:"link",label:"Donate for FBWM via Paypal",href:"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=merricksdad%40gmail%2ecom&lc=US&item_name=Charlie%20Ewing&item_number=FBWM&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"},
  2111. Mainwikipage:anchor("Wiki Support Page","http://fbwm.wikia.com/wiki/Known_Issues"),
  2112. Mainsetupinfo:anchor("Setup Info","http://fbwm.wikia.com/wiki/New_User_Setup"),
  2113. Maindiscuss:anchor("Known Bugs","http://fbwm.wikia.com/wiki/Known_Issues"),
  2114. Mainrevisionlog:anchor("Revision Log","http://fbwm.wikia.com/wiki/Revisions"),
  2115. },true),
  2116. }),
  2117. wmtab_scripts:tabSection("Get More!",{
  2118. }),
  2119.  
  2120. },
  2121. });
  2122. // add options shortcut to user script commands
  2123. GM_registerMenuCommand("Wall Manager "+WM.version+" Options", function(){WM.config.open();});
  2124. }catch(e){log("WM.optionsSetup: "+e);}},
  2125.  
  2126. init : function(){try{
  2127. //capture user id/alias/name and make it global
  2128. WM.currentUser.id = Graph.userID;
  2129. WM.currentUser.alias = Graph.userAlias;
  2130. WM.currentUser.profile = WM.currentUser.alias||WM.currentUser.id;
  2131.  
  2132. debug.print("UserID:"+WM.currentUser.id+"; UserAlias:"+WM.currentUser.alias+"; WM is Using:"+WM.currentUser.profile);
  2133.  
  2134. //get WM.quickOpts
  2135. WM.quickOpts = getOptJSON('quickopts_'+WM.currentUser.profile)||{};
  2136. WM.quickOpts["filterApp"]=(WM.quickOpts["filterApp"]||"All");
  2137. WM.quickOpts["displayMode"]=(WM.quickOpts["displayMode"]||"0");
  2138. WM.quickOpts["masterSwitch"]=(WM.quickOpts["masterSwitch"]||{});
  2139. WM.quickOpts["useGlobalSettings"]=(WM.quickOpts["useGlobalSettings"]||false);
  2140.  
  2141. //create the options menu
  2142. WM.optionsSetup();
  2143.  
  2144. //duplicate the options saved in WM.config
  2145. WM.updateSettingsValues();
  2146.  
  2147. //set up the config with its internal special variables
  2148. WM.changeConfigSettings();
  2149.  
  2150. //setup debug beyond its defaults
  2151. WM.changeDebugSettings();
  2152. //clean history
  2153. WM.history = getOptJSON('history_'+WM.currentUser.profile)||{};
  2154. WM.cleanHistory();
  2155. //prep the console now that we have an id and/or alias
  2156. //and then carry on with our init
  2157. WM.console.init({callback:WM.initConsole});
  2158. }catch(e){log("WM.init: "+e);}},
  2159. receiveSidekickMessage: function(event) {
  2160. if (isObject(event.data)) {
  2161. var data=event.data; //just shorten the typing
  2162. if (data.channel=="WallManager"){
  2163. //log(JSON.stringify(data));
  2164. //$("WM_debugWindow").childNodes[1].lastChild.scrollIntoView();
  2165. switch (data.msg){
  2166. case 2: //getting a comOpen response from sidekick
  2167. //WM.collector.tabs[data.tabID].comOpen=true;
  2168. break;
  2169. case 4: //getting a status package from sidekick
  2170. switch (data.params.action){
  2171. case "onFrameLoad":
  2172. WM.onFrameLoad(data.params);
  2173. break;
  2174. case "onFrameLoad3":
  2175. WM.onFrameLoad3(data.params);
  2176. break;
  2177. }
  2178. break;
  2179. }
  2180. }
  2181. }
  2182. },
  2183.  
  2184. run : function() {try{
  2185. // pre-load console images
  2186. //for(var img in imgs) try{new Image().src = imgs[img];}catch(e){log("preload: "+e);}
  2187.  
  2188. //special about:config entry for disabling storage of fb auth token
  2189. //should help multi account users
  2190. //if (getOpt("disableSaveAuthToken"))
  2191. Graph.authToken=null;
  2192. //patch 38 auth token stuff
  2193. var flagManualAuthSuccessful=getOpt("flagManualAuthSuccessful")||false;
  2194. if (WallManager.switches.manualAuthToken && !flagManualAuthSuccessful) {
  2195. var m="WM can no longer access your FB Access Token without your manual assistance.\nTo successfully fetch posts, please complete the following:\n\n*In a new browser window, visit: http://developers.facebook.com/tools/explorer\n\n*If required, allow that app access to your facebook information\n*Find the 'Get Access Token' button and click it\n*In the panel that appears, click 'extended permissions'\n*Be sure that 'read_stream' is selected or otherwise not blank\n*If you want to use autolike/autocomment also select 'publish_actions' from the 'user data permissions' tab*Click the 'Get Access Token' button\n*Now find the box that says 'Access Token' and select its value\n*Copy that value and paste it into the box on this promp\n\nNote: this token does not last forever, you may need to repeat this process";
  2196. var manualToken = prompt(m,"paste token here");
  2197. //validate manualToken at least somewhat
  2198. //halt if manual token is not given
  2199. if (manualToken=="" || manualToken==null || manualToken=="paste token here") {
  2200. alert("manual token not accepted, please refresh and try again");
  2201. return;
  2202. }
  2203. //pass the manual token along
  2204. Graph.authToken=manualToken;
  2205. //consider saving time by looking for auth tokens automatically from here out
  2206. var m = "WM thinks your auth token setup is successful.\nIf you like, I can make it so WM just checks your dev tool for new auth tokens every time.\n\nPress Cancel to continue entering auth codes manually.\n\n*If you have multiple facebook accounts on this computer using WM, please make sure you set up the API explorer with every account.";
  2207. var saveProgress = confirm(m);
  2208. if (saveProgress) {
  2209. setOpt("flagManualAuthSuccessful",true);
  2210. }
  2211. }
  2212.  
  2213. var G=Graph.fetchUser({callback:WM.init});
  2214. if (G){if (G.requestAlreadyOut) {
  2215. } else if (G.initRequestSlow) {
  2216. } else if (G.olderLimitReached) {
  2217. } else if (G.getAuthTokenFailed) {
  2218. }}
  2219. }catch(e){log("WM.run: "+e);}}
  2220. };
  2221.  
  2222.  
  2223. })();