// ==UserScript==
// @name FB Wall Manager
// @namespace MerricksdadWallManager
// @description Manages Wall Posts for Various FB Games
// @include http*://www.facebook.com/pages/FB-Wall-Manager/*
// @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @grant GM_log
// @grant GM_openInTab
// @grant GM_getResourceURL
// @version 3.1.10
// @copyright Charlie Ewing except where noted
// @require https://greasyfork.org/scripts/416-wm-common-library/code/WM%20Common%20Library.user.js
// @require https://greasyfork.org/scripts/417-wm-debug-console/code/WM%20Debug%20Console.user.js
// @require https://greasyfork.org/scripts/418-js-forms-library-b/code/JS%20Forms%20Library%20B.user.js
// @require https://greasyfork.org/scripts/419-wm-config-interface/code/WM%20Config%20Interface.user.js
// @require https://greasyfork.org/scripts/420-wm-graph-api-interface-beta-branch/code/WM%20Graph%20API%20Interface%20(Beta%20Branch).user.js
// @resource IconSheet http://i.imgur.com/sLxzUA6.png
// ==/UserScript==
// retired libraries
// @resource IconSheet http://images.wikia.com/fbwm/images/c/c0/Images.png
// @require http://userscripts.org/scripts/source/29910.user.js
// @require http://userscripts.org/scripts/source/129006.user.js
// @resource IconSheet http://i1181.photobucket.com/albums/x430/merricksdad/images.png
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js
// http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js
//userscripts library locations
// @require http://userscripts.org/scripts/source/123889.user.js
// @require http://userscripts.org/scripts/source/128747.user.js
// @require http://userscripts.org/scripts/source/150983.user.js
// @require http://userscripts.org/scripts/source/152610.user.js
// @require http://userscripts.org/scripts/source/150032.user.js
// for testing only
// @include file:///C:/FB-Wall-Manager/*
// retired autolike functions
// @include /^https?:\/\/www\.facebook\.com\/.*\/posts\/.*/
// Based on script built by Joe Simmons in Farmville Wall Manager
(function() {
//***************************************************************************************************************************************
//***** Preload
//***************************************************************************************************************************************
//dont run in iframes
try {
//this does not mean we are using GM's unsafe window
var unsafeWindow = unsafeWindow || window.wrappedJSObject || window;
if (unsafeWindow.frameElement != null) return;
} catch(e) {log("preload: "+e);}
//***************************************************************************************************************************************
//***** Debug Object
//***************************************************************************************************************************************
if (debug) {
debug.init();
if (debug.initialized) log("Debug Console Initialized");
}
//***************************************************************************************************************************************
//***** Globals
//***************************************************************************************************************************************
this.WallManager={
paused : false,
fetchPaused : false,
requestsOpen : 0,
reqTO : 30000,
newSidekicks : [],
accDefaultText : "Got this!",
failText : "Oh no! Sorry pardner!",
overLimitText : "Limit reached!",
version:"3.1.10",
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);}}
};
var WM=WallManager;
//returns the current date-time in unix format, not localized
WM.__defineGetter__("currentTime",function(){try{
return timeStamp();
}catch(e){log("WM.currentTime: "+e);}});
//returns the appID of the selected app tab on the collection panel, or 'all' if 'Show All' is selected
WM.__defineGetter__("currentAppTab",function(){try{
var tabCtrl=WM.console.collectTabControl;
if (tabCtrl||null) {
var tab = tabCtrl._selectedTab;
if (tab||null) return tab.appFilter;
}
return "all";
}catch(e){log("WM.currentAppTab: "+e);}});
var sandbox=this;
//allow certain options to be seen outside of the WallManager object
//graph extension is external but still calls on WM options if they exist
opts=WM.opts;
quickOpts=WM.quickOpts;
//***************************************************************************************************************************************
//***** global functions
//***************************************************************************************************************************************
sandbox.isNumber = function(o){return ((typeof o) == "number");};
sandbox.isArray = function(o) {return Object.prototype.toString.call(o) === "[object Array]";};
//***************************************************************************************************************************************
//***** Visual Console Object
//***************************************************************************************************************************************
WM.console = {
initialized: false,
sidekickNode: null, //remember the sidekicks list
feedNode: null, //remember where to put the feed data
loading: true, //set to false after sidekicks have time to load
priorityNode: null,
priorityBuild: null,
dynamicBuild: null,
//new content
tabContainer:null, //outer tab control
configButton:null, //userConfig.open control
collectTabControl:null, //app filter tab control
dynamicIcons: function(){
//define a crapload of icons
var icons={
//128x128 pixel icons
row0:["refresh","world","check",null,"moveUpLevelLeft","moveUpLevelRight","moveDownLevelLeft","moveDownLevelRight","filter","plus","minus","multiply","import","reset","object","array"],
row1:["expandDown","expandUp","expandLeft","expandRight","moveTopLeft","moveBottomLeft",null,"allSidekicks","location","sortAsc","sortDesc","tools","treeExpand","treeCollapse","exportGrab","grab"],
row2:["playDown","playUp","playLeft","playRight","like","unlike","uncheckAll","checkAll","layoutSmall","layoutDetail","layoutList","sidekick","refreshProcess","cancelProcess","importData","heartbeat"],
row3:["arrowDown","arrowUp","arrowRight","arrowLeft","rssUpRight","rssUpLeft","rssDownRight","rssDownLeft","pin","pinned","redPhone","shuffle",null,"birth","comment"],
row4:["plugin","identify","add","remove","openInNewWindow","restoreDown","stop","pause","trash","action","logo",null,"moveOutLevel","moveInLevel","removeGlobal","toGlobal"],
row5:["clone","hatch","tag","noImage","accordionExpandH","accordionCollapseH","accordionExpandV","accordionCollapseV","gotoLibrary","addFilter","removeFilter","maximize","addFeed","addGlobal","fromGlobal","checkGlobal"],
//32px icons
row6:["firefox","chrome",null,"tabs"],
//16px icons
row7:["treeCollapseS","treeExpandS","layoutSmallColor","layoutDetailColor","layoutListColor",null,null,null,null,null,null,"noImageSmall"],
};
var ret=".resourceIcon {display:block; background-image:url('"+WM.resources.iconsURL+"') !important;}\n";
//create css statements
//for rows 0-5,6,7
var sizes=[8,16,24,32,48,64];
for (var si=0,len=sizes.length;si<len;si++){
var s=sizes[si];
for (var r=0;r<=6;r++){
for (var i=0;i<20;i++){
var iconName=icons["row"+r][i];
if (iconName!=null) {
ret+="."+iconName+s+" {background-position:"+(-i*s)+"px "+(-r*s)+"px; width:"+s+"px; height:"+s+"px; background-size:"+(1024/(64/s))+"px;}\n";
}
}
}
r=6;
for (var i=0;i<20;i++){
var iconName=icons["row"+r][i];
if (iconName!=null) {
//6 rows of icons 2 times this size
var yOffset=(6*s*2);
ret+="."+iconName+s+" {background-position:"+(-(i*s))+"px "+(-yOffset)+"px; width:"+s+"px; height:"+s+"px; background-size:"+(1024/(64/(s*2)))+"px;}\n";
}
}
r=7;
for (var i=0;i<20;i++){
var iconName=icons["row"+r][i];
if (iconName!=null) {
//6 rows of icons 4 times this size
//plus 1 row of icons twice this size
var yOffset=(6*s*4) + (1*s*2);
ret+="."+iconName+s+" {background-position:"+(-(i*s))+"px "+(-yOffset)+"px; width:"+s+"px; height:"+s+"px; background-size:"+(1024/(64/(s*4)))+"px;}\n";
}
}
}
return ret;
},
globalStyle:function(){try{
return ""+
//icon sheets
WM.console.dynamicIcons()+
"html {height:100%; width:100%;}\n"+
"body {margin:0 !important; font-family:tahoma,arial; font-size:small;}\n"+
"a:hover {text-decoration: none !important;}\n"+
"#content {display:none !important; }\n"+
"#wmContent {background-color:#DDDDEE; position:relative;}\n"+
".post.classic {position:relative; min-height:90px; border-bottom:1px solid #CCCCDD; padding-bottom:10px; padding-top:10px; clear:both;}\n"+
".post.classic .actor {margin-top:5px; margin-bottom:10px; font-weight:700; color:#3B5998; display:inline;}\n"+
".post.classic .picture {padding-top:5px; padding-right:10px; float:left;}\n"+
".post.classic .picture img {width:90px; height:90px; background-color:white; border:1px solid; border-radius:5px;}\n"+
".post.classic .body {vertical-align:top;}\n"+
".post.classic .title {margin-top:5px; font-weight:700; color:#3B5998;display:block;}\n"+
".post.classic .caption {display:block; }\n"+
".post.classic .description {padding-top:5px; display:block;}\n"+
".post.classic .postDate {}\n"+
".post.classic .appName {position:relative; left:10px;}\n"+
".post.classic .linkText {color:#899ADB; float:right; padding-right:32px;}\n"+
".post.classic.noimage {min-height:1px;}\n"+
".post.short {float:left; position:relative;}\n"+
".post.short .floater {overflow:hidden; display:block; background-color: white; border:0px solid; border-radius:5px; position:absolute; z-index:3; padding:0; width:0px;}\n"+
".post.short:hover .floater {-moz-transition-property: width,height,padding,border;-moz-transition-delay:1s; width:240px; padding:5px 10px;border:1px solid;}\n"+
".post.short .actor {display:block;}\n"+
".post.short .picture {position:relative;}\n"+
".post.short .picture img {position:relative; width:100%; height:100%; background-color:white;}\n"+
".post.short .postDate {display:block;}\n"+
".post.short .appName {display:block;}\n"+
".post.short .linkText {display:block;}\n"+
".post.short .progress {opacity:0.25; background-color:#00FF00;}\n"+
".post.short.working .picture img {opacity:0.25;}\n"+
".post.short.excluded .picture img {opacity:0.25;}\n"+
".post.short.timeout .picture img {opacity:0.25;}\n"+
".post.short.paused .picture img {opacity:0.25;}\n"+
".post.short.nodef .picture img {opacity:0.25;}\n"+
".post.short.accepted .picture img {opacity:0.25;}\n"+
".post.short.failed .picture img {opacity:0.25;}\n"+
".post.short.colored .picture img {opacity:0.25;}\n"+
".post.short.scam .picture img {opacity:0.25;}\n"+
".post.short.pinned .picture img {opacity:0.25;}\n"+
".post.dev {position:relative; min-height:90px; border-bottom:1px solid #CCCCDD; padding-bottom:20px; padding-top:10px; clear:both;}\n"+
".post.dev>div:first-child {display: inline-block; margin-right: 16px; border: none;}\n"+
".wm.content > div > .toolBox {display:inline;}\n"+
".wm.content > div > .toolBox > div {display:inline;}\n"+
".post .toolBox {display:block; vertical-align:top; position:relative !important;}\n"+
".post .toolBox > div {display:block; float:right;}\n"+
"div.excluded {background-color:gray !important;}\n"+
"div.working {background-color:yellow !important;}\n"+
"div.timeout {background-color:orange !important;}\n"+
"div.paused {background-color:silver !important;}\n"+
"div.pinned {background-color:silver !important;}\n"+
"div.nodef {background-color:deepskyblue !important;}\n"+
"div.failed {background-color:red !important;}\n"+
"div.accepted {background-color:limegreen !important;}\n"+
"div.scam {background-color:purple !important;}\n"+
".pausedHover {display:none; position:absolute; right:50%; top:50%;}\n"+
".pausedHover>img {margin-left:-32px; margin-top:-32px;}\n"+
".pausedHover>img:hover {background-color:rgba(0,255,0,0.5); border-radius:20%;}\n"+
".post.paused.short>.floater>.pausedHover>img {background-color:rgba(0,255,0,0.5); border-radius:20%;}\n"+
".post.paused>.pausedHover, .post.paused>.floater>.pausedHover {display:block;}\n"+
".underline {border-bottom:1px solid #CCCCDD;}\n"+
".toolTip {display:none; border:1px solid #767676; border-radius:3px; background-color:white; color:black; position:absolute; font-size:8pt; padding:5px; line-height: 12px; z-index:9999;}\n"+
"*:hover > .toolTip {display:block;}\n"+
".menuNode {width:0px; height:0px; position:absolute; background:none; border:none;top:-5px;}\n"+
".toolTip.menuNode > ul {position:absolute; background-color: white; border: 1px solid; border-radius: 5px 5px 5px 5px; padding: 2px; min-width:100px;}\n"+
".toolTip.menuNode > ul > li {position:relative; line-height:1.28; }\n"+
".toolTip.right.menuNode {right:5px; }\n"+
".toolTip.left.menuNode {left:-5px; }\n"+
".toolTip.right.menuNode > ul {left:0px;}\n"+
".toolTip.right.menuNode > ul > li {text-align:left;}\n"+
".toolTip.left.menuNode > ul {right:0px;}\n"+
".toolTip.left.menuNode > ul > li {text-align:right;}\n"+
//little button div
".littleButton {background-color:threedshadow; border-radius:5px; margin:1px; display:inline-block; vertical-align:middle;}\n"+
".littleButton:hover {background-color:highlight !important;}\n"+
".littleButton>img {position:relative; display:block; margin:2px;}\n"+
".littleButton.oddOrange {background-color:#FF9968;}\n"+
".littleButton.oddBlack {background-color:#82976E;}\n"+
".littleButton.oddBlue {background-color:#51D1EA;}\n"+
".littleButton.oddGreen {background-color:#B7E54F;}\n"+
".menuEntry, .menuList > li {position:relative; border-radius:3px; border:1px solid white; padding:3px; min-width:100px;}\n"+
".menuEntry:hover, .menuList > li:hover {border-color:#CCCCDD; background-color:#E0E8F6; }\n"+
".accFailBlock {color: white !important;font-size: small !important;left: 16px;line-height: 12px;margin-bottom: -12px;padding: 0 !important;position: relative;top: -32px;}\n"+
".accFailBlock .fail {background-color: #C3463A; border-radius: 2px 2px 2px 2px; box-shadow: 1px 1px 1px rgba(0, 39, 121, 0.77); padding: 1px 2px;}\n"+
".accFailBlock .accept {background-color: #46B754; border-radius: 2px 2px 2px 2px; box-shadow: 1px 1px 1px rgba(0, 39, 121, 0.77); padding: 1px 2px;}\n"+
//rules manager
"#wmPriorityBuilder {margin:5px; position: relative; background-color:white; min-height:95%;}\n"+
"#wmPriorityBuilder .validator > :before {content:'and: '}\n"+
"#wmPriorityBuilder .validator:first-child > :before {content:'where: '}\n"+
"#wmPriorityBuilder .action > :before {content:'and: '}\n"+
"#wmPriorityBuilder .action:first-child > :before {content:'do: '}\n"+
//collection feed node
"#wmFeedNode {margin:5px; position: relative; background-color:white;}\n"+
//sidekick manager
"#wmSidekickList {margin:5px; position: relative; background-color:white; min-height:95%;}\n"+
//feeds manager
"#wmFeedsList {margin:5px; position: relative; background-color:white; min-height:95%;}\n"+
//dynamic grabber
"#wmDynamicBuilder {margin:5px; position: relative; background-color:white; min-height:95%;}\n"+
//friend tracker
"#wmFriendTracker {margin:5px; position: relative; background-color:white; min-height:95%;}\n"+
".expanded {display:block;}\n"+
".collapsed {display:none;}\n"+
"label {font-weight:bold; margin-right:5px;}\n"+
".unsaved {background-color:lightyellow !important;}\n"+
".whiteover:hover {background-color:#FFFFFF !important;}\n"+
".blueover:hover {background-color:#E0E8F6 !important;}\n"+
".red {background-color:#C3463A !important; border: 2px solid #982B2F !important; text-shadow: -1px -1px 1px #982B2F, 1px 1px 1px #982B2F, 1px -1px 1px #982B2F, -1px 1px 1px #982B2F; text-transform: none !important; font-color:white !important;}\n"+
".red:hover {background-color:#EA1515 !important;}\n"+
".green {background-color:#46B754 !important; border: 2px solid #256E46 !important; text-shadow: -1px -1px 1px #256E46, 1px 1px 1px #256E46, 1px -1px 1px #256E46, -1px 1px 1px #256E46; text-transform: none !important; font-color:white !important;}\n"+
".green:hover {background-color:#A6E11D !important;}\n"+
".blue {background-color:#51C2FB !important; border: 2px solid #057499 !important; text-shadow: -1px -1px 1px #057499, 1px 1px 1px #057499, 1px -1px 1px #057499, -1px 1px 1px #057499; text-transform: none !important; font-color:white !important;}\n"+
".blue:hover {background-color:#C2DEFF !important;}\n"+
".gray {background-color:#999999 !important; border: 2px solid #666666 !important; text-shadow: -1px -1px 1px #666666, 1px 1px 1px #666666, 1px -1px 1px #666666, -1px 1px 1px #666666; text-transform: none !important; font-color:white !important;}\n"+
".gray:hover {background-color:#C3C3C3 !important;}\n"+
".odd {background-image: -moz-linear-gradient(center top , orange, red); box-shadow: 1px 1px 1px black; -moz-transform: rotate(15deg);}\n"+
".odd:hover {-moz-transform: none;}\n"+
".post.mosquito {width:16px; height:16px;}\n"+
".post.tiny {width:24px; height:24px;}\n"+
".post.small {width:32px; height:32px;}\n"+
".post.medium {width:48px; height:48px;}\n"+
".post.large {width:64px; height:64px;}\n"+
".post.xlarge {width:96px; height:96px;}\n"+
".floater.mosquito {left:8px;top:8px;}\n"+
".floater.tiny {left:12px;top:12px;}\n"+
".floater.small {left:16px;top:16px;}\n"+
".floater.medium {left:24px;top:24px;}\n"+
".floater.large {left:32px;top:32px;}\n"+
".floater.xlarge {left:48px;top:48px;}\n"+
".post.mosquito.working .picture img {width:24px; height:24px;left:-4px;top:-4px;}\n"+
".post.tiny.working .picture img {width:32px; height:32px;left:-4px;top:-4px;}\n"+
".post.small.working .picture img {width:48px; height:48px;left:-8px;top:-8px;}\n"+
".post.medium.working .picture img {width:64px; height:64px;left:-8px;top:-8px;}\n"+
".post.large.working .picture img {width:96px; height:96px;left:-16px;top:-16px;}\n"+
".post.xlarge.working .picture img {width:128px; height:128px;left:-16px;top:-16px;}\n"+
//"div.pinned {border-radius: 6px; background-color: black !important;}\n"+
//".post.short.pinned .picture img {border-radius: 5px; height:80% !important; width:80% !important; margin-left:10%; margin-top:10%;}\n"+
"#wmContent>.jsfTabControl>.tabs {top:10%; width:100px; position:relative;}\n"+
"#wmContent>.jsfTabControl>.pages {border-radius:5px;}\n"+
"#wmContent>.jsfTabControl>.tabs>.jsfTab {text-align:center;}\n"+
".jsfTabControl>.tabs {font-family:impact; font-size:large; color:inactivecaptiontext;}\n"+
"input,select,label,textarea {font-family:tahoma,arial; font-size:small; vertical-align:baseline !important;}\n"+
".jsfComboBox {line-height:normal;}\n"+
"button {font-family:tahoma,arial; font-size:small; vertical-align:top !important;}\n"+
"input[type=\"checkbox\"] {font-family:tahoma,arial; font-size:small; vertical-align:middle !important;}\n"+
".nomargin {margin:0 !important;}\n"+
".hidden {display:none !important;}\n"+
".block {display:block !important;}\n"+
".alignTop {vertical-align:top !important;}\n"+
".fit {width:100% !important;}\n"+
".indent {margin-left:16px;}\n"+
"img.crisp {image-rendering: -moz-crisp-edges;}\n"+
".listItem {position:relative; clear:both;}\n"+
".listItem.disabled {opacity:0.5 !important; background-color:#eeeeee;}\n"+
".listItem .toolBox {position: absolute; right: 0px; top: 0;}\n"+
".listItem select {border:0px; padding:0px; margin: 0px; margin-left:6px; margin-right:6px; background-color:#eeeeee; vertical-align:middle;}\n"+
".listItem input {border:0px; padding:0px; margin: 0px; margin-left:6px; margin-right:6px; background-color:#eeeeee; vertical-align:middle;}\n"+
".listItem textarea {background-color:#eeeeee; border:0px;}\n"+
".header {background-color: window; font-family:impact; font-size:2em; color:inactivecaptiontext; border-radius:5px 5px 0 0; padding-left:6px;}\n"+
".headerCaption {background-color: window; color: inactivecaptiontext; font-family: arial; font-size: small; padding-bottom: 6px; padding-left: 16px; padding-right: 16px; padding-top: 6px; border-radius:0 0 5px 5px;}\n"+
".headerCaption+.toolBox, .header+.toolBox {border-bottom:1px solid activeborder; margin-bottom: 5px; margin-top: 5px; padding-bottom: 5px;}\n"+
".line {border-top:1px solid #c0c0c0; line-height:2em;}\n"+
".subsection {margin-left:28px;}\n"+
".optioncontainer {max-height:12em; overflow-y:auto; background-color:rgb(238, 238, 238);}\n"+
".optioncontainer>.line {line-height:normal;}\n"+
".singleCol .post.classic {}\n"+
".twoCol .post.classic {display: inline-block; width: 50%; vertical-align: top;}\n"+
".twoCol .post.classic > .body {padding-right:28px;}\n"+
".threeCol .post.classic {display: inline-block; width: 33%; vertical-align: top;}\n"+
".threeCol .post.classic > .body {padding-right:28px;}\n"+
".fourCol .post.classic {display: inline-block; width: 25%; vertical-align: top;}\n"+
".fourCol .post.classic > .body {padding-right:28px;}\n"+
".w400 {width:400px;}\n"+
""
}catch(e){log("WM.console.globalStyle: "+e);}},
init: function(params){try{
debug.print("WM.console.init:");
var validateFBElements=["globalContainer","content"];
params=params||{};
//if console does not already exist
if (!WM.console.tabContainer) {
try{
addGlobalStyle(WM.console.globalStyle(),"styleConsole");
}catch(e){log("WM.console.init.addGlobalStyle: "+e);};
//attach to facebook page
var baseNode=$("globalContainer");
if (baseNode) baseNode=baseNode.parentNode;
//or attach to page body
else baseNode=($("body")||document.body);
//sort fields shared by post sorting and grouping
var sortFields = [
{value:"age",title:"Time elasped since created (ms)."},
{value:"alreadyProcessed",title:"History contains a status code for this post."},
{value:"appID"},
{value:"appName",title:"App name as it appears on FB."},
{value:"date",title:"The datetime the post was created, in unix format. Does not contain millisecond data."},
{value:"fromID",title:"The FB id of the person who created this post."},
{value:"fromName",title:"The display name of the person who created this post."},
{value:"fromNameLastFirst",title:"As fromName but displayed as LastName, FirstName"},
{value:"id",title:"The post object id as it is connected with FB."},
{value:"idText",title:"Either the whichText of the post, or the statusText of a post already processed."},
{value:"isAccepted"},
{value:"isFailed"},
{value:"isTimeout"},
{value:"isExcluded"},
{value:"isCollect",title:"A flag for if this post is to be collected."},
{value:"isForMe"},
{value:"isLiked"},
{value:"isMyPost"},
{value:"isPaused"},
{value:"isPinned"},
{value:"isScam"},
{value:"isStale"},
{value:"isUndefined"},
{value:"isWishlist"},
{value:"isW2W",title:"If this post is a Wall-to-wall post or just a general feed post."},
{value:"msg",title:"The comment attached to the post body by the post creator."},
{value:"postedDay",title:"The year/month/day portion of the creation time for this post."},
{value:"postedHour",title:"The year/month/day/hour portion of the creation time for this post."},
{value:"priority",title:"Priority 0 being the first post you would want processed, and Priority 50 being default."},
{value:"status",title:"The status code returned by the sidekick associated with this post."},
{value:"which",title:"The sidekick-defined bonus type id for this kind of post."},
{value:"whichText",title:"The text associated with this bonus type id."},
{value:null,name:"(none)"},
];
//create our content window
baseNode.insertBefore(createElement("div",{id:"wmContent"},[
//toolbox
(WM.console.tabContainer=new jsForms.tabControl({
dock:"fillAndShare",
sizeOffset:{height:-3,width:0},
alignment:"left",
tabs:[
{ //collect tab
text:"Collect",
image:null,
onSelect:function(){WM.console.collectTabControl.redraw();},
content:[
createElement("div",{className:"header",textContent:"Collect"}),
createElement("div",{className:"headerCaption",textContent:"View friends' posts and manage all your collection needs."}),
createElement("div",{className:"toolBox medium"},[
createElement("span",{className:"littleButton oddBlue",title:"Fetch Newer Posts Now",onclick:function(){WM.fetch({newer:true,bypassPause:true});} },[createElement("img",{className:"resourceIcon rssUpRight24"})]),
createElement("span",{className:"littleButton",title:"Fetch Older Posts Now",onclick:function(){WM.fetch({older:true,bypassPause:true});} },[createElement("img",{className:"resourceIcon rssDownLeft24"})]),
WM.console.pauseFetchButton=createElement("span",{className:"littleButton oddOrange",title:"Pause Automatic Fetching",onclick:function(){WM.pauseFetching();} },[createElement("img",{className:"resourceIcon expandDown24"})]),
WM.console.pauseCollectButton=createElement("span",{className:"littleButton oddOrange",title:"Pause Automatic Collection",onclick:function(){WM.pauseCollecting();} },[createElement("img",{className:"resourceIcon stop24"})]),
createElement("span",{className:"littleButton",name:"0",title:"Classic View",onclick:WM.setDisplay},[createElement("img",{className:"resourceIcon layoutListColor24"})]),
createElement("span",{className:"littleButton",name:"1",title:"Short View",onclick:WM.setDisplay},[createElement("img",{className:"resourceIcon layoutSmallColor24"})]),
createElement("span",{className:"littleButton",name:"2",title:"Developer View",onclick:WM.setDisplay},[createElement("img",{className:"resourceIcon layoutDetailColor24"})]),
createElement("span",{className:"littleButton",title:"Reset Counters",onclick:function(){WM.resetCounters();}},[createElement("img",{className:"resourceIcon refresh24"})]),
createElement("span",{className:"littleButton oddOrange",title:"Clean Now",onclick:function(){WM.cleanPosts();}},[createElement("img",{className:"resourceIcon trash24"})]),
createElement("span",{className:"littleButton",title:"ReID All",onclick:function(){WM.reIDAll();}},[createElement("img",{className:"resourceIcon identify24"})]),
createElement("label",{className:"indent",textContent:"Sort By: "}),
createElement("select",{id:"wmSortBy",className:"", title:"Sort By:", onchange:function(){WM.sortPosts({by:this.value});WM.redrawPosts({postRedraw:false,reorder:true});} },(function(){
var ret=[];
for (var i=0;i<sortFields.length;i++) ret.push(createElement("option",{value:sortFields[i].value,title:sortFields[i].title||"",textContent:sortFields[i].name||sortFields[i].value}));
return ret;
})()),
createElement("span",{className:"littleButton oddGreen",title:"Sort Ascending",onclick:function(){WM.sortPosts({direction:"asc"});WM.redrawPosts({reorder:true, postRedraw:false});}},[createElement("img",{className:"resourceIcon sortAsc24"})]),
createElement("span",{className:"littleButton oddOrange",title:"Sort Descending",onclick:function(){WM.sortPosts({direction:"desc"});WM.redrawPosts({reorder:true, postRedraw:false});}},[createElement("img",{className:"resourceIcon sortDesc24"})]),
createElement("label",{className:"indent",textContent:"Group By: "}),
createElement("select",{id:"wmGroupBy",className:"", title:"Group By:", onchange:function(){WM.constructGroups({by:this.value});WM.redrawPosts({postRedraw:false,reorder:true});} },(function(){
var ret=[];
for (var i=0;i<sortFields.length;i++) ret.push(createElement("option",{value:sortFields[i].value,title:sortFields[i].title||"",textContent:sortFields[i].name||sortFields[i].value}));
return ret;
})()),
createElement("span",{className:"littleButton oddGreen",title:"Group Ascending",onclick:function(){WM.sortGroups({direction:"asc"});WM.redrawPosts({reorder:true, postRedraw:false});}},[createElement("img",{className:"resourceIcon sortAsc24"})]),
createElement("span",{className:"littleButton oddOrange",title:"Group Descending",onclick:function(){WM.sortGroups({direction:"desc"});WM.redrawPosts({reorder:true, postRedraw:false});}},[createElement("img",{className:"resourceIcon sortDesc24"})]),
createElement("label",{className:"indent",textContent:"Columns: ",title:"Classic Mode Only"}),
createElement("select",{title:"Cols:", onchange:function(){WM.setDisplayCols({cols:this.value});} },[
createElement("option",{value:1,textContent:"One",selected:WM.quickOpts.displayCols==1}),
createElement("option",{value:2,textContent:"Two",selected:WM.quickOpts.displayCols==2}),
createElement("option",{value:3,textContent:"Three",selected:WM.quickOpts.displayCols==3}),
createElement("option",{value:4,textContent:"Four",selected:WM.quickOpts.displayCols==4})
]),
createElement("span",{className:"littleButton oddBlue",title:"Autolike Queue"},[
createElement("img",{className:"resourceIcon like24"}),
createElement("div",{className:"accFailBlock"},[
WM.console.likeQueueCounterNode=createElement("span",{className:"accept",textContent:"0"})
])
]),
]),
//app filter tabs
(WM.console.collectTabControl=new jsForms.tabControl({
dock:"fillAndShare",
subStyle:"coolBar",
shareSinglePage:true,
preventAutoSelectTab:true,
tabs:[
{
//default show all tab
text:"Show ALL",
image:"",
imageClass:"resourceIcon allSidekicks32",
appFilter:"All",
onSelect:WM.setAppFilter,
selected:(WM.quickOpts.filterApp=="All"),
content:null, //because page is shared
}
],
sharedContent:[
//bonus display node
WM.console.feedNode=createElement("div",{id:"wmFeedNode",style:"position: relative;"}),
],
})).node,
],
},
{ //sidekicks tab
text:"Manage Sidekicks",
image:null,
content:[
createElement("div",{className:"header",textContent:"Manage Sidekicks"}),
createElement("div",{className:"headerCaption",textContent:"Control some of the features of sidekicks."}),
WM.console.sidekickNode=createElement("div",{id:"wmSidekickList",className:"scrollY"}),
],
},
{ //feeds tab
text:"Manage Feeds",
image:null,
content:[
createElement("div",{className:"header",textContent:"Manage Feeds"}),
createElement("div",{className:"headerCaption",textContent:"Add direct links to friends or other public profiles, and fetch posts from those feeds faster."}),
createElement("div",{className:"toolBox medium columnRight"},[
createElement("div",{},[
createElement("div",{className:"littleButton oddGreen",title:"Add Feed",onclick:WM.feedManager.newFeed},[createElement("img",{className:"resourceIcon plus24"})]),
])
]),
WM.console.feedManagerNode=createElement("div",{id:"wmFeedsList",className:"scrollY"}),
],
},
{ //rules tab
text:"Manage Rules",
image:null,
content:[
createElement("div",{className:"header",textContent:"Manage Rules"}),
createElement("div",{className:"headerCaption",textContent:"Create rules like macros to control exactly how posts are handled."}),
createElement("div",{className:"toolBox medium columnRight"},[
createElement("div",{},[
createElement("div",{className:"littleButton oddGreen",title:"Add Rule",onclick:WM.rulesManager.newRule},[createElement("img",{className:"resourceIcon plus24"})]),
createElement("div",{className:"littleButton oddBlue",title:"Reset All Limits",onclick:WM.rulesManager.resetAllLimits},[createElement("img",{className:"resourceIcon reset24"})]),
createElement("div",{className:"littleButton oddBlue",title:"Convert Dynamics",onclick:WM.rulesManager.convertDynamics},[createElement("img",{className:"resourceIcon exportGrab24"})]),
createElement("div",{className:"littleButton oddBlue",title:"Import Rule",onclick:WM.rulesManager.importRule},[createElement("img",{className:"resourceIcon importData24"})]),
createElement("div",{className:"littleButton oddBlue",title:"Export All Rules",onclick:WM.rulesManager.showData},[createElement("img",{className:"resourceIcon object24"})]),
WM.rulesManager.toggleHBNode=createElement("div",{className:"littleButton "+(WM.quickOpts.heartbeatDisabled?"oddOrange":"oddGreen"),title:"Toggle Heartbeat",onclick:WM.rulesManager.toggleHeartbeat},[createElement("img",{className:"resourceIcon heartbeat24"})]),
])
]),
WM.console.priorityBuild=createElement("div",{id:"wmPriorityBuilder",className:"scrollY"}),
],
},
{ //dynamics tab
text:"Dynamic Grabber",
image:null,
content:[
createElement("div",{className:"header",textContent:"Dynamic Grabber"}),
createElement("div",{className:"headerCaption",textContent:"Create tests to capture posts sidekicks might not."}),
createElement("div",{className:"toolBox medium columnRight"},[
createElement("div",{},[
createElement("div",{className:"littleButton oddGreen",title:"Add Test",onclick:WM.grabber.newTest},[createElement("img",{className:"resourceIcon plus24"})]),
createElement("div",{className:"littleButton oddBlue",title:"Import Test",onclick:WM.grabber.importTest},[createElement("img",{className:"resourceIcon importData24"})]),
])
]),
WM.console.dynamicBuild=createElement("div",{id:"wmDynamicBuilder",className:"scrollY"}),
],
},
{ //friends tab
text:"Friend Tracker",
image:null,
content:[
createElement("div",{className:"header",textContent:"Friend Tracker"}),
createElement("div",{className:"headerCaption",textContent:"Track player friends and your interactions with them."}),
createElement("div",{className:"toolBox medium columnRight"},[
createElement("span",{className:"littleButton oddOrange",title:"Clean Now",onclick:function(){WM.friendTracker.clearAll();}},[
createElement("img",{className:"resourceIcon trash24"})
]),
createElement("label",{className:"indent",textContent:"Sort By: "}),
createElement("select",{title:"Sort By:", onchange:function(){WM.friendTracker.sort({sortBy:this.value});} },[
createElement("option",{value:"acceptCount",textContent:"acceptCount",title:"How many posts WM remembers as collected successfully from this user."}),
createElement("option",{value:"failCount",textContent:"failCount",title:"How many posts WM remembers as failed from this user."}),
createElement("option",{value:"id",textContent:"id",title:"The facebook id of the user."}),
createElement("option",{value:"lastKnownPostDate",textContent:"lastKnownPostDate",title:"The date of the last known post WM received for this user."}),
createElement("option",{value:"name",textContent:"name",title:"The name of the user, with last name first."}),
createElement("option",{value:"postCount",textContent:"postCount",title:"How many posts WM remembers receiving related to this user."}),
createElement("option",{value:"totalCount",textContent:"totalCount",title:"How many posts WM remembers failed OR accepted from this user."})
]),
createElement("span",{className:"littleButton oddGreen",title:"Sort Ascending",onclick:function(){WM.friendTracker.sort({sortOrder:"asc"});}},[createElement("img",{className:"resourceIcon sortAsc24"})]),
createElement("span",{className:"littleButton oddOrange",title:"Sort Descending",onclick:function(){WM.friendTracker.sort({sortOrder:"desc"});}},[createElement("img",{className:"resourceIcon sortDesc24"})]),
]),
WM.console.friendBuild=createElement("div",{id:"wmFriendTracker",className:"scrollY"}),
],
},
{ //options tab
text:"Options",
image:null,
content:[
createElement("div",{className:"header",textContent:"Options"}),
createElement("div",{className:"headerCaption",textContent:"Manage script and sidekick configuration, or link to updates."}),
//config menu button
createElement("div",{},[
createElement("label",{textContent:"Open the options menu: "}),
WM.console.configButton=createElement("button",{
className:"jsfHidden",
textContent:"WM Options",
onclick:function(){
//open options menu
WM.config.open();
},
}),
]),
//update script button
createElement("div",{},[
createElement("label",{textContent:"Update Script (Current Version: "+WM.version+") :"}),
createElement("button",{
className:"",
textContent:"Update Script",
onclick:function(){
//open update url in new window/tab
window.open("http://userscripts.org/scripts/source/86674.user.js","_blank");
},
}),
]),
],
},
]
})).node
]), $("globalContainer"));
//destroy facebook content on page
if ($("content")) $("content").style.display="none !important";
//init sort order
$("wmSortBy").value=WM.quickOpts.sortBy;
//init group order
$("wmGroupBy").value=WM.quickOpts.groupBy;
//init display mode
with (WM.console.feedNode){
className = className.toggleWordB(["1","3"].inArray(WM.quickOpts.displayMode),"short");
}
WM.setDisplayCols({cols:WM.quickOpts.displayCols});
}
WM.console.initialized = true;
//give sidekicks time to dock
if (params["callback"]||null) {
var fx = params["callback"];
delete params["callback"];
doAction(fx);
}
}catch(e){log("WM.console.init: "+e);}},
}; //end WM.console
//***************************************************************************************************************************************
//***** Sidekick Docking Object
//***************************************************************************************************************************************
WM.dock = {
//restructure menu to append appID before every object
fixMenu: function(menu,app){try{
var ret={};
//for each object in menu
for (var o in menu){
//WM.message(o);
ret[app+o]=menu[o];
//fix button functions and arrays to be prepended by the appID of that sidekick
var t=menu[o]["type"];
switch(t){
case "button_highlight":
case "button_selectmulti":
case "button_selectprefix":
//fix elements in the clearfirst array
if (menu[o]["clearfirst"]){
for (var i=0,len=ret[app+o]["clearfirst"].length;i<len;i++){
ret[app+o]["clearfirst"][i] = app+ret[app+o]["clearfirst"][i];
}
}
//fix elements in the options array
if (menu[o]["options"]){
for (var i=0,len=ret[app+o]["options"].length;i<len;i++){
ret[app+o]["options"][i] = app+ret[app+o]["options"][i];
}
}
if (menu[o]["clearPrefix"]){
ret[app+o]["clearPrefix"]=app+ret[app+o]["clearPrefix"];
}
if (menu[o]["prefix"]){
ret[app+o]["prefix"]=app+ret[app+o]["prefix"];
}
}
//fix kids
if (menu[o]["kids"]){
//rebuild kids object
ret[app+o]["kids"]=WM.dock.fixMenu(menu[o]["kids"],app);
}
}
return ret;
} catch(e) {log("WM.dock.fixMenu: "+e);}},
//restructure tests to append appID before every object's return
fixTests: function(arr,app){try{
//for each test in array
for (var t=0,len=arr.length;t<len;t++) {
var ret=arr[t].ret, kids=arr[t].kids;
//replace return value
if (ret) {
if (ret!="exclude" && ret!="none") {
arr[t].ret=app.appID+ret;
}
}
//process subtests
if (kids) WM.dock.fixTests(kids,app);
}
} catch(e) {log("WM.dock.fixTests: "+e);}},
fixAcceptTexts:function(app){try{
var newAccText={};
for (var s in app.accText) {
newAccText[app.appID+s]=app.accText[s];
}
app.accText=newAccText;
} catch(e) {log("WM.dock.fixAcceptTexts: "+e);}},
onSidekickParsed: function(newset){try{
//save it into the NEW format for games
var app=(WM.apps[newset.appID]=new WM.App(newset));
//promptText JSON.stringify(WM.config.settings));
WM.updateSettingsValues();
//detach the menu from the newset to reduce duplication
delete app.menu;
//fire priority event
(function(){WM.rulesManager.doEvent("onSidekickDock",app);})();
//fetch its initial posts
//app.fetchPosts();
WM.newSidekicks.push(app);
}catch(e){log("WM.dock.onSidekickParsed: "+e);}},
parseNewSidekick: function(node){try{
if (node){
var v = node.getAttribute('data-ft');
node.setAttribute('data-ft','');
if (v||null) {
var newset = JSON.parse(v);
WM.dock.onSidekickParsed(newset);
}
}
} catch(e) {log("WM.dock.parseNewSidekick: "+e);}},
answerDockingDoor: function(){try{
//log("Sidekick requesting to dock");
//get all sidekicks that left info on the dock;
forNodes(".//div[@id='wmDock']/div[(@data-ft) and not(@data-ft='')]",{},function(node){
if (node.getAttribute('data-ft') !=""){
window.setTimeout(WM.dock.parseNewSidekick,1,node);
}
});
} catch(e) {log("WM.dock.answerDockingDoor: "+e);}},
};
//***************************************************************************************************************************************
//***** Collector Object
//***************************************************************************************************************************************
WM.collector = {
tabs : {}, //container for window objects
recycle : [], //container for reusable window objects
queue : [], //container for urls to do in order
count : 0,
windowExists : function(hwnd){
try{
var testUrl=tab.hwnd.location.toString();
return true;
}catch(e) {
return false;
}
},
//requires id, url and callback
open : function(params) {try{
//log("WM.collector.open()",{level:0});
//check for tab queueing
if (WM.opts.queuetabs && WM.collector.count && !(params.emergency||false)) {
if (params.first||false) {
//cut in line to be next processed
WM.collector.queue.unshift(params);
return;
}
//toss the next action in the queue while we wait for the current one to finish
WM.collector.queue.push(params);
//log("WM.collector.open: request queued",{level:1});
return;
}
var url = params.url;
var id = params.id;
//create a window or use a recycled one
var tabHwnd;
if (WM.collector.recycle.length) {
tabHwnd = WM.collector.recycle.shift();
//watch for missing window objects
try{
//use the existing window object if it responds
tabHwnd.location.href=url;
} catch (e) {
//window object missing, make a new one
//FF22 version
tabHwnd = GM_openInTab(url,"_blank");
//FF21 version
//tabHwnd = ((WM.opts.useGM_openInTab)?GM_openInTab:window.open)(url,"_blank");
}
} else {
//we do not use recycling, just make a new one
//FF22 version
tabHwnd = GM_openInTab(url,"_blank");
//FF21 version
//tabHwnd = ((WM.opts.useGM_openInTab)?GM_openInTab:window.open)(url,"_blank");
}
//window opening
if (tabHwnd) {
WM.collector.count++;
params.hwnd=tabHwnd; //store the window handle
params.openTime=timeStamp();
WM.collector.tabs[id]=params; //add the tab and all its data to the array
//pass data to the sidekick top window
var callback = params.callback;
if (callback) delete params.callback;
if (params.msg) {
remove($(params.msg));
delete(params.msg);
}
//details for posts, not for likes
var app, synApp, isPost;
if (isPost=(params.post||null)){
app=params.post.app;
synApp=app.parent||app;
}
if (callback) {
//log("WM.collector.open: callback fired",{level:3});
doAction(function(){
callback(params);
});
}
} else {
log("WM.collector: Tab or Window is not opening or your browser does not support controlling tabs and windows via scripts. Check your popup blocker.",{level:5});
}
}catch(e){log("WM.collector.open: "+e);}},
doNext : function(){try{WM.collector.open(WM.collector.queue.shift());}catch(e){log("WM.collector.doNext: "+e);}},
close : function(tab) {try{
//recycle or close the passed tab
try{
if (WM.opts.recycletabsall || WM.opts.queuetabs || (WM.collector.recycle.length < WM.opts.recycletabs)) {
//wipe it and put it away
if (tab.hwnd){
WM.collector.recycle.push(tab.hwnd);
tab.hwnd.location.href="about:blank";
if (WM.collector.windowExists(tab.hwnd)){
tab.hwnd.location.hash="";
}
} else {
//tab is busy, laggy or missing
tab.closeRetries=(tab.closeRetries||0)+1;
if (tab.closeRetries<3) {
setTimeout(function(){WM.collector.close(tab);},1000);
} else {
log("WM.collector.close: Control of window handle lost; cannot recycle. Window may be too busy to communicate with, or has been closed manually.");
}
return;
}
} else {
if (tab.hwnd) tab.hwnd.close();
}
} catch (e){log("WM.collector.close: recycler: "+e);}
try{
tab.hwnd=null;
delete tab.signal;
delete tab.stage;
delete tab.closeTries;
if (tab.toIntv) clearInterval(tab.toIntv);
delete tab;
tab=null;
WM.collector.count--
}catch(e){log("WM.collector.close: destroy tab: "+e);}
//check for items in queue to do next
if (WM.collector.queue.length) {
//check that queueing is still in practice
if (WM.opts.queuetabs) {
setTimeout(WM.collector.doNext,1000); //just do one
} else {
//options have changed since queueing was enacted, release all the queue into windows right now
var offset=1000;
while (WM.collector.queue.length && (WM.collector.count < WM.opts.maxrequests)) {
setTimeout(WM.collector.doNext,offset); //open all, up to the limit set in options
offset+=100;
}
}
}
} catch (e){log("WM.collector.close: "+e);}},
closeAll : function() {try{
//first delete the queue so close fx doesnt pick them up
WM.collector.queue=[]; //empty but dont destroy
//then close the active windows, moving any to the recycler if that is enabled
for (var t in WM.collector.tabs) {
WM.collector.close(WM.collector.tabs[t]);
}
//then close any recycled windows
if (WM.collector.recycle.length) {
for (var r=0, hwnd; r < WM.collector.recycle.length; r++) {
if (hwnd=WM.collector.recycle[r]) {
hwnd.close();
}
}
WM.collector.recycle=[];
}
} catch (e){log("WM.collector.closeAll: "+e);}},
createTimer : function(tab) {try{
//create a timeout handler based on options and store the timer on the tab
tab.toIntv=setTimeout(function(){
if (tab) if (tab.stage!=4) doAction(function(){
//tab has been active too long, do timeout
log("WM.collector.timer: request timeout ("+tab.id+")",{level:3});
WM.setAsFailed(null, -14, tab.post);
WM.clearURL(tab);
})
},WM.opts.reqtimeout*1000);
} catch (e){
log("WM.collector.createTimer: "+e);}
},
cancelProcess : function(params) {try{
params=params||{};
var c = WM.collector;
for (t in c.tabs) {
if (c.tabs[t] && c.tabs[t][params.search] && c.tabs[t][params.search]==params.find){
//matching collector tab found
tab=c.tabs[t];
//close the window
c.close(tab);
}
}
} catch (e){log("WM.collector.cancelProcess: "+e);}},
refreshProcess : function(params) {try{
params=params||{};
var c = WM.collector;
for (t in c.tabs) {
if (c.tabs[t] && c.tabs[t][params.search] && c.tabs[t][params.search]==params.find){
//matching collector tab found
tab=c.tabs[t];
//restart the window at its initial url
if (tab.hwnd.location.href==tab.url) {
tab.hwnd.location.reload();
} else {
tab.hwnd.location.href=tab.url;
}
}
}
} catch (e){log("WM.collector.refreshProcess: "+e);}},
};
//***************************************************************************************************************************************
//***** Dynamic Grabberrabber Object
//***************************************************************************************************************************************
WM.grabber = {
tests:[],
methods:["msg","fromID","fromName","url","body","html","targetID","targetName","caption","title","desc","comments",
"commentorID","commentorName","likeName","likeID","link","either","img","canvas"],
init:function(params){try{
params=(params||{});
var testsIn = getOptJSON("dynamics_"+WM.currentUser.profile) || [];
var globalsIn = getOptJSON("dynamics_global") || {};
//import locals and intermix globals we have a placeholder for
if (isArrayAndNotEmpty(testsIn)) {
for (var t=0; t<testsIn.length; t++) {
if (testsIn[t].isGlobal) {
//make sure the global test still exists
var glob=globalsIn[testsIn[t].uniqueID]||null;
if (glob){
//merge global and local data
//this retains our expanded/enabled parts
var merge=mergeJSON(glob, testsIn[t]);
WM.grabber.newTest(merge);
//flag it so we don't import it again below
glob.alreadyUsed=true;
} else {
//global missing, can't import
log("WM.grabber.init: Global test missing, cannot merge");
}
} else {
//load from locals
WM.grabber.newTest(testsIn[t]);
}
}
}
//import all globals not already accounted for
for (var t in globalsIn) {
var glob=globalsIn[t];
//avoid already imported globals
if (!glob.alreadyUsed){
glob.uniqueID=t;
glob.isGlobal=true;
WM.grabber.newTest(glob);
}
}
}catch(e){log("WM.grabber.init: "+e);}},
save:function(){try{
var ret=[];
var retGlobal={};
if (isArrayAndNotEmpty(WM.grabber.tests)) {
for (var t=0, len=WM.grabber.tests.length; t<len; t++){
var test=WM.grabber.tests[t];
if (!test.isGlobal) {
//save it locally
ret.push(test.saveableData);
} else {
//make a placeholder locally
ret.push({isGlobal:true, uniqueID:test.uniqueID, enabled:test.enabled, expanded:test.expanded});
//and save it globally
var glob=test.saveableData;
glob.uniqueID=test.uniqueID;
retGlobal[test.uniqueID]=glob;
}
}
}
setOptJSON("dynamics_"+WM.currentUser.profile,ret);
setOptJSON("dynamics_global",retGlobal);
}catch(e){log("WM.grabber.save: "+e);}},
newTest:function(params){try{
params=params||{};
var test = new WM.Test(params);
WM.grabber.tests.push(test);
WM.grabber.save();
}catch(e){log("WM.grabber.newTest: "+e);}},
importTest:function(){try{
var params=prompt("Input test data",null);
if (params) {
WM.grabber.newTest(JSON.parse(params));
}
}catch(e){log("WM.grabber.importTest: "+e);}},
//get the test object with id starting at optional node or at top level
//may return null
getTest:function(id,node){try{
var nodes=(node||WM.grabber.tests);
for (var i=0,len=nodes.length;i<len;i++){
if (nodes[i]["id"]==id) {
return nodes[i];
} else if (nodes[i]["kids"]) {
var ret = WM.grabber.getTest(id,nodes[i]["kids"]);
if (ret) return ret;
}
}
}catch(e){log("WM.grabber.getTest: "+e);}},
};
//***************************************************************************************************************************************
//***** Test Class
//***************************************************************************************************************************************
WM.Test = function(params){try{
this.objType="test";
var self=this;
params=params||{};
//defaults
this.enabled=!(params.disabled||false); //check for WM2 disabled param
this.expanded=true;
this.title="";
this.search=[]; //strings array
this.find=""; //string
this.findArray=[]; //string array
this.kids=[]; //test array
this.subTests=[]; //strings array
this.parent=null;
this.appID="";
this.ret="dynamic";
this._findMode="basic";
this.subNumRange={low:0,high:0};
this._isGlobal=false;
this.__defineGetter__("saveableData",function(){try{
var dat={};
//dat.id=this.id;
dat.label=this.title;
dat.enabled=this.enabled;
dat.search=this.search;
dat.find=(this.findMode=="basic")?this.findArray:this.find;
dat.ret=this.ret;
dat.expanded=this.expanded;
if (this.findMode=="subtests") dat.subTests=this.subTests;
if (this.findMode=="subnumrange") {
dat.subNumRange=this.subNumRange.low+","+this.subNumRange.high;
}
if (this.findMode=="regex") dat.regex=this.regex;
dat.appID=this.appID;
dat.kids=[];
if (isArrayAndNotEmpty(this.kids)) for (var i=0,kid;(kid=this.kids[i]);i++) {
dat.kids.push(kid.saveableData);
}
return dat;
}catch(e){log("WM.Test.saveableData: "+e);}});
//set/get wether this test is saved as global or profile
this.__defineGetter__("isGlobal",function(){try{
return this._isGlobal;
}catch(e){log("WM.Test.isGlobal: "+e);}});
this.__defineSetter__("isGlobal",function(v){try{
//only top level tests can be global
if (this.parent) {
confirm("Only top level tests can be set to global.");
return;
}
if (!v) {
if (!confirm("Disabling profile sharing on this test will prevent other users on this machine from loading it. Are you sure you wish to make this test locally available only?")) return;
}
this._isGlobal=v;
//make sure we have a uniqueID
//but don't destroy one that already exists
if (v && !exists(this.uniqueID)) this.uniqueID = unique();
//change the color/icon of the isGlobal button
if (this.toggleGlobalButton) {
var s=WM.opts.littleButtonSize;
with (this.toggleGlobalButton) className=className.swapWordB(v,"removeGlobal"+s,"addGlobal"+s);
with (this.toggleGlobalButton.parentNode) {
className=className.swapWordB(v,"oddOrange","oddGreen");
title=(v)?"Disable Profile Sharing":"Share With Other Profiles";
}
}
}catch(e){log("WM.Test.isGlobal: "+e);}});
//use passed params
for (var p in params) {
//omit specific params
if (!(["subNumRange","kids","disabled","label","find"].inArray(p)) ) {
//copy only params that make it past the checker
this[p]=params[p];
}
}
//calculate subNumRange as an object
if (exists(params.subNumRange)) {
var p=params.subNumRange.split(",");
this.subNumRange={low:p[0]||0, high:p[1]||0};
this._findMode="subnumrange";
}
//get the title from the label field
if (exists(params.label)) this.title=params.label;
//detect which findMode we are using
//subNumRange was already inspected above
if (this.regex) this._findMode="regex";
else if (exists(params.subTests)) this._findMode="subtests";
//and we default to "basic" already
//import the find field now
if (isArray(params.find)) this.findArray=params.find;
else this.find=params.find;
this.enable=function(){try{
this.enabled=true;
this.node.className=this.node.className.removeWord("disabled");
WM.grabber.save();
}catch(e){log("WM.Test.enable: "+e);}};
this.disable=function(){try{
this.enabled=false;
this.node.className=this.node.className.addWord("disabled");
WM.grabber.save();
}catch(e){log("WM.Test.disable: "+e);}};
this.remove=function(noConfirm){try{
var ask=WM.opts.dynamicConfirmDeleteTest;
if (noConfirm || (this.isGlobal && confirm("This test is shared with other profiles. Deleting it here will prevent it from loading for other users. Are you sure you wish to delete this test and its children.")) || !ask || (!this.isGlobal && ask && confirm("Delete test and all of its child nodes?"))){
//remove my data
var parentContainer=(this.parent)?this.parent.kids:WM.grabber.tests;
parentContainer.removeByValue(this);
//remove my node
remove(this.node);
doAction(WM.grabber.save);
}
}catch(e){log("WM.Test.remove: "+e);}};
this.moveUp=function(){try{
//where is this
var parentContainer=(this.parent)?this.parent.kids:WM.grabber.tests;
//only affects items not already the first in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex-1];
//swap me with my sibling
parentContainer[myIndex-1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(this.node,sibling.node);
//save it
WM.grabber.save();
}
}
}catch(e){log("WM.Test.moveUp: "+e);}};
this.moveDown=function(){try{
//where is this
var parentContainer=(this.parent)?this.parent.kids:WM.grabber.tests;
//only affects items not already the last in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex+1];
//swap me with my sibling
parentContainer[myIndex+1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(sibling.node,this.node);
//save it
WM.grabber.save();
}
}
}catch(e){log("WM.Test.moveDown: "+e);}};
this.moveUpLevel=function(){try{
if (this.parent) {
//this is not a top level node, so we can move it
var targetContainer=((this.parent.parent)?this.parent.parent.kids:WM.grabber.tests);
//remove from parent
this.parent.kids.removeByValue(this);
//set new parent
this.parent=(this.parent.parent||null); //never point to the top level
//move the object
targetContainer.push(this);
//move the node
if (this.parent) this.parent.kidsNode.appendChild(this.node);
else WM.console.dynamicBuild.appendChild(this.node);
//save it
WM.grabber.save();
}
}catch(e){log("WM.Test.moveUpLevel: "+e);}};
this.moveDownLevel=function(){try{
//where is this
var parentContainer=(this.parent)?this.parent.kids:WM.grabber.tests;
//create a new rule at my level
var newTest = new WM.Test({
parent:this.parent||null,
});
parentContainer.push(newTest);
//remove me from my current parent
parentContainer.removeByValue(this);
//attach me to my new parent
this.parent=newTest;
newTest.kids.push(this);
//move my node
newTest.kidsNode.appendChild(this.node);
//save it
WM.grabber.save();
}catch(e){log("WM.Test.moveDownLevel: "+e);}};
this.clone=function(){try{
var cloneTest=this.saveableData;
//global clones are not global
if (this.parent) this.parent.addChild(cloneTest);
else WM.grabber.newTest(cloneTest);
}catch(e){log("WM.Test.clone: "+e);}};
this.addChild=function(p){try{
var isNew=!exists(p);
p=p||{};
p.parent=this;
var test=new WM.Test(p);
this.kids.push(test);
if (isNew) WM.grabber.save();
}catch(e){log("WM.Test.addChild: "+e);}};
this.toggleContent=function(){try{
this.expanded=!this.expanded;
var btnSize=WM.opts.littleButtonSize;
with (this.contentNode)
className=className.swapWordB(this.expanded,"expanded","collapsed");
with (this.toggleImgNode)
className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
WM.grabber.save();
}catch(e){log("WM.Test.toggleContent: "+e);}};
this.populateBonusList=function(){try{
var node=this.bonusNode;
var bonuses={};
//get the list of accept texts for this app
if (this.appID!="") {
if (this.appID=="*") {
//populate list with bonuses from ALL docked sidekicks
} else {
//make sure the app is ready
//if it has not yet docked, it wont be
var app=WM.apps[this.appID];
bonuses = (app?(mergeJSON(app.accText,app.userDefinedTypes)||{}):{});
}
}
//add special return values
bonuses["dynamic"]="* Dynamic grab";
bonuses["none"]="* None";
bonuses["wishlist"]="* Flaged as Wishlist";
bonuses["exclude"]="* Excluded types";
bonuses["send"]="* Send Unknown";
bonuses["doUnknown"]="* Get Unknown";
bonuses["{%1}"]="* Subtest Value";
//sort by display text
bonuses=sortCollection(bonuses,"value");
//add each element to the dropdown
var elem;
node.innerHTML=""; //wipe previous list
for (var i in bonuses) {
var showI=i.removePrefix(this.appID);
node.appendChild(
elem=createElement("option",{textContent:((bonuses[i].startsWith("*"))?"":((showI.startsWith("send"))?"Send ":"Get "))+bonuses[i], value:i, selected:(this.ret==i)})
);
}
}catch(e){log("WM.Test.populateBonusList: "+e);}};
this.populateAppList=function(){try{
var node=this.appListNode;
var a={};
for (var i in WM.apps){
a[WM.apps[i].appID]=WM.apps[i].name;
}
//add special return values
a["*"]="* All";
//add each element to the dropdown
var elem;
node.innerHTML=""; //wipe previous list
for (var i in a) {
node.appendChild(elem=createElement("option",{textContent:a[i], value:i,selected:(this.appID==i)}));
}
//sort it
elementSortChildren(node,"textContent");
}catch(e){log("WM.Test.populateAppList: "+e);}};
this.calcSearch=function(){try{
//collect the checked search fields in their listed order
if (self.searchNode) {
self.search=[];
forNodes(".//input[(@type='checkbox')]",{node:self.searchNode},function(e){
if (e && e.checked){
self.search.push(e.value);
log(e.value);
}
});
}
WM.grabber.save();
}catch(e){log("WM.Test.calcSearch: "+e);}};
this.convertToRule=function(p){try{
var rule;
WM.rulesManager.rules.push(
rule=new WM.rulesManager.Rule( WM.rulesManager.ruleFromTest( this.saveableData ) )
);
if (WM.opts.rulesJumpToNewRule){
//jump to rule view
WM.console.tabContainer.selectTab(3);
//scroll to new rule
rule.node.scrollIntoView();
}
}catch(e){log("WM.Test.convertToRule: "+e);}};
//set/get find field modes
this.__defineGetter__("findMode",function(){try{
return this._findMode;
}catch(e){log("WM.Test.findMode: "+e);}});
this.__defineSetter__("findMode",function(v){try{
var lastV = this._findMode;
this._findMode=v;
if (lastV==v) return; //no change
//enable disable regex type
this.regex=(v=="regex" || v=="regexp");
//switch to array/string find field type
//this.setFindType((v=="basic")?"array":"string");
//show the correct find field
if (this.findNode) this.findNode.value=((v=="basic")?this.findArray.join("\n"):this.find);
//show/hide the subtests box
if (this.subTestsBoxNode) with (this.subTestsBoxNode) className=className.toggleWordB((v!="subtests"),"hidden");
//show/hide the subnumrange picker
if (this.subNumRangeBoxNode) with (this.subNumRangeBoxNode) className=className.toggleWordB((v!="subnumrange"),"hidden");
WM.grabber.save();
}catch(e){log("WM.Test.findMode: "+e);}});
//draw it
try{(((this.parent)?this.parent.kidsNode:null)||$("wmDynamicBuilder")).appendChild(
this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
createElement("div",{className:"line"},[
createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
]),
this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
self.enabled=this.checked;
with (self.node) className=className.toggleWordB(!this.checked,"disabled");
WM.grabber.save();
}}),
createElement("label",{textContent:"Title:"}),
this.titleNode=createElement("input",{value:(this.title||""), onchange:function(){self.title=this.value; WM.grabber.save();}}),
//toolbox
createElement("div",{className:"littleButton oddOrange", title:"Remove Test"},[
createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Clone Test"},[
createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize,onclick:function(){self.clone();}})]),
createElement("div",{className:"littleButton oddGreen", title:"Move Up"},[
createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize,onclick:function(){self.moveUp();}})]),
createElement("div",{className:"littleButton oddOrange", title:"Move Down"},[
createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize,onclick:function(){self.moveDown();}})]),
createElement("div",{className:"littleButton oddGreen", title:"Move Up Level"},[
createElement("img",{className:"resourceIcon moveUpLevelLeft"+WM.opts.littleButtonSize,onclick:function(){self.moveUpLevel();}})]),
createElement("div",{className:"littleButton oddOrange", title:"Move Down Level"},[
createElement("img",{className:"resourceIcon moveInLevel"+WM.opts.littleButtonSize,onclick:function(){self.moveDownLevel();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Show Source"},[
createElement("img",{className:"resourceIcon object"+WM.opts.littleButtonSize,onclick:function(){promptText(JSON.stringify(self.saveableData),true);}})]),
createElement("div",{className:"indent littleButton oddBlue", title:"Convert To Rule"},[
createElement("img",{className:"resourceIcon exportGrab"+WM.opts.littleButtonSize,onclick:function(){self.convertToRule();}})]),
createElement("div",{className:"indent littleButton "+((this.isGlobal)?"oddOrange":"oddGreen"), title:((this.isGlobal)?"Disable Profile Sharing":"Share With Other Profiles")},[
this.toggleGlobalButton=createElement("img",{className:"resourceIcon "+((this.isGlobal)?"removeGlobal":"addGlobal")+WM.opts.littleButtonSize,onclick:function(){self.isGlobal=!self.isGlobal; WM.grabber.save();}})]),
]),
this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
//appID
createElement("div",{className:"line"},[
createElement("label",{textContent:"appID:"}),
this.appIDNode=createElement("input",{value:(this.appID||""), onchange:function(){self.appID=this.value;WM.grabber.save();self.populateBonusList();}}),
this.appListNode=createElement("select",{onchange:function(){self.appIDNode.value=this.value; self.appID=this.value; WM.grabber.save(); self.populateBonusList();}}),
createElement("div",{className:"littleButton oddBlue", title:"Refresh App List"},[
createElement("img",{className:"resourceIcon refresh"+WM.opts.littleButtonSize,onclick:function(){self.populateAppList();}})]),
]),
//return type
createElement("div",{className:"line"},[
createElement("label",{textContent:"Return Type ('which'):"}),
this.retNode=createElement("input",{value:(this.ret||"dynamic"), onchange:function(){self.ret=this.value;WM.grabber.save();}}),
this.bonusNode=createElement("select",{onchange:function(){self.retNode.value=this.value; self.ret=this.value; WM.grabber.save();}}),
createElement("div",{className:"littleButton oddBlue", title:"Refresh Bonus List"},[
createElement("img",{className:"resourceIcon refresh"+WM.opts.littleButtonSize,onclick:function(){self.populateBonusList();}})]),
]),
//search list
createElement("div",{className:"line"},[
createElement("label",{textContent:"Search In Field(s):",title:"Specify fields in which to look for data. Adjust order as needed."}),
this.searchNode=createElement("div",{className:"subsection optioncontainer"},(function(){
var ret=[];
//draw first the methods we have already selected
if (isArrayAndNotEmpty(self.search)) for (var m=0; m<self.search.length; m++) {
var s = self.search[m];
ret.push(createElement("div",{className:"line"},[
createElement("div",{className:"littleButton oddGreen", title:"Move Up"},[
createElement("img",{className:"resourceIcon nomargin arrowUp16",onclick:function(){elementMoveUp(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("div",{className:"littleButton oddOrange", title:"Move Down"},[
createElement("img",{className:"resourceIcon nomargin arrowDown16",onclick:function(){elementMoveDown(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("div",{className:"littleButton oddGreen", title:"Move To Top"},[
createElement("img",{className:"resourceIcon nomargin moveTopLeft16",onclick:function(){elementMoveTop(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("div",{className:"littleButton oddOrange", title:"Move To Bottom"},[
createElement("img",{className:"resourceIcon nomargin moveBottomLeft16",onclick:function(){elementMoveBottom(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("input",{type:"checkbox",value:s,checked:true,onchange:function(){self.calcSearch();}}),
createElement("label",{textContent:s,title:WM.rulesManager.postParts[s]}),
]));
}
//draw the remaining items in their normal order
for (var m=0; m<WM.grabber.methods.length; m++){
var s = WM.grabber.methods[m];
//prevent duplicates
if (self.search.inArray(s)) continue;
ret.push(createElement("div",{className:"line"},[
createElement("div",{className:"littleButton oddGreen", title:"Move Up"},[
createElement("img",{className:"resourceIcon nomargin arrowUp16",onclick:function(){elementMoveUp(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("div",{className:"littleButton oddOrange", title:"Move Down"},[
createElement("img",{className:"resourceIcon nomargin arrowDown16",onclick:function(){elementMoveDown(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("div",{className:"littleButton oddGreen", title:"Move To Top"},[
createElement("img",{className:"resourceIcon nomargin moveTopLeft16",onclick:function(){elementMoveTop(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("div",{className:"littleButton oddOrange", title:"Move To Bottom"},[
createElement("img",{className:"resourceIcon nomargin moveBottomLeft16",onclick:function(){elementMoveBottom(this.parentNode.parentNode); self.calcSearch();}})
]),
createElement("input",{type:"checkbox",value:s,onchange:function(){self.calcSearch();}}),
createElement("label",{textContent:s,title:WM.rulesManager.postParts[s]}),
]));
}
return ret;
})()),
]),
//find mode
createElement("div",{className:"line"},[
createElement("label",{textContent:"Find Mode:",title:"Choose the mode you will use to find text."}),
this.findModeNode=createElement("select",{onchange:function(){self.findMode=this.value;}},[
createElement("option",{selected:(this.findMode=="basic"),value:"basic",textContent:"Basic",title:"Search for a list of words or phrases."}),
createElement("option",{selected:(this.findMode=="subnumrange"),value:"subnumrange",textContent:"Number Range",title:"Search for a range of numbers using an insertion point '{%1}' in your find parameter."}),
createElement("option",{selected:(this.findMode=="subtests"),value:"subtests",textContent:"Sub Tests",title:"Search for a list of words or phrases using an insertion point '{%1}' in your find parameter."}),
createElement("option",{selected:(this.findMode=="regex"),value:"regex",textContent:"Registered Expression",title:"Search for complex phrases using a regular expression."})
]),
]),
//find list
createElement("div",{className:"line"},[
createElement("label",{textContent:"Find:",title:"One per line (basic mode), or a single regular expression. First match is used, so mind the order."}),
createElement("div",{className:"subsection"},[
this.findNode=createElement("textarea",{className:"fit",textContent:((this.findMode=="basic")?this.findArray.join("\n"):this.find), onchange:function(){
if (self.findMode=="basic") self.findArray=this.value.split("\n");
else self.find=this.value;
WM.grabber.save();
}}),
])
]),
//subtests list
this.subTestsBoxNode=createElement("div",{className:("line").toggleWordB(this.findMode!="subtests","hidden")},[
createElement("label",{textContent:"Subtest Texts:",title:"Provide text replacements for the insertion point. No regular expressions."}),
createElement("div",{className:"subsection"},[
this.subTestsNode=createElement("textarea",{className:"fit",textContent:((isArray(this.subTests)?this.subTests.join("\n"):"")||""), onchange:function(){self.subTests=this.value.split("\n"); WM.grabber.save();}}),
])
]),
//subnumrange picker
this.subNumRangeBoxNode=createElement("div",{className:("line").toggleWordB(this.findMode!="subnumrange","hidden")},[
createElement("label",{textContent:"Subtest Number Range:",title:"Provide a start and end range for the insertion point."}),
this.subNumRangeLowNode=createElement("input",{value:this.subNumRange.low||0, onchange:function(){self.subNumRange.low=this.value; WM.grabber.save();}}),
this.subNumRangeHighNode=createElement("input",{value:this.subNumRange.high||0, onchange:function(){self.subNumRange.high=this.value; WM.grabber.save();}}),
]),
//kids subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"Child Tests:",title:"Child tests are nested tests which are applied to matching posts at the same time the parent test is applied. Child rules can have different return values that override the parent return value."}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addChild();},title:"Add Child"},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
]),
this.kidsNode=createElement("div",{className:"subsection"}),
]),
]),
])
);}catch(e){log("WM.Test.init.drawTest: "+e);}
//populate my bonus list
this.populateAppList();
this.populateBonusList();
//list the kids for this test
if (isArrayAndNotEmpty(params.kids)) for (var i=0,kid; (kid=params.kids[i]); i++) {
this.addChild(kid);
}
return self;
}catch(e){log("WM.Test.init: ")+e}};
//***************************************************************************************************************************************
//***** Feed Objects
//***************************************************************************************************************************************
WM.feedManager = {
feeds : [],
init : function(){
var feedsIn=(getOptJSON("feeds3_"+WM.currentUser.profile)||[]);
var feedsInGlobal=(getOptJSON("feeds3_global")||[]);
if (isArrayAndNotEmpty(feedsIn)) {
//import feeds from storage
for (var f=0;f<feedsIn.length;f++){
feed=feedsIn[f];
if (!feed.isGlobal){
WM.feedManager.feeds.push(new WM.Feed(feed));
} else {
var glob=feedsInGlobal[feed.uniqueID]||null;
if (glob){
var merge=mergeJSON(glob,feed);
WM.feedManager.newFeed(merge);
glob.alreadyUsed=true;
} else {
log("WM.feedManager.init: Global feed missing, cannot merge");
}
}
}
} else {
//never been used before, create base feeds
WM.feedManager.feeds.push(new WM.Feed({title:"My Home Feed", url:"https://graph.facebook.com/me/home", isRemoveable:false}));
//WM.feedManager.feeds.push(new WM.Feed({title:"My Profile Wall", url:"https://graph.facebook.com/me/feed", isRemoveable:false}));
//import oldstyle feeds
var feedsOld=getOpt("feeds_"+WM.currentUser.profile);
if (feedsOld){
feedsOld=feedsOld.split("\n");
if (isArrayAndNotEmpty(feedsOld)) for (var f=0;f<feedsOld.length;f++) {
//prevent empties
if (feedsOld[f]) {
//create the new feed
WM.feedManager.newFeed({id:feedsOld[f],title:feedsOld[f]});
}
}
}
WM.feedManager.save();
}
//import all global feeds not already accounted for
for (var t in feedsInGlobal) {
var glob=feedsInGlobal[t];
if (!glob.alreadyUsed){
glob.uniqueID=t;
glob.isGlobal=true;
WM.feedManager.newFeed(glob); //newFeed adds app filters, where New Feed() does not
}
}
},
newFeed : function(params){
params=params||{};
var feed = new WM.Feed(params);
WM.feedManager.feeds.push(feed);
//add filters for each app available
for (var a in WM.apps){
feed.addFilter({id:"app_"+a});
}
},
save :function(){
var retFeeds=[];
var retGlobal={};
if (isArrayAndNotEmpty(WM.feedManager.feeds)) for (var f=0,len=WM.feedManager.feeds.length; f<len; f++){
var feed=WM.feedManager.feeds[f];
if (!feed.isGlobal) {
retFeeds.push(feed.saveableData);
} else {
retFeeds.push({isGlobal:true, uniqueID:feed.uniqueID, enabled:feed.enabled, expanded:feed.expanded});
var glob=feed.saveableData;
glob.uniqueID=feed.uniqueID;
retGlobal[feed.uniqueID]=glob;
}
}
setOptJSON("feeds3_"+WM.currentUser.profile,retFeeds);
setOptJSON("feeds3_global",retGlobal);
},
};
//***************************************************************************************************************************************
//***** FeedFilter Class
//***************************************************************************************************************************************
WM.FeedFilter = function(params){try{
this.objType="feedFilter";
params=params||{};
var self=this;
//set defaults
this.enabled=true;
this.expanded=true;
this._olderLimitReached=false;
//initialize edges to the collector startup time
this.oldedge=Math.round(timeStamp()/1000); //older edge timestamp
this.newedge=Math.round(timeStamp()/1000); //newer edge timestamp
//use passed params
for (var p in params) this[p]=params[p];
this.enable=function(){try{
this.enabled=true;
this.node.className=this.node.className.removeWord("disabled");
WM.feedManager.save();
}catch(e){log("WM.FeedFilter.enable: "+e);}};
this.disable=function(){try{
this.enabled=false;
this.node.className=this.node.className.addWord("disabled");
WM.feedManager.save();
}catch(e){log("WM.FeedFilter.disable: "+e);}};
this.toggle=function(){try{
this.enabled=this.toggleNode.checked;
this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled");
WM.feedManager.save();
}catch(e){log("WM.FeedFilter.toggle: "+e);}};
this.toggleContent=function(){try{
this.expanded=!this.expanded;
var btnSize=WM.opts.littleButtonSize;
with (this.contentNode)
className=className.swapWordB(this.expanded,"expanded","collapsed");
with (this.toggleImgNode)
className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
WM.feedManager.save();
}catch(e){log("WM.FeedFilter.toggleContent: "+e);}};
//remove this
this.remove=function(){try{
if (this.node) remove(this.node);
if (this.parent) delete this.parent.filters[this.id];
WM.feedManager.save();
}catch(e){log("WM.FeedFilter.remove: "+e);}};
//fetch posts for this
this.fetchNewer=function(){try{
WM.fetch({
newer:true,
apps:WM.apps[this.appID],
feeds:[this.parent],
bypassPause:true,
bypassAppDisabled:true,
bypassFeedDisabled:true,
bypassFilterDisabled:true,
});
}catch(e){log("WM.FeedFilter.fetchNewer: "+e);}};
this.fetchOlder=function(){try{
WM.fetch({
older:true,
apps:WM.apps[this.appID],
feeds:[this.parent],
bypassPause:true,
bypassAppDisabled:true,
bypassFeedDisabled:true,
bypassFilterDisabled:true,
});
}catch(e){log("WM.FeedFilter.fetchOlder: "+e);}};
this.__defineGetter__("olderLimitReached",function(){try{
return this._olderLimitReached;
}catch(e){log("WM.FeedFilter.olderLimitReached: "+e);}});
this.__defineSetter__("olderLimitReached",function(v){try{
this._olderLimitReached=v;
//update the sidekick page button graphics
var node=this.olderLimitNode;
if (node) node.textContent=v;
if (v) {
WM.rulesManager.doEvent("onFeedFilterOlderLimitReached",this);
}
}catch(e){log("WM.FeedFilter.olderLimitReached: "+e);}});
this.__defineGetter__("appID",function(){try{
//this assumes its an app filter because so far thats all we use
return this.id.removePrefix("app_");
}catch(e){log("WM.FeedFilter.appID: "+e);}});
this.__defineGetter__("appName",function(){try{
//this assumes its an app filter because so far thats all we use
//it also assumes app objects are global, which they are
//but that they are also available and already filled in by the sidekick, which they may not be
var a = WM.apps[this.appID];
if (a!=undefined) {
//debug.print([this.appID,a]);
return a.name;
}
return "";
}catch(e){log("WM.FeedFilter.appName: "+e);}});
//draw it
try{
if (this.parent.filtersNode) this.parent.filtersNode.appendChild(
this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
createElement("div",{className:"line"},[
createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
]),
this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
self.enabled=this.checked;
with (self.node) className=className.toggleWordB(!this.checked,"disabled");
WM.feedManager.save();
}}),
createElement("span",{textContent:this.appName + " (" + this.id + ")"}),
//toolbox
createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer"},[
this.fetchNewerButton=createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();} })
]),
createElement("div",{className:"littleButton", title:"Fetch Older"},[
this.fetchOlderButton=createElement("img",{className:"resourceIcon rssDownLeft"+WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();} })
]),
]),
this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
createElement("div",{className:"line"},[
createElement("label",{textContent:"Older Limit Reached: ",title:"Reports if this filter has reached the user defined oldest post limit."}),
this.olderLimitNode=createElement("span",{textContent:this.olderLimitReached}),
]),
createElement("div",{className:"line"},[
createElement("label",{textContent:"Newer Edge: ",title:"A Unixtime indicator of the newest post-time you have fetched for this filter."}),
this.newedgeNode=createElement("span",{textContent:this.newedge}),
]),
createElement("div",{className:"line"},[
createElement("label",{textContent:"Older Edge: ",title:"A Unixtime indicator of the oldest post-time you have fetched for this filter."}),
this.oldedgeNode=createElement("span",{textContent:this.oldedge}),
]),
]),
])
);
}catch(e){log("WM.FeedFilter.init:addManagerElement: "+e);};
return self;
}catch(e){log("WM.FeedFilter.init: "+e);}};
//***************************************************************************************************************************************
//***** Feed Class
//***************************************************************************************************************************************
WM.Feed = function(params){try{
this.objType="feed";
params=params||{};
var self=this;
//set defaults
this.enabled=true;
this.expanded=true;
this.url="";
this.id="";
this.filters={};
this.feedName="";
this.isRemoveable=true; //set to false on own feeds
this.title="New Feed";
this._isGlobal=false;
//use passed params
var newFilters=params.filters||{};
delete params.filters;
this.__defineGetter__("isGlobal",function(){try{
return this._isGlobal;
}catch(e){log("WM.Feed.isGlobal: "+e);}});
this.__defineSetter__("isGlobal",function(v){try{
if (!v) {
if (!confirm("Disabling profile sharing on this feed will prevent other users on this machine from loading it. Are you sure you wish to make this feed locally available only?")) return;
}
this._isGlobal=v;
//make sure we have a uniqueID
//but don't destroy one that already exists
if (v && !exists(this.uniqueID)) this.uniqueID = unique();
//change the color/icon of the isGlobal button
if (this.toggleGlobalButton) {
var s=WM.opts.littleButtonSize;
with (this.toggleGlobalButton) className=className.swapWordB(v,"removeGlobal"+s,"addGlobal"+s);
with (this.toggleGlobalButton.parentNode) {
className=className.swapWordB(v,"oddOrange","oddGreen");
title=(v)?"Disable Profile Sharing":"Share With Other Profiles";
}
}
}catch(e){log("WM.Feed.isGlobal: "+e);}});
this.__defineGetter__("saveableData",function(){try{
var ret={};
ret.title=this.title;
ret.enabled=this.enabled;
ret.expanded=this.expanded;
ret.isRemoveable=this.isRemoveable;
ret.url=this.url;
if (this.isRemoveable) ret.id=this.id;
//capture filters
ret.filters={};
for (var f in this.filters) {
ret.filters[f]={
enabled:this.filters[f].enabled,
expanded:this.filters[f].expanded,
id:this.filters[f].id,
};
}
return ret;
}catch(e){log("WM.Feed.saveableData: "+e);}});
for (var p in params) this[p]=params[p];
this.enable=function(){try{
this.enabled=true;
this.node.className=this.node.className.removeWord("disabled");
WM.feedManager.save();
}catch(e){log("WM.Feed.enable: "+e);}};
this.disable=function(){try{
this.enabled=false;
this.node.className=this.node.className.addWord("disabled");
WM.feedManager.save();
}catch(e){log("WM.Feed.disable: "+e);}};
this.toggle=function(){try{
this.enabled=this.toggleNode.checked;
this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled");
WM.feedManager.save();
}catch(e){log("WM.Feed.toggle: "+e);}};
//create a filter for a specific app
//filter id must be "app_"+appID
//will not add duplicates
this.addFilter=function(params){try{
var isNew=!exists(params);
params=params||{};
params.parent=this;
//prevent duplicates
if (!exists(this.filters[params.id])) {
return (this.filters[params.id]=new WM.FeedFilter(params));
if (isNew) WM.feedManager.save();
} else {
return this.filters[params.id];
}
}catch(e){log("WM.Feed.addFilter: "+e);}};
//get the extents of the feed by merging all feed filter oldedge/newedge values
this.getMergedEdges=function(params){
/*
apps[]: an array of appID's to test against, otherwise read from all filters
*/
//console.log("getMergedEdges: "+JSON.stringify(params));
var retval = {newedge:Math.round(timeStamp()/1000), oldedge:0};
if (params.apps||null){
for (var c=0,l=params.apps.length;c<l;c++){
var filter = this.filters["app_"+params.apps[c]];
if (filter||null){
//get the youngest older edge and oldest newer edge so we don't lose posts because one feed is more active.
//this forces them to run at the same edges after the first pull
retval.newedge = Math.min(retval.newedge, filter.newedge);
retval.oldedge = Math.max(retval.oldedge, filter.oldedge);
} else {
log("getMergedEdges: no filter matching app_"+params.apps[c]+" on feed " + this.id);
}
}
} else {
for (var name in this.filters){
var filter = this.filters[name];
if (filter||null){
//get the youngest older edge and oldest newer edge so we don't lose posts because one feed is more active.
//this forces them to run at the same edges after the first pull
retval.newedge = Math.min(retval.newedge, filter.newedge);
retval.oldedge = Math.max(retval.oldedge, filter.oldedge);
} else {
log("getMergedEdges: no filter matching "+name+" on feed " + this.id);
}
}
}
return retval;
};
//remove this
this.remove=function(noConfirm){try{
if (this.isRemoveable) {
var ask=WM.opts.feedsConfirmDeleteFeed;
if (noConfirm || (this.isGlobal && confirm("This feed is shared with other profiles. Deleting it here will prevent it from loading for other users. Are you sure you wish to delete this feed and its filters.")) || !ask || (!this.isGlobal && ask && confirm("Delete feed and all of its filters?"))){
//remove my data
if (this.node) remove(this.node);
WM.feedManager.feeds.removeByValue(this);
WM.feedManager.save();
}
}
}catch(e){log("WM.Feed.remove: "+e);}};
//fetch posts for this
this.fetchNewer=function(){try{
WM.fetch({
newer:true,
feeds:[self],
bypassPause:true,
bypassAppDisabled:true,
bypassFeedDisabled:true,
});
}catch(e){log("WM.Feed.fetchNewer: "+e);}};
this.fetchOlder=function(){try{
WM.fetch({
older:true,
feeds:[self],
bypassPause:true,
bypassAppDisabled:true,
bypassFeedDisabled:true,
});
}catch(e){log("WM.Feed.fetchOlder: "+e);}};
this.toggleContent=function(){try{
this.expanded=!this.expanded;
var btnSize=WM.opts.littleButtonSize;
with (this.contentNode)
className=className.swapWordB(this.expanded,"expanded","collapsed");
with (this.toggleImgNode)
className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
WM.feedManager.save();
}catch(e){log("WM.Feed.toggleContent: "+e);}};
if (this.id && !this.url) this.url="https://graph.facebook.com/"+this.id+"/feed";
//draw it
try{
WM.console.feedManagerNode.appendChild(
this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
createElement("div",{className:"line"},[
createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
]),
this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
self.enabled=this.checked;
with (self.node) className=className.toggleWordB(!this.checked,"disabled");
WM.feedManager.save();
}}),
this.titleNode=createElement("input",{value:(this.title||""), onchange:function(){self.title=this.value; WM.feedManager.save();}}),
//toolbox
createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer"},[
this.fetchNewerButton=createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();} })
]),
createElement("div",{className:"littleButton", title:"Fetch Older"},[
this.fetchOlderButton=createElement("img",{className:"resourceIcon rssDownLeft"+WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();} })
]),
(this.isRemoveable)?createElement("div",{className:"littleButton oddOrange", title:"Remove Feed"},[
this.removeButtonNode=createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();} })
]):null,
(this.isRemoveable)?createElement("div",{className:"indent littleButton "+((this.isGlobal)?"oddOrange":"oddGreen"), title:((this.isGlobal)?"Disable Profile Sharing":"Share With Other Profiles")},[
this.toggleGlobalButton=createElement("img",{className:"resourceIcon "+((this.isGlobal)?"removeGlobal":"addGlobal")+WM.opts.littleButtonSize,onclick:function(){self.isGlobal=!self.isGlobal; WM.feedManager.save();}})
]):null,
]),
this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
(this.isRemoveable)?createElement("div",{className:"line"},[
createElement("label",{textContent:"Target FB Entity: ",title:"The request address from where WM gets posts for this fb entity."}),
this.idNode=createElement("input",{value:(this.id||""), onchange:function(){
self.id=this.value;
self.url="https://graph.facebook.com/"+this.value+"/feed";
self.urlNode.textContent=self.url;
WM.feedManager.save();
}}),
createElement("label",{textContent:"URL: ",title:"The request address from where WM gets posts for this fb entity."}),
this.urlNode=createElement("span",{textContent:this.url}),
]):null,
//app filters sub box
createElement("div",{className:"line"},[
createElement("label",{textContent:"App Filters: ",title:"This is a list of filters run on this feed by apps you collect for. Only filters for sidekick-supported apps are used."}),
this.filtersNode=createElement("div",{className:"subsection"}),
]),
]),
])
);
}catch(e){log("WM.Feed.init:addManagerElement: "+e);};
//add any passed filters
for (var f in newFilters){
this.addFilter(newFilters[f]);
}
return self;
}catch(e){log("WM.Feed.init: "+e);}};
//***************************************************************************************************************************************
//***** Friend Objects
//***************************************************************************************************************************************
WM.friendTracker = {
friends: {},
init : function(){
//import friends tracker data
var friendsIn=getOptJSON('friends_'+WM.currentUser.profile)||[];
if (isArrayAndNotEmpty(friendsIn)) for (var f=0,len=friendsIn.length;f<len;f++) {
WM.friendTracker.newFriend(friendsIn[f],true);
}
WM.friendTracker.sort();
},
clean : function(){
//clean friend tracker data
var len=0;
if (WM.opts.useFriendTracker && (len=WM.friendTracker.friends.length)) {
var ageDays=WM.opts.trackDays*day;
var timeNow=timeStamp();
for (var f=0; f<len; f++){
var friend=WM.friendTracker.friends[f];
if (friend.data && friend.data.posts){
for (var p in friend.data.posts){
var post=friend.data.posts[p];
if ((timeNow-(post.date*1000)) > ageDays) {
delete friend.data.posts[p];
}
}
}
}
}
},
clearAll : function(noConfirm){
var ask=WM.opts.trackConfirmClearUser;
if (noConfirm || !ask || (ask && confirm("Clear tracker history for all users?"))){
for (var f in WM.friendTracker.friends){
WM.friendTracker.friends[f].remove(true);
}
}
},
newFriend : function(params,preventSort){
params=params||{};
var friend = new WM.Friend(params);
WM.friendTracker.friends[friend.id]=friend;
if (!preventSort) WM.friendTracker.sort();
return friend;
},
save :function(){
var ret=[];
for (var f in WM.friendTracker.friends){
ret.push(WM.friendTracker.friends[f].saveableData);
}
setOptJSON("friends_"+WM.currentUser.profile,ret);
},
sort : function(params){
params=params||{};
if (exists(params.sortBy)) WM.quickOpts.sortFriendsBy=params.sortBy;
if (exists(params.sortOrder)) WM.quickOpts.sortFriendsOrder=params.sortOrder;
WM.saveQuickOpts();
var sortBy=params.sortBy||WM.quickOpts.sortFriendsBy||"name"
var sortOrder=params.sortOrd||WM.quickOpts.sortFriendsOrder||"asc"
var friendArray=[];
for (var f in WM.friendTracker.friends) {
friend=WM.friendTracker.friends[f];
friendArray.push({id:friend[sortBy],node:friend.node});
}
if (["asc","ascending"].inArray(sortOrder)) friendArray.sort(function(a,b){return a.id>b.id;});
else if (["desc","descending"].inArray(sortOrder)) friendArray.sort(function(a,b){return a.id<b.id;});
for (var f=0,len=friendArray.length; f<len; f++) {
WM.console.friendBuild.appendChild(friendArray[f].node);
}
},
track : function(post){
//dont track stuff older than our older tracking limit
var limit=WM.opts.trackTime*day;
if ( ( timeStamp()-(post.date*1000) ) < limit ) {
//get/create the friend record
var friend=WM.friendTracker.friends[post.fromID]||null;
if (!friend) {
friend=WM.friendTracker.newFriend({id:post.fromID,name:post.fromNameLastFirst});
}
//check if this is newer than last known post
if (WM.opts.trackLastKnownPost) {
var data=friend.lastKnownPost;
if (data) {
if (data.date<post.date){
data.date=post.date;
//data.id=post.id.removePrefix(post.fromID+"_");
}
} else {
friend.data.lastKnownPost={date:post.date};
}
}
//add it to history
if (WM.opts.trackCreated){
var data={date:post.date};
if (WM.opts.trackFailed){
data.failed=(post.status<0 && post.status !=-4 && post.status !=-6);
}
if (WM.opts.trackAccepted){
data.accepted=(post.status>0 || post.status ==-4 || post.status ==-6);
}
friend.data.posts[post.id.removePrefix(post.fromID+"_")]=data;
}
//save it
friend.updateStats();
WM.friendTracker.save();
//push events
WM.rulesManager.doEvent("onFriendDataChanged",friend);
}
},
trackStatus : function(post,acceptOrFail){
var friend=WM.friendTracker.friends[post.fromID]||null;
if (friend) {
var data=friend.data.posts[post.id.removePrefix(post.fromID+"_")]||null;
if (data){
if (acceptOrFail) {
data.accepted=true;
delete data.failed;
} else {
data.failed=true;
delete data.accepted;
}
friend.updateStats();
WM.rulesManager.doEvent("onFriendDataChanged",friend);
} else {
debug.print("post does not exist under friend");
//if post does not exists, we had more errors elsewhere
//or post id not fit our history range
}
} else {
debug.print("friend does not exist for this post");
//if friend does not exist, we had errors elsewhere
//don't bother fixing it here
}
},
};
//***************************************************************************************************************************************
//***** Friend Class
//***************************************************************************************************************************************
WM.Friend = function(params){try{
this.objType="friend";
params=params||{};
var self=this;
//set defaults
this.expanded=false;
this.id="";
this.name="";
this.data={
lastKnownPost:{date:0},
posts:{},
};
this.__defineGetter__("saveableData",function(){try{
var ret={};
ret.id=this.id;
ret.name=this.name;
ret.enabled=this.enabled;
ret.expanded=this.expanded;
//capture posts data
ret.data=this.data;
return ret;
}catch(e){log("WM.Friend.saveableData: "+e);}});
for (var p in params) this[p]=params[p];
//remove this
this.remove=function(noConfirm){try{
var ask=WM.opts.trackConfirmClearUser;
if (noConfirm || !ask || (ask && confirm("Clear history for this user?"))){
//remove my data
if (this.node) remove(this.node);
delete WM.friendTracker.friends[this.id];
WM.friendTracker.save();
}
}catch(e){log("WM.Friend.remove: "+e);}};
this.toggleContent=function(){try{
this.expanded=!this.expanded;
var btnSize=WM.opts.littleButtonSize;
with (this.contentNode)
className=className.swapWordB(this.expanded,"expanded","collapsed");
with (this.toggleImgNode)
className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
WM.friendTracker.save();
}catch(e){log("WM.Friend.toggleContent: "+e);}};
this.addToFeeds=function(){try{
WM.feedManager.newFeed({id:this.id, title:this.name});
WM.feedManager.save();
}catch(e){log("WM.Friend.addToFeeds: "+e);}};
this.countAccepted=function(){try{
var c=0;
if (this.data.posts) for (var p in this.data.posts) {
var post=this.data.posts[p];
if (post.accepted) c++;
}
return c;
}catch(e){log("WM.Friend.countAccepted: "+e);}};
this.countFailed=function(){try{
var c=0;
if (this.data.posts) for (var p in this.data.posts) {
var post=this.data.posts[p];
if (post.failed) c++;
}
return c;
}catch(e){log("WM.Friend.countFailed: "+e);}};
this.countCreated=function(){try{
var c=0;
if (this.data.posts) for (var p in this.data.posts) {
c++
}
return c;
}catch(e){log("WM.Friend.countFailed: "+e);}};
this.__defineGetter__("lastKnownPost",function(){try{
if (this.data && (this.data.lastKnownPost||null)){
return this.data.lastKnownPost;
}
return {id:null,date:0};
}catch(e){log("WM.Friend.lastKnownPost: "+e);}});
this.__defineGetter__("lastKnownPostDate",function(){try{
if (this.data && (this.data.lastKnownPost||null)){
return this.data.lastKnownPost.date;
}
return 0;
}catch(e){log("WM.Friend.lastKnownPostDate: "+e);}});
this.__defineGetter__("acceptCount",function(){try{
return this.countAccepted();
}catch(e){log("WM.Friend.acceptCount: "+e);}});
this.__defineGetter__("failCount",function(){try{
return this.countFailed();
}catch(e){log("WM.Friend.failCount: "+e);}});
this.__defineGetter__("postCount",function(){try{
return this.countCreated();
}catch(e){log("WM.Friend.postCount: "+e);}});
this.__defineGetter__("totalCount",function(){try{
return this.failCount+this.acceptCount;
}catch(e){log("WM.Friend.totalCount: "+e);}});
this.updateStats=function(){try{
var n=this.statsNode;
if (n) {
if (WM.opts.trackLastKnownPost){
d=new Date(((this.lastKnownPost.date*1000)||0)).toLocaleString();
if (!this.lastPostNode) {
n.appendChild(createElement("div",{className:"line"},[
createElement("label",{textContent:"Last Known Post Date: "}),
this.lastPostNode=createElement("span",{textContent:d})
]));
} else {
this.lastPostNode.textContent=d;
}
}
if (WM.opts.trackCreated){
if (!this.countCreatedNode) {
n.appendChild(createElement("div",{className:"line"},[
createElement("label",{textContent:"Posts Created: "}),
this.countCreatedNode=createElement("span",{textContent:this.countCreated()})
]));
} else {
this.countCreatedNode.textContent=this.countCreated();
}
}
if (WM.opts.trackAccepted){
if (!this.countAcceptedNode){
n.appendChild(createElement("div",{className:"line"},[
createElement("label",{textContent:"Posts Accepted: "}),
this.countAcceptedNode=createElement("span",{textContent:this.countAccepted()})
]));
} else {
this.countAcceptedNode.textContent=this.countAccepted();
}
}
if (WM.opts.trackFailed){
if (!this.countFailedNode){
n.appendChild(createElement("div",{className:"line"},[
createElement("label",{textContent:"Posts Failed: "}),
this.countFailedNode=createElement("span",{textContent:this.countFailed()})
]));
} else {
this.countFailedNode.textContent=this.countFailed();
}
}
}
}catch(e){log("WM.Friend.updateStats: "+e);}};
//draw it
try{
WM.console.friendBuild.appendChild(
this.node=createElement("div",{className:"listItem"},[
createElement("div",{className:"line"},[
createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
]),
this.titleNode=createElement("input",{value:(this.name||""), onchange:function(){self.name=this.value; WM.friendTracker.save();}}),
//toolbox
createElement("div",{className:"littleButton", title:"Add To Feeds"},[
createElement("img",{className:"resourceIcon addFeed"+WM.opts.littleButtonSize,onclick:function(){self.addToFeeds();} })
]),
createElement("div",{className:"littleButton oddOrange", title:"Clear Data"},[
createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();} })
]),
createElement("div",{onclick:function(){window.open("http://www.facebook.com/profile.php?id="+self.id,"_blank");},title:"Visit Wall",className:"littleButton oddBlue"},[
createElement("img",{className:"resourceIcon openInNewWindow"+WM.opts.littleButtonSize})
]),
]),
this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
createElement("div",{className:"line"},[
createElement("label",{textContent:"ID: ",title:"The facebook id of this user."}),
createElement("span",{textContent:self.id}),
]),
//post data sub box
createElement("div",{className:"line"},[
createElement("label",{textContent:"Statistics: ",title:"Statistics you selected to track."}),
this.statsNode=createElement("div",{className:"subsection"}),
]),
]),
])
);
}catch(e){log("WM.Friend.init:addManagerElement: "+e);};
this.updateStats();
return self;
}catch(e){log("WM.Friend.init: "+e);}};
//***************************************************************************************************************************************
//***** Rules Manager Object
//***************************************************************************************************************************************
WM.rulesManager = {
rules:[],
enabled:function(){return !WM.quickOpts.heartbeatDisabled;},
init:function(params){try{
params=(params||{});
// build a kidsNode getter
WM.rulesManager.__defineGetter__("kidsNode",function(){try{
return $("wmPriorityBuilder");
}catch(e){log("WM.rulesManager.kidsNode: "+e);}});
//import rules
WM.rulesManager.rules=[];
var rulesIn=getOptJSON("priority3_"+WM.currentUser.profile)||{};
var globalsIn=getOptJSON("priority3_global")||{};
//detect early beta rule lists
if (isObject(rulesIn)) for (var i in rulesIn){
var rule=rulesIn[i];
WM.rulesManager.rules.push( new WM.rulesManager.Rule(rule) );
//don't bother with globals here
//or use current version rule arrays
} else if (isArrayAndNotEmpty(rulesIn)) for (var i=0,rule;(rule=rulesIn[i]);i++) {
if (rule.isGlobal) {
var glob=globalsIn[rule.uniqueID]||null;
if (glob){
var merge=mergeJSON(glob,rule);
WM.rulesManager.rules.push( new WM.rulesManager.Rule(merge) );
glob.alreadyUsed=true;
} else {
log("WM.rulesManager.init: Global rule missing, cannot merge");
}
} else {
WM.rulesManager.rules.push( new WM.rulesManager.Rule(rule) );
}
}
//import all globals not already accounted for
for (var t in globalsIn) {
var glob=globalsIn[t];
//avoid already imported globals
if (!glob.alreadyUsed){
glob.uniqueID=t;
glob.isGlobal=true;
WM.rulesManager.rule.push( new WM.rulesManager.Rule(glob) );
}
}
}catch(e){log("WM.rulesManager.init: "+e);}},
//check to see if any rules match the post object
doEvent:function(event,obj){
//do nothing if disabled
if (!WM.rulesManager.enabled) return;
//log("WM.rulesManager.doEvent: event="+event+", post="+post.id);
for (var r=0,rule;(rule=WM.rulesManager.rules[r]);r++){
if (rule.enabled) (function(){rule.doEvent(event,obj);})();
}
},
//convert a test (such as dynamic grab entry) to a rule
ruleFromTest:function(test){
//[{"id":"_h6qil21n","label":"new test","search":["body"],"find":["nothing"],"ret":"dynamic","kids":[{"id":"_h6qiw4zf","find":[]}],"appID":"102452128776","disabled":true}]
//[{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[{"search":["body"],"operand":"lessThan","find":"chipmunk"}],"actions":[{"event":"onIdentify","action":"setColor","params":"orange"}],"kids":[{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]},{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]}],"eggs":[{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]},{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]}]}]
var ret={
title:(test.label||test.title)||"Converted Dynamic Test",
enabled:!(test.disabled||false),
limit:0,
limitCount:0,
expanded:true,
validators:function(){
var ret=[];
//add the initial validator
ret.push({
search:["appID"],
operand:"equals",
find:test.appID
});
//detect search/find method
var method="basic";
if (isArrayAndNotEmpty(test.subTests) && test.find.contains("{%1}")) method="subTests";
if (exists(test.subNumRange) && test.find.contains("{%1}")) method="subNumRange";
if (test.regex==true) method="regexp";
if (method=="regexp") {
//leave the expression just as it is
ret.push({
search:test.search||[],
operand:"matchRegExp",
find:test.find,
});
} else if (method=="basic") {
//convert the test.find array into a regular espression
ret.push({
search:test.search||[],
operand:"matchRegExp",
find:arrayToRegExp(test.find),
});
} else if (method=="subTests") {
//insert the subTests into the find insertion point as a regular expression
//but make the rest of the find parameter not return if found
var find=test.find;
if (find.contains("{%1}")){
find=find.split("{%1}");
find=(find[0].length?("(?:"+find[0]+")"):"")+arrayToRegExp(test.subTests)+(find[1].length?("(?:"+find[1]+")"):"");
}
ret.push({
search:test.search||[],
operand:"matchRegExp",
find:find
});
} else if (method=="subNumRange") {
//insert the subNumRange into the find insertion point as a regular expression
//but make the rest of the find parameter not return if found
var numRange=("string"==typeof test.subNumRange)?test.subNumRange.split(","):[test.subNumRange.low,test.subNumRange.high];
var find=test.find;
if (find.contains("{%1}")){
find=find.split("{%1}");
find=(find[0].length?("(?:"+find[0]+")"):"")+integerRangeToRegExp({min:numRange[0],max:numRange[1]})+(find[1].length?("(?:"+find[1]+")"):"");
}
ret.push({
search:test.search||[],
operand:"matchRegExp",
find:find
});
}
return ret;
}(),
actions:[
{
event:"onIdentify",
action:"setWhich",
params:test.ret||"dynamic",
}
],
kids:[],
eggs:[],
};
//convert children
if (isArrayAndNotEmpty(test.kids)) {
for (var k=0,kid;(kid=test.kids[k]);k++) {
ret.kids.push(WM.rulesManager.ruleFromTest(kid));
}
}
return ret;
},
//create a rule based on a specific post
ruleFromPost:function(post){
//create some data to get us started
var rule={
basedOn:post,
title:"Based On: "+post.id,
enabled:false, //start out not using this rule
validators:[
{search:["appID"],find:post.appID,operand:"equals"},
{search:["title"],find:post.name,operand:"matchRegExp"},
{search:["caption"],find:post.caption,operand:"matchRegExp"},
{search:["desc"],find:post.description,operand:"matchRegExp"},
{search:["link"],find:post.linkText,operand:"matchRegExp"},
],
actions:[
{event:"onIdentify",action:"setWhich",params:"dynamic"}
]
};
WM.rulesManager.rules.push(rule=new WM.rulesManager.Rule(rule));
if (WM.opts.rulesJumpToNewRule){
//jump to rule view
WM.console.tabContainer.selectTab(3);
//scroll to new rule
rule.node.scrollIntoView();
}
},
//copy all dynamics to new rules
//does not destroy dynamics as they are converted
convertDynamics:function(){
var tests=WM.grabber.tests;
if (isArrayAndNotEmpty(tests)) {
for (var t=0,test;(test=tests[t]);t++){
WM.rulesManager.rules.push( new WM.rulesManager.Rule( WM.rulesManager.ruleFromTest(test) ) );
}
}
},
//rest rule limits for all rules and their children
resetAllLimits:function(params){
params=params||{};
var ask=WM.opts.rulesConfirmResetLimit;
if (params.noConfirm || !ask || (ask && confirm("Reset Limit Counter?"))) {
if (isArrayAndNotEmpty(WM.rulesManager.rules)) for (var r=0,rule;(rule=WM.rulesManager.rules[r]);r++) {
rule.resetLimit({preventSave:true,resetChildren:true,noConfirm:true});
}
WM.rulesManager.saveRules();
}
},
saveRules : function(){try{
//pack rule objects
var retRules=[];
var retGlobal={};
if (isArrayAndNotEmpty(WM.rulesManager.rules)) {
for (var r=0,rule; (rule=WM.rulesManager.rules[r]);r++){
if (!rule.isGlobal) {
retRules.push(rule.saveableData);
} else {
//make a placeholder locally
retRules.push({isGlobal:true, uniqueID:rule.uniqueID, enabled:rule.enabled, expanded:rule.expanded});
//and save it globally
var glob=rule.saveableData;
glob.uniqueID=rule.uniqueID;
retGlobal[rule.uniqueID]=glob;
}
}
}
//save rules
setOptJSON("priority3_"+WM.currentUser.profile,retRules);
setOptJSON("priority3_global",retGlobal);
}catch(e){log("WM.rulesManager.saveRules: "+e);}},
showData : function(){try{
promptText(getOpt("priority3_"+WM.currentUser.profile),true);
}catch(e){log("WM.rulesManager.showData: "+e);}},
newRule : function(p){try{
var rule=new WM.rulesManager.Rule(p);
WM.rulesManager.rules.push(rule);
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.newRule: "+e);}},
importRule: function(){try{
var params=prompt("Input rule data",null);
if (params) {
var convertedInput=JSON.parse(params);
if (isArray(convertedInput)){
for (var i=0;i<convertedInput.length;i++){
WM.rulesManager.newRule(convertedInput[i]);
}
} else {
WM.rulesManager.newRule(convertedInput);
}
}
}catch(e){log("WM.rulesManager.importRule: "+e);}},
toggleHeartbeat: function(){try{
WM.quickOpts.heartbeatDisabled=!WM.quickOpts.heartbeatDisabled;
with (WM.rulesManager.toggleHBNode) {
className=className.swapWordB(WM.quickOpts.heartbeatDisabled,"oddOrange","oddGreen");
}
WM.saveQuickOpts();
log(WM.quickOpts.heartbeatDisabled);
}catch(e){log("WM.rulesManager.toggleHeartbeat: "+e);}},
};
//***************************************************************************************************************************************
//***** Rules Manager Enums & Functions
//***************************************************************************************************************************************
WM.rulesManager.ruleActions = {
"addToFeeds":{name:"Add Poster To Feeds",toolTip:"Add the post's creator to your feeds manager. Can also be used with onFriend* events."},
"appendLink":{name:"Append To Link",toolTip:"Add specific code to the end of the collection link.",hasParam:true,paramType:"textBox","default":""},
"birth":{name:"Birth Eggs",toolTip:"Clone the egg children to this rule's level, without destroying this rule."},
"cancelInterval":{name:"Cancel Interval",toolTip:"Destroy the repeating timer on this rule."},
"cancelTimer":{name:"Cancel Timer",toolTip:"Destroy the timer on this rule."} ,
"cleanPost":{name:"Clean Post",toolTip:"Remove the calling post from the collector."},
"commentPost":{name:"Comment Post",toolTip:"Create a comment on the calling post.",hasParam:true,paramLabel:"comment",paramType:"string","default":"Thanks!"},
"createInterval":{name:"Create Interval",toolTip:"Create a repeating timer on this rule, where 1000 equals 1 second.",hasParam:true,paramType:"timePicker","default":1000} ,
"createTimer":{name:"Create Timer",toolTip:"Create a timer on this rule, where 1000 equals 1 second.",hasParam:true,paramType:"timePicker","default":1000},
"decrementCounter":{name:"Decrement Limit Counter",toolTip:"Decrement the rule limit counter.",hasParam:true,paramType:"number","default":1},
"decrementParentCounter":{name:"Decrement Parent Limit Counter",toolTip:"Decrement the parent rule limit counter.",hasParam:true,paramType:"number","default":1},
"destroyRule":{name:"Destroy Rule",toolTip:"Permanently removes this rule and all of its children."},
"disableApp":{name:"Disable App",toolTip:"Disable the specified app. Leave blank to disable the app associated with the activating post.",hasParam:true,paramType:"textBox","default":""},
"disableAppOption":{name:"Disable App Option",toolTip:"Disable an option in the related sidekick by internal name.",hasParam:true,paramType:"textBox","default":""},
"disableAutocomment":{name:"Disable Autocomment",toolTip:"Disable the autocomment feature."},
"disableAutolike":{name:"Disable Autolike",toolTip:"Disable the autolike feature."},
"disableChildRules":{name:"Disable Child Rules",toolTip:"Disable the immediate children of this rule. Does not disable this rule."},
"disableHostOption":{name:"Disable Host Option",toolTip:"Disable an option in the wm host by internal name.",hasParam:true},
"disableRule":{name:"Disable Rule",toolTip:"Disable the current rule."},
"emergencyOpen":{name:"Emergency Open",toolTip:"Move the calling post directly to a new processing window, no matter what your opened window limit is."},
"emptyAutolikeQueue":{name:"emptyAutolikeQueue",toolTip:"Destroys the list of posts you intended to autolike or autocomment."},
"enableApp":{name:"Enable App",toolTip:"Enable the specified app. Leave blank to enable the app associated with the activating post.",hasParam:true,paramType:"textBox","default":""},
"enableAppOption":{name:"Enable App Option",toolTip:"Enable an option in the related sidekick by internal name.",hasParam:true,paramType:"textBox","default":""},
"enableAutocomment":{name:"Enable Autocomment",toolTip:"Enable the autocomment feature."},
"enableAutolike":{name:"Enable Autolike",toolTip:"Enable the autolike feature."},
"enableChildRules":{name:"Enable Child Rules",toolTip:"Enable the immediate children of this rule."},
"enableHostOption":{name:"Enable Host Option",toolTip:"Enable an option in the wm host by internal name.",hasParam:true},
"enableRule":{name:"Enable Rule",toolTip:"Enable the current rule."},
"fetchNewer":{name:"Fetch Newer Posts",toolTip:"Fetch some more posts for this app, feed or feed filter."},
"fetchOlder":{name:"Fetch Older Posts",toolTip:"Fetch some more posts for this app, feed or feed filter."},
"fetchHours":{name:"Fetch Hours of Posts",toolTip:"Fetch some more posts for this app, feed or feed filter.",hasParam:true,paramType:"number","default":24},
"forceOpen":{name:"Force Open",toolTip:"Move the calling post directly to the collector queue."},
"forceOpenFirst":{name:"Force Open First",toolTip:"Move the calling post directly to the collector queue AND cut in line to be next processed."},
"hatch":{name:"Hatch Eggs",toolTip:"Hatch the egg-children of the current rule, and destroy this rule."},
"incrementCounter":{name:"Increment Limit Counter",toolTip:"Increment the rule limit counter.",hasParam:true,paramType:"number","default":1},
"incrementParentCounter":{name:"Increment Parent Limit Counter",toolTip:"Increment the parent rule limit counter.",hasParam:true,paramType:"number","default":1},
"likePost":{name:"Like Post",toolTip:"Like the calling post."},
"openPostSource":{name:"Open Post Source",toolTip:"Opens the post source in a separate window/tab."},
"processLast":{name:"Move To Bottom",toolTip:"Move the post to the bottom of the collector window."},
"processFirst":{name:"Move To Top",toolTip:"Move the post to the top of the collector window."},
"pauseAllApps":{name:"Pause All Apps",toolTip:"Pause all apps currently associated with docked sidekicks."},
"pauseApp":{name:"Pause App",toolTip:"Pauses processing anything by this app.",hasParam:true,paramType:"textBox","default":""},
"pauseWM.collector":{name:"Pause WM.collector",toolTip:"Pauses collection of all posts."},
"pauseFetch":{name:"Pause Fetching",toolTip:"Pauses fetching of all posts."},
"pauseType":{name:"Pause Type",toolTip:"Pause collection of all bonuses of this type."},
"pinPost":{name:"Pin Post",toolTip:"Pins the calling post."}, //pin the post
"queueCommentPost":{name:"Queue Comment Post",toolTip:"Comment on the calling post by first using the autolike queue system to delay the autocomment.",hasParam:true,paramLabel:"comment",paramType:"string","default":"Thanks!"},
"queueLikePost":{name:"Queue Like Post",toolTip:"Like the calling post by first using the autolike queue system to delay the autolike."},
"refreshBrowser":{name:"Refresh Browser",toolTip:"Reloads the browser window."},
"reIDAll":{name:"ReID All",toolTip:"Re-ID all posts in the collector."},
"removePriority":{name:"Remove Priority",toolTip:"Sets the priority of the calling post to normal."},
"removePriorityApp":{name:"Remove Priority (App)",toolTip:"Sets the priority of all posts of the calling or specified app to normal.",hasParam:true,paramType:"textBox","default":""},
"removePriorityType":{name:"Remove Priority (Type)",toolTip:"Sets the priority of all posts of the calling app with specified or associated type to normal.",hasParam:true,paramType:"textBox","default":"dynamic"},
"resetAllLimits":{name:"Reset All Limit Counters",toolTip:"Reset all limits in the rules manager."},
"resetLimit":{name:"Reset Limit Counter",toolTip:"Reset the limit counter of the current rule."},
"resetBranchLimits":{name:"Reset Branch Limit Counters",toolTip:"Reset the limit counter of ALL rules that are lower in this branch (children, grandchildren, etc.). Does not reset the limit on this rule."},
"resetChildrenLimits":{name:"Reset Children Limit Counters",toolTip:"Reset the limit counter of immediate child rules of this rule. Does not reset the limit on this rule."},
"resetParentLimit":{name:"Reset Parent Limit Counter",toolTip:"Reset the limit counter of the parent rule."},
"setAppOption":{name:"Set App Option",toolTip:"Set an option in the related sidekick by internal name.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox","default":"",paramLabel:"Name"},{paramType:"textBox","default":"",paramLabel:"Value"}]},
"setAppTab":{name:"Set App Tab",toolTip:"Set the current collection tab by app ID.",hasParam:true,paramType:"textBox","default":"all"},
"setAsAccepted":{name:"Set As Accepted",toolTip:"Set the calling post as accepted.",hasParam:true,paramType:"checkBox",paramLabel:"saveToHistory","default":false},
"setAsExcluded":{name:"Set As Excluded",toolTip:"Set the calling post as excluded."},
"setAsFailed":{name:"Set As Failed",toolTip:"Set the calling post as failed.",hasParam:true,paramType:"checkBox",paramLabel:"saveToHistory","default":false},
"setColor":{name:"Set Post Color",toolTip:"Set the background color of the calling post.",hasParam:true,paramType:"colorPicker","default":"blue"},
"setHostOption":{name:"Set Host Option",toolTip:"Set the value a host option by internal name.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox","default":"",paramLabel:"Name"},{paramType:"textBox","default":"",paramLabel:"Value"}]},
"setPriority":{name:"Set Priority",toolTip:"Set the priority of the calling post.",hasParam:true,paramType:"number","default":50},
"setPriorityApp":{name:"Set Priority (App)",toolTip:"Set the priority of the calling app or specified app.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox",paramLabel:"App","default":""},{paramType:"number",paramLabel:"Priority","default":50}]},
"setPriorityType":{name:"Set Priority (Type)",toolTip:"Set the priority of the calling post type or specified type for the same app.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox",paramLabel:"Type Code","default":""},{paramType:"number",paramLabel:"Priority","default":50}]},
"setToCollect":{name:"Set To Collect",toolTip:"Set the calling post to be collected in normal order. Use Force Open to do more immediate collection, or Emergency Open to override your opened window limit."},
"setToCollectPriority1":{name:"Set To Collect Top Priority",toolTip:"Set the calling post to be collected and also set its priority to 1. Use Force Open to do more immediate collection, or Emergency Open to override your opened window limit."},
"setWhich":{name:"Set Type",toolTip:"Set the bonus type id of the calling post.",hasParam:true,paramType:"textBox","default":"dynamic"},
"uncheckType":{name:"Uncheck Post Type",toolTip:"Unchecks option to collect this bonus in the options menu."},
"unpauseAllApps":{name:"Unpause All Apps",toolTip:"Unpause all apps currently associated with docked sidekicks."},
"unpauseAllTypesAllApps":{name:"Unpause All Types",toolTip:"Unpause all bonus types by all apps."},
"unpauseAllTypesByApp":{name:"Unpause All Types By App",toolTip:"Unpause all bonus types associated with the given app, or the app associated with the activating post.",hasParam:true,paramType:"textBox","default":""},
"unpauseApp":{name:"Unpause App",toolTip:"Starts processing anything by this app.",hasParam:true,paramType:"textBox","default":""},
"unpauseWM.collector":{name:"Unpause WM.collector",toolTip:"Starts collection of posts."},
"unpauseFetch":{name:"Unpause Fetching",toolTip:"Starts fetching of posts."},
"unpauseType":{name:"Unpause Type",toolTip:"Unpause collection of all bonuses of this type."},
};
WM.rulesManager.ruleActionsCodes = {
"addToFeeds":1,"appendLink":2,"birth":3,"cancelInterval":4,"cancelTimer":5,"cleanPost":6,"commentPost":7,"createInterval":8,"createTimer":9,
"decrementCounter":10,"decrementParentCounter":11,"destroyRule":12,"disableApp":13,"disableAppOption":14,"disableAutolike":15,"disableChildRules":16,
"disableHostOption":17,"disableRule":18,"emergencyOpen":19,"emptyAutolikeQueue":20,"enableApp":21,"enableAppOption":22,"enableAutolike":23,
"enableChildRules":24,"enableHostOption":25,"enableRule":26,"fetchNewer":27,"fetchOlder":28,"forceOpen":29,"forceOpenFirst":30,"hatch":31,
"incrementCounter":32,"incrementParentCounter":33,"likePost":34,"openPostSource":35,"processLast":36,"processFirst":37,"pauseAllApps":38,
"pauseApp":39,"pauseWM.collector":40,"pauseFetch":41,"pauseType":42,"pinPost":43,"queueCommentPost":44,"queueLikePost":45,"refreshBrowser":46,
"reIDAll":47,"removePriority":48,"removePriorityApp":49,"removePriorityType":50,"resetAllLimits":51,"resetLimit":52,"resetBranchLimits":53,
"resetChildrenLimits":54,"resetParentLimit":55,"setAppOption":56,"setAppTab":57,"setAsAccepted":58,"setAsExcluded":59,"setAsFailed":60,"setColor":61,
"setHostOption":62,"setPriority":63,"setPriorityApp":64,"setPriorityType":65,"setToCollect":66,"setToCollectPriority1":67,"setWhich":68,
"uncheckType":69,"unpauseAllApps":70,"unpauseAllTypesAllApps":71,"unpauseAllTypesByApp":72,"unpauseApp":73,"unpauseWM.collector":74,"unpauseFetch":75,
"unpauseType":76,"fetchHours":77,"enableAutocomment":78,"disableAutocomment":79
};
WM.rulesManager.ruleActionByCode = function(code){
for (c in WM.rulesManager.ruleActionsCodes) {
if (WM.rulesManager.ruleActionsCodes[c]==code) return c;
}
return null;
};
WM.rulesManager.ruleEvents = {
//post events
"onIdentify":"Called after a post is (re)identified. Posts are first identified as soon as they are fetched.",
"onBeforeCollect":"Called before collection opens a sidekick window.",
"onAfterCollect":"Called after collection is tried. Activates regardless of return status.",
"onFailed":"Called when a post is marked failed. This could be actual or simulated by the user.",
"onAccepted":"Called when a post is marked accepted. This could be actual or simulated by the user.",
"onTimeout":"Called when a post is marked as timed out. This could be actual or simulated by the user.",
"onValidate":"Called when a post is first fetched, but after its first identification. Not called on posts which fail identification.",
//rule events
"onLimit":"Called when this rule limit counter equals the rule's limit.",
"onHatch":"Called when this rule's egg children are hatched.",
"onTimer":"Called when the timer on this rule activates.",
"onInterval":"Called when the repeating timer on this rule activates.",
"onBirth":"Called when this rule's egg children are birthed.",
"onRuleCreated":"Called when the rule is created (or loaded on startup).",
"onRuleButtonClicked":"Called when the rule button is clicked. Available only for control rules.",
//app events
"onSidekickDock":"Called when the sidekick for this app docks.",
"onSidekickReady":"Called when the sidekick for this app creates an app object, and after it appends the collection tab for that app.",
/*
paused/unpaused
enabled/disabled
failCountChanged
acceptCountChanged
*/
//console events
"onHeartbeat":"Called when the global heartbeat interval ticks.",
"onSetAppFilter":"Called when the collection panel app tab changes, including at startup if 'Show All' is selected as default",
//feed events
"onFeedFilterOlderLimitReached":"Called when a specific feed filter reaches its user-defined older limit.",
};
WM.rulesManager.ruleEventsCodes ={
"onIdentify":1,"onBeforeCollect":2,"onAfterCollect":3,"onFailed":4,"onAccepted":5,"onTimeout":6,"onValidate":7,"onLimit":8,"onHatch":9,"onTimer":10,
"onInterval":11,"onBirth":12,"onRuleCreated":13,"onSidekickDock":14,"onSidekickReady":15,"onHeartbeat":16,"onSetAppFilter":17,
"onFeedFilterOlderLimitReached":18,"onRuleButtonClicked":19
};
WM.rulesManager.ruleEventByCode = function(code){
for (c in WM.rulesManager.ruleEventsCodes) {
if (WM.rulesManager.ruleEventsCodes[c]==code) return c;
}
return null;
};
WM.rulesManager.postParts = {
"age":"The time between the current time and the post creation time (in ms).",
"acceptCount":"An app's accept counter value. Friend objects also have an acceptCount.",
"activatorType":"Returns the object type of the rule-activating object: app, post, rule, feed, feedfilter or unknown.",
"alreadyProcessed":"Reports if a post has already created a history entry.",
"appID":"The appID of the game for which a post belongs. You can read the appID from the following affected objects: app, post, and feedFilter.",
"appName":"The appName of the game for which this post belongs, as reported by the FB database.",
"body":"The body of a post is a compilation of the title, caption, and desc.",
"canvas":"The canvas of a post is its namespace granted by FB, ie. FarmVille's namespace is 'onthefarm'.",
"caption":"The caption of a post is one line just below its title (or 'name'). Not all posts have this field.",
"commentorID":"The commentorID is a list of IDs of all commentors.",
"commentorName":"The commentorName is a list of names of all commentors.",
"comments":"The comments are list of all comments made to the post, excluding the initial msg.",
"currentTime":"The current time (in ms) on your system, not localized. This global value can be referenced from any activating object type.",
"currentAppTab":"The currently selected collection tab's appID, or the word 'all' if the 'Show All' tab is selected.",
"date":"The date of a post is its creation time on FB, and is the 'created_time' parameter in fb data packets.",
"desc":"The desc of a post is two lines below the title. This is the 'description' parameter in fb data packets. Not all posts have this field.",
"either":"The either of a post is the compilation of the link and body.",
"enabled":"The enabled state of an activating object.",
"expanded":"The expanded state of an activating object.",
"failCount":"An app's fail counter value. Friend objects also have a failCount.",
"friendAcceptedCount":"Gets the accepted count from a FriendTracker friend object matching this post creator.",
"friendFailedCount":"Gets the failed count from a FriendTracker friend object matching this post creator.",
"fromID":"The fromID is the ID of the poster.",
"fromName":"The fromName is the name of the poster.",
"fromNameLastFirst":"The name of the poster, displayed as Lastname, Firstname",
"html":"The html of a post is the compilation of ALL visible FB attributes.",
"id":"Normally a post ID, which is usually the post creator's ID and a timestamp separated by an underscore. Alternately, you can ask for the id of an activating friend, feed or feed filter object.",
"idText":"The identified link text of a post.",
"img":"The img of a post is the url of the icon that displays with the post. This is the 'picture' parameter in fb data packets.",
"isAccepted":"Reports if the post is set as having already been successfully collected.",
"isAppPaused":"Reports if the associated app is paused.",
"isCollect":"Reports if the post is set to be collected.",
"isExcluded":"Reports if the post has been set as excluded.",
"isFailed":"Reports if the post is set as having already failed.",
"isForMe":"Reports if the W2W post targets the current user.",
"isLiked":"Reports if the post has been identified as already being liked by the current user.",
"isMyPost":"Reports if the post belongs to the current user.",
"isPaused":"Reports if the calling object (post or app) is paused. Not specific!",
"isPinned":"Reports if the post is marked as being pinned.",
"isRemovable":"Reports if a feed is removeable. Your own profile wall and your home feed are not removeable, only disableable.",
"isTimeout":"Reports if the post has been marked as a timed out collection attempt.",
"isTypePaused":"Reports if the associated bonus type is paused.",
"isScam":"Reports if a post is suspected of being a scam, usually when the canvas and appName do not match.",
"isStale":"Reports if a post is older than the user-set older limit.",
"isUndefined":"Reports if the post does not match any id given by the sidekick.",
"isWishlist":"Reports if the post is deemed a whichlist request.",
"isWorking":"Reports if the post is currently in the working state (being processed).",
"isW2W":"Reports if the post is a Wall-To-Wall post, meaning that it was posted to a specific user's wall.",
"lastKnownPostDate":"A friend object's last known post date (as unix time, no millisecond data).",
"likeID":"The likeID is a list of IDs of users who liked the post.",
"likeName":"The likeName is a list of names of users who liked this post.",
"limit":"This rule's limit number.",
"limitCount":"This rule's limit counter.",
"link":"The 'link' of a post is the link text, not the url. This is the 'action.name' in fb data packets.",
"linkHref":"The original url as it appeared from facebook. This SHOULD be exactly the same as 'url'.",
"linkText":"The original link text as it appeared from facebook. You may want to NOT use 'link' and instead use this one.",
"msg":"The msg of a post is the part the poster added as a comment during the post's creation.",
"name":"With posts, this is the same as 'title', because its the FB name of a post object. With friend objects, this is the friend's text name.",
"parentLimit":"The parent rule's limit number, or NULL if no parent exists.",
"parentLimitCount":"The parent rule's limit counter, or NULL if no parent exists.",
"postCount":"A friend object's count of posts it is tracking.",
"postedDay":"A partial date-time value containing only the year/month/day portions, which corresponds to the post creation time.",
"postedHour":"A partial date-time value containing only the year/month/day/hour portions, which corresponds to the post creation time.",
"priority":"The priority of a post which could have been set by a rule, or by default of 50.",
"status":"The status of a post is the return code given by a sidekick, or 0 if it has not been processed.",
"targetID":"The targetID is a list of targets' IDs that the poster intended the post to display to.",
"targetName":"The targetName is a list of targets the poster intended the post to display to.",
"title":"The title of a post contains the bold text, usually including the poster's name, at the top of the post. This is the 'name' parameter in facebook data packets.",
"totalCount":"An app's failcount and acceptcount combined. Friend objects also have a totalCount.",
"typesPaused":"An app's list of paused bonus types. Only accessible from an activating post. Please stick to the contains/notContains operators because this is an array, not text.",
"url":"The url of a post is the address to which the post redirects the user when clicked. This is the 'link' or 'action.link' parameter in fb data packets. This is the original url supplied by the app, not a modified url, such as WM's removal of https or a sidekick-modified url. Alternately, you can ask for the URL of a feed object.",
"which":"The 'which' of a post is its identified codename that defines its bonus type and ties it to option menu entries. The codename starts with an appID and ends with something the sidekick developer uses to key the bonus type.",
"whichText":"Text associated with this bonus type.",
};
WM.rulesManager.postPartsCodes = {
"age":1,"acceptCount":2,"activatorType":3,"alreadyProcessed":4,"appID":5,"appName":6,"body":7,"canvas":8,"caption":9,"commentorID":10,
"commentorName":11,"comments":12,"currentTime":13,"currentAppTab":14,"date":15,"desc":16,"either":17,"enabled":18,"expanded":19,"failCount":20,
"fromID":21,"fromName":22,"fromNameLastFirst":23,"html":24,"id":25,"idText":26,"img":27,"isAccepted":28,"isAppPaused":29,"isCollect":30,
"isExcluded":31,"isFailed":32,"isForMe":33,"isLiked":34,"isMyPost":35,"isPaused":36,"isPinned":37,"isRemovable":38,"isTimeout":39,"isTypePaused":40,
"isScam":41,"isStale":42,"isUndefined":43,"isWishlist":44,"isWorking":45,"isW2W":46,"lastKnownPostDate":47,"likeID":48,"likeName":49,"limit":50,
"limitCount":51,"link":52,"linkHref":53,"linkText":54,"msg":55,"name":56,"parentLimit":57,"parentLimitCount":58,"postCount":59,"postedDay":60,
"postedHour":61,"priority":62,"status":63,"targetID":64,"targetName":65,"title":66,"totalCount":67,"typesPaused":68,"url":69,"which":70,
"whichText":71,"friendAcceptedCount":72,"friendFailedCount":73
};
WM.rulesManager.postPartByCode = function(code){
for (c in WM.rulesManager.postPartsCodes) {
if (WM.rulesManager.postPartsCodes[c]==code) return c;
}
return null;
};
WM.rulesManager.ruleOperands = {
"equals":"Property and query must match.",
"notEquals":"Property and query must not match.",
"startsWith":"Property must start with query value.",
"notStartsWith":"Property cannot start with query value.",
"endsWith":"Property must end with query value.",
"notEndsWith":"Property cannot end with query value.",
"contains":"Property contains anywhere the query value.",
"notContains":"Property does not contain the query value.",
"matchRegExp":"Property must match the registered expression.",
"notMatchRegExp":"Property must not match the registered expression.",
"greaterThan":"Property must be greater than query value.",
"lessThan":"Property must be less than query value.",
"greaterThanOrEquals":"Property must be greater than or equal to query value.",
"lessThanOrEquals":"Property must be less than or equal to query value.",
"equalsExactly":"Property and query must match exactly via binary comparison.",
"notEqualsExactly":"Property and query must not match exactly via binary comparison.",
};
WM.rulesManager.ruleOperandsCodes = {
"equals":1,
"notEquals":2,
"startsWith":3,
"notStartsWith":4,
"endsWith":5,
"notEndsWith":6,
"contains":7,
"notContains":8,
"matchRegExp":9,
"notMatchRegExp":10,
"greaterThan":11,
"lessThan":12,
"greaterThanOrEquals":13,
"lessThanOrEquals":14,
"equalsExactly":15,
"notEqualsExactly":16,
};
WM.rulesManager.ruleOperandByCode = function(code){
for (c in WM.rulesManager.ruleOperandsCodes) {
if (WM.rulesManager.ruleOperandsCodes[c]==code) return c;
}
return null;
};
//***************************************************************************************************************************************
//***** RuleValidator Class
//***************************************************************************************************************************************
WM.rulesManager.RuleValidator = function(params){try{
var isNew=(!exists(params));
var self=this;
//return saveable data from this branch
this.__defineGetter__("saveableData",function(){try{
var s=self.search, modSearch=[]; //use a second array to avoid accidental overwrite of first byRef
for (var c=0;c<s.length;c++){
modSearch.push(WM.rulesManager.postPartsCodes[s[c]]);
}
var ret = {search:modSearch, operand:WM.rulesManager.ruleOperandsCodes[self.operand], find:self.find}
return ret;
}catch(e){log("WM.rulesManager.RuleValidator.saveableData: "+e);}});
//remove this from parent
this.remove=function(){try{
var ask=WM.opts.rulesConfirmDeleteValidator;
if (!ask || (ask && confirm("Delete rule validator?"))){
remove(this.node);
this.parent.validators.removeByValue(this);
doAction(WM.rulesManager.saveRules);
}
}catch(e){log("WM.rulesManager.RuleValidator.remove: "+e);}};
this.moveUp=function(){try{
//where is this
var parentContainer = this.parent.validators;
//only affects items not already the first in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex-1];
//swap me with my sibling
parentContainer[myIndex-1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(this.node,sibling.node);
//save it
WM.rulesManager.saveRules();
}
}
}catch(e){log("WM.rulesManager.RuleValidator.moveUp: "+e);}};
//move down in the list
this.moveDown=function(){try{
//where is this
var parentContainer = this.parent.validators;
//only affects items not already the first in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex+1];
//swap me with my sibling
parentContainer[myIndex+1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(sibling.node,this.node);
//save it
WM.rulesManager.saveRules();
}
}
}catch(e){log("WM.rulesManager.RuleValidator.moveDown: "+e);}};
//copy this validator on the parent
this.clone=function(){try{
this.parent.addValidator({search:this.search, operand:this.operand, find:this.find});
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.RuleValidator.clone: "+e);}};
//init
//this.id=params.id||unique();
this.parent=params.parent||null;
if (!this.parent) {
log("WM.rulesManager.RuleValidator: no parent specified: abort init");
return null;
}
//this.validationNode=parent.validationNode;
this.search=params.search||["appID"];
if (!isArray(this.search)) this.search=[].push(this.search);
//convert number codes to text commands
for (var e in this.search) {
//t=this.search[e];
if (isNumber(this.search[e])) this.search[e]=WM.rulesManager.postPartByCode(this.search[e]);
//log([this.search[e],t])
}
this.operand=params.operand||"matchRegExp";
if (isNumber(this.operand)) this.operand=WM.rulesManager.ruleOperandByCode(this.operand);
this.find=params.find||"";
//draw it
this.parent.validationNode.appendChild(this.node=createElement("div",{className:"validator"},[
//search portion for this validator
createElement("div",{className:"line"},[
this.searchNode=(this.objSearch=new jsForms.comboBox({
className:"jsfComboBox selectPostPart",
onChange:function(){
self.search=this.value;
WM.rulesManager.saveRules();
},
items:(function(){
var ret=[];
for (var i in WM.rulesManager.postParts){
ret.push(new jsForms.checkBox({
text:i,
value:i,
toolTipText:WM.rulesManager.postParts[i],
checked:(self.search.inArray(i)),
size:{width:"200%"},
}));
}
return ret;
})(),
borderStyle:"none",
//borderRadius:{topLeft:"1px", bottomRight:"1px",topRight:"1px",bottomLeft:"1px"},
//explicitClose:true,
highlightSelected:true,
dropDownSize:{height:"200px"},
backColor:"#EEEEEE",
})).node,
//operator portion for this validator
this.operandNode=createElement("select",{className:"selectOperand",onchange:function(){self.operand=this.value;WM.rulesManager.saveRules();}},(function(){
var ret=[],elem;
for (var i in WM.rulesManager.ruleOperands){
ret.push(elem=createElement("option",{textContent:i,value:i,title:WM.rulesManager.ruleOperands[i]}));
if (i==self.operand) elem.selected=true;
}
return ret;
})()),
//find portion for this validator
/*
right here we need to bring up an element based on
the post part chosen
for most cases, we just need an input box to accept string values
for special case "which" we need a dropdown of bonus types
for boolean flags we need a default value of true and maybe
some kind of limitation to true and false in the box
*/
this.findNode=createElement("input",{className:"findBox",value:this.find,onchange:function(){self.find=this.value;WM.rulesManager.saveRules();}}),
//toolbox
createElement("div",{className:"littleButton oddOrange",onclick:function(){self.remove();},title:"Delete Validator"},[
createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize}),
]),
createElement("div",{className:"littleButton oddBlue",onclick:function(){self.clone();},title:"Clone Validator"},[
createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize}),
]),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.moveUp();},title:"Move Up"},[
createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize}),
]),
createElement("div",{className:"littleButton oddOrange",onclick:function(){self.moveDown();},title:"Move Down"},[
createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize}),
]),
(self.parent.basedOn)?createElement("div",{className:"indent littleButton oddBlue",onclick:function(){
//if a validator search array exists
if (isArrayAndNotEmpty(self.search)){
//fill the 'find' box with the post data linked to the search terms
var f="";
var post=self.parent.basedOn;
for (var s=0;s<self.search.length;s++){
if (s>0) f+=" ";
f+=(post.testData[self.search[s]]||post[self.search[s]]||"");
}
self.findNode.value=f;
self.find=f;
WM.rulesManager.saveRules();
}
},title:"Capture Text From Linked Post"},[
createElement("img",{className:"resourceIcon importData"+WM.opts.littleButtonSize}),
]):null,
]),
]));
//if (isNew) WM.rulesManager.saveRules();
return self;
}catch(e){log("WM.rulesManager.RuleValidator.init(): "+e);}};
//***************************************************************************************************************************************
//***** RuleAction Class
//***************************************************************************************************************************************
WM.rulesManager.RuleAction = function(params){try{
var isNew=(!exists(params));
var self=this;
//return saveable data from this branch
this.__defineGetter__("saveableData",function(){try{
var a= {event:WM.rulesManager.ruleEventsCodes[this.event], action:WM.rulesManager.ruleActionsCodes[this.action]};
if (this.hasParam) a.params=this.params;
if (this.paramCount==2) a.params2=this.params2;
return a;
}catch(e){log("WM.rulesManager.RuleAction.saveableData: "+e);}});
//remove this from parent
this.remove=function(){try{
var ask=WM.opts.rulesConfirmDeleteAction;
if (!ask || (ask && confirm("Delete rule action?"))){
remove(this.node);
this.parent.actions.removeByValue(this);
doAction(WM.rulesManager.saveRules);
}
}catch(e){log("WM.rulesManager.RuleAction.remove: "+e);}};
//move up in the list
this.moveUp=function(){try{
//where is this
var parentContainer = this.parent.actions;
//only affects items not already the first in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex-1];
//swap me with my sibling
parentContainer[myIndex-1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(this.node,sibling.node);
//save it
WM.rulesManager.saveRules();
}
}
}catch(e){log("WM.rulesManager.RuleAction.moveUp: "+e);}};
//move down in the list
this.moveDown=function(){try{
//where is this
var parentContainer = this.parent.actions;
//only affects items not already the first in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex+1];
//swap me with my sibling
parentContainer[myIndex+1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(sibling.node,this.node);
//save it
WM.rulesManager.saveRules();
}
}
}catch(e){log("WM.rulesManager.RuleAction.moveDown: "+e);}};
//copy this validator on the parent
this.clone=function(){try{
this.parent.addAction(this.saveableData());
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.RuleAction.clone: "+e);}};
//init
//this.id=params.id||unique();
this.parent=params.parent||null;
if (!this.parent) {
log("WM.rulesManager.RuleAction: no parent specified: abort init");
return null;
}
//this.actionsNode=parent.actionsNode;
this.action=params.action||"incrementCounter";
//log(this.action);
if (isNumber(this.action)) this.action=WM.rulesManager.ruleActionByCode(this.action);
this.event=params.event||"onAccepted";
if (isNumber(this.event)) this.event=WM.rulesManager.ruleEventByCode(this.event);
//setup default values and param types
//log(this.action);
var def=WM.rulesManager.ruleActions[this.action];
this.hasParam = def.hasParam;
this.params = params.params||def["default"]||((def.paramData||null)?def.paramData[0]["default"]:"");
this.params2 = params.params2||((def.paramData||null)?def.paramData[1]["default"]:"");
this.paramCount = def.paramCount;
//draw it
this.parent.actionsNode.appendChild(this.node=createElement("div",{className:"action"},[
//event for this action
createElement("div",{className:"line"},[
this.eventNode=createElement("select",{className:"selectEvent",onchange:function(){self.event=this.value; if (self.event=="onRuleButtonClicked") {self.parent.ruleButtonHousingNode.style.display="";} else {self.parent.ruleButtonHousingNode.style.display="none";}; WM.rulesManager.saveRules();}},(function(){
var actioneventsret=[],elem;
for (var i in WM.rulesManager.ruleEvents){
actioneventsret.push(elem=createElement("option",{textContent:i,value:i,title:WM.rulesManager.ruleEvents[i]}));
if (i==self.event) elem.selected=true;
}
return actioneventsret;
})()),
//function to call on the event
this.actionNode=createElement("select",{className:"selectFunction",onchange:function(){
self.action=this.value;
WM.rulesManager.saveRules();
//set the param type
var action = WM.rulesManager.ruleActions[this.value];
self.paramNode.style.display=((action.hasParam)?"":"none");
self.param2Node.style.display=((action.hasParam && (action.paramCount==2))?"":"none");
}},(function(){
var actionfuncsret=[],elem;
for (var i in WM.rulesManager.ruleActions){
entry=WM.rulesManager.ruleActions[i];
actionfuncsret.push(elem=createElement("option",{textContent:entry.name,value:i,title:entry.toolTip}));
if (i==self.action) elem.selected=true;
}
return actionfuncsret;
})()),
//this is for special cases only and should be hidden otherwise
this.paramNode=createElement("input",{className:"paramBox",value:this.params,onchange:function(){self.params=this.value;WM.rulesManager.saveRules();}}),
this.param2Node=createElement("input",{className:"paramBox",value:this.params2,onchange:function(){self.params2=this.value;WM.rulesManager.saveRules();}}),
//toolbox
createElement("div",{className:"littleButton oddOrange",onclick:function(){self.remove();},title:"Delete Action"},[
createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize}),
]),
createElement("div",{className:"littleButton oddBlue",onclick:function(){self.clone();},title:"Clone Action"},[
createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize}),
]),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.moveUp();},title:"Move Up"},[
createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize}),
]),
createElement("div",{className:"littleButton oddOrange",onclick:function(){self.moveDown();},title:"Move Down"},[
createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize}),
]),
]),
]));
//hide param node when not used
self.paramNode.style.display=((self.hasParam)?"":"none");
self.param2Node.style.display=((self.hasParam && (self.paramCount==2))?"":"none");
//if (isNew) WM.rulesManager.saveRules();
return self;
}catch(e){log("WM.rulesManager.RuleAction.init(): "+e);}};
//***************************************************************************************************************************************
//***** Rule Class
//***************************************************************************************************************************************
WM.rulesManager.Rule = function(params){try{
this.objType="rule";
var self=this;
params=params||{};
//set defaults
this.parent=null;
this.enabled=true;
this.kids=[]; //child nodes
this.eggs=[]; //hatchable child nodes
this.actions=[]; //events:actions list
this.validators=[]; //search:find list
this.limitCount=0;
this.limit=0;
this.actionsNode=null;
this.validationNode=null;
this.node=null;
this.isChild=false;
this.isEgg=false;
this.expanded=true;
this.timers={};
this.intervals={};
this._isGlobal=false;
//return savable data from this branch
this.__defineGetter__("saveableData",function(){try{
var ret={};
//ret.id=this.id;
ret.title=this.title;
ret.enabled=this.enabled;
ret.limit=this.limit;
ret.limitCount=this.limitCount;
//ret.level=this.level;
ret.expanded=this.expanded;
ret.validators=[];
if (isArrayAndNotEmpty(this.validators)) for (var i=0,validator;(validator=this.validators[i]);i++) {
ret.validators.push(validator.saveableData);
}
ret.actions=[];
if (isArrayAndNotEmpty(this.actions)) for (var i=0,action;(action=this.actions[i]);i++) {
ret.actions.push(action.saveableData);
}
ret.kids=[];
if (isArrayAndNotEmpty(this.kids)) for (var i=0,kid;(kid=this.kids[i]);i++) {
ret.kids.push(kid.saveableData);
}
ret.eggs=[];
if (isArrayAndNotEmpty(this.eggs)) for (var i=0,egg;(egg=this.eggs[i]);i++) {
ret.eggs.push(egg.saveableData);
}
return ret;
}catch(e){log("WM.rulesManager.Rule.saveableData: "+e);}});
//set/get wether this rule is saved as global or profile
this.__defineGetter__("isGlobal",function(){try{
return self._isGlobal;
}catch(e){log("WM.rulesManager.Rule.isGlobal: "+e);}});
this.__defineSetter__("isGlobal",function(v){try{
//only top level rule can be global
if (self.parent) {
confirm("Only top level rule can be set to global.");
return;
}
if (!v) {
if (!confirm("Disabling profile sharing on this rule will prevent other users on this machine from loading it. Are you sure you wish to make this rule locally available only?")) return;
}
self._isGlobal=v;
//make sure we have a uniqueID
//but don't destroy one that already exists
if (v && !exists(self.uniqueID)) self.uniqueID = unique();
//change the color/icon of the isGlobal button
if (self.toggleGlobalButton) {
var s=WM.opts.littleButtonSize;
with (self.toggleGlobalButton) className=className.swapWordB(v,"removeGlobal"+s,"addGlobal"+s);
with (self.toggleGlobalButton.parentNode) {
className=className.swapWordB(v,"oddOrange","oddGreen");
title=(v)?"Disable Profile Sharing":"Share With Other Profiles";
}
}
}catch(e){log("WM.rulesManager.Rule.isGlobal: "+e);}});
this.__defineGetter__("parentLimit",function(){try{
if (self.parent||null) return self.parent.limit;
return null;
}catch(e){log("WM.rulesManager.Rule.parentLimit: "+e);}});
this.__defineGetter__("isBranchDisabled",function(){try{
var p=self.parent,ret=false;
while(p) {
if (!p.enabled) return true;
p=p.parent;
}
return false;
}catch(e){log("WM.rulesManager.Rule.isBranchDisabled: "+e);}});
this.__defineGetter__("parentLimitCount",function(){try{
if (self.parent||null) return self.parent.limitCount;
return null;
}catch(e){log("WM.rulesManager.Rule.parentLimitCount: "+e);}});
//copy passed params to this object
for (var p in params) {
//omit specific params
if (!(["actions","validators","kids","eggs"].inArray(p)) ) {
//copy only params that make it past the checker
this[p]=params[p];
}
}
this.usesRuleButton=function(){
for (var action in this.actions) {
if (action.event=="onRuleButtonClicked") {return true;}
}
return false;
};
this.moveUp=function(){try{
//where is this
var parentContainer =
(this.isChild)?this.parent.kids:
(this.isEgg)?this.parent.eggs:
WM.rulesManager.rules;
//only affects items not already the first in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex-1];
//swap me with my sibling
parentContainer[myIndex-1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(this.node,sibling.node);
//save it
WM.rulesManager.saveRules();
}
}
}catch(e){log("WM.rulesManager.Rule.moveUp: "+e);}};
this.moveDown=function(){try{
//where is this
var parentContainer =
(this.isChild)?this.parent.kids:
(this.isEgg)?this.parent.eggs:
WM.rulesManager.rules;
//only affects items not already the last in the list
//and not the only child in the list
if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
//which index is this?
var myIndex=parentContainer.inArrayWhere(this);
if (myIndex != -1) {
//I have a proper index here
//who is my sibling
var sibling = parentContainer[myIndex+1];
//swap me with my sibling
parentContainer[myIndex+1]=this;
parentContainer[myIndex]=sibling;
//place my node before my sibling node
sibling.node.parentNode.insertBefore(sibling.node,this.node);
//save it
WM.rulesManager.saveRules();
}
}
}catch(e){log("WM.rulesManager.Rule.moveDown: "+e);}};
this.moveUpLevel=function(){try{
if (this.parent) {
//this is not a top level node, so we can move it
var targetContainer=((this.parent.parent)?this.parent.parent.kids:WM.rulesManager.rules);
//remove from parent
this.parent[(this.isChild)?"kids":(this.isEgg)?"eggs":null].removeByValue(this);
//set new parent
this.parent=(this.parent.parent||null); //never point to the top level
//set flags
this.isChild=(this.parent!=null);
this.isEgg=false;
//move the object
targetContainer.push(this);
//move the node
if (this.parent) this.parent.kidsNode.appendChild(this.node);
else WM.console.priorityBuild.appendChild(this.node);
//save it
WM.rulesManager.saveRules();
}
}catch(e){log("WM.rulesManager.Rule.moveUpLevel: "+e);}};
this.moveDownLevel=function(){try{
//where is this
var parentContainer =
(this.isChild)?this.parent.kids:
(this.isEgg)?this.parent.eggs:
WM.rulesManager.rules;
//create a new rule at my level
var newRule = new WM.rulesManager.Rule({
parent:this.parent||null,
isChild:this.isChild,
isEgg:this.isEgg,
});
parentContainer.push(newRule);
//remove me from my current parent
parentContainer.removeByValue(this);
//attach me to my new parent
this.parent=newRule;
this.isChild=true;
this.isEgg=false;
newRule.kids.push(this);
//move my node
newRule.kidsNode.appendChild(this.node);
//save it
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.moveDownLevel: "+e);}};
this.enable=function(){try{
this.enabled=true;
this.node.className=this.node.className.removeWord("disabled");
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.enable: "+e);}};
this.disable=function(){try{
this.enabled=false;
this.node.className=this.node.className.addWord("disabled");
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.disable: "+e);}};
this.disableChildren=function(){try{
if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
kid.disable();
}
}catch(e){log("WM.rulesManager.Rule.disableChildren: "+e);}};
this.enableChildren=function(){try{
if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
kid.enable();
}
}catch(e){log("WM.rulesManager.Rule.enableChildren: "+e);}};
this.toggle=function(){try{
//if(this.enabled)this.disable(); else this.enable();
//this.enabled=!this.enabled;
this.enabled=this.toggleNode.checked;
this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled");
WM.rulesManager.saveRules();
//this.toggleNode.checked=();
}catch(e){log("WM.rulesManager.Rule.toggle: "+e);}};
this.clone=function(){try{
var cloneRule=this.saveableData;
//cloneRule.id=unique();
if (this.isChild) this.parent.addChild(cloneRule);
else if (this.isEgg) this.parent.addEgg(cloneRule);
else WM.rulesManager.newRule(cloneRule);
}catch(e){log("WM.rulesManager.RuleAction.clone: "+e);}};
this.resetLimit=function(params){try{
params=params||{};
var ask=WM.opts.rulesConfirmResetLimit;
if (params.noConfirm || !ask || (ask && confirm("Reset Limit Counter?"))) {
this.limitCount=0;
this.limitCounterNode.value=this.limitCount;
if (!(params.resetChildren||false)) {
if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
kid.resetLimit(params);
}
}
if (!(params.preventSave||false)) WM.rulesManager.saveRules();
}
}catch(e){log("WM.rulesManager.Rule.resetLimit: "+e);}};
this.resetBranchLimits=function(params){try{
params=params||{};
//resets the limits of entire branch rules, but not self limit
if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
kid.resetLimit({resetChildren:true,noConfirm:params.noConfirm||false});
}
}catch(e){log("WM.rulesManager.Rule.resetBranchLimits: "+e);}};
this.resetChildrenLimits=function(params){try{
params=params||{};
//resets the limits of all immediate children, but not self limit
if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
kid.resetLimit({noConfirm:params.noConfirm||false});
}
}catch(e){log("WM.rulesManager.Rule.resetChildrenLimits: "+e);}};
this.incrementLimitCounter=function(o,n){try{
this.limitCount=parseInt(parseInt(this.limitCount)+(exists(n)?parseInt(n):1));
this.limitCounterNode.value=this.limitCount;
WM.rulesManager.saveRules();
//for reaching of limit
if (this.limit && (this.limitCount>=this.limit)) this.onEvent("onLimit",o);
}catch(e){log("WM.rulesManager.Rule.incrementLimitCounter: "+e);}};
this.decrementLimitCounter=function(o,n){try{
this.limitCount=parseInt(parseInt(this.limitCount)-(exists(n)?parseInt(n):1));
//dont allow to drop below 0
if (this.limitCount<0) this.limitCount=0;
this.limitCounterNode.value=this.limitCount;
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.decrementLimitCounter: "+e);}};
this.remove=function(noConfirm){try{
var ask=WM.opts.rulesConfirmDeleteRule;
if (noConfirm || (this.isGlobal && confirm("This rule is shared with other profiles. Deleting it here will prevent it from loading for other users. Are you sure you wish to delete this rule and its children.")) || !ask || (!this.isGlobal && ask && confirm("Delete rule and all of its child nodes?"))){
//destroy intervals and timers
this.cleanup();
//remove my data
var parentContainer=((this.isChild)?this.parent.kids:(this.isEgg)?this.parent.eggs:WM.rulesManager.rules);
parentContainer.removeByValue(this);
//remove my node
remove(this.node);
doAction(WM.rulesManager.saveRules);
}
}catch(e){log("WM.rulesManager.Rule.remove: "+e);}};
this.cancelAllTimers=function(){try{
//find the correct timer by target
for (var t in this.timers){
if (this.timers[t]!=null) {
window.clearTimeout(this.timers[t]);
delete this.timers[t];
}
}
}catch(e){log("WM.rulesManager.Rule.cancelAllTimers: "+e);}};
this.cancelTimer=function(target){try{
//find the correct timer by target
var timer=null;
for (var t in this.timers){
if (this.timers[t].target==target) {
timer=this.timers[t];
break;
}
}
if (timer) {
window.clearTimeout(timer.timer);
delete this.timers[timer.id];
}
}catch(e){log("WM.rulesManager.Rule.cancelTimer: "+e);}};
this.createTimer=function(t,o){try{
this.cancelTimer(o);
var self=this;
var stamp=unique();
var timer=window.setTimeout(function(){
self.onEvent("onTimer",o);
},t);
this.timers[stamp]={timer:timer, target:o, id:stamp};
}catch(e){log("WM.rulesManager.Rule.createTimer: "+e);}};
this.cancelAllIntervals=function(){try{
//find the correct timer by target
for (var t in this.intervals){
if (this.intervals[t]!=null) {
window.clearInterval(this.intervals[t]);
delete this.intervals[t];
}
}
}catch(e){log("WM.rulesManager.Rule.cancelAllIntervals: "+e);}};
this.cancelInterval=function(target){try{
//find the correct timer by target
var interval=null;
for (var t in this.intervals){
if (this.intervals[t].target==target) {
interval=this.intervals[t];
break;
}
}
if (interval) {
window.clearInterval(interval.timer);
delete this.intervals[interval.id];
}
}catch(e){log("WM.rulesManager.Rule.cancelInterval: "+e);}};
this.createInterval=function(t,o){try{
this.cancelInterval(o);
var self=this;
var stamp=unique();
var interval=window.setInterval(function(){
self.onEvent("onInterval",o);
},t);
this.intervals[stamp]={timer:interval, target:o, id:stamp};
}catch(e){log("WM.rulesManager.Rule.createInterval: "+e);}};
this.doEvent=function(event,obj,logit){try{
//check to see if post matches this rule, if it does, perform actions
//if (this.validators){
//logit=logit||(obj.objType=="post");
var obj=(obj||{});
//var self=this;
var matchPost=true, found=[];
for (var vl=0,validator;(validator=this.validators[vl]);vl++) { try{
//within the search array, each result is handled as an OR
var result=false;
for (var s=0,search; (search=validator.search[s]); s++) {
var v =
//special request for object type of the object that activated this rule
(search=="activatorType")?(
(exists(obj))?(obj.objType||"unknown"):"unknown"
):
//special specific app being paused test
(search=="isAppPaused")?(
(exists(obj) && exists(obj.app))?obj.app.paused:false
):
//special specific bonus type being paused
(search=="isTypePaused")?(
(exists(obj) && exists(obj.which) && exists(obj.app))?obj.app.typesPaused.inArray(obj.which):false
):
//read from post object test data
(exists(obj) && exists(obj.testData) && exists(obj.testData[search]))?obj.testData[search]:
//read from activating object standard parameters
(exists(obj) && exists(obj[search]))?obj[search]:
//read from this rule object
exists(self[search])?self[search]:
//read from parameters in the console/main object
exists(WM[search])?WM[search]:
//there is no known reference for this query
"undefined";
//fail validators that do not work with this obj
if (v=="undefined") {result=false; break;}
//convert functions to values
if (typeof v=="function") v=v();
var query = validator.find;
//make the query the same type as the value
if (!(typeof query == typeof v)) {
switch (typeof v) {
case "boolean":
//convert texts of false/true to actual booleans
query = cBool(query);
break;
case "number":
//convert text numbers to real numbers
query=(query=Number(query))?query:0;
//if (query==null) query=0;
break;
}
}
//if (logit) log([search, v, query]);
//compare
switch(validator.operand){
case "equals": result=result||(v==query); break;
case "notEquals": result=result||(v!=query); break;
case "startsWith": result=result||(v.startsWith(query)); break;
case "notStartsWith": result=result||(!v.startsWith(query)); break;
case "endsWith": result=result||(v.endsWith(query)); break;
case "notEndsWith": result=result||(!v.endsWith(query)); break;
case "contains": result=result||(v.contains(query)); break;
case "notContains": result=result||(!v.contains(query)); break;
case "greaterThan": result=result||(v>query); break;
case "lessThan": result=result||(v<query); break;
case "greaterThanOrEquals": result=result||(v>=query); break;
case "lessThanOrEquals": result=result||(v<=query); break;
case "matchRegExp":
var f; //storage space for match array
var regex = new RegExp(query,"gi");
f=regex.exec(v);
result=result||isArrayAndNotEmpty(f);
//result=result||((f=v.match(regex))!=null);
if (f) found=found.concat(f);
break;
case "notMatchRegExp":
var regex = new RegExp(query,"gi");
result=result||(v.match(regex)==null);
break;
case "equalsExactly": result=result||(v===query); break;
case "notEqualsExactly": result=result||!(v===query); break;
}
//any result of true inside this loop is an automatic success
if (result) break;
}
//evaluate our current result with the previous results
//outside the search array, each value is handled as an AND
//any one non-match is a complete failure
matchPost=(matchPost && result);
if (!matchPost) break;
}catch(e){
log("WM.rulesManager.Rule.doEvent: "+e+"{event:" +event+ ", search:"+search+", value:"+v+", query:"+query+", result:"+result+"}");
continue;
}}
//if after all testing we still matched the object
//then perform this rule's events and check children
if (matchPost) {
//log("post matches all validators");
//first do every child rule
for (var k=0,kid;(kid=this.kids[k]);k++){
kid.doEvent(event,obj,true);
}
//now finish up with this rule's actions
this.onEvent(event,obj,found||null);
}
//}
//log("WM.rulesManager.Rule.doEvent: {obj="+obj.id+", event="+event+", matchPost="+matchPost+"}");
}catch(e){log("WM.rulesManager.Rule.doEvent: "+e);}};
this.onEvent=function(event,obj,found){try{
var actionFilter=["*"];
/*
handle special events
*/
if (event=="onRuleCreated") {
/*
we do want onRuleCreated events to fire even if the rule is disabled,
or intervals won't be set and ready for later, if the user does enable the rule
this session. But we want to filter which actions are available.
*/
if (!this.enabled || this.isBranchDisabled) actionFilter=["createInterval","createTimer"];
} else if ((event=="onInterval")||(event=="onTimer")) {
//special case for intervals and timers
if (!this.enabled || this.isBranchDisabled) return;
} else {
//always break if this rule is disabled
if (!this.enabled || this.isBranchDisabled) return;
}
/*
end handle special events
*/
obj=obj||null;
//var self=this;
var post=(self!=obj)?obj:null;
var app=post?(exists(obj.app)?obj.app:obj):null;
//some insertable post parts
var inserts=["appID","which","fromID"];
//perform an action based on an event call
//post object may be null if called from this
for (var a1=0,action;(action=this.actions[a1]);a1++){
//filter actions: allow only those in the filter list, or all actions if "*" is in the list
if (actionFilter.inArray("*") || actionFilter.inArray(action.action) ) if (action.event==event){
var param=action.params;
var param2=action.params2;
var hasParam=action.hasParam;
//format some post parts into the param
if (hasParam && param) {
for (var i=0;i<inserts.length;i++){
if (post && (post.replace||null)) {
param.replace(new RegExp("{%"+inserts[i]+"}","gi"), post.testData[inserts[i]] || post[inserts[i]]);
}
}
}
switch(action.action){
case "destroyRule":(function(){
doAction(function(){
self.remove(true);
});
})(); break;
case "pauseType":(function(){
var w=post.which, a=app;
doAction(function(){
WM.pauseByType(a,w);
});
})(); break;
case "unpauseType":(function(){
var w=post.which, a=app;
doAction(function(){
WM.unPauseByType(a,w);
});
})(); break;
case "uncheckType":(function(){
var w=post.which, a=app;
doAction(function(){
WM.disableOpt(w,a);
//WM.stopCollectionOf(post.which);
});
})(); break;
case "checkType":(function(){
var w=post.which, a=app;
doAction(function(){
WM.enableOpt(w,a);
//WM.startCollectionOf(post.which);
});
})(); break;
case "disableAppOption":(function(){
var c=param, f=found, a=app;
if (f) c=c.format2(f);
doAction(function(){
WM.disableOpt(c,a);
});
})(); break;
case "enableAppOption":(function(){
var c=param, f=found, a=app;
if (f) c=c.format2(f);
doAction(function(){
WM.enableOpt(c,a);
});
})(); break;
case "disableHostOption":(function(){
//debug.print("option disabled");
var c=param, f=found;
if (f) c=c.format2(f);
doAction(function(){
WM.disableOpt(c);
});
})(); break;
case "enableHostOption":(function(){
//debug.print("option enabled");
var c=param, f=found;
if (f) c=c.format2(f);
doAction(function(){
WM.enableOpt(c);
});
})(); break;
case "disableAutolike":(function(){
doAction(function(){
//debug.print("autolike disabled");
WM.disableOpt("useautolike");
});
})(); break;
case "enableAutolike":(function(){
doAction(function(){
//debug.print("autolike enabled");
WM.enableOpt("useautolike");
});
})(); break;
case "disableAutocomment":(function(){
doAction(function(){
WM.disableOpt("useautocomment");
});
})(); break;
case "enableAutocomment":(function(){
doAction(function(){
WM.enableOpt("useautocomment");
});
})(); break;
case "pauseApp":(function(){
var a = WM.apps[param]||app;
doAction(function(){
a.pause();
});
})(); break;
case "unpauseApp":(function(){
var a = WM.apps[param]||app;
doAction(function(){
a.unPause();
});
})(); break;
case "appendLink":(function(){
var p=post, v=param||"";
if (p) doAction(function(){
p.link=p.linkHref+v;
});
})(); break;
case "unpauseAllTypesByApp":(function(){
var a = WM.apps[param]||app;
doAction(function(){
a.unpauseAllTypes();
});
})(); break;
case "unpauseAllTypesAllApps":(function(){
doAction(function(){
for (var a in WM.apps){
a.unpauseAllTypes();
}
});
})(); break;
case "unpauseAllApps":(function(){
doAction(function(){
for (var a in WM.apps){
a.unpause();
}
});
})(); break;
case "pauseAllApps":(function(){
doAction(function(){
for (var a in WM.apps){
a.pause();
}
});
})(); break;
case "refreshBrowser":(function(){
doAction(function(){
window.location.reload();
});
})(); break;
case "pauseWM.collector":(function(){
doAction(function(){
WM.pauseCollecting(true);
});
})(); break;
case "unpauseWM.collector":(function(){
doAction(function(){
WM.pauseCollecting(false);
});
})(); break;
case "pauseFetch":(function(){
doAction(function(){
WM.pauseFetching(true);
});
})(); break;
case "unpauseFetch":(function(){
doAction(function(){
WM.pauseFetching(false);
});
})(); break;
case "likePost":(function(){
doAction(function(){
post.like();
});
})(); break;
case "queueLikePost":(function(){
doAction(function(){
WM.queAutoLike(post);
});
})(); break;
case "commentPost":(function(){
var p=param,f=found;
if (f) p=p.format2(f);
doAction(function(){
post.comment(p);
});
})(); break;
case "queueCommentPost":(function(){
var p=param,f=found;
if (f) p=p.format2(f);
//log(["queueCommentPost fired",p]);
doAction(function(){
WM.queAutoComment(post,p);
});
})(); break;
case "cleanPost":(function(){
doAction(function(){
post.remove();
});
})(); break;
case "incrementCounter":(function(){
var o=obj,p=param,f=found;
//if (f) p=p.format2(f);
doAction(function(){
self.incrementLimitCounter(o,p);
});
})(); break;
case "decrementCounter":(function(){
var o=obj,p=param,f=found;
//if (f) p=p.format2(f);
doAction(function(){
self.decrementLimitCounter(o,p);
});
})(); break;
case "incrementParentCounter":(function(){
var o=obj,p=param, f=found;
//if (f) p=p.format2(f);
if (this.parent) {
doAction(function(){
//passes the activating object, not this rule
self.parent.incrementLimitCounter(o,p);
});
}
})(); break;
case "decrementParentCounter":(function(){
var o=obj,p=param, f=found;
//if (f) p=p.format2(f);
if (this.parent){
doAction(function(){
//passes the activating object, not this rule
self.parent.decrementLimitCounter(o,p);
});
}
})(); break;
case "setColor":(function(){
var c=param;
var f=found;
if (f) c=c.format2(f);
doAction(function(){
post.setColor(c);
});
})(); break;
case "pinPost":(function(){
doAction(function(){
post.pin();
});
})(); break;
case "setAsAccepted":(function(){
var saveit=param;
doAction(function(){
post.accept(saveit);
});
})(); break;
case "setAsFailed":(function(){
var saveit=param;
doAction(function(){
post.fail(saveit);
});
})(); break;
case "setAsExcluded":(function(){
doAction(function(){
post.exclude();
});
})(); break;
case "processFirst":(function(){
doAction(function(){
post.moveToTop();
});
})(); break;
case "processLast":(function(){
doAction(function(){
post.moveToBottom();
});
})(); break;
case "setPriority":(function(){
var p=param, f=found;
if (f) p=p.format2(f);
doAction(function(){
post.setPriority(p);
});
})(); break;
case "setPriorityApp":(function(){
var p=param2, a=WM.apps[param]||app, f=found;
if (f) p=p.format2(f);
doAction(function(){
app.setPriority(p);
});
})(); break;
case "removePriorityApp":(function(){
var p=param2, a=WM.apps[param]||app, f=found;
if (f) p=p.format2(f);
doAction(function(){
app.setPriority(50);
});
})(); break;
case "setPriorityType":(function(){
var p=param2, a=app, f=found, w=param||post.which;
if (f) p=p.format2(f);
doAction(function(){
app.setPriorityByType(w,p);
});
})(); break;
case "removePriorityType":(function(){
var a=app, f=found, w=param||post.which;
if (f) p=p.format2(f);
doAction(function(){
app.setPriorityByType(w,50);
});
})(); break;
case "removePriority":(function(){
doAction(function(){
post.setPriority(50);
});
})(); break;
case "resetLimit":(function(){
doAction(function(){
self.resetLimit({noConfirm:true});
});
})(); break;
case "resetParentLimit":(function(){
if (this.parent) {
doAction(function(){
self.parent.resetLimit({noConfirm:true});
});
}
})(); break;
case "resetChildrenLimits":(function(){
doAction(function(){
self.resetChildrenLimits({noConfirm:true});
});
})(); break;
case "resetBranchLimits":(function(){
doAction(function(){
self.resetBranchLimits({noConfirm:true});
});
})(); break;
case "hatch":(function(){
var o=obj;
doAction(function(){
self.hatch(o);
});
})(); break;
case "birth":(function(){
var o=obj;
doAction(function(){
this.birth(o);
});
})(); break;
case "fetchNewer":(function(){
doAction(function(){
app.fetchPosts({newer:true,bypassPause:true});
});
})(); break;
case "fetchOlder":(function(){
doAction(function(){
app.fetchPosts({older:true,bypassPause:true});
});
})(); break;
case "fetchHours":(function(){
var p=param, f=found, a=app;
if (f) p=p.format2(f);
doAction(function(){
//var t0=timestamp()/1000; //let the fetch script calc it from the feed
var t1=Math.round((timeStamp()-(p*hour))/1000);
//t=t.substr(0,t.length-3);
log("fetchHours: "+p+" please wait...");
WM.fetch({bypassPause:true, older:true, targetEdge:t1, currentEdge:Math.round(timeStamp()/1000), apps:app});
});
})(); break;
case "disableRule":(function(){
doAction(function(){
self.disable();
});
})(); break;
case "enableRule":(function(){
doAction(function(){
self.enable();
});
})(); break;
case "disableChildRules":(function(){
doAction(function(){
self.disableChildren();
});
})(); break;
case "enableChildRules":(function(){
doAction(function(){
self.enableChildren();
});
})(); break;
case "disableApp":(function(){
//check for specified app
var a = WM.apps[param]||app;
doAction(function(){
a.disable();
});
})(); break;
case "enableApp":(function(){
var a = WM.apps[param]||app;
doAction(function(){
a.enable();
});
})(); break;
case "forceOpen":(function(){
doAction(function(){
post.forceOpen();
});
})(); break;
case "forceOpenFirst":(function(){
doAction(function(){
post.forceOpen({first:true});
});
})(); break;
case "emergencyOpen":(function(){
doAction(function(){
post.forceOpen({emergency:true});
});
})(); break;
case "setToCollect":(function(){
doAction(function(){
post.collect();
});
})(); break;
case "setToCollectPriority1":(function(){
doAction(function(){
post.collect();
post.setPriority(1);
});
})(); break;
case "createTimer":(function(){
var o=obj, p=param, f=found;
if (f) p=p.format2(f);
//allow new time format entry
//if the calculated time differs from the passed time, then use that calculated time, as long as it doesn't translate to 0
var t=calcTime(p);
if (t!=0 && t!=p) p=t;
//debug.print(["b",param,t,p]);
doAction(function(){
self.createTimer(p,o);
});
})(); break;
case "cancelTimer":(function(){
var o=obj;
doAction(function(){
if (o.objType=="rule") self.cancelAllTimers();
else self.cancelTimer(o);
});
})(); break;
case "createInterval":(function(){
var o=obj, p=param, f=found;
if (f) p=p.format2(f);
//allow new time format entry
//if the calculated time differs from the passed time, then use that calculated time, as long as it doesn't translate to 0
var t=calcTime(p);
if (t!=0 && t!=p) p=t;
//debug.print(["b",param,t,p]);
doAction(function(){
self.createInterval(p,o);
});
})(); break;
case "cancelInterval":(function(){
var o=obj;
doAction(function(){
if (o.objType=="rule") self.cancelAllIntervals();
else self.cancelInterval(o);
});
})(); break;
case "setWhich":(function(){
var w=param;
var f=found;
if (f) w=w.format2(f);
doAction(function(){
post.setWhich(w);
});
})(); break;
case "reIDAll": (function(){
doAction(function(){
WM.reIDAll();
});
})(); break;
case "resetAllLimits":(function(){
doAction(function(){
WM.rulesManager.resetAllLimits();
});
})(); break;
case "openPostSource":(function(){
doAction(function(){
post.openSource();
});
})(); break;
case "emptyAutolikeQueue":(function(){
doAction(function(){
WM.emptyAutoLikeQueue();
});
})(); break;
case "setHostOption":(function(){
var c=param, c2=param2, f=found;
if (f) c=c.format2(f); //format only param1
doAction(function(){
WM.setOpt(c,c2);
});
})(); break;
case "setAppOption":(function(){
var c=param, c2=param2, f=found, a=app;
if (f) c=c.format2(f); //format only param1
doAction(function(){
WM.setOpt(c,c2,a);
});
})(); break;
case "setAppTab":(function(){
if (param=="all") {
doAction(function(){
//switch to Show All
WM.console.collectTabControl.selectTab(0);
});
} else {
//check for specified app
var a = WM.apps[param]||app;
if (a||null) doAction(function(){
//switch to associated tab
click(a.collectionTabNode);
});
}})(); break;
}
}
}
}catch(e){log("WM.rulesManager.Rule.onEvent: "+e);}};
this.addAction=function(p){try{
var isNew=!exists(p);
p=p||{};
p.parent=this;
var ret=new WM.rulesManager.RuleAction(p);
this.actions.push(ret);
if (isNew) WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.addAction: "+e);}};
this.addValidator=function(p){try{
var isNew=!exists(p);
p=p||{};
p.parent=this;
var ret=new WM.rulesManager.RuleValidator(p);
this.validators.push(ret);
if (isNew) WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.addValidator: "+e);}};
this.addChild=function(p){try{
var isNew=!exists(p);
p=p||{};
p.parent=this;
p.isChild=true;
var rule=new WM.rulesManager.Rule(p);
this.kids.push(rule);
if (isNew) WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.addChild: "+e);}};
this.addEgg=function(p){try{
var isNew=!exists(p);
p=p||{};
p.parent=this;
p.isEgg=true;
var rule=new WM.rulesManager.Rule(p);
this.eggs.push(rule);
if (isNew) WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.addEgg: "+e);}};
//move eggs to parent node and destroy this node
this.hatch=function(obj){try{
var ask=WM.opts.rulesConfirmHatch
if (!ask || (ask && confirm("Hatch egg child and remove current rule and all its children?")) ) {
this.onEvent("onHatch",obj||this);
for (var e=0,egg; (egg=this.eggs[e]); e++){
egg.moveUpLevel();
}
this.remove(true); //with noConfirm
}
}catch(e){log("WM.rulesManager.Rule.hatch: "+e);}};
//clone eggs to parent node
this.birth=function(obj){try{
this.onEvent("onBirth",obj||this);
for (var e=0,egg; (egg=this.eggs[e]); e++){
var cloneRule=egg.saveableData;
if (this.isChild) this.parent.addChild(cloneRule);
else WM.rulesManager.newRule(cloneRule);
}
}catch(e){log("WM.rulesManager.Rule.birth: "+e);}};
//self rule button clicked
this.ruleButtonClicked=function(obj){try{
this.onEvent("onRuleButtonClicked",obj||this);
}catch(e){log("WM.rulesManager.Rule.ruleButtonClicked: "+e);}};
this.toggleContent=function(){try{
this.expanded=!this.expanded;
var btnSize=WM.opts.littleButtonSize;
with (this.contentNode)
className=className.swapWordB(this.expanded,"expanded","collapsed");
with (this.toggleImgNode)
className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
WM.rulesManager.saveRules();
}catch(e){log("WM.rulesManager.Rule.toggleContent: "+e);}};
this.populateBonusList=function(){try{
var node=this.bonusDropDown;
var bonuses=[];
//get the list of accept texts for this app
if (this.appID!="") {
if (this.appID=="* All") {
//populate list with bonuses from ALL docked sidekicks
} else bonuses = mergeJSON(WM.apps[this.appID].accText,WM.apps[this.appID].userDefinedTypes);
}
bonuses["dynamic"]="* Dynamic grab";
bonuses["none"]="* None";
bonuses["wishlist"]="* Flaged as Wishlist";
bonuses["exclude"]="* Excluded types";
bonuses["send"]="* Send Unknown";
bonuses["doUnknown"]="* Get Unknown";
bonuses["*"]="* All"; //perform rule on ALL bonus types for this app
//sort by display text
bonuses=sortCollection(bonuses,"value");
//add each element to the dropdown
var elem;
node.innerHTML=""; //wipe previous list
for (var i in bonuses) {
var showI=i.removePrefix(this.appID);
node.appendChild(elem=createElement("option",{textContent:((bonuses[i].startsWith("*"))?"":((showI.startsWith("send"))?"Send ":"Get "))+bonuses[i], value:showI}));
if (this.bonus== showI) elem.selected = true;
}
}catch(e){log("WM.rulesManager.Rule.populateBonusList: "+e);}};
//draw to priority/rule manager or to the parent node's kids or eggs section
try{(((this.parent)?this.parent[(this.isChild)?"kidsNode":"eggsNode"]:null)||$("wmPriorityBuilder")).appendChild(
this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
createElement("div",{className:"line"},[
createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
]),
this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
self.enabled=this.checked;
with (self.node) className=className.toggleWordB(!this.checked,"disabled");
WM.rulesManager.saveRules();
}}),
createElement("label",{textContent:"Title:"}),
this.titleNode=createElement("input",{className:"w400",value:(this.title||""), onchange:function(){self.title=this.value; WM.rulesManager.saveRules();}}),
//toolbox
createElement("div",{className:"littleButton oddOrange", title:"Remove Rule"},[
createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();}})]),
createElement("div",{className:"littleButton oddBlue",title:"Hatch Egg Children"},[
createElement("img",{className:"resourceIcon hatch"+WM.opts.littleButtonSize,onclick:function(){self.hatch();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Reset Limit Counter"},[
createElement("img",{className:"resourceIcon reset"+WM.opts.littleButtonSize,onclick:function(){self.resetLimit();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Clone Rule"},[
createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize,onclick:function(){self.clone();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Birth Egg Children"},[
createElement("img",{className:"resourceIcon birth"+WM.opts.littleButtonSize,onclick:function(){self.birth();}})]),
createElement("div",{className:"littleButton oddGreen", title:"Move Up"},[
createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize,onclick:function(){self.moveUp();}})]),
createElement("div",{className:"littleButton oddOrange", title:"Move Down"},[
createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize,onclick:function(){self.moveDown();}})]),
createElement("div",{className:"littleButton oddGreen", title:"Move Up Level"},[
createElement("img",{className:"resourceIcon moveUpLevelLeft"+WM.opts.littleButtonSize,onclick:function(){self.moveUpLevel();}})]),
createElement("div",{className:"littleButton oddOrange", title:"Move Down Level"},[
createElement("img",{className:"resourceIcon moveInLevel"+WM.opts.littleButtonSize,onclick:function(){self.moveDownLevel();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Show Source"},[
createElement("img",{className:"resourceIcon object"+WM.opts.littleButtonSize,onclick:function(){promptText(JSON.stringify(self.saveableData),true);}})]),
createElement("div",{className:"indent littleButton "+((this.isGlobal)?"oddOrange":"oddGreen"), title:((this.isGlobal)?"Disable Profile Sharing":"Share With Other Profiles")},[
this.toggleGlobalButton=createElement("img",{className:"resourceIcon "+((this.isGlobal)?"removeGlobal":"addGlobal")+WM.opts.littleButtonSize,onclick:function(){self.isGlobal=!self.isGlobal; WM.rulesManager.saveRules();}})]),
]),
this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
(this.basedOn)?createElement("div",{className:"line"},[
createElement("label",{textContent:"This rule is linked to a post: ",title:"This rule is linked to a post. Validators can draw information from that post so you can easily capture similar posts just by editing the captured texts to suit your needs. Post linking is not carried from session to session."}),
this.basedOnNode=createElement("span",{textContent:this.basedOn.id}),
]):null,
createElement("div",{className:"line"},[
createElement("label",{textContent:"Limit:"}),
this.limitNode=createElement("input",{value:(this.limit||0), onchange:function(){self.limit=this.value;WM.rulesManager.saveRules();}}),
]),
createElement("div",{className:"line"},[
createElement("label",{textContent:"Counter:"}),
this.limitCounterNode=createElement("input",{value:(this.limitCount||0), onchange:function(){self.limitCount=this.value;WM.rulesManager.saveRules();}}),
]),
this.ruleButtonHousingNode=createElement("div",{className:"line", style:(this.usesRuleButton())?"":"display:none;"},[
createElement("label",{textContent:"Rule Button:"}),
this.ruleButtonNode=createElement("button",{type:"button", textContent:"onRuleButtonClicked()", onclick:function(){self.ruleButtonClicked();}}),
]),
//validation subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"For Activating Objects:",title:"These validators attempt to match a post or other activating object, such as feed, feed filter, app, or this rule. All activators that match here then have the following actions performed at certain events."}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addValidator();},title:"Add Validator"},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
]),
this.validationNode=createElement("div",{className:"subsection"}),
]),
//actions subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"Do Actions:",title:"Actions to perform on matching posts."}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addAction();},title:"Add Action"},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
]),
this.actionsNode=createElement("div",{className:"subsection"}),
]),
//kids subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"Child Rules:",title:"Child rules are nested rules which are applied to matching posts at the same time the parent rule is applied. Child rules can have different validators, but will only activate if the parent validators have already matched a post."}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addChild();},title:"Add Child"},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
]),
this.kidsNode=createElement("div",{className:"subsection"}),
]),
//egg kids subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"Egg Rules:", title:"Eggs are potential future rules. When 'hatched', these eggs take the place of the parent rule. The parent rule and its normal children are destroyed."}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addEgg();},title:"Add Egg"},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
]),
this.eggsNode=createElement("div",{className:"subsection"}),
]),
]),
])
);}catch(e){log("WM.rulesManager.Rule.init.drawRule: "+e);}
//list the actions for this rule
if (isArrayAndNotEmpty(params.actions)) for (var i=0,action; (action=params.actions[i]); i++) {
this.addAction(action);
}
//list the validators for this rule
if (isArrayAndNotEmpty(params.validators)) for (var i=0,validator; (validator=params.validators[i]); i++) {
this.addValidator(validator);
}
//list the kids for this rule
if (isArrayAndNotEmpty(params.kids)) for (var i=0,kid; (kid=params.kids[i]); i++) {
this.addChild(kid);
}
//list the egg kids for this rule
if (isArrayAndNotEmpty(params.eggs)) for (var i=0,egg; (egg=params.eggs[i]); i++) {
this.addEgg(egg);
}
//create cleanup function
this.cleanup=function(){try{
for (var t in this.timers) {
window.clearTimeout(this.timers[t]);
}
for (var i in this.intervals) {
window.clearInterval(this.intervals[i]);
}
var self=this;
removeEventListener("beforeunload",self.cleanup,false);
}catch(e){log("WM.rulesManager.Rule.cleanup: "+e);}};
addEventListener("beforeunload",self.cleanup,false);
this.onEvent("onRuleCreated");
return self;
}catch(e){log("WM.rulesManager.Rule.init: "+e);}};
//***************************************************************************************************************************************
//***** App Class
//***************************************************************************************************************************************
WM.App = function(params){try{
this.objType="app";
var self=this;
//expected: id, name, namespace, icon
params=params||{};
//create the masterswitch
var testms=WM.quickOpts.masterSwitch[params.appID];
WM.quickOpts.masterSwitch[params.appID]=(testms==null||testms=="undefined")?true:testms;
//set defaults
this._enabled=WM.quickOpts.masterSwitch[params.appID]||false;
this._paused=false;
this.tests={};
this.typesPaused=[];
this.pausedTypesListNodes={};
this._acceptCount=0;
this._failCount=0;
this.node=null;
this.expanded=false;
this.kids=[]; //contains additional filtered apps
//setup config for this sidekick
this.opts = {};
this.config = new Config({
storageName:"settings_"+params.appID+"_"+(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();
this.config.title = "FB Wall Manager "+WM.version+" (!! Global Settings !!))";
this.config.storageName = "settings_"+params.appID+"_global";
this.config.values=this.config.read();
this.config.configure();
this.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();
this.config.title = "FB Wall Manager "+WM.version;
this.config.storageName = "settings_"+params.appID+"_"+WM.currentUser.profile;
this.config.values=this.config.read();
this.config.configure();
this.config.reload();
}
},
},
},
});
//setup user defined accept texts
try{
if (WM.quickOpts.userDefinedTypes) {
this.userDefinedTypes=WM.quickOpts.userDefinedTypes[params.appID]||{};
} else {
WM.quickOpts.userDefinedTypes={};
WM.quickOpts.userDefinedTypes[params.appID]={};
WM.saveQuickOpts();
}
}catch(e){log("WM.App.init: userDefinedTypes: "+e);}
//use passed params
for (var p in params) this[p]=params[p];
//enable/disable all sidekick functions
this.enable=function(){try{this.enabled=true;}catch(e){log("WM.App.enable: "+e);}};
this.disable=function(){try{this.enabled=false;}catch(e){log("WM.App.disable: "+e);}};
this.toggle=function(){try{this.enabled=!this.enabled;}catch(e){log("WM.App.toggle: "+e);}};
//pause collection for this app
this.pause=function(){try{this.paused=true;}catch(e){log("WM.App.pause: "+e);}}
this.unPause=function(){try{this.paused=false;}catch(e){log("WM.App.unPause: "+e);}}
//user defined types
this.addUDT=function(params,drawOnly){try{
//validate params or ask for input
if (!exists(params) || !params.id) {
params=params||{};
var udtname=prompt("Enter the text name of the bonus type you wish to make (ie 'Horse')\n","");
var udtid=this.appID+udtname.noSpaces().toLowerCase();
udtid=prompt("OK, your type will read as '"+udtname+"'.\nNow modify this bonus type code to suit your needs.\n\nTip: You should prefix this code with the appID '"+this.appID+"', but it is not required.\nTip: Most sidekicks use lowercase and no spaces, but again, this is not a requirement.\n", udtid);
if (udtid.trim()){
params.id=udtid.trim();
params.name=udtname;
} else {
alert("You supplied a blank user defined type ID. No type was created.");
return false;
}
}
if (!drawOnly){
this.userDefinedTypes[params.id]=params.name;
WM.quickOpts.userDefinedTypes[this.appID]=this.userDefinedTypes;
WM.saveQuickOpts();
}
//draw the udt node
if (this.udtNode){
this.udtNode.appendChild(
createElement("div",{className:"listItem"},[
createElement("label",{textContent:params.id+" : "}),
createElement("input",{value:params.name,title:"The display name of this type, used wherever bonus types are identified or selected.", onchange:function(){
self.userDefinedTypes[params.id]=this.value;
WM.quickOpts.userDefinedTypes[self.appID]=self.userDefinedTypes;
WM.saveQuickOpts();
}}),
createElement("div",{className:"littleButton oddOrange", title:"Remove User-Defined Type"},[
createElement("img",{className:"resourceIcon trash" +WM.opts.littleButtonSize,onclick:function(){
var ask=WM.opts.appsConfirmDeleteUDT;
if (!ask || (ask && confirm("Delete User Defined Type?"))) {
delete self.userDefinedTypes[params.id];
WM.quickOpts.userDefinedTypes[self.appID]=self.userDefinedTypes;
WM.saveQuickOpts();
remove (this.parentNode.parentNode);
}
}})
]),
(this.accText[params.id]||null)?createElement("span",{title:"The type id you created exactly matches one provided by the sidekick for this app. If you did not intend to overwrite that bonus's display text, you may wish to create another type id and destroy this one.",style:"color:red;",textContent:"Overwrites a sidekick-provided bonus type id."}):null,
])
);
}
}catch(e){log("WM.App.addUDT: "+e);}}
//unpause all bonus types for this app
this.unpauseAllTypes=function(){try{
for (var i=this.typesPaused.length-1;i>=0;i--){
WM.unPauseByType(this,this.typesPaused[i]);
}
}catch(e){log("WM.App.unpauseAllTypes: "+e);}};
//mass set priority for entire app post collection
this.setPriority=function(n){try{
for (var p in WM.posts) {
var post=WM.posts[p];
if (post.app==this) post.setPriority(n);
}
}catch(e){log("WM.App.setPriority: "+e);}};
//mass set priority for all posts of type
this.setPriorityByType=function(w,n){try{
for (var p in WM.posts) {
var post=WM.posts[p];
if (post.app==this && post.which==w) post.setPriority(n);
}
}catch(e){log("WM.App.setPriorityByType: "+e);}};
//reset accept/fail counters
this.resetCounter=function(){try{
this.acceptCount=0;
this.failCount=0;
}catch(e){log("WM.App.resetCounter: "+e);}};
//reset all config options for this app
//except those outside the standard branch (dontsteal,blockautolike,etc.)
this.resetConfig=function(){try{
var ask=WM.opts.configConfirmRestore;
if (!ask || (ask && confirm("Restore sidekick settings to defaults?"))) {
this.config.configure({reset:true});
this.config.save();
}
}catch(e){log("WM.App.resetConfig: "+e);}};
//fetch posts only for this app
//normally used for initial fetching only
this.fetchPosts=function(){try{
WM.fetch({bypassPause:true, apps:this});
}catch(e){log("WM.App.fetchPosts: "+e);}};
this.fetchNewer=function(){try{
WM.fetch({
newer:true,
apps:this,
bypassPause:true,
bypassAppDisabled:true
});
}catch(e){log("WM.App.fetchNewer: "+e);}};
this.fetchOlder=function(){try{
WM.fetch({
older:true,
apps:this,
bypassPause:true,
bypassAppDisabled:true
});
}catch(e){log("WM.App.fetchOlder: "+e);}};
//get a list of posts for this app from the global posts list
this.__defineGetter__("posts",function(){try{
return matchByParam(WM.posts,"app",this,"object");
}catch(e){log("WM.App.getPosts: "+e);}});
//detect if this sidekick said it was chrome compatible
this.__defineGetter__("isVer3",function(){try{
return this.flags.postMessageCompatible || this.flags.worksInChrome;
}catch(e){log("WM.App.isVer3: "+e);}});
//detect if is paused
this.__defineGetter__("paused",function(){try{
return this._paused;
}catch(e){log("WM.App.paused: "+e);}});
this.__defineSetter__("paused",function(v){try{
this._paused=v;
//update the sidekick page button graphics
var btn=this.pauseButtonNode;
if (btn) {
var btnSize=WM.opts.littleButtonSize;
with (btn.parentNode)
className=className.swapWordB(this._paused,"oddGreen","oddOrange");
with (btn)
className=className.swapWordB(this._paused,"playRight"+btnSize,"pause"+btnSize);
}
//do events
if (this._paused) WM.rulesManager.doEvent("onAppPaused",this);
else WM.rulesManager.doEvent("onAppUnpaused",this);
}catch(e){log("WM.App.paused: "+e);}});
//detect if is enabled
this.__defineGetter__("enabled",function(){try{
return this._enabled;
}catch(e){log("WM.App.enabled: "+e);}});
this.__defineSetter__("enabled",function(v){try{
this._enabled=v;
//update the WM.quickOpts
WM.quickOpts.masterSwitch[this.appID]=this._enabled;
WM.saveQuickOpts();
//update the sidekick page graphics
if (this.toggleNode) this.toggleNode.checked=this._enabled;
if (this.node) with (this.node){
className=className.swapWordB(this._enabled,"enabled","disabled");
}
//do events
if (this._enabled) WM.rulesManager.doEvent("onAppEnabled",this);
else WM.rulesManager.doEvent("onAppDisabled",this);
}catch(e){log("WM.App.enabled: "+e);}});
this.__defineGetter__("acceptCount",function(){try{
return this._acceptCount;
}catch(e){log("WM.App.acceptCount: "+e);}});
this.__defineSetter__("acceptCount",function(v){try{
this._acceptCount=v;
if (this.acceptCounterNode) this.acceptCounterNode.textContent=v;
}catch(e){log("WM.App.acceptCount: "+e);}});
this.__defineGetter__("failCount",function(){try{
return this._failCount;
}catch(e){log("WM.App.failCount: "+e);}});
this.__defineSetter__("failCount",function(v){try{
this._failCount=v;
if (this.failCounterNode) this.failCounterNode.textContent=v;
}catch(e){log("WM.App.failCount: "+e);}});
this.__defineGetter__("totalCount",function(){try{
return this._failCount+this._acceptCount;
}catch(e){log("WM.App.totalCount: "+e);}});
//detect if this app is bundled with another app
//return the main app in this bundle
this.__defineGetter__("synApp",function(){try{
return this.parent||this;
}catch(e){log("WM.App.synApp: "+e);}});
this.toggleContent=function(){try{
this.expanded=!this.expanded;
var btnSize=WM.opts.littleButtonSize;
with (this.contentNode)
className=className.swapWordB(this.expanded,"expanded","collapsed");
with (this.toggleImgNode)
className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
}catch(e){log("WM.App.toggleContent: "+e);}};
this.showConfig=function(){try{
this.config.open();
}catch(e){log("WM.App.showConfig: "+e);}};
this.disableOpt=function(w){try{
this.opts[w]=false;
this.config.set(w,false);
this.config.save();
}catch(e){log("WM.App.disableOpt: "+e);}};
this.enableOpt=function(w){try{
this.opts[w]=true;
this.config.set(w,true);
this.config.save();
}catch(e){log("WM.App.enableOpt: "+e);}};
//add menu elements
try{
/* no longer used in WM3
if (this.menu) {
//prefix all menu elements with the appID
this.menu=WM.dock.fixMenu(this.menu,this.appID);
//append this app's menu settings
this.settingsBranch=WM.config.append({branch:"wmtab_games",data:this.menu});
}
//prefix all test returns with the appID
WM.dock.fixTests(this.tests,this);
//prefix all accept text id's with the appID
WM.dock.fixAcceptTexts(this);
*/
//new method
if (this.menu) this.config.append({data:this.menu});
//I should really move these into the sidekick realm
var data={};
data["dynamic"+this.appID]=checkBox(this.name+" ("+this.appID+")",true);
WM.config.append({branch:"enableDynamic",data:data});
data={}; data[this.appID+"dontsteal"]=checkBox(this.name);
WM.config.append({branch:"dontstealBlock",data:data});
data={}; data["hide"+this.appID]=checkBox(this.name);
WM.config.append({branch:"filterapps",data:data});
data={}; data["nolike"+this.appID]=checkBox(this.name);
WM.config.append({branch:"blockautolikebygame",data:data});
} catch(e) {log("WM.App.init:addMenuElements: "+e);};
//draw to #sidekickList (WM.console.sidekickNode)
try{
WM.console.sidekickNode.appendChild(
this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
createElement("div",{className:"line"},[
createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
]),
this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
self.enabled=this.checked;
with (self.node) className=className.toggleWordB(!this.checked,"disabled");
}}),
(this.icon)?createElement("img",{className:"icon crisp", src:this.icon,style:"width: 32px;vertical-align: middle"}):null,
createElement("label",{textContent: this.name}),
//toolbox
createElement("div",{className:"littleButton odd"+(this.paused?"Green":"Orange"), title:"Pause/Unpause"},[
this.pauseButtonNode=createElement("img",{className:"resourceIcon "+(this.paused?"playRight":"pause")+WM.opts.littleButtonSize,onclick:function(){self.paused=!self.paused;}})]),
createElement("div",{className:"littleButton oddBlue", title:"Reset config for this app"},[
createElement("img",{className:"resourceIcon uncheckAll"+WM.opts.littleButtonSize,onclick:function(){self.resetConfig();}})]),
createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer Posts"},[
createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();}})]),
createElement("div",{className:"littleButton", title:"Fetch Older Posts"},[
createElement("img",{className:"resourceIcon rssDownLeft" +WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();}})]),
//new sidekick config button
this.configButton=createElement("button",{textContent:"Options", onclick:function(){self.config.open();}}),
]),
this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
createElement("div",{className:"line"},[
createElement("label",{textContent:"App ID:"}),
createElement("span",{textContent:this.appID}),
]),
createElement("div",{className:"line"},[
createElement("label",{textContent:"Support Provided By:"}),
(this.desc)?createElement("span",{textContent: this.desc}):null, //provided in sidekick block
]),
createElement("div",{className:"line"},[
createElement("label",{textContent:"Sidekick Help Link:"}),
(this.helpLink)?createElement("a",{href:this.helpLink,textContent:this.helpLink}):null, //provided in sidekick block
]),
//browsers supported
createElement("div",{className:"line"},[
createElement("label",{textContent:"Browsers Supported:",style:"vertical-align:top;"}),
createElement("img",{className:"resourceIcon firefox16", style:"display:inline-block;",title:"FireFox"}),
(this.isVer3)?createElement("img",{className:"resourceIcon chrome16", style:"display:inline-block;",title:"Google Chrome"}):null,
]),
//types paused subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"Types Paused:",title:"This is a list of bonus types that are currently paused for this app."}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.unpauseAllTypes();},title:"Unpause all types by this app."},[
createElement("img",{className:"resourceIcon playRight"+WM.opts.littleButtonSize}),
]),
this.typesPausedNode=createElement("div",{className:"subsection"}),
]),
//attached apps
createElement("div",{className:"line"},[
createElement("label",{textContent:"Attached Apps:",title:"Additional apps filtered and processed by this sidekick."}),
this.filtersNode=createElement("div",{className:"subsection"}),
]),
//helpers subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"Helpers:",title:"Sidekick helpers"}),
this.helpersNode=createElement("div",{className:"subsection"}),
]),
//user defined types subbox
createElement("div",{className:"line"},[
createElement("label",{textContent:"User-Defined Types:",title:"User Defined Types ('which')"}),
createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addUDT();},title:"Add New User Defined Type"},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
]),
this.udtNode=createElement("div",{className:"subsection"}),
]),
]),
])
);
}catch(e){log("WM.App.init:addSidekickElement: "+e);};
//create feed filters for this app
try{
var feeds=WM.feedManager.feeds;
for (var f=0,len=feeds.length;f<len;f++){
feeds[f].addFilter({id:"app_"+this.appID});
}
}catch(e){log("WM.App.init:createFeedFilters: ")+e;}
//draw to collection filter coolbar
try{
//create game filter buttons on the WM.console
var coolBar = WM.console.collectTabControl;
if (coolBar) {
//add a tab for this filter
var tab = coolBar.addTab({
text:(this.name||""),
image:(this.icon||null),
appFilter:this.appID,
onSelect:WM.setAppFilter,
selected:(WM.quickOpts.filterApp==this.appID),
});
this.collectionTabNode=tab.buttonNode;
//force the image to have the 'crisp' drawing style
tab.buttonNode.childNodes[0].className="icon crisp";
//add accept/fail counters
this.failCount=0;
this.acceptCount=0;
tab.buttonNode.insertBefore(
createElement("div",{className:"accFailBlock"},[
this.failCounterNode=createElement("span",{className:"fail",textContent:"0"}),
this.acceptCounterNode=createElement("span",{className:"accept",textContent:"0"}),
])
, tab.textNode);
}
} catch(e) {log("WM.App.init:addConsoleElement: "+e);};
//show additional filtered apps
try{
if (isArrayAndNotEmpty(this.addFilters)) {
for (var f,filt;(filt=this.addFilters[f]);f++){
//create an app object for this filter
filt.parent=this;
this.kids.push(new WM.App(filt));
if (this.filtersNode) this.filtersNode.appendChild(
createElement("div",{className:"line"},[
createElement("img",{className:"icon crisp", src:filt.icon||null}),
createElement("text",filt.name),
])
);
}
}
} catch(e) {log("WM.App.init:addFilteredApps: "+e);};
//draw my user defined types
try{
for (var u in this.userDefinedTypes){
this.addUDT({id:u,name:this.userDefinedTypes[u]},true);
}
}catch(e){log("WM.App.init: drawUDTs: "+e);}
//do events
WM.rulesManager.doEvent("onSidekickReady",this);
return self;
}catch(e){log("WM.App.init: "+e);}};
//***************************************************************************************************************************************
//***** Post Class
//***************************************************************************************************************************************
WM.Post = function(params){try{
this.objType="post";
var self=this;
params=params||{};
//set defaults
this.state=""; //classnames
this.flags=0; //similar to classnames
this.node=null; //collector panel node
this.originalData=JSON.stringify(params); //clone the original data from facebook for viewing later
//convert FQL data to what we have previously expected from graph api
this.id=params.post_id;
this.app=WM.apps[params.app_id];
this.fromID=params.source_id.toString()||this.id.split("_")[0];
this.permalink="http://www.facebook.com/"+this.id.split("_")[0]+"/posts/"+this.id.split("_")[1];
this.fromName=params.fromName;
this.date=params.created_time;
this.message=params.message;
this.name=params.attachment.name;
this.title=params.name;
this.caption=params.attachment.caption;
this.description=params.attachment.description;
this.picture=params.app_data.images;
try{
this.picture = JSON.parse(this.picture)[0].fbml.match(/https?\:.+\.(png|gif|jpg)/)[0];
} catch(e){
//picture data match failed, no big deal
//leave the data messed up because rules manager might still be able to use it
}
this.linkHref=(params.action_links||null)?params.action_links[0].href:(params.attachment.media||null)?params.attachment.media[0].href:"";
this.linkText=(params.action_links||null)?params.action_links[0].text:(params.attachment.media||null)?params.attachment.media[0].alt:"";
this.targetID=params.target_id;
this.isLiked=params.like_info.user_likes;
//convert a unix date to a readable date
this.realtime=(new Date(this.date*1000).toLocaleString());
//set a timer on the post for delayed deletion
this.drawTime=timeStamp();
this._isLiked=false;
this._isPinned=false;
this._isPaused=false;
this._isScam=false;
this._isW2W=false;
this._isForMe=false;
this._isMyPost=false;
this._isWishlist=false;
this._isUndefined=false;
this._status=0;
this._isTimeout=false;
this._isFailed=false;
this._isAccepted=false;
this._isExcluded=false;
this._isStale=false;
this._isCollect=false;
this._isWorking=false;
this._which=null;
this._idText="";
//use passed params
//for (var p in params) this[p]=params[p];
//link to our application array of objects
//this.app=WM.apps[this.application.id];
//shortcuts to app details
this.__defineGetter__("synApp",function(){try{
return this.app.synApp;
}catch(e){log("WM.Post.synApp: "+e);}});
this.__defineGetter__("postedDay",function(){try{
var d=new Date(this.date*1000);
return d.getFullYear()+"/"+d.getMonth()+"/"+d.getDay();
}catch(e){log("WM.Post.postedDay: "+e);}});
this.__defineGetter__("postedHour",function(){try{
var d=new Date(this.date*1000);
var h=d.getHours();
var pm=(h/12)>1;
return d.getFullYear()+"/"+d.getMonth()+"/"+d.getDay()+" "+((h>12)?h-12:h)+":00"+((pm)?"PM":"AM");
}catch(e){log("WM.Post.postedHour: "+e);}});
this.__defineGetter__("appID",function(){try{
return this.app.appID;
}catch(e){log("WM.Post.appID: "+e);}});
this.__defineGetter__("appName",function(){try{
return this.app.name;
}catch(e){log("WM.Post.appName: "+e);}});
//get/set priority
this.__defineGetter__("priority",function(){try{
return this._priority;
}catch(e){log("WM.Post.priority: "+e);}});
this.__defineSetter__("priority",function(v){try{
this._priority=v;
}catch(e){log("WM.Post.priority: "+e);}});
//get/set liked status
this.__defineGetter__("isLiked",function(){try{
return this._isLiked;
}catch(e){log("WM.Post.isLiked: "+e);}});
this.__defineSetter__("isLiked",function(v){try{
this._isLiked=v;
//remove the toolbutton if liked
if (this.node) with (this.node)
className=className.toggleWordB(this._isLiked,"liked");
if (this.likeButtonNode) with (this.likeButtonNode)
className=className.toggleWordB(this._isLiked,"hidden");
}catch(e){log("WM.Post.isLiked: "+e);}});
//identification flags
this.__defineGetter__("isScam",function(){try{
return this._isScam;
}catch(e){log("WM.Post.isScam: "+e);}});
this.__defineSetter__("isScam",function(v){try{
this._isScam=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isScam,"scam");
}catch(e){log("WM.Post.isScam: "+e);}});
this.__defineGetter__("isMyPost",function(){try{
return this._isMyPost;
}catch(e){log("WM.Post.isMyPost: "+e);}});
this.__defineSetter__("isMyPost",function(v){try{
this._isMyPost=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isMyPost,"isMyPost");
}catch(e){log("WM.Post.isMyPost: "+e);}});
this.__defineGetter__("isW2W",function(){try{
return this._isW2W;
}catch(e){log("WM.Post.isW2W: "+e);}});
this.__defineSetter__("isW2W",function(v){try{
this._isW2W=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isW2W,"w2w");
}catch(e){log("WM.Post.isW2W: "+e);}});
this.__defineGetter__("isForMe",function(){try{
return this._isForMe;
}catch(e){log("WM.Post.isForMe: "+e);}});
this.__defineSetter__("isForMe",function(v){try{
this._isForMe=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isForMe,"isForMe");
}catch(e){log("WM.Post.isForMe: "+e);}});
this.__defineGetter__("isWishlist",function(){try{
return this._isWishlist;
}catch(e){log("WM.Post.isWishlist: "+e);}});
this.__defineSetter__("isWishlist",function(v){try{
this._isWishlist=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isWishlist,"wishlist");
}catch(e){log("WM.Post.isWishlist: "+e);}});
this.__defineGetter__("isUndefined",function(){try{
return this._isUndefined;
}catch(e){log("WM.Post.isUndefined: "+e);}});
this.__defineSetter__("isUndefined",function(v){try{
this._isUndefined=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isUndefined,"noDef");
}catch(e){log("WM.Post.isUndefined: "+e);}});
this.__defineGetter__("isStale",function(){try{
return this._isStale;
}catch(e){log("WM.Post.isStale: "+e);}});
this.__defineSetter__("isStale",function(v){try{
this._isStale=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isStale,"stale");
}catch(e){log("WM.Post.isStale: "+e);}});
this.__defineGetter__("isTimeout",function(){try{
return this._isTimeout;
}catch(e){log("WM.Post.isTimeout: "+e);}});
this.__defineSetter__("isTimeout",function(v){try{
this._isTimeout=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isTimeout,"timeout");
}catch(e){log("WM.Post.isTimeout: "+e);}});
this.__defineGetter__("isCollect",function(){try{
return this._isCollect;
}catch(e){log("WM.Post.isCollect: "+e);}});
this.__defineSetter__("isCollect",function(v){try{
this._isCollect=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isCollect,"collect");
}catch(e){log("WM.Post.isCollect: "+e);}});
this.__defineGetter__("isExcluded",function(){try{
return this._isExcluded;
}catch(e){log("WM.Post.isExcluded: "+e);}});
this.__defineSetter__("isExcluded",function(v){try{
this._isExcluded=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isExcluded,"excluded");
}catch(e){log("WM.Post.isExcluded: "+e);}});
this.__defineGetter__("isAccepted",function(){try{
return this._isAccepted;
}catch(e){log("WM.Post.isAccepted: "+e);}});
this.__defineSetter__("isAccepted",function(v){try{
this._isAccepted=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isAccepted,"accepted");
}catch(e){log("WM.Post.isAccepted: "+e);}});
this.__defineGetter__("isFailed",function(){try{
return this._isFailed;
}catch(e){log("WM.Post.isFailed: "+e);}});
this.__defineSetter__("isFailed",function(v){try{
this._isFailed=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isFailed,"failed");
}catch(e){log("WM.Post.isFailed: "+e);}});
this.__defineGetter__("isWorking",function(){try{
return this._isWorking;
}catch(e){log("WM.Post.isWorking: "+e);}});
this.__defineSetter__("isWorking",function(v){try{
this._isWorking=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isWorking,"working");
}catch(e){log("WM.Post.isWorking: "+e);}});
this.__defineGetter__("isColored",function(){try{
return this._isColored;
}catch(e){log("WM.Post.isColored: "+e);}});
this.__defineSetter__("isColored",function(v){try{
this._isColored=v;
if (this._isColored && this.colorOverride && this.node) this.node.style.setProperty("background-color",this.colorOverride,"important");
}catch(e){log("WM.Post.isColored: "+e);}});
//get/set post pinned
this.__defineGetter__("isPinned",function(){try{
return this._isPinned;
}catch(e){log("WM.Post.isPinned: "+e);}});
this.__defineSetter__("isPinned",function(v){try{
this._isPinned=v;
//rotate the pin icon
var btnSize=WM.opts.littleButtonSize;
if (this.pinImageNode) with (this.pinImageNode)
className=className.swapWordB(this._isPinned,"pinned"+btnSize,"pin"+btnSize);
//pinned class
if (this.node) with (this.node)
className=className.toggleWordB(this._isPinned,"pinned");
}catch(e){log("WM.Post.isPinned: "+e);}});
//get/set post paused
this.__defineGetter__("isPaused",function(){try{
return this._isPaused;
}catch(e){log("WM.Post.isPaused: "+e);}});
this.__defineSetter__("isPaused",function(v){try{
this._isPaused=v;
if (this.node) with (this.node)
className=className.toggleWordB(this._isPaused,"paused");
}catch(e){log("WM.Post.isPaused: "+e);}});
//get/set status
this.__defineGetter__("status",function(){try{
return this._status;
}catch(e){log("WM.Post.status: "+e);}});
this.__defineSetter__("status",function(v){try{
this._status=v;
if (this.statusTextNode) this.statusTextNode.textContent=this._status;
}catch(e){log("WM.Post.status: "+e);}});
//get/set idText
this.__defineGetter__("idText",function(){try{
return this._idText;
}catch(e){log("WM.Post.idText: "+e);}});
this.__defineSetter__("idText",function(v){try{
this._idText=v;
if (this.linkNode) this.linkNode.textContent=((this._idText||null) && WM.opts.debugrecog)?this._idText:this.linkText;
}catch(e){log("WM.Post.idText: "+e);}});
//get/set which bonus type this is
this.__defineGetter__("which",function(){try{
return this._which;
}catch(e){log("WM.Post.which: "+e);}});
this.__defineSetter__("which",function(v){try{
this._which=v;
if (this.whichTextNode) this.whichTextNode.textContent=this._which;
}catch(e){log("WM.Post.which: "+e);}});
//check if in history already
this.__defineGetter__("alreadyProcessed",function(){try{
return exists(WM.history[this.id]);
}catch(e){log("WM.Post.alreadyProcessed: "+e);}});
/*
//update the namespace parameter if it does not exist
if (!exists(this.app.namespace)) this.app.namespace=this.application.namespace;
//validate the application namespace for sidekicks that provide namespace checking
if (exists(this.app.namespace) && (this.app.namespace!=this.application.namespace)) {
//Graph API namespace does not match sidekick known namespace, flag as scam
this.isScam=true;
}
//now drop the application object we got from FB
if (exists(this.application)) delete this.application;
*/
//this.fromID=this.from.id;
//this.fromName=this.from.name;
this.fromNameLastFirst=this.fromName;
var sp=this.fromName.split(" ");
if (isArray(sp) && sp.length>1) {
this.fromNameLastFirst = sp.pop()+", "+sp.join(" ");
}
//(re)identify this post
this.identify=function(params){try{
params=params||{};
//shortcuts
var post=this;
var app=post.app;
var synApp=app.synApp;
//set/reset priority, state, status & flags
this.priority=50;
this.status=0;
this.state="";
//prevent reset of some data holders
if (!params.reid) {
this.testData={};
this.isLiked=false;
this.isMyPost=false;
this.isW2W=false;
this.isForMe=false;
this.isScam=false;
}
//reset data holders
this.isStale=false;
this.isCollect=false;
this.isExcluded=false;
this.isFailed=false;
this.isAccepted=false;
this.isPaused=false;
this.isPinned=false;
this.isUndefined=false;
this.isWishlist=false;
this.isTimeout=false;
//avoid posts that belong to a disabled sidekick
if(!WM.quickOpts.masterSwitch[app.appID]) {
//master switch is off
this.isExcluded=true;
return true; //draw without identifying anything
}
//hide posts by apps that we specifically told to hide
if (WM.opts["hide"+app.appID]) {this.remove(); return false;}
//avoid potential scam posts
/*if (WM.opts.scamblock) {
if (!params.reid) {
this.isScam=(!this.linkHref.match(new RegExp("^http(s):\/\/apps\.facebook\.com\/"+app.namespace))!=null);
}
if (this.isScam){
this.isExcluded=true;
if (WM.opts.hidescams) {this.remove(); return false;}
}
}*/
//avoid posts by self
if (!params.reid) {
var whoPosted = this.fromID;
var whoName = this.fromName;
this.isMyPost=(whoPosted==WM.currentUser.id);
}
if (this.isMyPost){
this.isExcluded=true;
if (WM.opts.hidemyposts) {this.remove(); return false;}
}
//avoid W2W posts not for me
if (!params.reid){
this.isForMe = (this.targetID==WM.currentUser.id);
this.isW2W = (this.targetID||null);
}
if (WM.opts[app.appID+"dontsteal"] && this.isW2W && !this.isForMe){
this.isExcluded=true;
if (WM.opts.hidenotforme) {this.remove(); return false;}
}
//avoid posts older than olderLimit
if (olderLimit!=0) {
if (this.isStale=this.checkStale(olderLimit)){
if (WM.opts.skipstale) this.isExcluded=true;
if (WM.opts.hidestale) {this.remove(); return false;}
}
}
//get bonus type
var w=(this.which = WM.which(this,{reid:params.reid}));
//check for exclude type
if (w=="exclude") {
this.isExcluded=true;
}
//check for pause
if(synApp.typesPaused.inArray(w)) this.isPaused=true;
//check if undefined
if (w=="none") {
this.isUndefined=true;
}
if (w==synApp.appID+"doUnknown" || w==synApp.appID+"send") {
this.isUndefined=true;
}
//special pin undefined option
if (WM.opts.pinundefined && this.isUndefined) this.isPinned=true;
//check if liked by me
if (this.isLiked){
if (WM.opts.skipliked){
if (WM.opts.markliked) this.status=1; //mark liked as accepted
this.isExcluded=true;
}
if (WM.opts.hideliked) {this.remove(); return false;}
}
//check history
this.status=this.status||0;
if (this.alreadyProcessed) {
//post previously processed
this.status=(WM.history[this.id].status||0);
var gotItem=((this.status>0) || (this.status==-6) || (this.status==-4) || (this.status==-15 && WM.opts.accepton15));
if (gotItem) {
this.isAccepted=true;
} else if (this.status<0) {
this.isFailed=true;
}
if (WM.opts.hideaccepted && gotItem) {this.remove(); return false;}
if (WM.opts.hidefailed && this.status<0) {this.remove(); return false;}
}
//check if excluded
if (this.isExcluded && WM.opts.hideexcluded) {this.remove(); return false;}
//set identified text
this.idText=WM.getAccText(synApp.appID,w,(this.alreadyProcessed),this.status);
//check if wanted
this.isCollect=(!this.alreadyProcessed &&
(w=="dynamic" || WM.apps[this.synApp.appID].opts[w] ||
(w.startsWith("send") && WM.apps[this.synApp.appID].opts["sendall"])
)
);
//check if wishlist
if (w.find("wishlist")) {
this.isWishlist=true;
if (WM.opts.hideunwanted && !WM.opts.donthidewishlists) {this.remove(); return false;}
}
//if unwanted
if (!this.isCollect && WM.opts.hideunwanted) {this.remove(); return false;}
//debug post
/*var pkg=debug.print("WM.Post.debug: ");
pkg.msg.appendChild(createElement("button",{type:"button",onclick:function(){
//response.responseText.toClipboard();
promptText(JSON.stringify(self));
}},[
createElement("img",{src:"http://i1181.photobucket.com/albums/x430/merricksdad/array.png",title:"Show Data",style:"width:16px;height:16px; vertical-align:bottom;"})
]));*/
//return true to draw, false to hide
return true;
}catch(e){log("WM.Post.identify: "+e);}};
//open this post using the collector system
this.open=function(params){try{
params=params||{};
var post = this;
var id = this.id;
var app = this.app;
var synApp = this.synApp;
//perform the onBefore Collect event
WM.rulesManager.doEvent("onBeforeCollect",post);
//fix the link based on sidekick alterlink information
var alterLink=(synApp.alterLink||null);
var targetHref = post.linkHref;
var doAlterLink=(synApp.flags.alterLink||false);
if (doAlterLink && alterLink) {
//alert("doing alterlink...");
//pack the alterlink into an array, or detect an array
if (!isArray(alterLink)) alterLink=[alterLink];
//iterate link alteration commands
for (var alt=0,alteration;(alteration=alterLink[alt]);alt++) {
//alert("making alteration...");
//note that only find and replace functionality is available right now, no wildcards or array inserts will work
var find = (alteration.find||"");
alteration.dataSource=(alteration.dataSource||"either");
//check if user is wanting a regex or string replacement
if (alteration.isRegex||false) find=new RegExp(find,"");
targetHref = targetHref.replace(find,(alteration.replace||""));
//check for word specific changes
if ((alteration.words||null) && (alteration.conversionChart||null)){
//alert("inserting words...");
//new alterlink capability to change data source from 'either' to another post part
var dataSource = post.testData[alteration.dataSource].toLowerCase();
//alert(dataSource);
for (var w=0,len=alteration.words.length; w<len; w++) {
var word=(alteration.words[w]).toLowerCase();
if (dataSource.contains(word)) {
//replace the word
targetHref=targetHref.replace("{%1}",alteration.conversionChart[word.noSpaces()]);
break;
}
}
}
}
}
//fix the link, removing https and switching to http only
targetHref = targetHref.replace('https://','http://');
//open the bonus page in a new tab or the previously opened tab object to save memory
this.isWorking=true;
post.state="working";
WM.requestsOpen++;
doAction(function(){WM.collector.open({url:targetHref,id:id,callback:(synApp.isVer3?WM.onFrameLoad3:WM.onFrameLoad),post:post,first:params.first||false,emergency:params.emergency||false});});
}catch(e){log("WM.Post.open: "+e);}};
//open this post using the collector system even if already tried
this.forceOpen=function(params){try{
var post=self;
this.isCollect=true;
this.isFailed=false;
this.isTimeout=false;
this.isAccepted=false;
post.open(params);
}catch(e){log("WM.Post.forceOpen: "+e);}};
//like this post using the collector system
this.like=function(){try{
//var url=this.permalink;
var self=this;
//setTimeout(function(){WM.collector.open({url:url+"#likeit=true",id:url,callback:WM.onLikePost,post:self});},100);
//Graph.likePost(this.id,{callback:WM.onLikePost,post:self});
setTimeout(function(){Graph.likePost(self.id,{callback:WM.onLikePost,post:self});},100);
}catch(e){log("WM.Post.like: "+e);}};
//comment on this post using the collector system
this.comment=function(commentOverride){try{
if (commentOverride=="") commentOverride = null;
//not ready
//confirm("Feature not ready");
//return;
//var url=this.permalink;
var self=this;
var say = commentOverride || WM.opts.autocommentlist.split("\n").pickRandom();
//setTimeout(function(){WM.collector.open({url:url+"#commentit=true&say="+say,id:url,callback:WM.onLikePost,post:self});},100);
log("commenting "+say);
//Graph.commentPost(this.id,say);
setTimeout(function(){Graph.commentPost(self.id,say);},100);
}catch(e){log("WM.Post.comment: "+e);}};
//cancel collection in progress
this.cancelProcess=function(){
//tell the collector to cancel any processes with post equal this
//this will cancel both collect and like activities
WM.collector.cancelProcess({search:"post",find:this});
this.processCancelled=true;
},
//cancel collection in progress
this.refreshProcess=function(){
//tell the collector to reload the href on processes with post equal this
//this will reload both collect and like activities
WM.collector.refreshProcess({search:"post",find:this});
this.processRestarted=true;
},
//change the background color of this post
this.setColor=function(color){try{
this.colorOverride=color;
this.isColored=(cBool(color));
}catch(e){log("WM.Post.setColor: "+e);}};
//change the bonus type of this post
//and mark it for collection if needed
//and update its idText
this.setWhich=function(w){try{
this.which=w;
if ((w=="dynamic") || WM.apps[this.synApp.appID].opts[w] || (w.startsWith("send") && WM.apps[this.synApp.appID].opts["sendall"]) ) {
this.isCollect=!this.alreadyProcessed;
}
//update the identified text
this.idText=WM.getAccText(this.synApp.appID,w,(this.alreadyProcessed),this.status);
}catch(e){log("WM.Post.setWhich: "+e);}};
//get the time passed since this post was created
this.__defineGetter__("age",function(){try{
return timeStamp()-(this.date*1000);
}catch(e){log("WM.Post.age: "+e);}});
this.__defineGetter__("whichText",function(){try{
if (this.which=="dynamic") return "Dynamic Grab";
return this.synApp.userDefinedTypes[this.which]||this.synApp.accText[this.which];
}catch(e){log("WM.Post.whichText: "+e);}});
this.draw=function(redraw,reorder){try{
var post=this;
var app=post.app;
var synApp=app.synApp;
//clean old display
if (this.node && redraw) {
remove(this.node);
this.node=null;
}
//prefetch css words
var tags=("")
.toggleWordB(post.isAccepted,"accepted")
.toggleWordB(post.isFailed,"failed")
.toggleWordB(post.isTimeout,"timeout")
.toggleWordB(post.isExcluded,"excluded")
.toggleWordB(post.isStale,"stale")
.toggleWordB(post.isCollect && !(post.isAccepted||post.isFailed),"collect")
.toggleWordB(post.isWorking,"working")
.toggleWordB(post.isW2W,"w2w")
.toggleWordB(post.isForMe,"isForMe")
.toggleWordB(post.isMyPost,"isMyPost")
.toggleWordB(post.isColored,"colored")
.toggleWordB(post.isPaused,"paused")
.toggleWordB(post.isPinned,"pinned")
.toggleWordB(post.isUndefined,"noDef")
.toggleWordB(post.isWishlist,"wishlist")
.toggleWordB(post.isScam,"scam")
.toggleWordB(post.isLiked,"liked");
//detect hidden/drawn image
var hideimage = (WM.opts.hideimages || (WM.opts.hideimagesunwanted && (post.which==="none" || post.which==="exclude") ) );
var fakeimage = hideimage && WM.quickOpts.displayMode!="0";
hideimage=hideimage && WM.quickOpts.displayMode=="0";
//shared elements
if (redraw){
post.toolboxNode=createElement("div",{className:"toolBox small inline"});
post.imageNode=createElement("img",{className:((!fakeimage && post.picture)?"":"resourceIcon noImageSmall16"),src:((!fakeimage)?post.picture:""||""),onerror:function(){this.className=this.className+" resourceIcon noImageSmall16"}});
post.imageLinkNode=(!hideimage)?
createElement("span",{href:jsVoid,className:"picture",onclick:function(){post.forceOpen();} },[
post.imageNode
]):null;
post.floaterNode=null;
post.bodyNode=null;
post.actorNode=createElement("a",{className:"actor",textContent:post.fromName,href:"http://www.facebook.com/profile.php?id="+post.fromID});
post.titleNode=createElement("span",{className:"title",textContent:post.name});
post.captionNode=createElement("span",{className:"caption",textContent:post.caption});
post.descriptionNode=createElement("span",{className:"description",textContent:post.description});
post.dateNode=createElement("span",{className:"postDate",textContent:[post.date,post.realtime]});
post.viaNode=createElement("a",{className:"appName",textContent:" via "+app.name,href:"http://apps.facebook.com/"+app.namespace+"/",title:app.appID});
post.linkNode=createElement("a",{className:"linkText"+(post.isExcluded?" excluded":"")+(post.idText?" identified":""),textContent:((post.idText||null) && WM.opts.debugrecog)?post.idText:post.linkText,href:post.linkHref,title:post.linkText});
post.statusNode=createElement("span",{className:"status",textContent:"Status: "+(post.status||"0")+ " " + (WM.statusText[post.status||"0"])});
post.pausedNode=createElement("div",{className:"pausedHover",title:"Collection for this post is paused, click to reactivate.",onclick:function(){post.isPaused=false;}},[createElement("img",{className:"resourceIcon playRight64"})]);
//create the layout
switch (WM.quickOpts.displayMode||"0"){
case "0": //classic mode
post.node=createElement("div",{id:"post_"+post.id,className:"wm post classic "+tags+((hideimage)?" noimage":""),title:(post.isScam?"Post is possible scam":"")},[
post.toolboxNode,
post.actorNode,
post.imageLinkNode,
(!WM.opts.hidebody)?post.bodyNode=createElement("div",{className:"body"},[
post.titleNode,
(post.caption||null)?post.captionNode:null,
(post.description||null)?post.descriptionNode:null,
]):null,
createElement("div",{style:"margin-top:5px;"},[
(!WM.opts.hidedate)?post.dateNode:null,
(!WM.opts.hidevia)?post.viaNode:null,
post.linkNode,
]),
post.pausedNode,
]);
break;
case "1": case "3": //short mode and old priority mode
post.node=createElement("div",{id:"post_"+post.id,className:"wm post short "+WM.opts.thumbsize+tags,title:(post.isScam?"Post is possible scam":"")},[
post.imageLinkNode,
post.floaterNode=createElement("div",{id:"floater_"+post.id,className:"floater "+WM.opts.thumbsize},[
post.toolboxNode,
post.actorNode,
post.dateNode,
post.viaNode,
post.linkNode,
post.statusNode,
post.pausedNode,
]),
]);
post.imageNode.onmousemove=WM.moveFloater;
break;
case "2": //dev mode
var fnLine=function(label,text){
return createElement("div",{className:"line"},[
createElement("label",{textContent:label+": "}),
createElement("span",{textContent:text})
]);
};
post.node=createElement("div",{id:"post_"+post.id,className:"listItem wm post dev "+tags,title:(post.isScam?"Post is possible scam":"")},[
post.idNode=fnLine("id", post.id),
post.toolboxNode,
//post.imageLinkNode,
createElement("div",{className:"subsection"},(function(){
var ret = [];
ret.push(post.actorNode=fnLine("fromName (fromID)", post.fromName+"("+post.fromID+")"));
ret.push(post.titleNode=fnLine("title",post.name));
if (post.message||null)ret.push(post.messageNode=fnLine("msg",post.message));
if (post.caption||null)ret.push(post.captionNode=fnLine("caption",post.caption));
if (post.description||null)ret.push(post.descriptionNode=fnLine("desc",post.description));
ret.push(post.appImageNode=fnLine("img",post.picture));
ret.push(post.dateNode=fnLine("date",post.realtime));
ret.push(post.appNameNode=fnLine("appName",app.name));
ret.push(post.appIDNode=fnLine("appID",app.appID));
ret.push(post.canvasNode=fnLine("canvas",app.namespace));
ret.push(post.urlNode=fnLine("url",post.linkHref));
//show likes
if (post.likes||null){
if (post.likes.data||null){
ret.push(fnLine("likes",""));
ret.push(post.likesNode=createElement("div",{className:"subsection"},(function(){
var data=post.likes.data;
var retData=[];
for(var d=0,lenL=data.length; d<lenL; d++){
retData.push(fnLine("likeName(likeID)",data[d].name+"("+data[d].id+")"));
}
return retData;
})()));
}
}
//show comments
if (post.comments||null){
if (post.comments.data||null){
ret.push(fnLine("comments",""));
ret.push(post.commentsNode=createElement("div",{className:"subsection"},(function(){
var data=post.comments.data;
var retData=[];
for(var d=0,lenC=data.length; d<lenC; d++){
retData.push(fnLine("commentorName(commentorID)",data[d].from.name+"("+data[d].from.id+")"));
retData.push(fnLine("comment",data[d].message));
}
return retData;
})()));
}
}
ret.push(post.idTextNode=fnLine("idText",post.idText));
ret.push(post.whichNode=fnLine("which",post.which));
ret.push(post.linkTextNode=fnLine("linkText",post.linkText));
return ret;
})() ),
post.pausedNode
]);
break;
}
//add the toolbox
post.addToolBox();
//use color override
if (post.colorOverride) {
post.node.style.setProperty("background-color",post.colorOverride,"important");
}
}
//if a filter exists check against filter
var filter=(WM.quickOpts.filterApp||"All");
if (filter!="All" && filter!=app.appID) {
//dont show this post in this filter
if (this.node) remove(this.node);
return;
}
//insert the post into view by sort order
if (redraw || reorder) {
var groupBy=WM.quickOpts.groupBy;
if (groupBy){
//detect/create group
var group=WM.newGroup({by:post[groupBy]});
var sibling=post.nextSibling();
if (sibling) group.insertBefore(post.node,sibling.node);
else group.appendChild(post.node);
} else {
var sibling=post.nextSibling();
if (sibling) WM.console.feedNode.insertBefore(post.node, sibling.node);
else WM.console.feedNode.appendChild(post.node);
}
}
}catch(e){log("WM.Post.draw: "+e);}};
this.openSource=function(){try{
var url=this.permalink;
//FF22 version
GM_openInTab(url,"_blank");
//FF21 version
//((WM.opts.useGM_openInTab)?GM_openInTab:window.open)(url,"_blank");
}catch(e){log("WM.Post.openSource: "+e);}};
this.addClass=function(s){try{
if (this.node){
this.node.className=this.node.className.addWord(s);
}
}catch(e){log("WM.Post.addWord: "+e);}};
this.removeClass=function(s){try{
if (this.node){
this.node.className=this.node.className.removeWord(s);
}
}catch(e){log("WM.Post.removeWord: "+e);}};
this.pause=function(){try{
this.isPaused=true;
}catch(e){log("WM.Post.pause: "+e);}};
this.unPause=function(){try{
this.isPaused=false;
}catch(e){log("WM.Post.unPause: "+e);}};
this.exclude=function(){try{
this.isExcluded=true;
}catch(e){log("WM.Post.exclude: "+e);}};
this.collect=function(){try{
this.isCollect=true;
}catch(e){log("WM.Post.collect: "+e);}};
this.stopCollect=function(){try{
this.isCollect=false;
}catch(e){log("WM.Post.collect: "+e);}};
this.togglePin=function(){try{
this.isPinned=!this.isPinned;
}catch(e){log("WM.Post.togglePin: "+e);}};
this.pin=function(){try{
this.isPinned=true;
}catch(e){log("WM.Post.pin: "+e);}};
this.unPin=function(){try{
this.isPinned=false;
}catch(e){log("WM.Post.unPin: "+e);}};
this.addToFeeds=function(){try{
WM.feedManager.newFeed({id:this.fromID, title:this.fromName});
WM.feedManager.save();
}catch(e){log("WM.Post.addToFeeds: "+e);}};
this.accept=function(mark){try{
this.isAccepted=true;
this.isFailed=false;
this.isTimeout=false;
this.isWorking=false;
this.isCollect=false;
if (mark) WM.setAsAccepted(null, 3,this);
}catch(e){log("WM.Post.accept: "+e);}};
this.fail=function(mark){try{
this.isFailed=true;
this.isAccepted=false;
this.isTimeout=false;
this.isWorking=false;
this.isCollect=false;
if (mark) WM.setAsFailed(null, -18,this);
}catch(e){log("WM.Post.fail: "+e);}};
this.timeout=function(){try{
this.isTimeout=true;
this.isAccepted=false;
this.isFailed=false;
this.isWorking=false;
this.isCollect=false;
}catch(e){log("WM.Post.timeout: "+e);}};
this.remove=function(){try{
var node=(this.node||$("post_"+this.id));
if (node && node.parentNode) remove(node);
this.node=null;
//turn this post into a ghost so we can keep its data
//for linked objects, but not process it in reid or redraw
this.isGhost=true;
//delete WM.posts[this.id];
}catch(e){log("WM.Post.remove: "+e);}};
this.addToolBox=function(){try{
var post=this;
if (!WM.opts.showtoolbox) return;
var toolNode = post.toolboxNode;
if (toolNode) toolNode.appendChild(
createElement("div",{},[
createElement("div",{onclick:function(){post.forceOpen();},title:"Open Post",className:"littleButton oddBlue"+((!WM.opts.showopen)?" hidden":"")},[
createElement("img",{className:"resourceIcon action"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.cancelProcess();},title:"Cancel Process or AutoLike",className:"littleButton oddBlue"+((!WM.opts.showcancelprocess)?" hidden":"")},[
createElement("img",{className:"resourceIcon cancelProcess"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.refreshProcess();},title:"Restart Process or AutoLike",className:"littleButton oddBlue"+((!WM.opts.showrestartprocess)?" hidden":"")},[
createElement("img",{className:"resourceIcon refreshProcess"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){WM.pauseByType(post.app, post.which);},title:"Pause all bonuses of this type",className:"littleButton oddOrange"+((!WM.opts.showpausetype)?" hidden":"")},[
createElement("img",{className:"resourceIcon stop"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){WM.unPauseByType(post.app, post.which);},title:"Unpause all bonuses of this type",className:"littleButton oddGreen"+((!WM.opts.showunpausetype)?" hidden":"")},[
createElement("img",{className:"resourceIcon playRight"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){window.open(post.permalink,"_blank");},title:"Show Post Source",className:"littleButton oddBlue"+((!WM.opts.showpostsrc)?" hidden":"")},[
createElement("img",{className:"resourceIcon openInNewWindow"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.remove();},title:"Clean",className:"littleButton oddOrange"+((!WM.opts.showclean)?" hidden":"")},[
createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.togglePin();},title:"Pin",className:"littleButton"+((!WM.opts.showpin)?" hidden":"")},[
post.pinImageNode=createElement("img",{className:"resourceIcon "+(post.isPinned?"pinned":"pin")+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.moveToBottom();},title:"Move to Bottom",className:"littleButton oddOrange"+((!WM.opts.showmovebottom)?" hidden":"")},[
createElement("img",{className:"resourceIcon moveBottomLeft"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.moveToTop();},title:"Move to Top",className:"littleButton oddGreen"+((!WM.opts.showmovetop)?" hidden":"")},[
createElement("img",{className:"resourceIcon moveTopLeft"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.reID();},title:"ReID Post",className:"littleButton"+((!WM.opts.showreid)?" hidden":"")},[
createElement("img",{className:"resourceIcon identify"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.addToFeeds();},title:"Add To Feeds",className:"littleButton"+((!WM.opts.showaddfeed)?" hidden":"")},[
createElement("img",{className:"resourceIcon addFeed"+WM.opts.littleButtonSize})]),
post.likeButtonNode=createElement("div",{onclick:function(){post.like();},title:"Like Post",className:"likeButton littleButton oddBlue"+(post.isLiked?" hidden":"")+((!WM.opts.showlike)?" hidden":"")},[
createElement("img",{className:"resourceIcon like"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.comment();},title:"Auto Comment",className:"littleButton oddBlue"+((!WM.opts.showautocomment)?" hidden":"")},[
createElement("img",{className:"resourceIcon comment"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.fail(true);},title:"Mark as Failed",className:"littleButton oddOrange"+((!WM.opts.showmarkfailed)?" hidden":"")},[
createElement("img",{className:"resourceIcon minus"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){post.accept(true);},title:"Mark as Accepted",className:"littleButton oddGreen"+((!WM.opts.showmarkaccepted)?" hidden":"")},[
createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){WM.rulesManager.ruleFromPost(post);},title:"Create Rule From Post",className:"littleButton oddBlue"+((!WM.opts.showmakerule)?" hidden":"")},[
createElement("img",{className:"resourceIcon gotoLibrary"+WM.opts.littleButtonSize})]),
createElement("div",{onclick:function(){promptText(self.originalData,true);},title:"Show Original Data",className:"littleButton"+((!WM.opts.showoriginaldata)?" hidden":"")},[
createElement("img",{className:"resourceIcon object"+WM.opts.littleButtonSize})]),
])
);
}catch(e){log("WM.Post.addToolBox: "+e);}};
/*
this.__defineGetter__("linkText", function(){try{
if (this.actions.length >=3) return this.actions.last().name||"";
else return "";
}catch(e){log("WM.Post.linkText: "+e);}});
this.__defineGetter__("linkHref", function(){try{
return this.link||((this.actions.length>=3)?(this.actions.last().link||""):"")||"";
if (this.actions.length >=3) return this.actions.last().link||"";
else return this.link||"";
}catch(e){log("WM.Post.linkHref: "+e);}});
*/
/*this.actionLink=function(action){try{
//for (var a=0,act;(act=this.actions[a]);a++) if (act.name.toLowerCase()==action.toLowerCase()) {return act.link; break;}
return this.linkHref;
}catch(e){log("WM.Post.actionLink: "+e);}};*/
this.__defineGetter__("body", function(){try{
return (this.title||"")+" "+(this.caption||"")+" "+(this.description||"");
}catch(e){log("WM.Post.body: "+e);}});
this.__defineGetter__("either", function(){try{
return this.linkText+" "+this.body;
}catch(e){log("WM.Post.either: "+e);}});
/*this.__defineGetter__("date", function(){try{
return this["created_time"];
}catch(e){log("WM.Post.date: "+e);}});*/
this.checkStale=function(timeOverride) {try{
if (exists(timeOverride) && timeOverride==0) return false;
var now = timeStamp();
var pubTime = this.date*1000; //(this.date.length < 10)?this.date+"000":this.date;
//var pubTime = this.date+"000";
var aDay = (1000 * 60 * 60 * 24);
return (now-pubTime)>(timeOverride||aDay);
}catch(e){log("WM.Post.checkStale: "+e);}};
//req must equal "id" or "name"
/*this.getTargets=function(req){try{
req = req||"id";
var ret = [];
if (exists(this.to)) {
for (var i=0,target; (target=this.to.data[i]);i++) ret.push(target[req]);
}
return ret;
}catch(e){log("WM.Post.getTargets: "+e);}};*/
//ret must equal "id" or "message" or "name" or "count"
this.getComments=function(req){try{
var ret = [];
if (exists(this.comments)) if (this.comments.count) {
switch(req){
case "message": for (var i=0,comment; (comment=this.comments.data[i]);i++) ret.push(comment[req]); break;
case "id":case "name": for (var i=0,comment; (comment=this.comments.data[i]);i++) ret.push(comment.from[req]); break;
case "count": return this.comments.count; break;
}
}
return ret;
}catch(e){log("WM.Post.getComments: "+e);}};
//ret must equal "id" or "name" or "count"
this.getLikes=function(req){try{
req = req||"id";
var ret = [];
if (exists(this.likes)) if (this.likes.count) {
switch(req){
case "id":case "name": for (var i=0,like; (like=this.likes.data[i]); i++) ret.push(like[req]); break;
case "count": return this.likes.count; break;
}
}
return ret;
}catch(e){log("WM.Post.getLikes: "+e);}};
this.moveToTop = function(){try{
if (this.node||null) this.node.parentNode.insertBefore(this.node,this.node.parentNode.childNodes[0]);
}catch(e){log("WM.Post.moveToTop: "+e);}};
this.moveToBottom = function(){try{
if (this.node||null) this.node.parentNode.appendChild(this.node);
}catch(e){log("WM.Post.moveToBottom: "+e);}};
this.setPriority = function(value){try{
this.priority=value;
remove(this.node);
this.draw();
}catch(e){log("WM.Post.setPriority: "+e);}};
this.reID = function(params){try{
params=params||{};
//reidentify
var oldW=this.which;
if (this.identify({reid:true})) {
WM.rulesManager.doEvent("onIdentify",this);
}
//sort me into proper location
WM.sortPosts();
//redraw||reorder
if (!this.isGhost) {
this.draw(true,true);
}
}catch(e){log("WM.Post.reID: "+e);}};
//return the next visible sibling post
//now checks for group and visibility
this.nextSibling = function(){try{
//determine if there is an app filter
var filter=(WM.quickOpts.filterApp||"All");
//determine display grouping
var groupBy=WM.quickOpts.groupBy;
var group=(groupBy)?WM.newGroup({by:this[groupBy]}):null;
//get visible posts
var visiblePosts=(filter=="All")?WM.posts:matchByParam(WM.posts,"appID",filter);
if (groupBy) visiblePosts=matchByParam(visiblePosts,groupBy,this[groupBy]);
//search for the current post
var found=false, sibling=null;
for (var p in visiblePosts) {
if (found && visiblePosts[p].node) {
if ((!groupBy && visiblePosts[p].node.parentNode==WM.console.feedNode)
|| (groupBy && visiblePosts[p].node.parentNode==group)){
sibling=visiblePosts[p];
break
}
} else if (visiblePosts[p]==this) found=true;
}
//return what is found
return sibling;
//warning: returns null if this is the last visible post
}catch(e){log("WM.Post.nextSibling: "+e);}};
//return the previous visible sibling post
//now checks for group and visibility
this.previousSibling = function(){try{
//determine if there is an app filter
var filter=(WM.quickOpts.filterApp||"All");
//determine display grouping
var groupBy=WM.quickOpts.groupBy;
var group=(groupBy)?WM.newGroup({by:this[groupBy]}):null;
//get visible posts
var visiblePosts=(filter=="All")?WM.posts:matchByParam(WM.posts,"appID",filter);
if (groupBy) visiblePosts=matchByParam(visiblePosts,groupBy,this[groupBy]);
//search for the current post
var sibling=null;
for (var p in visiblePosts) {
if (visiblePosts[p]==this) break;
else if (visiblePosts[p].node) {
if ((!groupBy && visiblePosts[p].node.parentNode==WM.console.feedNode)
|| (groupBy && visiblePosts.node.parentNode==group)){
sibling=visiblePosts[p];
}
}
}
//return what is found
return sibling;
//warning: returns null if this is the first visible post
}catch(e){log("WM.Post.previousSibling: "+e);}};
//get the friend object related with this post (from the friend tracker)
this.__defineGetter__("relatedFriend",function(){try{
return WM.friendTracker.friends[this.fromID]||null;
}catch(e){log("WM.Post.relatedFriend: "+e);}});
this.__defineGetter__("friendAcceptedCount",function(){try{
return this.relatedFriend.acceptCount;
}catch(e){log("WM.Post.friendAcceptedCount: "+e);}});
this.__defineGetter__("friendFailedCount",function(){try{
return this.relatedFriend.failCount;
}catch(e){log("WM.Post.friendFailedCount: "+e);}});
//a little cleanup
params = null;
}catch(e){log("WM.Post.init: "+e);}};
//***************************************************************************************************************************************
//***** Main Program
//***************************************************************************************************************************************
//*****short text versions for WM.config menu options*****
var checkBox=function(l,d,c,n){return ({type:"checkbox",label:l,"default":d||false,kids:c,newitem:n});}
var hidden=function(l,d,c){return ({type:"hidden",label:l,"default":d,kids:c});}
var optionBlock=function(l,c,hideSelectAll,n){hideSelectAll=hideSelectAll||false;return ({type:"optionblock",label:l,kids:c,hideSelectAll:hideSelectAll,newitem:n});}
var separator=function(l,s,c,hideSelectAll){hideSelectAll=hideSelectAll||false; return ({type:"separator",label:l,section:s,kids:c}); }
var section=function(l,c){return ({type:"section",label:l,kids:c});}
var tabSection=function(l,c){return ({type:"tab",label:l,kids:c});}
var inputBox=function(l,d,n){return ({type:"float",label:l,"default":(d||0),newitem:n});}
var textArea=function(l,d,n){return ({type:"textarea",label:l,"default":(d||0),newitem:n});}
var colorBox=function(l,d,n){return ({type:"colorbox",label:l,"default":(d||"black"),newitem:n});}
var button=function(l,s){return ({type:"button",label:l,script:s});}
var anchor=function(l,u,t){return ({type:"link",label:l,href:u,title:(t||'')});}
//***************************************************************************************************************************************
//***** Immediate
//***************************************************************************************************************************************
log("Script: WM initialized",{level:0});
// section for reclaiming memory and stopping memory leaks
var newIntv=null; //refresh interval
var oldIntv=null; //refresh interval
var procIntv=null; //process interval
var cleanIntv=null; //post removal interval
var hbIntv=null; //global heartbeat interval
var olderLimit=day; //default 1 day
var cleanup=function() {try{
//destroy intervals
if (newIntv) window.clearInterval(newIntv);
if (oldIntv) window.clearInterval(oldIntv);
if (procIntv) window.clearInterval(procIntv);
if (cleanIntv) window.clearInterval(cleanIntv);
if (hbIntv) window.clearInterval(hbIntv);
oldIntv = newIntv = procIntv = cleanIntv = hbIntv = null;
//close the sidekick tabs
WM.collector.closeAll();
//remove this event listener
window.removeEventListener("beforeunload", cleanup, false);
window.removeEventListener("message", WM.receiveSidekickMessage, false);
window.removeEventListener("resize", WM.onWindowResize, false);
//clean up memory
WallManager=null;
Graph=null;
jsForms=null;
olderLimit=null;
opts=null; quickOpts=null;
}catch(e){log("cleanup: "+e);}}
window.addEventListener("beforeunload", cleanup, false);
window.addEventListener("resize", WM.onWindowResize, false);
//start it up
WM.run();
})(); // anonymous function wrapper end
/* recent changes
3.1.1
*fixed the update script button to point to the standard branch
*posts are once again able to fetch source user names with the post
*reinstated post.showOriginalData so developers can see the return data from FQL and how it differs from GraphAPI returns
3.1.2
*fixed the button on feed manager where you can fetch for a specific friend feed manually
*added option to fetch all apps for a specific feed in a single request, default is to fetch for each app with a separate request
*fixed drawing order bug caused by graph update
*temporarily removed checking for feed already having reached older limit, feed will now draw until facebook has no posts to show if you tell it to
**hide stale posts will still make it so you can't see those posts if you have it enabled
*fixed friend tracker last known post date
3.1.3
*when action_links is null from facebook, WM now uses attachment.href for the link, which should match, but is not a guarantee.
*when action_links is null from facebook, WM now uses "" as the link text. Neither attachment or app_data appears to have a duplicate of the link text.
*added error checking for if the return data for a post cannot be transformed into a useful post object. This could happen if some of the data WM requires to function is not available, or is mangled for some reason.
*added option to have WM filter posts by app instead of asking FB for posts by app. This will hopefully remove a lot of that empty data set issue, but you will occasionally get return data without any useful posts in it.
3.1.4
*reinstated olderLimitReached and any function or rules manager event having to do with that
*removed some console.log debuggery
*fixed issue with disabled feeds still being processed when fetching multiple apps at once (with those new options)
3.1.4.1
*fixed alternative spotting of link url and link text associated with app posts (when action_links data is null)
3.1.6
*added rule manager options: friendAcceptCount and friendFailedCount
*/