PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/media/js/newsblur/models/folders.js

https://github.com/samuelclay/NewsBlur
JavaScript | 379 lines | 323 code | 52 blank | 4 comment | 91 complexity | a51e8f1faa626824cb4a384c425b70b8 MD5 | raw file
Possible License(s): MIT, GPL-2.0, BSD-3-Clause
  1. NEWSBLUR.Models.FeedOrFolder = Backbone.Model.extend({
  2. initialize: function(model) {
  3. if (_.isNumber(model) || model['feed_id']) {
  4. this.feed = NEWSBLUR.assets.feeds.get(model['feed_id'] || model);
  5. // The feed needs to exists as a model as well. Otherwise, toss it.
  6. if (this.feed) {
  7. this.set('is_feed', true);
  8. }
  9. } else if (model && model.fake) {
  10. this.folders = model.folders;
  11. } else if (model) {
  12. var title = _.keys(model)[0];
  13. var children = model[title];
  14. this.set('is_folder', true);
  15. this.set('folder_title', title);
  16. this.folder_views = [];
  17. this.folders = new NEWSBLUR.Collections.Folders([], {
  18. title: title,
  19. parent_folder: this.collection,
  20. parse: true
  21. });
  22. this.folders.reset(_.compact(children), {parse: true});
  23. }
  24. },
  25. parse: function(attrs) {
  26. if (_.isNumber(attrs)) {
  27. attrs = {'feed_id': attrs};
  28. }
  29. return attrs;
  30. },
  31. is_feed: function() {
  32. return !!this.get('is_feed');
  33. },
  34. is_folder: function() {
  35. return !!this.get('is_folder');
  36. },
  37. get_view: function($folder) {
  38. var view = _.detect(this.folder_views, function(view) {
  39. if ($folder) {
  40. return view.el == $folder.get(0);
  41. }
  42. });
  43. if (!view && this.folders.length) {
  44. view = this.folders.get_view($folder);
  45. }
  46. return view;
  47. },
  48. feed_ids_in_folder: function(include_inactive) {
  49. if (this.is_feed() && (include_inactive || (!include_inactive && this.feed.get('active')))) {
  50. return this.feed.id;
  51. } else if (this.is_folder()) {
  52. return this.folders.feed_ids_in_folder(include_inactive);
  53. }
  54. },
  55. feeds_with_unreads: function(options) {
  56. if (this.is_feed()) {
  57. return this.feed.has_unreads(options) && this.feed;
  58. } else if (this.is_folder()) {
  59. return this.folders.feeds_with_unreads(options);
  60. }
  61. },
  62. move_to_folder: function(to_folder, options) {
  63. options = options || {};
  64. var view = options.view || this.get_view();
  65. var in_folder = this.collection.options.title;
  66. var folder_title = this.get('folder_title');
  67. if (in_folder == to_folder) return false;
  68. NEWSBLUR.reader.flags['reloading_feeds'] = true;
  69. NEWSBLUR.assets.move_folder_to_folder(folder_title, in_folder, to_folder, function() {
  70. NEWSBLUR.reader.flags['reloading_feeds'] = false;
  71. _.delay(function() {
  72. NEWSBLUR.reader.$s.$feed_list.css('opacity', 1).animate({'opacity': 0}, {
  73. 'duration': 100,
  74. 'complete': function() {
  75. NEWSBLUR.app.feed_list.make_feeds();
  76. }
  77. });
  78. }, 250);
  79. });
  80. return true;
  81. },
  82. rename: function(new_folder_name) {
  83. var folder_title = this.get('folder_title');
  84. var in_folder = this.collection.options.title;
  85. NEWSBLUR.assets.rename_folder(folder_title, new_folder_name, in_folder);
  86. this.set('folder_title', new_folder_name);
  87. },
  88. delete_folder: function() {
  89. var folder_title = this.get('folder_title');
  90. var in_folder = this.collection.options.title;
  91. var feed_ids_in_folder = this.feed_ids_in_folder();
  92. NEWSBLUR.reader.flags['reloading_feeds'] = true;
  93. NEWSBLUR.assets.delete_folder(folder_title, in_folder, feed_ids_in_folder, function() {
  94. NEWSBLUR.reader.flags['reloading_feeds'] = false;
  95. });
  96. this.trigger('delete');
  97. },
  98. has_unreads: function(options) {
  99. options = options || {};
  100. if (options.include_selected && this.get('selected')) {
  101. return true;
  102. }
  103. return this.folders.has_unreads(options);
  104. },
  105. view_setting: function(setting) {
  106. if (this.is_folder()) {
  107. return NEWSBLUR.assets.view_setting('river:' + this.get('folder_title'), setting);
  108. } else {
  109. return NEWSBLUR.assets.view_setting(this.id, setting);
  110. }
  111. }
  112. });
  113. NEWSBLUR.Collections.Folders = Backbone.Collection.extend({
  114. options: {
  115. title: ''
  116. },
  117. initialize: function(models, options) {
  118. _.bindAll(this, 'propagate_feed_selected');
  119. this.options = _.extend({}, this.options, options);
  120. this.parent_folder = options && options.parent_folder;
  121. this.comparator = NEWSBLUR.Collections.Folders.comparator;
  122. this.bind('change:feed_selected', this.propagate_feed_selected);
  123. this.bind('change:counts', this.propagate_change_counts);
  124. this.bind('reset', this.reset_folder_views);
  125. },
  126. model: NEWSBLUR.Models.FeedOrFolder,
  127. reset_folder_views: function() {
  128. this.each(function(item) {
  129. if (item.is_feed()) {
  130. item.feed.views = [];
  131. item.feed.folders = [];
  132. }
  133. });
  134. },
  135. folders: function() {
  136. return this.select(function(item) {
  137. return item.is_folder();
  138. });
  139. },
  140. find_folder: function(folder_name) {
  141. var found_folder;
  142. this.any(function(folder) {
  143. if (folder.is_folder()) {
  144. if (folder.get('folder_title').toLowerCase() == folder_name ||
  145. folder.get('folder_title').toLowerCase().replace(/-/g, ' ') == folder_name) {
  146. found_folder = folder;
  147. return found_folder;
  148. }
  149. found_folder = folder.folders.find_folder(folder_name);
  150. return found_folder;
  151. }
  152. });
  153. return found_folder;
  154. },
  155. get_view: function($folder) {
  156. var view;
  157. this.any(function(item) {
  158. if (item.is_folder()) {
  159. view = item.get_view($folder);
  160. return view;
  161. }
  162. });
  163. if (view) {
  164. return view;
  165. }
  166. },
  167. child_folder_names: function() {
  168. var names = [];
  169. this.each(function(item) {
  170. if (item.is_folder()) {
  171. names.push(item.get('folder_title'));
  172. _.each(item.folders.child_folder_names(), function(name) {
  173. names.push(name);
  174. });
  175. }
  176. });
  177. return names;
  178. },
  179. parent_folder_names: function() {
  180. var names = [this.options.title];
  181. if (this.parent_folder) {
  182. var parents = _.compact(_.flatten(this.parent_folder.parent_folder_names()));
  183. names = names.concat(parents);
  184. }
  185. return names;
  186. },
  187. feed_ids_in_folder: function(include_inactive) {
  188. return _.compact(_.flatten(this.map(function(item) {
  189. return item.feed_ids_in_folder(include_inactive);
  190. })));
  191. },
  192. feeds_with_unreads: function(options) {
  193. options = options || {};
  194. return _.compact(_.flatten(this.map(function(item) {
  195. return item.feeds_with_unreads(options);
  196. })));
  197. },
  198. selected: function() {
  199. var selected_folder;
  200. this.any(function(folder) {
  201. if (folder.is_folder()) {
  202. if (folder.get('selected')) {
  203. selected_folder = folder;
  204. return selected_folder;
  205. }
  206. selected_folder = folder.folders.selected();
  207. return selected_folder;
  208. }
  209. });
  210. return selected_folder;
  211. },
  212. deselect: function() {
  213. this.each(function(item) {
  214. if (item.is_folder()) {
  215. item.set('selected', false);
  216. item.folders.deselect();
  217. }
  218. });
  219. },
  220. unread_counts: function(sum_total, seen_feeds) {
  221. if (!seen_feeds) seen_feeds = [];
  222. var counts = this.reduce(function(counts, item) {
  223. if (item.is_feed() && !_.contains(seen_feeds, item.feed.id) && item.feed.get('active')) {
  224. var feed_counts = item.feed.unread_counts();
  225. counts['ps'] += feed_counts['ps'];
  226. counts['nt'] += feed_counts['nt'];
  227. counts['ng'] += feed_counts['ng'];
  228. seen_feeds.push(item.feed.id);
  229. } else if (item.is_folder()) {
  230. var folder_counts = item.folders.unread_counts(false, seen_feeds);
  231. counts['ps'] += folder_counts['ps'];
  232. counts['nt'] += folder_counts['nt'];
  233. counts['ng'] += folder_counts['ng'];
  234. }
  235. return counts;
  236. }, {
  237. ps: 0,
  238. nt: 0,
  239. ng: 0
  240. });
  241. this.counts = counts;
  242. if (sum_total) {
  243. var unread_view = NEWSBLUR.reader.get_unread_view_name();
  244. if (unread_view == 'positive') return counts['ps'];
  245. if (unread_view == 'neutral') return counts['ps'] + counts['nt'];
  246. if (unread_view == 'negative') return counts['ps'] + counts['nt'] + counts['ng'];
  247. }
  248. return counts;
  249. },
  250. has_unreads: function(options) {
  251. options = options || {};
  252. return this.any(function(item) {
  253. if (item.is_feed()) {
  254. return item.feed.has_unreads(options);
  255. } else if (item.is_folder()) {
  256. return item.has_unreads(options);
  257. }
  258. });
  259. },
  260. propagate_feed_selected: function() {
  261. if (this.parent_folder) {
  262. this.parent_folder.trigger('change:feed_selected');
  263. }
  264. },
  265. propagate_change_counts: function() {
  266. if (this.parent_folder) {
  267. this.parent_folder.trigger('change:counts');
  268. }
  269. },
  270. update_all_folder_visibility: function() {
  271. this.each(function(item) {
  272. if (item.is_folder()) {
  273. item.folders.trigger('change:counts');
  274. item.folders.update_all_folder_visibility();
  275. }
  276. });
  277. },
  278. view_setting: function(setting) {
  279. return NEWSBLUR.assets.view_setting('river:' + (this.get('folder_title') || ''), setting);
  280. }
  281. }, {
  282. comparator: function(modelA, modelB) {
  283. // toUpperCase for historical reasons
  284. var sort_order = NEWSBLUR.assets.preference('feed_order').toUpperCase();
  285. if (NEWSBLUR.Collections.Folders.organizer_sortorder) {
  286. sort_order = NEWSBLUR.Collections.Folders.organizer_sortorder.toUpperCase();
  287. }
  288. var high = 1;
  289. var low = -1;
  290. if (NEWSBLUR.Collections.Folders.organizer_inversesort) {
  291. high = -1;
  292. low = 1;
  293. }
  294. if (modelA.is_feed() != modelB.is_feed()) {
  295. // Feeds above folders
  296. return modelA.is_feed() ? -1 : 1;
  297. }
  298. if (modelA.is_folder() && modelB.is_folder()) {
  299. // Folders are alphabetical
  300. return modelA.get('folder_title').toLowerCase() > modelB.get('folder_title').toLowerCase() ? 1 : -1;
  301. }
  302. var feedA = modelA.feed;
  303. var feedB = modelB.feed;
  304. if (!feedA || !feedB) {
  305. return !feedA ? 1 : -1;
  306. }
  307. if (sort_order == 'ALPHABETICAL' || !sort_order) {
  308. return feedA.get('feed_title').toLowerCase() > feedB.get('feed_title').toLowerCase() ? high : low;
  309. } else if (sort_order == 'MOSTUSED') {
  310. return feedA.get('feed_opens') < feedB.get('feed_opens') ? high :
  311. (feedA.get('feed_opens') > feedB.get('feed_opens') ? low :
  312. (feedA.get('feed_title').toLowerCase() > feedB.get('feed_title').toLowerCase() ? high : low));
  313. } else if (sort_order == 'RECENCY') {
  314. return feedA.get('last_story_seconds_ago') < feedB.get('last_story_seconds_ago') ? high :
  315. (feedA.get('last_story_seconds_ago') > feedB.get('last_story_seconds_ago') ? low :
  316. (feedA.get('feed_title').toLowerCase() > feedB.get('feed_title').toLowerCase() ? high : low));
  317. } else if (sort_order == 'FREQUENCY') {
  318. return feedA.get('average_stories_per_month') < feedB.get('average_stories_per_month') ? high :
  319. (feedA.get('average_stories_per_month') > feedB.get('average_stories_per_month') ? low :
  320. (feedA.get('feed_title').toLowerCase() > feedB.get('feed_title').toLowerCase() ? high : low));
  321. } else if (sort_order == 'SUBSCRIBERS') {
  322. return feedA.get('num_subscribers') < feedB.get('num_subscribers') ? high :
  323. (feedA.get('num_subscribers') > feedB.get('num_subscribers') ? low :
  324. (feedA.get('feed_title').toLowerCase() > feedB.get('feed_title').toLowerCase() ? high : low));
  325. }
  326. }
  327. });