This is the host object which is created under the WM version 4.x script
Stan na
Ten skrypt nie powinien być instalowany bezpośrednio. Jest to biblioteka dla innych skyptów do włączenia dyrektywą meta // @require https://update.greasyfork.org/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;
})();