Greasy Fork is available in English.

WM Feed Manager Objects

This is the host object which is created under the WM version 4.x script

Dette script bør ikke installeres direkte. Det er et bibliotek, som andre scripts kan inkludere med metadirektivet // @require https://update.greatest.deepsurf.us/scripts/6898/28425/WM%20Feed%20Manager%20Objects.js

  1. // ==UserScript==
  2. // @name WM Feed Manager Objects
  3. // @namespace MerricksdadWMFeedManagerObjects
  4. // @description This is the host object which is created under the WM version 4.x script
  5. // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
  6. // @version 4.0.0.4
  7. // @copyright Charlie Ewing except where noted
  8. // ==/UserScript==
  9.  
  10. //this script requires some functions in the WM Common Library
  11. //this script needs access to a pre-defined JSON object
  12.  
  13.  
  14. (function(){
  15.  
  16. //***************************************************************************************************************************************
  17. //***** Feed Objects
  18. //***************************************************************************************************************************************
  19. WM.feedManager = {
  20. feeds : [],
  21. init : function(){
  22. var feedsIn=(getOptJSON("feeds3_"+WM.currentUser.profile)||[]);
  23. var feedsInGlobal=(getOptJSON("feeds3_global")||[]);
  24. if (isArrayAndNotEmpty(feedsIn)) {
  25. //import feeds from storage
  26. for (var f=0;f<feedsIn.length;f++){
  27. feed=feedsIn[f];
  28. if (!feed.isGlobal){
  29. WM.feedManager.feeds.push(new WM.Feed(feed));
  30. } else {
  31. var glob=feedsInGlobal[feed.uniqueID]||null;
  32. if (glob){
  33. var merge=mergeJSON(glob,feed);
  34. WM.feedManager.newFeed(merge);
  35. glob.alreadyUsed=true;
  36. } else {
  37. log("WM.feedManager.init: Global feed missing, cannot merge");
  38. }
  39. }
  40. }
  41. } else {
  42. //never been used before, create base feeds
  43. WM.feedManager.feeds.push(new WM.Feed({title:"My Home Feed", url:"https://graph.facebook.com/me/home", isRemoveable:false}));
  44. //WM.feedManager.feeds.push(new WM.Feed({title:"My Profile Wall", url:"https://graph.facebook.com/me/feed", isRemoveable:false}));
  45. //import oldstyle feeds
  46. var feedsOld=getOpt("feeds_"+WM.currentUser.profile);
  47. if (feedsOld){
  48. feedsOld=feedsOld.split("\n");
  49. if (isArrayAndNotEmpty(feedsOld)) for (var f=0;f<feedsOld.length;f++) {
  50. //prevent empties
  51. if (feedsOld[f]) {
  52. //create the new feed
  53. WM.feedManager.newFeed({id:feedsOld[f],title:feedsOld[f]});
  54. }
  55. }
  56. }
  57. WM.feedManager.save();
  58. }
  59. //import all global feeds not already accounted for
  60. for (var t in feedsInGlobal) {
  61. var glob=feedsInGlobal[t];
  62. if (!glob.alreadyUsed){
  63. glob.uniqueID=t;
  64. glob.isGlobal=true;
  65. WM.feedManager.newFeed(glob); //newFeed adds app filters, where New Feed() does not
  66. }
  67. }
  68. },
  69. newFeed : function(params){
  70. params=params||{};
  71. var feed = new WM.Feed(params);
  72. WM.feedManager.feeds.push(feed);
  73. //add filters for each app available
  74. for (var a in WM.apps){
  75. feed.addFilter({id:"app_"+a});
  76. }
  77. },
  78. save :function(){
  79. var retFeeds=[];
  80. var retGlobal={};
  81. if (isArrayAndNotEmpty(WM.feedManager.feeds)) for (var f=0,len=WM.feedManager.feeds.length; f<len; f++){
  82. var feed=WM.feedManager.feeds[f];
  83. if (!feed.isGlobal) {
  84. retFeeds.push(feed.saveableData);
  85. } else {
  86. retFeeds.push({isGlobal:true, uniqueID:feed.uniqueID, enabled:feed.enabled, expanded:feed.expanded});
  87. var glob=feed.saveableData;
  88. glob.uniqueID=feed.uniqueID;
  89. retGlobal[feed.uniqueID]=glob;
  90. }
  91. }
  92. setOptJSON("feeds3_"+WM.currentUser.profile,retFeeds);
  93. setOptJSON("feeds3_global",retGlobal);
  94. },
  95. };
  96.  
  97. //***************************************************************************************************************************************
  98. //***** FeedFilter Class
  99. //***************************************************************************************************************************************
  100. WM.FeedFilter = function(params){try{
  101. this.objType="feedFilter";
  102. params=params||{};
  103. var self=this;
  104. //set defaults
  105. this.enabled=true;
  106. this.expanded=true;
  107. this._olderLimitReached=false;
  108. //initialize edges to the collector startup time
  109. this.oldedge=Math.round(timeStamp()/1000); //older edge timestamp
  110. this.newedge=Math.round(timeStamp()/1000); //newer edge timestamp
  111. //use passed params
  112. for (var p in params) this[p]=params[p];
  113.  
  114. this.enable=function(){try{
  115. this.enabled=true;
  116. this.node.className=this.node.className.removeWord("disabled");
  117. WM.feedManager.save();
  118. }catch(e){log("WM.FeedFilter.enable: "+e);}};
  119.  
  120. this.disable=function(){try{
  121. this.enabled=false;
  122. this.node.className=this.node.className.addWord("disabled");
  123. WM.feedManager.save();
  124. }catch(e){log("WM.FeedFilter.disable: "+e);}};
  125.  
  126. this.toggle=function(){try{
  127. this.enabled=this.toggleNode.checked;
  128. this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled");
  129. WM.feedManager.save();
  130. }catch(e){log("WM.FeedFilter.toggle: "+e);}};
  131.  
  132. this.toggleContent=function(){try{
  133. this.expanded=!this.expanded;
  134. var btnSize=WM.opts.littleButtonSize;
  135. with (this.contentNode)
  136. className=className.swapWordB(this.expanded,"expanded","collapsed");
  137. with (this.toggleImgNode)
  138. className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
  139. WM.feedManager.save();
  140. }catch(e){log("WM.FeedFilter.toggleContent: "+e);}};
  141. //remove this
  142. this.remove=function(){try{
  143. if (this.node) remove(this.node);
  144. if (this.parent) delete this.parent.filters[this.id];
  145. WM.feedManager.save();
  146. }catch(e){log("WM.FeedFilter.remove: "+e);}};
  147. //fetch posts for this
  148. this.fetchNewer=function(){try{
  149. WM.fetch({
  150. newer:true,
  151. apps:WM.apps[this.appID],
  152. feeds:[this.parent],
  153. bypassPause:true,
  154. bypassAppDisabled:true,
  155. bypassFeedDisabled:true,
  156. bypassFilterDisabled:true,
  157. });
  158. }catch(e){log("WM.FeedFilter.fetchNewer: "+e);}};
  159.  
  160. this.fetchOlder=function(){try{
  161. WM.fetch({
  162. older:true,
  163. apps:WM.apps[this.appID],
  164. feeds:[this.parent],
  165. bypassPause:true,
  166. bypassAppDisabled:true,
  167. bypassFeedDisabled:true,
  168. bypassFilterDisabled:true,
  169. });
  170. }catch(e){log("WM.FeedFilter.fetchOlder: "+e);}};
  171. this.__defineGetter__("olderLimitReached",function(){try{
  172. return this._olderLimitReached;
  173. }catch(e){log("WM.FeedFilter.olderLimitReached: "+e);}});
  174. this.__defineSetter__("olderLimitReached",function(v){try{
  175. this._olderLimitReached=v;
  176. //update the sidekick page button graphics
  177. var node=this.olderLimitNode;
  178. if (node) node.textContent=v;
  179. if (v) {
  180. WM.rulesManager.doEvent("onFeedFilterOlderLimitReached",this);
  181. }
  182. }catch(e){log("WM.FeedFilter.olderLimitReached: "+e);}});
  183.  
  184. this.__defineGetter__("appID",function(){try{
  185. //this assumes its an app filter because so far thats all we use
  186. return this.id.removePrefix("app_");
  187. }catch(e){log("WM.FeedFilter.appID: "+e);}});
  188.  
  189. this.__defineGetter__("appName",function(){try{
  190. //this assumes its an app filter because so far thats all we use
  191. //it also assumes app objects are global, which they are
  192. //but that they are also available and already filled in by the sidekick, which they may not be
  193. var a = WM.apps[this.appID];
  194. if (a!=undefined) {
  195. //debug.print([this.appID,a]);
  196. return a.name;
  197. }
  198. return "";
  199. }catch(e){log("WM.FeedFilter.appName: "+e);}});
  200. //draw it
  201. try{
  202. if (this.parent.filtersNode) this.parent.filtersNode.appendChild(
  203. this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
  204. createElement("div",{className:"line"},[
  205. createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
  206. this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
  207. ]),
  208. this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
  209. self.enabled=this.checked;
  210. with (self.node) className=className.toggleWordB(!this.checked,"disabled");
  211. WM.feedManager.save();
  212. }}),
  213. createElement("span",{textContent:this.appName + " (" + this.id + ")"}),
  214. //toolbox
  215. /*createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer"},[
  216. this.fetchNewerButton=createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();} })
  217. ]),
  218. createElement("div",{className:"littleButton", title:"Fetch Older"},[
  219. this.fetchOlderButton=createElement("img",{className:"resourceIcon rssDownLeft"+WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();} })
  220. ]),*/
  221. ]),
  222. this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
  223. createElement("div",{className:"line"},[
  224. createElement("label",{textContent:"Older Limit Reached: ",title:"Reports if this filter has reached the user defined oldest post limit."}),
  225. this.olderLimitNode=createElement("span",{textContent:this.olderLimitReached}),
  226. ]),
  227. createElement("div",{className:"line"},[
  228. createElement("label",{textContent:"Newer Edge: ",title:"A Unixtime indicator of the newest post-time you have fetched for this filter."}),
  229. this.newedgeNode=createElement("span",{textContent:this.newedge}),
  230. ]),
  231. createElement("div",{className:"line"},[
  232. createElement("label",{textContent:"Older Edge: ",title:"A Unixtime indicator of the oldest post-time you have fetched for this filter."}),
  233. this.oldedgeNode=createElement("span",{textContent:this.oldedge}),
  234. ]),
  235. ]),
  236. ])
  237. );
  238. }catch(e){log("WM.FeedFilter.init:addManagerElement: "+e);};
  239. return self;
  240. }catch(e){log("WM.FeedFilter.init: "+e);}};
  241.  
  242. //***************************************************************************************************************************************
  243. //***** Feed Class
  244. //***************************************************************************************************************************************
  245. WM.Feed = function(params){try{
  246. this.objType="feed";
  247. params=params||{};
  248. var self=this;
  249.  
  250. //set defaults
  251. this.enabled=true;
  252. this.expanded=true;
  253. this.url="";
  254. this.id="";
  255. this.filters={};
  256. this.feedName="";
  257. this.isRemoveable=true; //set to false on own feeds
  258. this.title="New Feed";
  259. this._isGlobal=false;
  260.  
  261. //use passed params
  262. var newFilters=params.filters||{};
  263. delete params.filters;
  264. this.__defineGetter__("isGlobal",function(){try{
  265. return this._isGlobal;
  266. }catch(e){log("WM.Feed.isGlobal: "+e);}});
  267. this.__defineSetter__("isGlobal",function(v){try{
  268. if (!v) {
  269. 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;
  270. }
  271. this._isGlobal=v;
  272. //make sure we have a uniqueID
  273. //but don't destroy one that already exists
  274. if (v && !exists(this.uniqueID)) this.uniqueID = unique();
  275. //change the color/icon of the isGlobal button
  276. if (this.toggleGlobalButton) {
  277. var s=WM.opts.littleButtonSize;
  278. with (this.toggleGlobalButton) className=className.swapWordB(v,"removeGlobal"+s,"addGlobal"+s);
  279. with (this.toggleGlobalButton.parentNode) {
  280. className=className.swapWordB(v,"oddOrange","oddGreen");
  281. title=(v)?"Disable Profile Sharing":"Share With Other Profiles";
  282. }
  283. }
  284. }catch(e){log("WM.Feed.isGlobal: "+e);}});
  285. this.__defineGetter__("saveableData",function(){try{
  286. var ret={};
  287. ret.title=this.title;
  288. ret.enabled=this.enabled;
  289. ret.expanded=this.expanded;
  290. ret.isRemoveable=this.isRemoveable;
  291. ret.url=this.url;
  292. if (this.isRemoveable) ret.id=this.id;
  293. //capture filters
  294. ret.filters={};
  295. for (var f in this.filters) {
  296. ret.filters[f]={
  297. enabled:this.filters[f].enabled,
  298. expanded:this.filters[f].expanded,
  299. id:this.filters[f].id,
  300. };
  301. }
  302. return ret;
  303. }catch(e){log("WM.Feed.saveableData: "+e);}});
  304. for (var p in params) this[p]=params[p];
  305.  
  306. this.enable=function(){try{
  307. this.enabled=true;
  308. this.node.className=this.node.className.removeWord("disabled");
  309. WM.feedManager.save();
  310. }catch(e){log("WM.Feed.enable: "+e);}};
  311.  
  312. this.disable=function(){try{
  313. this.enabled=false;
  314. this.node.className=this.node.className.addWord("disabled");
  315. WM.feedManager.save();
  316. }catch(e){log("WM.Feed.disable: "+e);}};
  317.  
  318. this.toggle=function(){try{
  319. this.enabled=this.toggleNode.checked;
  320. this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled");
  321. WM.feedManager.save();
  322. }catch(e){log("WM.Feed.toggle: "+e);}};
  323.  
  324. //create a filter for a specific app
  325. //filter id must be "app_"+appID
  326. //will not add duplicates
  327. this.addFilter=function(params){try{
  328. var isNew=!exists(params);
  329. params=params||{};
  330. params.parent=this;
  331. //prevent duplicates
  332. if (!exists(this.filters[params.id])) {
  333. return (this.filters[params.id]=new WM.FeedFilter(params));
  334. if (isNew) WM.feedManager.save();
  335. } else {
  336. return this.filters[params.id];
  337. }
  338. }catch(e){log("WM.Feed.addFilter: "+e);}};
  339.  
  340. //get the extents of the feed by merging all feed filter oldedge/newedge values
  341. this.getMergedEdges=function(params){
  342. /*
  343. apps[]: an array of appID's to test against, otherwise read from all filters
  344. */
  345. //console.log("getMergedEdges: "+JSON.stringify(params));
  346. var retval = {newedge:Math.round(timeStamp()/1000), oldedge:0};
  347. if (params.apps||null){
  348. for (var c=0,l=params.apps.length;c<l;c++){
  349. var filter = this.filters["app_"+params.apps[c]];
  350. if (filter||null){
  351. //get the youngest older edge and oldest newer edge so we don't lose posts because one feed is more active.
  352. //this forces them to run at the same edges after the first pull
  353. retval.newedge = Math.min(retval.newedge, filter.newedge);
  354. retval.oldedge = Math.max(retval.oldedge, filter.oldedge);
  355. } else {
  356. log("getMergedEdges: no filter matching app_"+params.apps[c]+" on feed " + this.id);
  357. }
  358. }
  359. } else {
  360. for (var name in this.filters){
  361. var filter = this.filters[name];
  362. if (filter||null){
  363. //get the youngest older edge and oldest newer edge so we don't lose posts because one feed is more active.
  364. //this forces them to run at the same edges after the first pull
  365. retval.newedge = Math.min(retval.newedge, filter.newedge);
  366. retval.oldedge = Math.max(retval.oldedge, filter.oldedge);
  367. } else {
  368. log("getMergedEdges: no filter matching "+name+" on feed " + this.id);
  369. }
  370. }
  371. }
  372. return retval;
  373. };
  374. //remove this
  375. this.remove=function(noConfirm){try{
  376. if (this.isRemoveable) {
  377. var ask=WM.opts.feedsConfirmDeleteFeed;
  378. 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?"))){
  379. //remove my data
  380. if (this.node) remove(this.node);
  381. WM.feedManager.feeds.removeByValue(this);
  382. WM.feedManager.save();
  383. }
  384. }
  385. }catch(e){log("WM.Feed.remove: "+e);}};
  386.  
  387. //fetch posts for this
  388. this.fetchNewer=function(){try{
  389. WM.fetch({
  390. newer:true,
  391. feeds:[self],
  392. bypassPause:true,
  393. bypassAppDisabled:true,
  394. bypassFeedDisabled:true,
  395. });
  396. }catch(e){log("WM.Feed.fetchNewer: "+e);}};
  397.  
  398. this.fetchOlder=function(){try{
  399. WM.fetch({
  400. older:true,
  401. feeds:[self],
  402. bypassPause:true,
  403. bypassAppDisabled:true,
  404. bypassFeedDisabled:true,
  405. });
  406. }catch(e){log("WM.Feed.fetchOlder: "+e);}};
  407.  
  408. this.toggleContent=function(){try{
  409. this.expanded=!this.expanded;
  410. var btnSize=WM.opts.littleButtonSize;
  411. with (this.contentNode)
  412. className=className.swapWordB(this.expanded,"expanded","collapsed");
  413. with (this.toggleImgNode)
  414. className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
  415. WM.feedManager.save();
  416. }catch(e){log("WM.Feed.toggleContent: "+e);}};
  417.  
  418. if (this.id && !this.url) this.url="https://graph.facebook.com/"+this.id+"/feed";
  419. //draw it
  420. try{
  421. WM.console.feedManagerNode.appendChild(
  422. this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
  423. createElement("div",{className:"line"},[
  424. createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
  425. this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
  426. ]),
  427. this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
  428. self.enabled=this.checked;
  429. with (self.node) className=className.toggleWordB(!this.checked,"disabled");
  430. WM.feedManager.save();
  431. }}),
  432. this.titleNode=createElement("input",{value:(this.title||""), onchange:function(){self.title=this.value; WM.feedManager.save();}}),
  433. //toolbox
  434. /*createElement("div",{className:"littleButton oddBlue", title:"Fetch Newer"},[
  435. this.fetchNewerButton=createElement("img",{className:"resourceIcon rssUpRight"+WM.opts.littleButtonSize,onclick:function(){self.fetchNewer();} })
  436. ]),
  437. createElement("div",{className:"littleButton", title:"Fetch Older"},[
  438. this.fetchOlderButton=createElement("img",{className:"resourceIcon rssDownLeft"+WM.opts.littleButtonSize,onclick:function(){self.fetchOlder();} })
  439. ]),*/
  440. (this.isRemoveable)?createElement("div",{className:"littleButton oddOrange", title:"Remove Feed"},[
  441. this.removeButtonNode=createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();} })
  442. ]):null,
  443. (this.isRemoveable)?createElement("div",{className:"indent littleButton "+((this.isGlobal)?"oddOrange":"oddGreen"), title:((this.isGlobal)?"Disable Profile Sharing":"Share With Other Profiles")},[
  444. this.toggleGlobalButton=createElement("img",{className:"resourceIcon "+((this.isGlobal)?"removeGlobal":"addGlobal")+WM.opts.littleButtonSize,onclick:function(){self.isGlobal=!self.isGlobal; WM.feedManager.save();}})
  445. ]):null,
  446.  
  447. ]),
  448. this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
  449. (this.isRemoveable)?createElement("div",{className:"line"},[
  450. createElement("label",{textContent:"Target FB Entity: ",title:"The request address from where WM gets posts for this fb entity."}),
  451. this.idNode=createElement("input",{value:(this.id||""), onchange:function(){
  452. self.id=this.value;
  453. self.url="https://graph.facebook.com/"+this.value+"/feed";
  454. self.urlNode.textContent=self.url;
  455. WM.feedManager.save();
  456. }}),
  457. createElement("label",{textContent:"URL: ",title:"The request address from where WM gets posts for this fb entity."}),
  458. this.urlNode=createElement("span",{textContent:this.url}),
  459. ]):null,
  460. //app filters sub box
  461. createElement("div",{className:"line"},[
  462. 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."}),
  463. this.filtersNode=createElement("div",{className:"subsection"}),
  464. ]),
  465. ]),
  466. ])
  467. );
  468. }catch(e){log("WM.Feed.init:addManagerElement: "+e);};
  469. //add any passed filters
  470. for (var f in newFilters){
  471. this.addFilter(newFilters[f]);
  472. }
  473. return self;
  474. }catch(e){log("WM.Feed.init: "+e);}};
  475.  
  476. })();