// ==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 // @downloadURL https://update.greasyfork.icu/scripts/421/FB%20Wall%20Manager.user.js // @updateURL https://update.greasyfork.icu/scripts/421/FB%20Wall%20Manager.meta.js // ==/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.idb[params.by]; if (["descending","desc"].inArray(params.direction.toLowerCase())) return a[params.by]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; i0) || (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 (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;folderLimit) filter.olderLimitReached=true; } } } } else { //update base feed feed = WM.feedManager.feeds[0]; for (var c=0,l=params.apps.length;colderLimit) 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 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;sidiv: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;i1) && (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 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.id0 || 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;i1) && (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;s0) 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 "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=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;f1; 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