您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This is the host object which is created under the WM version 4.x script
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://updategreasyfork.deno.dev/scripts/6908/27580/WM%20Host%20Object.js
// ==UserScript== // @name WM Host Object // @namespace MerricksdadWMHostObject // @description This is the host object which is created under the WM version 4.x script // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/ // @version 4.0.0.1 // @copyright Charlie Ewing except where noted // ==/UserScript== //this script requires some functions in the WM Common Library //this script needs access to a pre-defined JSON object (function(){ this.WallManager={ paused : false, fetchPaused : false, requestsOpen : 0, reqTO : 30000, //request timeout default newSidekicks : [], accDefaultText : "Got this!", failText : "Oh no! Sorry pardner!", overLimitText : "Limit reached!", version:"4.0.0.0", currentUser:{ id:"", profile:"", alias:"" }, resources:{ iconsURL:GM_getResourceURL("IconSheet") }, apps:{}, posts:{}, history:{}, config:null, opts:{}, quickOpts:{}, displayGroups:{}, likeQueue:[], switches:{ manualAuthToken:true }, statusText : { "20":"Sidekick returned force accept", "3":"Marked as accepted by user", "2":"Responseless Collection", "1":"Accepted", "0":"Unknown", "-1":"Failed", "-2":"None Left", "-3":"Over Limit (App)", "-4":"Over Limit, Sent One Anyway", "-5":"Server Error", "-6":"Already Got", "-7":"Server Down For Repairs", "-8":"Problem Getting Passback Link", "-9":"Final Request Returned Null Page", "-10":"Final Request Failure", "-11":"Expired", "-12":"Not a Neighbor", "-13":"Requirements not met", "-14":"Timeout", "-15":"Unrecognized Response", "-16":"Passback Link is missing", "-17":"Window Missing", "-18":"Marked as failed by user", "-20":"Sidekick returned force fail", "-19":"Over Limit (Bonus Type)", "-21":"Cancelled mid-process by user", }, sortGroups : function(params){ params=params||{}; params.direction=(WM.quickOpts.groupDirection=(params.direction||WM.quickOpts.groupDirection||"desc")); //default descending to keep time ordered posts in order newest to oldest WM.saveQuickOpts(); //reorder the groups var groupsArray=[]; for (var g in WM.displayGroups) { groupsArray.push({id:g,node:WM.displayGroups[g].parentNode,box:WM.displayGroups[g]}); } if (["asc","ascending"].inArray(params.direction.toLowerCase())) groupsArray.sort(function(a,b){return a.id>b.id;}); else if (["desc","descending"].inArray(params.direction.toLowerCase())) groupsArray.sort(function(a,b){return a.id<b.id;}); WM.displayGroups={}; for (var g=0; g<groupsArray.length; g++) { WM.displayGroups[groupsArray[g].id]=groupsArray[g].box; WM.console.feedNode.appendChild(groupsArray[g].node); } }, newGroup : function(params){ params=params||{}; //prevent duplicates if (WM.displayGroups[params.by]||null) return WM.displayGroups[params.by]; //create the nodes var box; var group=createElement("div",{className:"listItem"},[ createElement("div",{className:"line", onclick:function(){ //toggle rollout with (this.nextSibling) className=className.swapWordB((className.containsWord("collapsed")),"expanded","collapsed"); with (this.firstChild.firstChild) className=className.swapWordB((className.containsWord("treeCollapse"+WM.opts.littleButtonSize)),"treeExpand"+WM.opts.littleButtonSize,"treeCollapse"+WM.opts.littleButtonSize); }},[ createElement("div",{className:"littleButton",title:"Toggle Content"},[ createElement("img",{className:"resourceIcon treeCollapse"+WM.opts.littleButtonSize}), ]), createElement("label",{textContent:params.label||params.by}) ]), box=createElement("div",{className:"subsection rollout expanded"}), ]); //add it to our group list WM.displayGroups[params.by]=box; WM.sortGroups(); return box; }, pauseCollecting : function(doPause){ var isPaused; if (exists(doPause)) isPaused = (WM.paused = doPause); else isPaused=(WM.paused = !WM.paused); var btn=WM.console.pauseCollectButton; btn.className = btn.className.swapWordB(isPaused,"oddGreen","oddOrange"); btn.title = (isPaused)?"Start Automatic Collection":"Pause Automatic Collection"; var img = btn.childNodes[0]; img.className = img.className.swapWordB(isPaused,"playRight24","stop24"); }, pauseFetching : function(doPause){ var isPaused; if (exists(doPause)) isPaused = (WM.fetchPaused = doPause); else isPaused=(WM.fetchPaused = !WM.fetchPaused); var btn=WM.console.pauseFetchButton; btn.className = btn.className.swapWordB(isPaused,"oddGreen","oddOrange"); btn.title = (isPaused)?"Start Automatic Fetching":"Pause Automatic Fetching"; }, clearGroups : function(params){ //destroy previous groups for (var g in WM.displayGroups){ remove(WM.displayGroups[g].parentNode); //kill the node delete WM.displayGroups[g]; //remove from list } }, clearPosts : function(){ //remove all post nodes from the collector panel for (var p in WM.posts){ if (WM.posts[p].node) remove(WM.posts[p].node); } }, constructGroups : function(params){ params=params||{}; //this specifically allows a null so we can remove grouping var by=exists(params.by)?params.by:WM.quickOpts.groupBy; //if nothing changed, just cancel if (by==WM.quickOpts.groupBy) return; //set the new group order WM.quickOpts.groupBy=by; WM.saveQuickOpts(); WM.clearGroups(); }, sortPosts : function(params){ params=params||{}; params.direction=(WM.quickOpts.sortDirection=(params.direction||WM.quickOpts.sortDirection||"desc")); //default descending to keep time ordered posts in order newest to oldest params.by=(WM.quickOpts.sortBy=(exists(params.by)?params.by:(WM.quickOpts.sortBy||"created_time"))); //default by date WM.saveQuickOpts(); //convert to array var postsArray=methodsToArray(WM.posts); //sort postsArray.sort(function(a,b){ if (["ascending","asc"].inArray(params.direction.toLowerCase())) return a[params.by]>b[params.by]; if (["descending","desc"].inArray(params.direction.toLowerCase())) return a[params.by]<b[params.by]; }); //convert back to object WM.posts=arrayToMethods(postsArray); }, doWhichTestTree : function(post, testList, testData, custom) {try{ //match post to an app var app=post.app; var synApp=app.synApp, w=null; for (var i=0,test;((test=testList[i]) && (w===null));i++) { //run only for tests that are not specifically disabled if (test.enabled===false) continue; //set find mode var findMode="auto"; //finish constructing dynamic collection tests var ret = test.ret; if (custom) { if (!ret) ret = "dynamic"; //default to dynamic if (ret!="dynamic" && ret!="none" && ret!="exclude" && !ret.startsWith(synApp.appID)) ret=synApp.appID+ret; //add appID except to magic words findMode=test.findMode; } //part to make dynamic collection tests work only if they are the correct appID //also do not process disabled tests if (!custom || (custom && (!test.appID || (app.appID==test.appID)))){ //if the test is not disabled (by test enabled both existing and being false) //OR if the test IS a dynamic test and the appID matches //OR if the test IS a dynamic test and no appID was supplied //then run the test //detect test type var testType=(test.search||null); var types=WM.grabber.methods; if (!testType) for (var tt=0,len=types.length; tt<len; tt++) {if (test[types[tt]]||"") {testType=types[tt];break;}} //select the type of data to use var src=""; if (isArray(testType)){ //new search array format for (var t=0,tlen=testType.length;t<tlen;t++) src+=(testData[testType[t]]||""); } else src = (testData[testType]||""); //old test method like testType:text if (src){ //begin processing this test var subTests=test.subTests, kids=test.kids, allowNone=false, subNumRange=test.subNumRange,text=(test.find||test[testType]||""); //process subtests array if (subTests && (findMode=="auto" || findMode=="subtests") && text) { for (var i2=0,subTest,found=false;((subTest=subTests[i2]) && (!found));i2++) { var testX = text.replace('{%1}',subTest).toLowerCase(); //do a standard test with the replaced search string found=src.find(testX); //return a found value, replacing %1 with a lowercase no-space text equal to the subtest string w=(found)?ret.replace('{%1}',subTest.noSpaces().toLowerCase()):w; testX=null; } //process number array } else if (subNumRange && (findMode=="auto" || findMode=="subnumrange") && text){ var start=parseInt(subNumRange.split(",")[0]), end=parseInt(subNumRange.split(",")[1]); for (var i2=start,found=false;((!found) && i2<=end);i2++) { var testX = text.replace('{%1}',i2).toLowerCase(); //do a standard test with the replaced search string found=src.find(testX); //return a found value, replacing %1 with a lowercase no-space text equal to the subtest string w=(found)?ret.replace('{%1}',i2):w; testX=null; } //process text array, process similar to subtests } else if (text && (findMode=="auto" || findMode=="basic") && (isArray(text))) { for (var i2=0,subTest,found=false;((subTest=text[i2]) && (!found));i2++) { var testX = subTest.toLowerCase(); //do a standard test with the replaced search string found=src.find(testX); //return the same value no matter which element from the array is found w=(found)?ret:w; testX=null; } //process regex } else if (text && (test.regex||test.isRegex||null) ) { var mods = (test.mods||"gi"); var testRegex = new RegExp(text,mods); var match=src.match(testRegex); if (match) match=match[0]; //always take the first match w=ret||match||w; //process single text } else if (text) { try{ w=(src.find(text.toLowerCase() ))?ret:w; } catch(e){ log("WM.doWhichTestTree:"+e); log("--app:"+app.appID); log("--test:"+JSON.stringify(test)); } } } //see if test has type 2 subtests (child node tests based on parent test) w = ((kids && w)?WM.doWhichTestTree(post, kids, testData, custom):w) || w; //if kids return null, default to key found above //if this test tree returned "none", start over with next tree by replacing "none" with null //true "none" is handled in the which() function below if (w==="none") w=null; }//end custom checker } return w; }catch(e){log("WM.doWhichTestTree: "+e);}}, which : function(post,params) {try{ //prevent the rules manager from mistaking main as a post object if (!post) return; params=params||{}; //match post to an app var w, app=post.app, synApp=app.synApp; //create various data for the tests to use if (!params.reid) post.testData = { title: (post.name||"undefined").toLowerCase(), msg: (post.message||"undefined").toLowerCase(), caption: (post.caption||"undefined").toLowerCase(), desc: (post.description||"undefined").toLowerCase(), link: (post.linkText||"undefined").toLowerCase(), url: Url.decode(post.linkHref).toLowerCase(), img: (post.picture||"undefined").toLowerCase(), fromName: post.fromName.toLowerCase(), fromID: post.fromID.toLowerCase(), targetName: "undefined", //","+post.getTargets("name").join(",").toLowerCase(), //targetID: "undefined", //","+post.getTargets("id").join(",").toLowerCase(), canvas: "undefined", //app.namespace.toLowerCase(), likeName: "undefined", //","+post.getLikes("name").join(",").toLowerCase(), likeID: "undefined", //","+post.getLikes("id").join(",").toLowerCase(), comments: "undefined", //post.getComments("message").join(" \n").toLowerCase(), commentorName: "undefined", //","+post.getComments("name").join(",").toLowerCase(), commentorID: "undefined", //","+post.getComments("id").join(",").toLowerCase(), }; var testData=post.testData; //replacement for old options like body, either and html testData.body = testData.title+testData.caption+testData.desc; testData.either = testData.link+testData.body; testData.html = testData.fromID + testData.fromName + testData.targetID + testData.targetName + testData.message + testData.href + testData.either + testData.img + testData.canvas + testData.likeID + testData.likeName + testData.commentorID + testData.commentorName + testData.comments; var dynamicTests = WM.grabber.tests; //check user built dynamic tests first if enabled and told to run first if (WM.opts["dynamic"+app.appID] && WM.opts.dynamicFirst && dynamicTests) { w=WM.doWhichTestTree(post,dynamicTests, testData, true)||"none"; } //process this game's tests if dynamic didn't already get one if (w=="none" || !w || w=="") { w=((tests=synApp.tests)?WM.doWhichTestTree(post,tests, testData):"none")||"none"; } //check user built dynamic tests last if enabled and not told to run first if (w=="none" || !w || w=="") { if (WM.opts["dynamic"+app.appID] && !WM.opts.dynamicFirst && dynamicTests) { w=WM.doWhichTestTree(post,dynamicTests,testData, true)||"none"; } } //switch to undefined collection if enabled w=(w==="none" && app.opts["doUnknown"])?"doUnknown":w; return w; }catch(e){log("WM.which: "+e);}}, resetAccepted : function(params) { params=params||{}; var ask=WM.opts.historyConfirmClear; if (params.noConfirm || !ask || (ask && confirm("Delete all history for this profile?"))){ doAction(function(){ WM.history={}; setOpt('history_'+WM.currentUser.profile,'{}'); }); } }, onWindowResize : function(){ WM.resizeConsole(); }, onHeartbeat : function(){ if (WM.rulesManager.enabled) { //affect rules at the base level WM.rulesManager.doEvent("onHeartbeat",{}); //affect rules at the app level if (WM.opts.heartbeatAffectsApps) { for (var a in WM.apps) { (function(){ WM.rulesManager.doEvent("onHeartbeat",WM.apps[a]); })(); } } //affect rules at the post level if (WM.opts.heartbeatAffectsPosts) { for (var p in WM.posts) if (!WM.posts[p].isGhost) { (function(){ WM.rulesManager.doEvent("onHeartbeat",WM.posts[p]); })(); } } //affect rules at the rule level if (WM.opts.heartbeatAffectsRules) { for (var r=0; r<WM.rulesManager.rules.length; r++) { (function(){ WM.rulesManager.doEvent("onHeartbeat",WM.rulesManager.rules[r]); })(); } } //affect rules at the feed and feed filter levels if (WM.opts.heartbeatAffectsFeeds || WM.opts.heartbeatAffectsFeedFilters) { var feeds=WM.feedManager.feeds; for (var f=0,len=feeds.length; f<len; f++) { //do the feed if (WM.opts.heartbeatAffectsFeeds) (function(){ WM.rulesManager.doEvent("onHeartbeat",feeds[f]); })(); //do the feed filters if (WM.opts.heartbeatAffectsFeedFilters) { for (var ff in feeds[f].filters){ (function(){ WM.rulesManager.doEvent("onHeartbeat",feeds[f].filters[ff]); })(); } } } } } //check for new sidekick arrivals if (isArrayAndNotEmpty(WM.newSidekicks)) { while (WM.newSidekicks.length>0) { var app=WM.newSidekicks.shift(); app.fetchPosts(); } } //check for autolike queue contents var quePost = WM.checkAutoLikeQue(); if (quePost) { //log([quePost.fn,quePost.post.id]); switch (quePost.fn) { case "like":quePost.post.like();break; case "comment":quePost.post.comment(quePost.say);break; } } }, //this is for when the WM.config and globalConfig settings change onSave : function() { //recopy the settings array from WM.config WM.updateSettingsValues(); //hide or show counters if (WM.opts.showcounters) WM.showCounters(); else WM.hideCounters(); //update intervals WM.setIntervals(); //set new user colors WM.setColors(); //update config settings WM.changeConfigSettings(); //update those settings we use as global variables WM.changeDebugSettings(); //set console heights //WM.resizeConsole(); }, updateSettingsValues : function(){try{ WM.opts = WM.config.values; //new: do this for each of the apps too for (var a in WM.apps) WM.apps[a].opts=WM.apps[a].config.values; }catch(e){"WM.updateSettingsValues: "+e}}, getAccText: function(appID,w,past,status){ var app=WM.apps[appID].synApp; //detect and use a status code message if (!(status>-1 || status==-4 || status==-6)) return WM.statusText[status]; //or return a generic message based on post type 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":"") ); }, stopCollectionOf : function(w){ for (var p in WM.posts) if (!WM.posts[p].isGhost && WM.posts[p].which==w) WM.posts[p].stopCollect(); }, startCollectionOf : function(w){ for (var p in WM.posts) if (!WM.posts[p].isGhost && WM.posts[p].which==w) WM.posts[p].collect(); }, pauseByType : function(app,w){ if (!isArray(w)) w=[w]; //mark as paused all those posts not yet done for (var p in WM.posts) if (!WM.posts[p].isGhost && w.inArray(WM.posts[p].which)) WM.posts[p].pause(); //store the paused type but dont save it var a=(app.parent||app); for (var i=0; i<w.length; i++) { var t=w[i]; //add it to the array without making a duplicate if (!a.typesPaused.inArray(t)) { a.typesPaused.push(t); //add a visible node a.typesPausedNode.appendChild( a.pausedTypesListNodes[t]=createElement("div",{className:"line"},[ createElement("span",{textContent:(a.userDefinedTypes[t]||a.accText[t])+" ("+t+") "}), createElement("div",{className:"littleButton oddGreen", title:"Unpause Type"},[ createElement("img",{className:"resourceIcon playRight"+WM.opts.littleButtonSize,onclick:function(){ WM.unPauseByType(a,t); }}) ]) ]) ); } } }, unPauseByType : function(app,w){ if (!isArray(w)) w=[w]; //unpause all those posts not yet done for (var p in WM.posts) if (!WM.posts[p].isGhost && w.inArray(WM.posts[p].which)) WM.posts[p].unPause(); //remove paused type from list but dont save it var a=(app.parent||app); for (var i=0; i<w.length; i++) { //remove the visible node remove (a.pausedTypesListNodes[w[i]]); //delete the visible node entry delete a.pausedTypesListNodes[w[i]]; //remove it from the array a.typesPaused.removeByValue(w[i]); } }, setAsAccepted : function(comment,status, post) {try{ var app=post.app; var synApp=app.synApp; post.state="accepted"; post.status=status; post.accept(); WM.history[post.id]={status:status, date:timeStamp(), which:(post.which||"undefined").removePrefix(synApp.appID), appID:app.appID}; setOptJSON('history_'+WM.currentUser.profile,WM.history); //do friend tracking if (WM.opts.useFriendTracker && WM.opts.trackAccepted){ WM.friendTracker.trackStatus(post,true); } var postNode=post.node||$("post_"+post.id); if (postNode){ var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode}); var text=WM.getAccText(synApp.appID,post.which,true,status); link.textContent = (comment || text || WM.statusText[status] || WM.accDefaultText); WM.updatePostStatus(post.id); } app.acceptCount++; //perform the onAccepted event WM.rulesManager.doEvent("onAccepted",post); //try autolike try{ if (WM.opts.useautolike && (WM.opts.autolikeall || WM.opts.autolikeaccepted || (WM.opts.autolikesent && (post.which||"undefined").startsWith("send")) )) { if (!WM.opts["nolike"+app.appID]){ WM.queAutoLike(post); //post.like(); } } } catch(e){log("setAsAccepted: autolike failed: "+e,{level:3});} //try autocomment try{ if (WM.opts.useautocomment && (WM.opts.autolikeall || WM.opts.autolikeaccepted || (WM.opts.autolikesent && (post.which||"undefined").startsWith(synApp.appID+"send")) )) { if (!WM.opts["nolike"+app.appID]){ //setTimeout(function(){post.comment();},100+(WM.opts.autolikedelay*1000)); WM.queAutoComment(post,null); } } } catch(e){log("setAsAccepted: autocomment failed: "+e,{level:3});} }catch(e){log("WM.setAsAccepted: "+e);}}, disableOpt : function(w,app){try{ var targetConfig=(app||null)?app.config:WM.config; ((app||null)?app.opts:WM.opts)[w]=false; targetConfig.set(w,false); targetConfig.save(); debug.print([w,app,false]); }catch(e){log("WM.disableOpt: "+e);}}, enableOpt : function(w,app){try{ var targetConfig=(app||null)?app.config:WM.config; ((app||null)?app.opts:WM.opts)[w]=true; targetConfig.set(w,true); targetConfig.save(); debug.print([w,app,true]); }catch(e){log("WM.enableOpt: "+e);}}, setOpt : function(w,v,app){try{ var targetConfig=(app||null)?app.config:WM.config; ((app||null)?app.opts:WM.opts)[w]=v; targetConfig.set(w,v); targetConfig.save(); debug.print([w,app,v]); }catch(e){log("WM.setOpt: "+e);}}, resetCounters : function(){try{ for (var a in WM.apps) WM.apps[a].resetCounter(); }catch(e){log("WM.resetCounters: "+e);}}, setAsFailed : function(comment, status, post){try{ var app=post.app; var synApp=app.synApp; var postNode=post.node||$("post_"+post.id); //special effects for timeout and cancelProcess if ((!WM.opts.failontimeout && status==-14) || status==-21) { post.state="timeout"; post.timeout(); if (status==-14) WM.rulesManager.doEvent("onTimeout",post); } else { post.state="failed"; post.fail(); // don't pass true or it will loop here WM.history[post.id]={status:status, date:timeStamp(), which:(post.which||"undefined").removePrefix(synApp.appID), appID:app.appID}; setOptJSON('history_'+WM.currentUser.profile,WM.history); WM.rulesManager.doEvent("onFailed",post); } post.status=status; //do friend tracking if (WM.opts.useFriendTracker && WM.opts.trackFailed){ WM.friendTracker.trackStatus(post,false); } if (postNode) { var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode}); if (link) { //I can see no reason the link should be missing, but since its been proven to fail, here is a patch link.textContent = (comment || WM.statusText[status] || WM.failText); } WM.updatePostStatus(post.id); } app.failCount++; //try autolike try{ if (WM.opts.useautolike && WM.opts.autolikeall) { if (!WM.opts["nolike"+app.appID]){ WM.queAutoLike(post); //post.like(); //setTimeout(function(){post.like();},100+(WM.opts.autolikedelay*1000)); } } } catch(e){log("setAsFailed: autolike failed: "+e,{level:3});} //try autocomment try{ if (WM.opts.useautocomment && WM.opts.autolikeall) { if (!WM.opts["nolike"+app.appID]){ //setTimeout(function(){post.comment();},100+(WM.opts.autolikedelay*1000)); WM.queAutoComment(post,null); } } } catch(e){log("setAsFailed: autocomment failed: "+e,{level:3});} }catch(e){log("WM.setAsFailed: "+e);}}, setPriority : function(){ var postNode=selectSingleNode(".//ancestor::*[starts-with(@id,'post_')]",{node:this}); var id=postNode.id.replace("post_",""); WM.posts[id]["priority"]=this.getAttribute("name"); remove(postNode); WM.posts[id].draw(); }, clearURL : function(tab){ WM.collector.close(tab); WM.requestsOpen--; }, //constantly update sidekick channel data skChannel : {}, fetchSidekickData : function(){try{ if (WM) { var node=selectSingleNode("./div",{node:$("wmDataDump")}); while (node){ log("WM.fetchSidekickData: found "+JSON.parse(node.getAttribute("data-ft"))); WM.skChannel=mergeJSON(WM.skChannel,JSON.parse(node.getAttribute("data-ft"))); remove(node); node=selectSingleNode("./div",{node:$("wmDataDump")}); } setTimeout(WM.fetchSidekickData,1000); } }catch(e){log("WM.fetchSidekickData: "+e);}}, //this is WM3's method of handling conversations with sidekicks onFrameLoad3 : function(tab){try{ log("onFrameLoad3(): tab="+tab.id); var postID=tab.postID||tab.id; var post=tab.post||WM.posts[postID]; //detect if post process was cancelled by user if (post.processCancelled){ //reset the cancel memory post.processCancelled = false; log("onFrameLoad3: process cancelled by user"); //set the timeout flag even though its not timed out WM.setAsFailed(null,-21,post); WM.clearURL(tab); return; } //detect if valid WM.collector window still exists var windowExists=(tab.hwnd && !tab.hwnd.closed); /*try{ var testUrl=tab.hwnd.location.toString(); } catch(e) { windowExists=false; }*/ //make sure the post object still exists if (!(post||null)){ log("onFrameLoad3: post is null"); WM.clearURL(tab); return; } //check if window object is missing if (!windowExists) { log("windowExists = false"); if (!tab.hwnd) log("onFrameLoad3: tab.hwnd is null"); if (tab.hwnd.closed) log("onFrameLoad3: tab.hwnd is closed"); WM.setAsFailed(null,-17,post); WM.clearURL(tab); return; } //check timer on this open post var openTime=tab.openTime; var nowTime=timeStamp(); if ((WM.opts.reqtimeout*1000)<(nowTime-openTime)){ log("onFrameLoad3: post timed out"); WM.setAsFailed(null,-14,post); WM.clearURL(tab); return; } //create the retry function var retry=function(){setTimeout(function(){WM.onFrameLoad3(tab); return;},1000); return;}; //look for status data var tabID = tab.id; var skData = WM.skChannel[tabID]||null; if (skData) { //data exists for this post if (skData.status) { //status is available delete WM.skChannel[tabID]; //get useful post data var app=post.app; var synApp=app.parent||app; var postNode=post.node||$("post_"+post.id); var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode}); var w=post.which||"undefined"; //confirm status var gotItem=((skData.status>0) || (skData.status==-6) || (skData.status==-4) || (skData.status==-15 && WM.opts.accepton15)); var failedItem=(skData.status<0); if (gotItem){ //build debug block switch(skData.status){ case -6: case -4: case 1: // this bonus is available or we still have the ability to send something for no return //dont break before next case -15: case 2: if (!synApp.flags.requiresTwo){ WM.setAsAccepted(null, skData.status, post); } break; default: //should not have come here for any reason, but if we did assume its a status code I didnt script for WM.setAsFailed(null, skData.status, post); log("onFrameLoad3: unexpected status code: "+skData.status,{level:2}); break; } } else { WM.setAsFailed(null,skData.status,post); } // click "yes" to accept it, if we got this far we actually found an accept button if(synApp.flags.requiresTwo && gotItem) { if (skData.nopopLink) { var req; req=GM_xmlhttpRequest({ method: "GET", url: skData.nopopLink, timeout: WM.opts.reqtimeout*1000, onload: function(response) { //search for error messages var test=response.responseText; if (test==""){ //no text was found at requested href log("onFrameLoad3: final stage: null response",{level:2}); WM.setAsFailed(null, -9,post); } else { //if no errors then we got it WM.setAsAccepted(null, skData.status,post); } WM.clearURL(tab); if(req)req=null; }, onerror: function(response) { log("onFrameLoad3: final stage: error returned",{level:2}); //if final request fails, drop the request for now WM.setAsFailed(null, -10,post); WM.clearURL(tab); if(req)req=null; }, onabort: function(response) { log("onFrameLoad3: final stage: request aborted",{level:2}); WM.setAsFailed(null, -10,post); WM.clearURL(tab); if(req)req=null; }, ontimeout: function(response) { log("onFrameLoad3: final stage: request timeout",{level:2}); WM.setAsFailed(null, -10,post); WM.clearURL(tab); if(req)req=null; }, }); } else { log("onFrameLoad3: skData.nopopLink is null and a string was expected",{level:3}); WM.setAsFailed(null, -16,post); WM.clearURL(tab); return; } } else WM.clearURL(tab); //<- default page clearer, do not remove } else retry(); } else { retry(); //send the tab its init message (again) tab.hwnd.postMessage({ channel:"WallManager", msg:1, tabID:tab.id, },"*"); //log("useGM_openInTab: "+WM.opts.useGM_openInTab); } }catch(e){log("WM.onFrameLoad3: "+e);}}, //this is WM1-2's method of handling conversation with sidekicks //WM3 defaults to this if sidekick is not WM3 compatible onFrameLoad : function(tab,noDebug){try{ //tab object contains {id,post,url} if (!noDebug) log("onFrameLoad()",{level:0}); var id=tab.id; var post=tab.post||WM.posts[id]; if (!(post||null)) { //resource deleted while post was out WM.clearURL(tab); return; } //detect if post process was cancelled by user if (post.processCancelled){ //reset the cancel memory post.processCancelled = false; log("onFrameLoad3: process cancelled by user"); //set the timeout flag even though its not timed out WM.setAsFailed(null,-21,post); WM.clearURL(tab); return; } var app=post.app; var synApp=app.parent||app; var httpsTrouble=synApp.flags.httpsTrouble; var responseLess=synApp.flags.skipResponse; var postNode=post.node||$("post_"+post.id); var link=selectSingleNode(".//a[contains(@class,'linkText')]",{node:postNode}); var w=post.which||"undefined"; tab.tries=(tab.tries||0)+1; if (tab.tries>WM.opts.reqtimeout) { log("onFrameLoad: request timeout",{level:3}); WM.setAsFailed(null, -14, post); WM.clearURL(tab); return; } var retry=function(){setTimeout(function(e){WM.onFrameLoad(tab, true);}, 1000);}; var failedItem=false, gotItem=false, nopopLink; //check if window object is missing var windowExists=(tab.hwnd && !tab.hwnd.closed); if (!windowExists) {WM.setAsFailed(null,-17,post); WM.clearURL(tab); return;} //check if window document does not yet exist //if (!(tab.hwnd.document||null)) {retry(); return;} //get sidekick return value var hashMsg="",hashStatus=0; try{ //if error encountered, reload the page if (tab.hwnd.document.title==="Problem loading page"){ log("processPosts: problem loading page",{level:1}); tab.hwnd.location.reload(); retry(); return; } var temphash = tab.hwnd.location.hash; //capture a hash if we can hashMsg = ((temphash)?temphash.removePrefix("#"):null) || tab.hwnd.location.href.split("#")[1]; hashStatus=(responseLess)?2:(hashMsg||null)?parseInt(hashMsg.split('status=')[1].split("&")[0]):0; gotItem=((hashStatus>0) || (hashStatus==-6) || (hashStatus==-4) || (hashStatus==-15 && WM.opts.accepton15)); failedItem=(hashStatus<0); if (!gotItem && !failedItem) {retry(); return;} } catch(e){ var errText=""+e; if (errText.contains("hashMsg is undefined")) { //this known issue occurs when a page is not yet fully loaded and the //WM script tries to read the page content retry(); return; } else if (errText.contains("Permission denied to access property")) { //we've reached some known cross domain issue if (responseLess) { //if the sidekick creator has chosen to use responseless collection //simply assume the page has loaded and mark the item as collected gotItem=true;failedItem=false;hashStatus=2; } else { console.log("WM.onFrameLoad(before retry): "+e); retry(); return; } } else if (errText.contains("NS_ERROR_INVALID_POINTER") || errText.contains("tab.hwnd.document is null") ) { WM.setAsFailed(null,-17,post); WM.clearURL(tab); return; } else { log("onFrameLoad: "+e,{level:3}); retry(); return; } } //if gotItem then we have been offered the item so far if (gotItem){ //build debug block switch(hashStatus){ case -6: case -4: case 1: // this bonus is available or we still have the ability to send something for no return if (synApp.flags.requiresTwo){ try{ nopopLink=hashMsg.split("&link=[")[1].split("]")[0]; }catch(e){ //known rare issue where no link is passed back by pioneer trail } } //dont break before next case -15: case 2: if (!synApp.flags.requiresTwo){ WM.setAsAccepted(null, hashStatus,post); } break; default: //should not have come here for any reason, but if we did assume its a status code I didnt script for WM.setAsFailed(null, hashStatus,post); log("onFrameLoad: unexpected status code: "+hashStatus,{level:2}); break; } } else { WM.setAsFailed(null,hashStatus,post); } // click "yes" to accept it, if we got this far we actually found an accept button if(synApp.flags.requiresTwo && gotItem) { if (nopopLink) { var req; req=GM_xmlhttpRequest({ method: "GET", url: nopopLink, timeout: WM.opts.reqtimeout*1000, onload: function(response) { //search for error messages var test=response.responseText; if (test==""){ //no text was found at requested href log("onFrameLoad: final stage: null response",{level:2}); WM.setAsFailed(null, -9,post); } else { //if no errors then we got it WM.setAsAccepted(null, hashStatus,post); } WM.clearURL(tab); if(req)req=null; }, onerror: function(response) { log("onFrameLoad: final stage: error returned",{level:2}); //if final request fails, drop the request for now WM.setAsFailed(null, -10,post); WM.clearURL(tab); if(req)req=null; }, onabort: function(response) { log("onFrameLoad: final stage: request aborted",{level:2}); WM.setAsFailed(null, -10,post); WM.clearURL(tab); if(req)req=null; }, ontimeout: function(response) { log("onFrameLoad: final stage: request timeout",{level:2}); WM.setAsFailed(null, -10,post); WM.clearURL(tab); if(req)req=null; }, }); } else { log("onFrameLoad: nopopLink is null and a string was expected",{level:3}); WM.setAsFailed(null, -16,post); WM.clearURL(tab); return; } } else WM.clearURL(tab); }catch(e){log("WM.onFrameLoad: "+e);}}, toggle : function(opt,app){ var targetConfig=(app||null)?app.config:WM.config; var targetOpts=(app||null)?app.opts:WM.opts; if (targetOpts[opt]){ targetConfig.set(opt, false); targetOpts[opt] = false; } else { targetConfig.set(opt, true); targetOpts[opt] = true; } targetConfig.save(); }, getAppDropDownList : function(selectedIndex,allowBlank){ var retApps=[]; //add the fake initial option retApps.push(createElement("option",{textContent:"select an app",value:""})); retApps.push(createElement("option",{textContent:"* All",value:""})); if (allowBlank) retApps.push(createElement("option",{textContent:"all apps",value:""})); for(var i in WM.apps){ if (!WM.apps[i].parent) { var elem = createElement("option",{textContent:WM.apps[i].name,value:i}); if ((selectedIndex||null) == i) elem.selected = true; retApps.push(elem); } } return retApps; }, getBonusDropDownList : function(params){ params=params||{}; var selected = params.selected||""; var appID = params.appID||null; var dropID = params.dropID||false; //force the element value to drop its appID prefix var optsret=[], bonuses={}; if (appID) bonuses = mergeJSON(WM.apps[appID].accText,WM.apps[appID].userDefinedTypes); bonuses["dynamic"]="* Dynamic: Just Grab It"; bonuses["none"]="* None: Break Identification Circuit"; bonuses["wishlist"]="* Flag as Wishlist"; bonuses["exclude"]="* Exclude: Prevent Collection"; bonuses["send"]="* Send Unknown"; bonuses["doUnknown"]="* Get Unknown"; //create option values and names; for (var i in bonuses) { var elem 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)}); else elem = createElement("option",{textContent:bonuses[i],value:i}); if (appID) {if (selected==((dropID)?i.removePrefix(appID):i) ) elem.selected = true;} else {if (selected==i) elem.selected=true;} optsret.push(elem); } return optsret; }, reIDAll : function(){ for (var p in WM.posts) { if (!WM.posts[p].isGhost && WM.posts[p].identify({reid:true})) WM.rulesManager.doEvent("onIdentify",WM.posts[p]); } WM.sortPosts(); //in this case sorting may cancel movetotop and movetobottom WM.clearGroups(); WM.redrawPosts({postRedraw:true}); }, updatePostStatus : function(id){ var status = WM.posts[id].status; var statusNode = selectSingleNode(".//*[contains(@class,'status')]",{node:$("post_"+id)}); if (statusNode) statusNode.textContent="Status: "+(status||"0") + " " + (WM.statusText[status||"0"]); status=null; statusNode=null; }, onLikePost : function(post){ post.isLiked=true; return; //pre beta 40 stuff var postID=tab.id; var post=tab.post||WM.posts[postID]; //detect if post process was cancelled by user if (post.processCancelled){ //reset the cancel memory post.processCancelled = false; log("onLikePost: feedback cancelled by user"); WM.collector.close(tab); return; } //detect if valid WM.collector window still exists var windowExists=(tab.hwnd && !tab.hwnd.closed); //check if window object is missing if (!windowExists) { log("onLikePost: tab.hwnd is null or closed"); WM.collector.close(tab); return; } try{ var like=tab.hwnd.location.hash.removePrefix("#").getUrlParam("status")=="1"; if (like) { if (tab.post) { //tell the post it is liked tab.post.isLiked = true; //delete the post reference from the tab delete tab.post; } WM.collector.close(tab); return; } } catch (e){ //log(""+e); } tab.tries=(tab.tries||0)+1; if (tab.tries<WM.opts.autoliketimeout) setTimeout(function(){WM.onLikePost(tab);}, 1000); else { log("onLikePost: unable to finish feedback",{level:3}); doAction(function(){WM.collector.close(tab);}); } }, toggleSidekick : function(){ var appID = this.id.split("master_")[1]; var opt = !(WM.quickOpts["masterSwitch"][appID]||false); //toggle WM.quickOpts["masterSwitch"][appID]=opt; var className = this.parentNode.className; this.parentNode.className = ((opt)?className.removeWord("disabled"):className.addWord("disabled")); this.textContent=((opt)?"Disable":"Enable"); WM.saveQuickOpts(); }, saveQuickOpts : function(){ setOptJSON('quickopts_'+WM.currentUser.profile, WM.quickOpts); }, setAppFilter : function(tab){ WM.quickOpts.filterApp=tab.appFilter; WM.saveQuickOpts(); WM.clearGroups(); WM.redrawPosts({postRedraw:false,reorder:true}); WM.rulesManager.doEvent("onSetAppFilter",WM.apps[tab.appFilter]); //debug.print(["Collection Tab Selected",WM.currentAppTab,WM.apps[tab.appFilter]]); }, setDisplay : function(){ var x=this.getAttribute("name"); WM.quickOpts.displayMode=x; WM.saveQuickOpts(); WM.redrawPosts({postRedraw:true,reorder:true}); WM.setDisplayCols(); }, setDisplayCols : function(params){ params=params||{}; params.cols=params.cols||WM.quickOpts.displayCols; WM.quickOpts.displayCols=params.cols||1; WM.saveQuickOpts(); with (WM.console.feedNode) { className=className .toggleWordB(params.cols==1,"singleCol") .toggleWordB(params.cols==2,"twoCol") .toggleWordB(params.cols==3,"threeCol") .toggleWordB(params.cols==4,"fourCol"); } }, redrawPosts : function(params){ params=params||{}; var feedNode=WM.console.feedNode; //set the proper display mode feedNode.className=feedNode.className .toggleWordB((WM.quickOpts.displayMode=="1" || WM.quickOpts.displayMode=="3"),"short"); //avoid order issues by removing the posts from the panel WM.clearPosts(); //redraw||reorder for (var p in WM.posts) { var post=WM.posts[p]; if (!post.isGhost) { post.draw(params.postRedraw,params.reorder); } } }, moveFloater : function(ev){ if (isChrome) return; var img=this, offset=trueOffset(img), scrolled=trueScrollOffset(img), post=selectSingleNode(".//ancestor::div[starts-with(@id,'post')]",{node:img}), floater=$(post.id.replace("post","floater")), special={}; //log( (scrolled.left) +","+ (scrolled.top) ); special.x=(ev.clientX > (document.documentElement.clientWidth/2))?-(240+4+22):0; //width+overshot+BorderAndPadding special.y=(ev.clientY > (document.documentElement.clientHeight/2))?-(120+4+12):0; floater.style.left=(ev.clientX-(offset.left-scrolled.left))+(2+special.x)+"px"; floater.style.top=(ev.clientY-(offset.top-scrolled.top))+(2+special.y)+"px"; }, //create a drip system for autolike, instead of an offset queAutoLike : function(post){ var nowTime = timeStamp(); var lastInQue = WM.likeQueue.last(); var targetTime = nowTime + (1000*WM.opts.autolikedelay); if (lastInQue||null) { if (lastInQue.timer>nowTime) { targetTime = lastInQue.timer + (1000*WM.opts.autolikedelay); } } WM.likeQueue.push({post:post, timer:targetTime, fn:"like"}); WM.console.likeQueueCounterNode.textContent = WM.likeQueue.length; }, //create a drip system for autolike, instead of an offset queAutoComment : function(post,say){ var nowTime = timeStamp(); var lastInQue = WM.likeQueue.last(); var targetTime = nowTime + (1000*WM.opts.autolikedelay); if (lastInQue||null) { if (lastInQue.timer>nowTime) { targetTime = lastInQue.timer + (1000*WM.opts.autolikedelay); } } WM.likeQueue.push({post:post, timer:targetTime, say:say, fn:"comment"}); WM.console.likeQueueCounterNode.textContent = WM.likeQueue.length; //log(["autocomment added",say]); }, //dump the autolike queue emptyAutoLikeQue : function() { WM.likeQueue=[]; WM.console.likeQueueCounterNode.textContent = 0; }, //get the next ready autolike target checkAutoLikeQue : function() { if (WM.likeQueue.length<1) return null; var nowTime = timeStamp(); if (WM.likeQueue[0].timer<=nowTime) { WM.console.likeQueueCounterNode.textContent = (WM.likeQueue.length-1); var t=nowTime; for (var i in WM.likeQueue) { i.timer = t; t+=(1000*WM.opts.autolikedelay); } return WM.likeQueue.shift(); // no longer returns the post, but the block of what to do with what post } return null; }, processPosts : function(){ //dont run if menu is open or if requests are still out or if the console is paused if($("Config") || (WM.requestsOpen >= WM.opts.maxrequests) || WM.paused) return; 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}); if (postNode) { var post = WM.posts[postNode.id.replace('post_','')]; if (post) post.open(); } }, olderPosts : function (params) { WM.fetch({older:true}); }, newerPosts : function (params) { WM.fetch({newer:true}); }, fetchRange : function (params) { WM.fetch({bypassPause:true, older:true, targetEdge:params.oldedge, currentEdge:params.newedge}); }, cleanPosts : function () {try{ for (var p in WM.posts) if (!WM.posts[p].isGhost) { var post = WM.posts[p]; with (post) if (!( isPinned || isCollect || isWorking || (isTimeout && !WM.opts.cleanTimedOut) )) post.remove(); } }catch(e){log("WM.cleanPosts(): "+e);}}, setIntervals : function() {try{ //setup the timer to try post collection if (procIntv) window.clearInterval(procIntv); procIntv=window.setInterval(WM.processPosts, 2000); //setup the timer to get new posts if (newIntv) window.clearInterval(newIntv); if(calcTime(WM.opts.newinterval)>0) newIntv=window.setInterval(WM.newerPosts, calcTime(WM.opts.newinterval)); //setup the timer to get older posts if (oldIntv) window.clearInterval(oldIntv); if(calcTime(WM.opts.oldinterval)>0) oldIntv=window.setInterval(WM.olderPosts, calcTime(WM.opts.oldinterval)+2000); olderLimit=calcTime(WM.opts.maxinterval)||0; //setup the timer to clean up old posts from the feed if (cleanIntv) window.clearInterval(cleanIntv); if(calcTime(WM.opts.cleaninterval)>0) cleanIntv=window.setInterval(WM.cleanPosts, calcTime(WM.opts.cleaninterval)+250); //setup global heartbeat if (hbIntv) window.clearInterval(hbIntv); hbIntv=window.setInterval(WM.onHeartbeat, WM.opts.heartRate); }catch(e){log("WM.setIntervals: "+e);}}, hideCounters : function(){try{ hideNodes("//*[contains(@class,'accFailBlock')]"); }catch(e){log("WM.hideCounters: "+e);}}, showCounters : function(){try{ showNodes("//*[contains(@class,'accFailBlock')]"); }catch(e){log("WM.showCounters: "+e);}}, validatePost : function(fbPost){try{ //validate required post fields /*if (!( exists(fbPost.application) && exists(fbPost.link) && fbPost.type=="link")) { return; }*/ //accept only posts we have sidekicks for var app; if (!exists(app=WM.apps[fbPost.app_id])) return; //prevent redrawing same post in case one slips past the graph validator var postID=fbPost.post_id; if (WM.posts[postID]||null) return; //accept only posts for which a sidekick is enabled if (!WM.quickOpts.masterSwitch[app.appID]) return; //create a Post object from the post data var post=(WM.posts[fbPost]=new WM.Post(fbPost)); if (post) { var hasID=post.identify(); WM.sortPosts(); //make sure new posts fit the current sort order and direction if (hasID) { WM.rulesManager.doEvent("onValidate",post); WM.rulesManager.doEvent("onIdentify",post); post.draw(true,true); //track the post if (WM.opts.useFriendTracker && !post.isMyPost){ WM.friendTracker.track(post); } } } else { log("WM.validatePost: Unable to transform post data into a useful post object. (id:"+fbPost.post_id+")"); } }catch(e){log("WM.validatePost: "+e);}}, handleEdges : function(params){ /* apps friends edge:{newer,older} */ //console.log("handleEdges: "+JSON.stringify(params)); if (params.friends||null) { //update user created feeds for (var f=0,l=WM.feedManager.feeds.length;f<l;f++){ var feed = WM.feedManager.feeds[f]; //if this feed is listed in those passed back... if (params.friends.contains(feed.id)){ //update each app filter in this feed for (var c=0,l=params.apps.length;c<l;c++) { var appID=params.apps[c]; filter = feed.filters["app_"+appID]; if (!(filter||null)) { //this filter does not exist, create one filter=feed.addFilter({id:"app_"+appID}); } if (params.edge.older) filter.oldedge = params.edge.older; if (params.edge.newer) filter.newedge = params.edge.newer; filter.oldedgeNode.textContent = filter.oldedge; filter.newedgeNode.textContent = filter.newedge; if (timeStamp()-(filter.oldedge*1000)>olderLimit) filter.olderLimitReached=true; } } } } else { //update base feed feed = WM.feedManager.feeds[0]; for (var c=0,l=params.apps.length;c<l;c++) { var appID=params.apps[c]; //update each app filter in this feed filter = feed.filters["app_"+appID]; if (!(filter||null)) { //this filter does not exist, create one filter=feed.addFilter({id:"app_"+appID}); } if (params.edge.older) filter.oldedge = params.edge.older; if (params.edge.newer) filter.newedge = params.edge.newer; filter.oldedgeNode.textContent = filter.oldedge; filter.newedgeNode.textContent = filter.newedge; if (timeStamp()-(filter.oldedge*1000)>olderLimit) filter.olderLimitReached=true; } } }, fetch : function(params) {try{ /* older:bool newer:bool apps:[] feed:[] targetEdge:unixtime currentEdge:unixtime bypassPause:bool bypassFeedDisabled:bool bypassAppDisabled:bool */ params=params||{}; if (WM.fetchPaused && !params.bypassPause) return; //convert a single passed app to a single entry list if (exists(params.apps) && ((params.apps.objType||null)=="app")) { var ret={}; ret[params.apps.appID]=params.apps; params.apps=ret; } var useApps = params.apps||WM.apps; //convert a single passed feed to an array if (exists(params.feeds) && ((params.feeds.objType||null)=="feed")) { params.feeds=[params.feeds]; } params.currentEdge = params.currentEdge||null; //nullify undefined edge //for each feed individually var feeds=params.feeds||WM.feedManager.feeds; for (var f=0,len=feeds.length;f<len;f++) { var feed=feeds[f]; var friend=(feed.url!="https://graph.facebook.com/me/home")?[feed.id]:null; //ignore the old me feed because it is a duplicate of the wall feed if (feed.url!="https://graph.facebook.com/me/feed") if (feed.enabled || params.bypassFeedDisabled) { //for each app make a separate fetch call for the given feed //override this: no more by-app fetching if (false && !WM.opts.groupFetching && (useApps||null)) { for (var a in useApps) { var app=useApps[a]; //only fetch for enabled apps //where we are fetching new //or if we are fetching old we are not at our older limit var feedFilter=feed.filters["app_"+a]; if ((app.enabled || params.bypassAppDisabled) && (feedFilter.enabled || params.bypassFilterDisabled) && !( params.older && feedFilter.olderLimitReached ) ){ var G=Graph.fetchPostsFQL_B({ callback:WM.validatePost, direction:(params.newer?1:(params.older?-1:0)), limit:WM.opts.fetchQty, targetEdge:(params.targetEdge||null), //special for new rules manager actions friends:friend, apps:[app.appID], currentEdge:params.currentEdge||(params.newer?feedFilter.newedge:(params.older?feedFilter.oldedge:null)), edgeHandler:WM.handleEdges, noAppFiltering:WM.opts.noAppFiltering }); } } //join apps together before fetching a single time for the given feed } else { //get the keys of the apps collection var keys=Object.keys(useApps); //if any sidekicks are docked if (keys.length) { //get the values of the apps collection var appsToProcess=keys.map(function (key) { return useApps[key]; //filter out which apps are able to be fetched for }).filter(function(o,i,p){ //get the feed filter text var feedFilter=feed.filters["app_"+o.appID]; //get if the app is enabled var isEnabled = (o.enabled || params.bypassAppDisabled); var isFilterEnabled=true,isOlderLimitReached=false; if (feedFilter||null) { //get if the feed filter is enabled isFilterEnabled = (feedFilter.enabled || params.bypassFilterDisabled); //get if the feed filter has already reached its older edge limit isOlderLimitReached = (params.older && feedFilter.olderLimitReached); } else { //feed filter does not exist for this app //assume it was deleted by the user on purpose //and don't fetch for this app on this feed log("WM.fetch: could not find filter for " + o.appID + "in feed " + feed.id); return false; } if (isEnabled && isFilterEnabled && !isOlderLimitReached) return true; return false; //simply the array }).map(function(o,i,p){ //just get the id's of apps to do, not the entire app object return o.appID; }); //make sure we matched filters to process if (appsToProcess.length){ //get the shared edges of the passed apps var edges = feed.getMergedEdges({apps:appsToProcess}); //console.log("getMergedEdges returned: "+JSON.stringify(edges)); var G=Graph.fetchPostsFQL_B({ callback:WM.validatePost, direction:(params.newer?1:(params.older?-1:0)), limit:WM.opts.fetchQty, targetEdge:(params.targetEdge||null), //special for new rules manager actions friends:friend, apps:appsToProcess, currentEdge:params.currentEdge||(params.newer?edges.newedge:(params.older?edges.oldedge:null)), edgeHandler:WM.handleEdges, noAppFiltering:WM.opts.noAppFiltering }); } } } } } }catch(e){log("WM.fetch: "+e);}}, changeDebugSettings : function(){try{ if (debug && debug.initialized) { debug.doDebug = WM.opts.debug; debug.debugLevel = parseInt(WM.opts.debugLevel); debug.debugMaxComments = WM.opts.debugMaxComments; debug.useScrollIntoView = WM.opts.debugScrollIntoView; debug.stackRepeats = WM.opts.debugStackRepeats; } else { if (debug) debug.init(); setTimeout(WM.changeDebugSettings,1000); } }catch(e){log("WM.changeDebugSettings: "+e);}}, changeConfigSettings : function(){try{ WM.config.sectionsAsTabs=WM.opts.configSectionsAsTabs; WM.config.separatorsAsTabs=WM.opts.configSeparatorsAsTabs; WM.config.useScrollIntoView=WM.opts.configScrollIntoView; WM.config.confirms={ save:WM.opts.configConfirmSave, cancel:WM.opts.configConfirmCancel, "import":WM.opts.configConfirmImport, restore:WM.opts.configConfirmRestore }; }catch(e){log("WM.changeConfigSettings: "+e);}}, resizeConsole : function(){try{ //negotiate height with fb bluebar var node=$("pagelet_bluebar"); var h=(node)?elementOuterHeight(node):0; with($("wmContent")){ style.height=document.documentElement.offsetHeight-h+"px"; style.width=document.documentElement.offsetWidth+"px"; } WM.console.tabContainer.redraw(); WM.console.collectTabControl.redraw(); }catch(e){log("WM.resizeConsole: "+e);}}, setColors : function(){try{ var colors=["excluded","working","timeout","paused","nodef","failed","accepted","scam","pinned"]; var css=""; for (var c=0, color; (color=colors[c]); c++) { css+=("div."+color+"{background-color:"+WM.opts["colors"+color]+" !important;}\n"); } //set the new transition delay timer 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"); remove($("user_colors_css")); addGlobalStyle(css,"user_colors_css"); }catch(e){log("WM.setColors: "+e);}}, initConsole : function(){try{ WM.console.loading=false; if (WM.console.initialized) log("WM Console Initialized"); //show options menu button with (WM.console.configButton) { className = className.removeWord("jsfHidden"); } //set console heights WM.resizeConsole(); //load feed sources WM.feedManager.init(); //import friend tracker data //and delete posts out of bounds with our "track for how many days" WM.friendTracker.init(); WM.friendTracker.clean(); //initialize user colors WM.setColors(); //set up the priorities and limits object //and new rules manager WM.rulesManager.init(); //decipher the dynamic tests WM.grabber.init(); //show counters if (WM.opts.showcounters) WM.showCounters(); else WM.hideCounters(); //set intervals WM.setIntervals(); //set autopause if (WM.opts.autopausecollect) WM.pauseCollecting(true); if (WM.opts.autopausefetch) WM.pauseFetching(true); //open a channel for sidekick communication WM.fetchSidekickData(); //add an entrypoint for sidekicks since we know FB gave us access var createDock = function(){ document.body.appendChild( createElement('div',{id:'wmDock',style:'display:none;',onclick:function(){ WM.dock.answerDockingDoor(); }}) ); document.body.appendChild( createElement('div',{id:'wmDataDump',style:'display:none;'}) ); }; createDock(); }catch(e){log("WM.initConsole: "+e);}}, cleanHistory : function(params){try{ log("Cleaning History"); params=params||{}; var ask=WM.opts.historyConfirmClean; if (params.noConfirm || !ask || (ask && confirm("Clean and pack history for this profile?"))){ //history = getOptJSON("history_"+WM.currentUser.profile)||{}; var ageDays=parseInt(WM.opts.itemage); var timeNow=timeStamp(); for(var i in WM.history) { if( ( (timeNow-WM.history[i].date) /day) > ageDays) { delete WM.history[i]; } } setOptJSON("history_"+WM.currentUser.profile, WM.history); } }catch(e){log("WM.cleanHistory: "+e);}}, optionsSetup : function(){try{ debug.print("WM.optionsSetup:"); //create the settings tree WM.config = new Config({ storageName:"settings_"+(WM.quickOpts.useGlobalSettings?"global":WM.currentUser.profile), onSave:WM.onSave, title:"FB Wall Manager "+WM.version+(WM.quickOpts.useGlobalSettings?" (!! Global Settings !!)":""), logo:createElement("span",{}[ createElement("img",{className:"logo",src:"",textContent:"v"+WM.version}), createElement("text","v"+WM.version) ]), css:( WM.console.dynamicIcons()+ jsForms.globalStyle() ), settings:{ btn_useGlobal:{ type:"button", label:"Use Global Settings", title:"Switch to using a global storage for settings. Those settings can then be used by other accounts (not browser profiles).", script:function(){ if (WM.quickOpts.useGlobalSettings||false) { //already using global settings return; } if (confirm("Switch to using global (shared) settings?")){ WM.quickOpts.useGlobalSettings=true; WM.saveQuickOpts(); WM.config.title = "FB Wall Manager "+WM.version+" (!! Global Settings !!))"; WM.config.storageName = "settings_global"; WM.config.values=WM.config.read(); WM.config.configure(); WM.config.reload(); } }, }, btn_useOwnProfile:{ type:"button", label:"Use Profile Settings", title:"Switch to using your own profile storage for settings.", script:function(){ if (!(WM.quickOpts.useGlobalSettings||false)) { //already using profile settings return; } if (confirm("Switch to using your own profile settings?")){ WM.quickOpts.useGlobalSettings=false; WM.saveQuickOpts(); WM.config.title = "FB Wall Manager "+WM.version; WM.config.storageName = "settings_"+WM.currentUser.profile; WM.config.values=WM.config.read(); WM.config.configure(); WM.config.reload(); } }, }, wmtab_opts:tabSection("Host Options",{ section_basicopts:section("Basics",{ /*authTokenTools:optionBlock("Authorization",{ devAuthToken:checkBox("Automatically check my developer tool app for my Auth Token"), },true),*/ intervals:optionBlock("Post Fetching",{ newinterval:{ label:"Get Newer Posts Interval", type:"selecttime", title:"Fetch new posts from facebook after a set time.", options:{ "off":"Off", "tenth":"6 seconds", "sixth":"10 seconds", "half":"30 seconds", "one":"1 minute", "two":"2 minutes", "three":"3 minutes", "four":"4 minutes", "five":"5 minutes", "ten":"10 minutes", }, "default":"t:30s" }, fetchQty:{ label:"Fetch how many? (subject to filtering)", type:"select", title:"Posts fetched per request. Higher numbers affect speed of fetching.", options:{ "5":"5", "10":"10", "25":"25", "50":"50", "100":"100", "250":"250", "500":"500 (FB maximum)", //known maximum fetch as of 9/8/2013 }, "default":"25" }, oldinterval:{ label:"Get Older Posts Interval", type:"selecttime", title:"Fetch previous posts from facebook after a set time.", options:{ "off":"Off", "tenth":"6 seconds", "sixth":"10 seconds", "half":"30 seconds", "one":"1 minute", "two":"2 minutes", "three":"3 minutes", "four":"4 minutes", "five":"5 minutes", "ten":"10 minutes", }, "default":"off" }, maxinterval:{ label:"How old is too old?", type:"selecttime", title:"Tell WM what you think is a good max post age to fetch. Also affects which posts are considered 'stale'.", options:{ "off":"Off/Infinite", "hour":"1", "2hour":"2", "3hour":"3", "4hour":"4", "8hour":"8", "12hour":"12", "18hour":"18", "24hour":"24", "32hour":"32", "48hour":"48", }, "default":"t:1d" }, groupFetching:checkBox("All installed sidekicks in one request (default: one request per sidekick)",false,{},true), noAppFiltering:checkBox("Have WM filter posts for you instead of having facebook do it (may prevent some empty data set issues)",false,{},true), },true), autoPauseBlock:optionBlock("Fetching/Collecting Autopause",{ autopausefetch:checkBox("Pause Fetching after First Fetch"), autopausecollect:checkBox("Pause Collection on Startup"), },true), multiTaskBlock:optionBlock("Multi-task",{ maxrequests:inputBox("Max requests simultaneously",1), recycletabs:inputBox("Recycle Windows/Tabs",1), recycletabsall:checkBox("Recycle All",true), },true), queBlock:optionBlock("Task-Queue",{ queuetabs:checkBox("Force all posts and autolikes through one tab using a queue (overrides multi-task)",true), },true), timeoutBlock:optionBlock("Time-outs",{ reqtimeout:inputBox("Item Acceptance Page Timeout (seconds)",30), failontimeout:checkBox("Mark Timeout as Failure (default: retry indefinitely)"), },true), }), section_access:section("Accessibility",{ shortModeBlock:optionBlock("Short Mode",{ thumbsize:{ label:"Thumbnail Size", type:"select", title:"Size of bonus thumbnails in display mode: short and .", options:{ "mosquito":"16px", "tiny":"24px", "small":"32px", "medium":"48px", "large":"64px", "xlarge":"96px", }, "default":"medium" }, transitiondelay:inputBox("Hover Box Delay (s)",1), },true), accessTweaksBlock:optionBlock("Tweaks",{ debugrecog:checkBox("Show Identified Text (instead of original link text)",true), showcounters:checkBox("Show Accept/Fail Counts",true), showdynamictips:checkBox("Show Dynamic Console Tips",true), appsConfirmDeleteUDT:checkBox("Confirm Delete User Defined Types",true), },true), toolBoxBlock:optionBlock("Customize Post Toolbox",{ showtoolbox:checkBox("Enable ToolBox", true), showopen:checkBox("Open Post",true), showmarkfailed:checkBox("Mark As Failed",true), showmarkaccepted:checkBox("Mark As Accepted",true), showlike:checkBox("Like Post",true), showreid:checkBox("Re-ID Post",true), showmovetop:checkBox("Move to Top",true), showmovebottom:checkBox("Move to Bottom",true), showpin:checkBox("Pin Post",true), showclean:checkBox("Clean Post",true), showpostsrc:checkBox("Show Post Source",true), //new stuff showcancelprocess:checkBox("Cancel Process or Like",true), showrestartprocess:checkBox("Restart Process or Like",true), showpausetype:checkBox("Pause Bonus Type",true), showunpausetype:checkBox("Unpause Bonus Type",true), showaddfeed:checkBox("Add To Feeds",true), showmakerule:checkBox("Rule From Post",true), showoriginaldata:checkBox("Show Original Data",true), showautocomment:checkBox("Auto Comment",true), },true), littleToolBoxBlock:optionBlock("Customize Mini Toolbox",{ littleButtonSize:{ label:"Mini Toolbutton Size (requires refresh to redraw)", type:"select", title:"Size of buttons on mini toolbars", options:{ "16":"16px", "24":"24px", "32":"32px", }, "default":"24", }, },true), userColorsBlock:optionBlock("Colors",{ colorsaccepted:colorBox("Accepted","limegreen"), colorsfailed:colorBox("Failed","red"), colorsworking:colorBox("Working","yellow"), colorsexcluded:colorBox("Excluded","gray"), colorspaused:colorBox("Paused","silver"), colorsnodef:colorBox("No Definition","deepskyblue"), colorsscam:colorBox("Potential Scam","purple"), colorspinned:colorBox("Pinned","black"), colorstimeout:colorBox("Timeout","orange"), },true), }), section_feedback:section("Feedback",{ 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}, gotoapiexplorer:anchor("Visit API Explorer","http://developers.facebook.com/tools/explorer?&version=v1.0"), autoSetup:optionBlock("Setup",{ useautocomment:checkBox("Use Auto-comment (experimental)"), useautolike:checkBox("Use Auto-like"), //autoliketimeout:inputBox("Timeout (seconds)",30), autolikedelay:inputBox("Ban-Prevention Delay (seconds)",3), },true), autoLikeBlock:optionBlock("Perform Feedback For",{ autolikeall:checkBox("All Posts"), autolikeaccepted:checkBox("Accepted Posts"), autolikesent:checkBox("Sent Posts"), },true), autoCommentListBlock:optionBlock("Comments (experimental)",{ autocommentlist:textArea("Random Comments (One per line)","Thanks\nThank you\nthanks"), },true), blockautolikebygame:optionBlock("Block Feedback by Game",{},false), }), section_filters:section("Filters",{ displayfilters:optionBlock("Remove Feed Parts (Classic Mode Only)",{ hideimages:checkBox("Images (All)"), hideimagesunwanted:checkBox("Images (Unwanted Posts)"), hidebody:checkBox("Post Body Text"), hidevia:checkBox("Via App"), hidedate:checkBox("Date/Time"), },true), filters:optionBlock("Hide By Type",{ hidemyposts:checkBox("My Posts"), hideunwanted:checkBox("Unwanted"), hideaccepted:checkBox("Accepted"), hidefailed:checkBox("Failed"), hidescams:checkBox("Scams"), hidestale:checkBox("Stale Posts"), hideexcluded:checkBox("Excluded"), hideliked:checkBox("Liked By Me"), hideunsupported:checkBox("Unsupported Apps"), donthidewishlists:checkBox("Don't Hide Known Wish Lists"), }), //allow hiding all posts by particular games filterapps:optionBlock("Hide By App",{}), //now added dynamically as appID+"dontsteal" dontstealBlock:optionBlock("Don't take W2W posts not for me",{}), skipopts:optionBlock("Skip By Type",{ skipliked:checkBox("Liked By Me"), skipstale:checkBox("Day-Old Posts"), }), filterTweaksBlock:optionBlock("Tweaks",{ accepton15:checkBox("Mark 'Unrecognized Response' As Accepted"), markliked:checkBox("Mark Liked As Accepted (must check Skip Liked)"), },true), filterCleanupBlock:optionBlock("Cleanup",{ cleaninterval:{ label:"Cleanup Interval", type:"selecttime", title:"Remove unwanted posts from collection console after a set time.", options:{ "off":"Off", "one":"1 minute", "two":"2 minutes", "five":"5 minutes", "ten":"10 minutes", "fifteen":"15 minutes", "thirty":"30 minutes", "hour":"1 hour", }, "default":"off" }, cleanTimedOut:checkBox("Clean timed out posts",true), },true), }), section_history:section("History",{ itemage:inputBox("How long to keep tried items in memory (days)",2), oblock_historyConfirms:optionBlock("Confirm (Changes available on next config open)",{ historyConfirmClear:{type:"checkbox",label:"Clear History",title:"Confirm before clearing history.","default":true}, },true), reset:button("Clear History", WM.resetAccepted ), }), section_feedopts:section("Feeds Manager",{ oblock_feedsConfirms:optionBlock("Confirm",{ feedsConfirmDeleteFeed:{type:"checkbox",label:"Delete Rule",title:"Require confirmation to delete a feed.","default":true}, },true), }), section_dynamicopts:section("Dynamic Grabber",{ dynamicopts:optionBlock("Dynamic Collection",{ dynamicFirst:checkBox("Run Dynamics BEFORE Sidekicks",true), },true), enableDynamic:optionBlock("Enable Dynamics by Game",{}), oblock_dynamicConfirms:optionBlock("Confirm",{ dynamicConfirmDeleteTest:{type:"checkbox",label:"Delete Rule",title:"Require confirmation to delete a test.","default":true}, },true), }), section_friendtrackopts:section("Friend Tracker",{ useFriendTracker:checkBox("Enable Friend Tracking",true), trackTime:inputBox("Track For How Many Days",2), trackeropts:optionBlock("Track Data",{ trackCreated:checkBox("Post Creation Counts",true), trackLastKnownPost:checkBox("Last Known Post Time",true), trackAccepted:checkBox("Bonuses Accepted",true), trackFailed:checkBox("Bonuses Failed",true), },true), oblock_trackerConfirms:optionBlock("Confirm",{ trackConfirmClearUser:{type:"checkbox",label:"Clear User Data",title:"Require confirmation to clear user data.","default":true}, },true), }), section_rulesopts:section("Rules Manager",{ oblock_rulesHeartbeat:optionBlock("Heartbeat",{ heartRate:inputBox("Global Heartbeat Delay (ms)",1000), 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."}, 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."}, 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."}, 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."}, 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."}, },true), oblock_rulesConfirms:optionBlock("Confirm",{ rulesConfirmDeleteValidator:{type:"checkbox",label:"Delete Validator",title:"Require confirmation to delete a rule's validator.","default":true}, rulesConfirmDeleteAction:{type:"checkbox",label:"Delete Action",title:"Require confirmation to delete a rule's action.","default":true}, rulesConfirmDeleteRule:{type:"checkbox",label:"Delete Rule",title:"Require confirmation to delete a rule.","default":true}, rulesConfirmResetLimit:{type:"checkbox",label:"Reset Limit",title:"Require confirmation to reset individual limits.","default":true}, rulesConfirmResetAllLimits:{type:"checkbox",label:"Reset All Limits",title:"Require confirmation to reset all limits.","default":true}, rulesConfirmHatch:{type:"checkbox",label:"Hatch Eggs",title:"Require confirmation to hatch eggs.","default":true}, },true), 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}, }), section_dev:section("Debug",{ oblock_debugTweaks:optionBlock("Tweaks",{ pinundefined:checkBox("Pin Undefined Bonus Types"), },true), debugOpts:optionBlock("Debug",{ debug:checkBox("Enable Debug",true), debugLevel:{ label:"Debug Sensitivity", title:"Sets the level of errors and warnings to report. 0 is all, 5 shows only the worst.", type:"select", options:{ "0":"Function calls", "1":"Function subsections & debug notes", "2":"Captured expected errors", "3":"Known open errors", "4":"Unexpected errors", "5":"Fatal errors", }, "default":"0" }, debugMaxComments:inputBox("Max debug lines (0 for no limit)",100), debugScrollIntoView:checkBox("Use scrollIntoView"), debugStackRepeats:checkBox("Stack Immediate Repeats"), },true), advDebugOpts:optionBlock("Advanced Debug",{ devDebugFunctionSubsections:checkBox("Debug Function Subsections",false), devDebugGraphData:checkBox("Debug Graph Packets (not available for Chrome)",false), },true), GM_special:optionBlock("Script-runner Options",{ useGM_openInTab:checkBox("Use GM_openInTab instead of window.open",false), },true), }), section_configopts:section("Config",{ oblock_configConfirms:optionBlock("Confirm (Changes available on next config open)",{ configConfirmSave:{type:"checkbox",label:"Save",title:"Confirm before saving settings.","default":true}, configConfirmCancel:{type:"checkbox",label:"Cancel",title:"Confirm before closing settings without saving.","default":true}, configConfirmImport:{type:"checkbox",label:"Import",title:"Confirm before importing settings.","default":true}, configConfirmRestore:{type:"checkbox",label:"Restore Defaults",title:"Confirm before restoring defaults.","default":true}, },true), oblock_configStyling:optionBlock("Styling (Changes available on next config open)",{ 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."}, 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."}, },true), oblock_configTweaks:optionBlock("Tweaks (Changes available on next config open)",{ 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."}, },true), }), }), wmtab_games:tabSection("Sidekick Options",{ 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."}, }), wmtab_info:tabSection("Info",{ MainMessageCenter:separator("Documentation - Messages - Help",null,{ Mainupdate:anchor("Update Script","http://userscripts.org/scripts/source/86674.user.js"), 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¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"}, Mainwikipage:anchor("Wiki Support Page","http://fbwm.wikia.com/wiki/Known_Issues"), Mainsetupinfo:anchor("Setup Info","http://fbwm.wikia.com/wiki/New_User_Setup"), Maindiscuss:anchor("Known Bugs","http://fbwm.wikia.com/wiki/Known_Issues"), Mainrevisionlog:anchor("Revision Log","http://fbwm.wikia.com/wiki/Revisions"), },true), }), wmtab_scripts:tabSection("Get More!",{ }), }, }); // add options shortcut to user script commands GM_registerMenuCommand("Wall Manager "+WM.version+" Options", function(){WM.config.open();}); }catch(e){log("WM.optionsSetup: "+e);}}, init : function(){try{ //capture user id/alias/name and make it global WM.currentUser.id = Graph.userID; WM.currentUser.alias = Graph.userAlias; WM.currentUser.profile = WM.currentUser.alias||WM.currentUser.id; debug.print("UserID:"+WM.currentUser.id+"; UserAlias:"+WM.currentUser.alias+"; WM is Using:"+WM.currentUser.profile); //get WM.quickOpts WM.quickOpts = getOptJSON('quickopts_'+WM.currentUser.profile)||{}; WM.quickOpts["filterApp"]=(WM.quickOpts["filterApp"]||"All"); WM.quickOpts["displayMode"]=(WM.quickOpts["displayMode"]||"0"); WM.quickOpts["masterSwitch"]=(WM.quickOpts["masterSwitch"]||{}); WM.quickOpts["useGlobalSettings"]=(WM.quickOpts["useGlobalSettings"]||false); //create the options menu WM.optionsSetup(); //duplicate the options saved in WM.config WM.updateSettingsValues(); //set up the config with its internal special variables WM.changeConfigSettings(); //setup debug beyond its defaults WM.changeDebugSettings(); //clean history WM.history = getOptJSON('history_'+WM.currentUser.profile)||{}; WM.cleanHistory(); //prep the console now that we have an id and/or alias //and then carry on with our init WM.console.init({callback:WM.initConsole}); }catch(e){log("WM.init: "+e);}}, receiveSidekickMessage: function(event) { if (isObject(event.data)) { var data=event.data; //just shorten the typing if (data.channel=="WallManager"){ //log(JSON.stringify(data)); //$("WM_debugWindow").childNodes[1].lastChild.scrollIntoView(); switch (data.msg){ case 2: //getting a comOpen response from sidekick //WM.collector.tabs[data.tabID].comOpen=true; break; case 4: //getting a status package from sidekick switch (data.params.action){ case "onFrameLoad": WM.onFrameLoad(data.params); break; case "onFrameLoad3": WM.onFrameLoad3(data.params); break; } break; } } } }, run : function() {try{ // pre-load console images //for(var img in imgs) try{new Image().src = imgs[img];}catch(e){log("preload: "+e);} //special about:config entry for disabling storage of fb auth token //should help multi account users //if (getOpt("disableSaveAuthToken")) Graph.authToken=null; //patch 38 auth token stuff var flagManualAuthSuccessful=getOpt("flagManualAuthSuccessful")||false; if (WallManager.switches.manualAuthToken && !flagManualAuthSuccessful) { 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"; var manualToken = prompt(m,"paste token here"); //validate manualToken at least somewhat //halt if manual token is not given if (manualToken=="" || manualToken==null || manualToken=="paste token here") { alert("manual token not accepted, please refresh and try again"); return; } //pass the manual token along Graph.authToken=manualToken; //consider saving time by looking for auth tokens automatically from here out 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."; var saveProgress = confirm(m); if (saveProgress) { setOpt("flagManualAuthSuccessful",true); } } var G=Graph.fetchUser({callback:WM.init}); if (G){if (G.requestAlreadyOut) { } else if (G.initRequestSlow) { } else if (G.olderLimitReached) { } else if (G.getAuthTokenFailed) { }} }catch(e){log("WM.run: "+e);}} }; this.WM=WallManager; })();