您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This is the host object which is created under the WM version 4.x script
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://updategreasyfork.deno.dev/scripts/6898/27563/WM%20Feed%20Manager%20Objects.js
// ==UserScript== // @name WM Feed Manager Objects // @namespace MerricksdadWMFeedManagerObjects // @description This is the host object which is created under the WM version 4.x script // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/ // @version 4.0.0.0 // @copyright Charlie Ewing except where noted // ==/UserScript== //this script requires some functions in the WM Common Library //this script needs access to a pre-defined JSON object (function(){ //*************************************************************************************************************************************** //***** Feed Objects //*************************************************************************************************************************************** WM.feedManager = { feeds : [], init : function(){ var feedsIn=(getOptJSON("feeds3_"+WM.currentUser.profile)||[]); var feedsInGlobal=(getOptJSON("feeds3_global")||[]); if (isArrayAndNotEmpty(feedsIn)) { //import feeds from storage for (var f=0;f<feedsIn.length;f++){ feed=feedsIn[f]; if (!feed.isGlobal){ WM.feedManager.feeds.push(new WM.Feed(feed)); } else { var glob=feedsInGlobal[feed.uniqueID]||null; if (glob){ var merge=mergeJSON(glob,feed); WM.feedManager.newFeed(merge); glob.alreadyUsed=true; } else { log("WM.feedManager.init: Global feed missing, cannot merge"); } } } } else { //never been used before, create base feeds WM.feedManager.feeds.push(new WM.Feed({title:"My Home Feed", url:"https://graph.facebook.com/me/home", isRemoveable:false})); //WM.feedManager.feeds.push(new WM.Feed({title:"My Profile Wall", url:"https://graph.facebook.com/me/feed", isRemoveable:false})); //import oldstyle feeds var feedsOld=getOpt("feeds_"+WM.currentUser.profile); if (feedsOld){ feedsOld=feedsOld.split("\n"); if (isArrayAndNotEmpty(feedsOld)) for (var f=0;f<feedsOld.length;f++) { //prevent empties if (feedsOld[f]) { //create the new feed WM.feedManager.newFeed({id:feedsOld[f],title:feedsOld[f]}); } } } WM.feedManager.save(); } //import all global feeds not already accounted for for (var t in feedsInGlobal) { var glob=feedsInGlobal[t]; if (!glob.alreadyUsed){ glob.uniqueID=t; glob.isGlobal=true; WM.feedManager.newFeed(glob); //newFeed adds app filters, where New Feed() does not } } }, newFeed : function(params){ params=params||{}; var feed = new WM.Feed(params); WM.feedManager.feeds.push(feed); //add filters for each app available for (var a in WM.apps){ feed.addFilter({id:"app_"+a}); } }, save :function(){ var retFeeds=[]; var retGlobal={}; if (isArrayAndNotEmpty(WM.feedManager.feeds)) for (var f=0,len=WM.feedManager.feeds.length; f<len; f++){ var feed=WM.feedManager.feeds[f]; if (!feed.isGlobal) { retFeeds.push(feed.saveableData); } else { retFeeds.push({isGlobal:true, uniqueID:feed.uniqueID, enabled:feed.enabled, expanded:feed.expanded}); var glob=feed.saveableData; glob.uniqueID=feed.uniqueID; retGlobal[feed.uniqueID]=glob; } } setOptJSON("feeds3_"+WM.currentUser.profile,retFeeds); setOptJSON("feeds3_global",retGlobal); }, }; //*************************************************************************************************************************************** //***** FeedFilter Class //*************************************************************************************************************************************** WM.FeedFilter = function(params){try{ this.objType="feedFilter"; params=params||{}; var self=this; //set defaults this.enabled=true; this.expanded=true; this._olderLimitReached=false; //initialize edges to the collector startup time this.oldedge=Math.round(timeStamp()/1000); //older edge timestamp this.newedge=Math.round(timeStamp()/1000); //newer edge timestamp //use passed params for (var p in params) this[p]=params[p]; this.enable=function(){try{ this.enabled=true; this.node.className=this.node.className.removeWord("disabled"); WM.feedManager.save(); }catch(e){log("WM.FeedFilter.enable: "+e);}}; this.disable=function(){try{ this.enabled=false; this.node.className=this.node.className.addWord("disabled"); WM.feedManager.save(); }catch(e){log("WM.FeedFilter.disable: "+e);}}; this.toggle=function(){try{ this.enabled=this.toggleNode.checked; this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled"); WM.feedManager.save(); }catch(e){log("WM.FeedFilter.toggle: "+e);}}; this.toggleContent=function(){try{ this.expanded=!this.expanded; var btnSize=WM.opts.littleButtonSize; with (this.contentNode) className=className.swapWordB(this.expanded,"expanded","collapsed"); with (this.toggleImgNode) className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize); WM.feedManager.save(); }catch(e){log("WM.FeedFilter.toggleContent: "+e);}}; //remove this this.remove=function(){try{ if (this.node) remove(this.node); if (this.parent) delete this.parent.filters[this.id]; WM.feedManager.save(); }catch(e){log("WM.FeedFilter.remove: "+e);}}; //fetch posts for this this.fetchNewer=function(){try{ WM.fetch({ newer:true, apps:WM.apps[this.appID], feeds:[this.parent], bypassPause:true, bypassAppDisabled:true, bypassFeedDisabled:true, bypassFilterDisabled:true, }); }catch(e){log("WM.FeedFilter.fetchNewer: "+e);}}; this.fetchOlder=function(){try{ WM.fetch({ older:true, apps:WM.apps[this.appID], feeds:[this.parent], bypassPause:true, bypassAppDisabled:true, bypassFeedDisabled:true, bypassFilterDisabled:true, }); }catch(e){log("WM.FeedFilter.fetchOlder: "+e);}}; this.__defineGetter__("olderLimitReached",function(){try{ return this._olderLimitReached; }catch(e){log("WM.FeedFilter.olderLimitReached: "+e);}}); this.__defineSetter__("olderLimitReached",function(v){try{ this._olderLimitReached=v; //update the sidekick page button graphics var node=this.olderLimitNode; if (node) node.textContent=v; if (v) { WM.rulesManager.doEvent("onFeedFilterOlderLimitReached",this); } }catch(e){log("WM.FeedFilter.olderLimitReached: "+e);}}); this.__defineGetter__("appID",function(){try{ //this assumes its an app filter because so far thats all we use return this.id.removePrefix("app_"); }catch(e){log("WM.FeedFilter.appID: "+e);}}); this.__defineGetter__("appName",function(){try{ //this assumes its an app filter because so far thats all we use //it also assumes app objects are global, which they are //but that they are also available and already filled in by the sidekick, which they may not be var a = WM.apps[this.appID]; if (a!=undefined) { //debug.print([this.appID,a]); return a.name; } return ""; }catch(e){log("WM.FeedFilter.appName: "+e);}}); //draw it try{ if (this.parent.filtersNode) this.parent.filtersNode.appendChild( this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[ createElement("div",{className:"line"},[ createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[ this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}), ]), this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){ self.enabled=this.checked; with (self.node) className=className.toggleWordB(!this.checked,"disabled"); WM.feedManager.save(); }}), createElement("span",{textContent:this.appName + " (" + this.id + ")"}), //toolbox createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer"},[ this.fetchNewerButton=createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();} }) ]), createElement("div",{className:"littleButton", title:"Fetch Older"},[ this.fetchOlderButton=createElement("img",{className:"resourceIcon rssDownLeft"+WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();} }) ]), ]), this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[ createElement("div",{className:"line"},[ createElement("label",{textContent:"Older Limit Reached: ",title:"Reports if this filter has reached the user defined oldest post limit."}), this.olderLimitNode=createElement("span",{textContent:this.olderLimitReached}), ]), createElement("div",{className:"line"},[ createElement("label",{textContent:"Newer Edge: ",title:"A Unixtime indicator of the newest post-time you have fetched for this filter."}), this.newedgeNode=createElement("span",{textContent:this.newedge}), ]), createElement("div",{className:"line"},[ createElement("label",{textContent:"Older Edge: ",title:"A Unixtime indicator of the oldest post-time you have fetched for this filter."}), this.oldedgeNode=createElement("span",{textContent:this.oldedge}), ]), ]), ]) ); }catch(e){log("WM.FeedFilter.init:addManagerElement: "+e);}; return self; }catch(e){log("WM.FeedFilter.init: "+e);}}; //*************************************************************************************************************************************** //***** Feed Class //*************************************************************************************************************************************** WM.Feed = function(params){try{ this.objType="feed"; params=params||{}; var self=this; //set defaults this.enabled=true; this.expanded=true; this.url=""; this.id=""; this.filters={}; this.feedName=""; this.isRemoveable=true; //set to false on own feeds this.title="New Feed"; this._isGlobal=false; //use passed params var newFilters=params.filters||{}; delete params.filters; this.__defineGetter__("isGlobal",function(){try{ return this._isGlobal; }catch(e){log("WM.Feed.isGlobal: "+e);}}); this.__defineSetter__("isGlobal",function(v){try{ if (!v) { if (!confirm("Disabling profile sharing on this feed will prevent other users on this machine from loading it. Are you sure you wish to make this feed locally available only?")) return; } this._isGlobal=v; //make sure we have a uniqueID //but don't destroy one that already exists if (v && !exists(this.uniqueID)) this.uniqueID = unique(); //change the color/icon of the isGlobal button if (this.toggleGlobalButton) { var s=WM.opts.littleButtonSize; with (this.toggleGlobalButton) className=className.swapWordB(v,"removeGlobal"+s,"addGlobal"+s); with (this.toggleGlobalButton.parentNode) { className=className.swapWordB(v,"oddOrange","oddGreen"); title=(v)?"Disable Profile Sharing":"Share With Other Profiles"; } } }catch(e){log("WM.Feed.isGlobal: "+e);}}); this.__defineGetter__("saveableData",function(){try{ var ret={}; ret.title=this.title; ret.enabled=this.enabled; ret.expanded=this.expanded; ret.isRemoveable=this.isRemoveable; ret.url=this.url; if (this.isRemoveable) ret.id=this.id; //capture filters ret.filters={}; for (var f in this.filters) { ret.filters[f]={ enabled:this.filters[f].enabled, expanded:this.filters[f].expanded, id:this.filters[f].id, }; } return ret; }catch(e){log("WM.Feed.saveableData: "+e);}}); for (var p in params) this[p]=params[p]; this.enable=function(){try{ this.enabled=true; this.node.className=this.node.className.removeWord("disabled"); WM.feedManager.save(); }catch(e){log("WM.Feed.enable: "+e);}}; this.disable=function(){try{ this.enabled=false; this.node.className=this.node.className.addWord("disabled"); WM.feedManager.save(); }catch(e){log("WM.Feed.disable: "+e);}}; this.toggle=function(){try{ this.enabled=this.toggleNode.checked; this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled"); WM.feedManager.save(); }catch(e){log("WM.Feed.toggle: "+e);}}; //create a filter for a specific app //filter id must be "app_"+appID //will not add duplicates this.addFilter=function(params){try{ var isNew=!exists(params); params=params||{}; params.parent=this; //prevent duplicates if (!exists(this.filters[params.id])) { return (this.filters[params.id]=new WM.FeedFilter(params)); if (isNew) WM.feedManager.save(); } else { return this.filters[params.id]; } }catch(e){log("WM.Feed.addFilter: "+e);}}; //get the extents of the feed by merging all feed filter oldedge/newedge values this.getMergedEdges=function(params){ /* apps[]: an array of appID's to test against, otherwise read from all filters */ //console.log("getMergedEdges: "+JSON.stringify(params)); var retval = {newedge:Math.round(timeStamp()/1000), oldedge:0}; if (params.apps||null){ for (var c=0,l=params.apps.length;c<l;c++){ var filter = this.filters["app_"+params.apps[c]]; if (filter||null){ //get the youngest older edge and oldest newer edge so we don't lose posts because one feed is more active. //this forces them to run at the same edges after the first pull retval.newedge = Math.min(retval.newedge, filter.newedge); retval.oldedge = Math.max(retval.oldedge, filter.oldedge); } else { log("getMergedEdges: no filter matching app_"+params.apps[c]+" on feed " + this.id); } } } else { for (var name in this.filters){ var filter = this.filters[name]; if (filter||null){ //get the youngest older edge and oldest newer edge so we don't lose posts because one feed is more active. //this forces them to run at the same edges after the first pull retval.newedge = Math.min(retval.newedge, filter.newedge); retval.oldedge = Math.max(retval.oldedge, filter.oldedge); } else { log("getMergedEdges: no filter matching "+name+" on feed " + this.id); } } } return retval; }; //remove this this.remove=function(noConfirm){try{ if (this.isRemoveable) { var ask=WM.opts.feedsConfirmDeleteFeed; if (noConfirm || (this.isGlobal && confirm("This feed is shared with other profiles. Deleting it here will prevent it from loading for other users. Are you sure you wish to delete this feed and its filters.")) || !ask || (!this.isGlobal && ask && confirm("Delete feed and all of its filters?"))){ //remove my data if (this.node) remove(this.node); WM.feedManager.feeds.removeByValue(this); WM.feedManager.save(); } } }catch(e){log("WM.Feed.remove: "+e);}}; //fetch posts for this this.fetchNewer=function(){try{ WM.fetch({ newer:true, feeds:[self], bypassPause:true, bypassAppDisabled:true, bypassFeedDisabled:true, }); }catch(e){log("WM.Feed.fetchNewer: "+e);}}; this.fetchOlder=function(){try{ WM.fetch({ older:true, feeds:[self], bypassPause:true, bypassAppDisabled:true, bypassFeedDisabled:true, }); }catch(e){log("WM.Feed.fetchOlder: "+e);}}; this.toggleContent=function(){try{ this.expanded=!this.expanded; var btnSize=WM.opts.littleButtonSize; with (this.contentNode) className=className.swapWordB(this.expanded,"expanded","collapsed"); with (this.toggleImgNode) className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize); WM.feedManager.save(); }catch(e){log("WM.Feed.toggleContent: "+e);}}; if (this.id && !this.url) this.url="https://graph.facebook.com/"+this.id+"/feed"; //draw it try{ WM.console.feedManagerNode.appendChild( this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[ createElement("div",{className:"line"},[ createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[ this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}), ]), this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){ self.enabled=this.checked; with (self.node) className=className.toggleWordB(!this.checked,"disabled"); WM.feedManager.save(); }}), this.titleNode=createElement("input",{value:(this.title||""), onchange:function(){self.title=this.value; WM.feedManager.save();}}), //toolbox createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer"},[ this.fetchNewerButton=createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();} }) ]), createElement("div",{className:"littleButton", title:"Fetch Older"},[ this.fetchOlderButton=createElement("img",{className:"resourceIcon rssDownLeft"+WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();} }) ]), (this.isRemoveable)?createElement("div",{className:"littleButton oddOrange", title:"Remove Feed"},[ this.removeButtonNode=createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();} }) ]):null, (this.isRemoveable)?createElement("div",{className:"indent littleButton "+((this.isGlobal)?"oddOrange":"oddGreen"), title:((this.isGlobal)?"Disable Profile Sharing":"Share With Other Profiles")},[ this.toggleGlobalButton=createElement("img",{className:"resourceIcon "+((this.isGlobal)?"removeGlobal":"addGlobal")+WM.opts.littleButtonSize,onclick:function(){self.isGlobal=!self.isGlobal; WM.feedManager.save();}}) ]):null, ]), this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[ (this.isRemoveable)?createElement("div",{className:"line"},[ createElement("label",{textContent:"Target FB Entity: ",title:"The request address from where WM gets posts for this fb entity."}), this.idNode=createElement("input",{value:(this.id||""), onchange:function(){ self.id=this.value; self.url="https://graph.facebook.com/"+this.value+"/feed"; self.urlNode.textContent=self.url; WM.feedManager.save(); }}), createElement("label",{textContent:"URL: ",title:"The request address from where WM gets posts for this fb entity."}), this.urlNode=createElement("span",{textContent:this.url}), ]):null, //app filters sub box createElement("div",{className:"line"},[ createElement("label",{textContent:"App Filters: ",title:"This is a list of filters run on this feed by apps you collect for. Only filters for sidekick-supported apps are used."}), this.filtersNode=createElement("div",{className:"subsection"}), ]), ]), ]) ); }catch(e){log("WM.Feed.init:addManagerElement: "+e);}; //add any passed filters for (var f in newFilters){ this.addFilter(newFilters[f]); } return self; }catch(e){log("WM.Feed.init: "+e);}}; })();