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

이 스크립트는 직접 설치해서 쓰는 게 아닙니다. 다른 스크립트가 메타 명령 // @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. })();