PageRenderTime 67ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 1ms

/media/js/newsblur/reader/reader.js

https://github.com/speedsticko/NewsBlur
JavaScript | 5494 lines | 4719 code | 588 blank | 187 comment | 1056 complexity | 532f19f2d3a17b9fd9db0a9280b807df MD5 | raw file
Possible License(s): MIT

Large files files are truncated, but you can click here to view the full file

  1. (function($) {
  2. NEWSBLUR.Reader = Backbone.Router.extend({
  3. init: function(options) {
  4. var defaults = {};
  5. // ===========
  6. // = Globals =
  7. // ===========
  8. NEWSBLUR.assets = new NEWSBLUR.AssetModel();
  9. this.model = NEWSBLUR.assets;
  10. this.story_view = 'page';
  11. this.options = _.extend({}, defaults, options);
  12. this.$s = {
  13. $body: $('body'),
  14. $layout: $('.NB-layout'),
  15. $sidebar: $('.NB-sidebar'),
  16. $feed_lists: $('.NB-feedlists'),
  17. $feed_list: $('#feed_list'),
  18. $social_feeds: $('.NB-socialfeeds-folder'),
  19. $story_titles: $('#story_titles'),
  20. $content_pane: $('.content-pane'),
  21. $story_taskbar: $('#story_taskbar'),
  22. $story_pane: $('#story_pane .NB-story-pane-container'),
  23. $feed_view: $('.NB-feed-story-view'),
  24. $feed_stories: $('.NB-feed-stories'),
  25. $feed_iframe: $('.NB-feed-iframe'),
  26. $story_iframe: $('.NB-story-iframe'),
  27. $intelligence_slider: $('.NB-intelligence-slider'),
  28. $mouse_indicator: $('#mouse-indicator'),
  29. $feed_link_loader: $('#NB-feeds-list-loader'),
  30. $feeds_progress: $('#NB-progress'),
  31. $dashboard: $('.NB-feeds-header-dashboard'),
  32. $river_sites_header: $('.NB-feeds-header-river-sites'),
  33. $river_blurblogs_header: $('.NB-feeds-header-river-blurblogs'),
  34. $river_global_header: $('.NB-feeds-header-river-global'),
  35. $starred_header: $('.NB-feeds-header-starred'),
  36. $tryfeed_header: $('.NB-feeds-header-tryfeed'),
  37. $taskbar: $('.taskbar_nav'),
  38. $feed_floater: $('.NB-feed-story-view-floater'),
  39. $feedbar: $('.NB-feedbar')
  40. };
  41. this.flags = {
  42. 'bouncing_callout': false,
  43. 'has_unfetched_feeds': false,
  44. 'count_unreads_after_import_working': false,
  45. 'import_from_google_reader_working': false,
  46. 'sidebar_closed': this.options.hide_sidebar
  47. };
  48. this.locks = {};
  49. this.counts = {
  50. 'page': 1,
  51. 'feature_page': 0,
  52. 'unfetched_feeds': 0,
  53. 'fetched_feeds': 0,
  54. 'page_fill_outs': 0,
  55. 'recommended_feed_page': 0,
  56. 'interactions_page': 1,
  57. 'activities_page': 1
  58. };
  59. this.cache = {
  60. 'iframe_story_positions': {},
  61. 'feed_view_story_positions': {},
  62. 'iframe_story_positions_keys': [],
  63. 'feed_view_story_positions_keys': [],
  64. 'river_feeds_with_unreads': [],
  65. '$feed_in_social_feed_list': {}
  66. };
  67. this.views = {};
  68. this.layout = {};
  69. this.constants = {
  70. FEED_REFRESH_INTERVAL: (1000 * 60) * 1, // 1 minute
  71. FILL_OUT_PAGES: 50,
  72. FIND_NEXT_UNREAD_STORY_TRIES: 12,
  73. RIVER_STORIES_FOR_STANDARD_ACCOUNT: 5,
  74. MIN_FEED_LIST_SIZE: 206
  75. };
  76. // ==================
  77. // = Event Handlers =
  78. // ==================
  79. $(window).bind('resize.reader', _.throttle($.rescope(this.resize_window, this), 1000));
  80. this.$s.$body.bind('click.reader', $.rescope(this.handle_clicks, this));
  81. this.$s.$body.bind('keyup.reader', $.rescope(this.handle_keyup, this));
  82. this.handle_keystrokes();
  83. // ============
  84. // = Bindings =
  85. // ============
  86. _.bindAll(this, 'show_stories_error');
  87. // ==================
  88. // = Initialization =
  89. // ==================
  90. var refresh_page = this.check_and_load_ssl();
  91. if (refresh_page) return;
  92. this.load_javascript_elements_on_page();
  93. this.apply_resizable_layout();
  94. this.add_body_classes();
  95. if (NEWSBLUR.Flags['start_import_from_google_reader']) {
  96. this.start_import_from_google_reader();
  97. }
  98. NEWSBLUR.app.sidebar_header = new NEWSBLUR.Views.SidebarHeader({collection: NEWSBLUR.assets.feeds});
  99. NEWSBLUR.app.sidebar = new NEWSBLUR.Views.Sidebar();
  100. NEWSBLUR.app.feed_list = new NEWSBLUR.Views.FeedList({el: this.$s.$feed_list[0]});
  101. NEWSBLUR.app.story_titles = new NEWSBLUR.Views.StoryTitlesView({collection: NEWSBLUR.assets.stories});
  102. NEWSBLUR.app.story_list = new NEWSBLUR.Views.StoryListView({collection: NEWSBLUR.assets.stories});
  103. NEWSBLUR.app.original_tab_view = new NEWSBLUR.Views.OriginalTabView({collection: NEWSBLUR.assets.stories});
  104. NEWSBLUR.app.story_tab_view = new NEWSBLUR.Views.StoryTabView({collection: NEWSBLUR.assets.stories});
  105. NEWSBLUR.app.feed_selector = new NEWSBLUR.Views.FeedSelector();
  106. NEWSBLUR.app.follow_requests_module = new NEWSBLUR.Views.FollowRequestsModule();
  107. this.load_intelligence_slider();
  108. this.handle_mouse_indicator_hover();
  109. this.position_mouse_indicator();
  110. this.handle_login_and_signup_forms();
  111. this.apply_story_styling();
  112. this.apply_tipsy_titles();
  113. this.load_recommended_feeds();
  114. this.setup_dashboard_graphs();
  115. this.setup_feedback_table();
  116. this.setup_howitworks_hovers();
  117. this.setup_interactions_module();
  118. this.setup_activities_module();
  119. this.setup_unfetched_feed_check();
  120. },
  121. // ========
  122. // = Page =
  123. // ========
  124. check_and_load_ssl: function() {
  125. if (window.location.protocol == 'http:' && this.model.preference('ssl')) {
  126. window.location.href = window.location.href.replace('http:', 'https:');
  127. return true;
  128. }
  129. },
  130. load_javascript_elements_on_page: function() {
  131. $('.NB-javascript').removeClass('NB-javascript');
  132. },
  133. resize_window: function() {
  134. var flag;
  135. var view = this.story_view;
  136. if (this.flags['page_view_showing_feed_view']) {
  137. view = 'feed';
  138. flag = 'page';
  139. } else if (this.flags['feed_view_showing_story_view']) {
  140. view = 'story';
  141. flag = 'story';
  142. }
  143. this.flags.scrolling_by_selecting_story_title = true;
  144. clearTimeout(this.locks.scrolling);
  145. this.locks.scrolling = _.delay(_.bind(function() {
  146. this.flags.scrolling_by_selecting_story_title = false;
  147. }, this), 1000);
  148. this.make_story_titles_pane_counter();
  149. this.position_mouse_indicator();
  150. this.switch_taskbar_view(view, {
  151. skip_save_type: flag,
  152. resize: true
  153. });
  154. NEWSBLUR.app.story_titles.fill_out();
  155. this.flags.fetch_story_locations_in_feed_view = this.flags.fetch_story_locations_in_feed_view ||
  156. _.throttle(function() {
  157. NEWSBLUR.app.story_list.reset_story_positions();
  158. }, 2000);
  159. this.flags.fetch_story_locations_in_feed_view();
  160. if ((NEWSBLUR.reader.layout.contentLayout.panes.north &&
  161. NEWSBLUR.reader.layout.contentLayout.panes.north.width() < 600) ||
  162. (NEWSBLUR.reader.layout.contentLayout.panes.center &&
  163. NEWSBLUR.reader.layout.contentLayout.panes.center.width() < 700)) {
  164. this.$s.$feed_view.addClass('NB-feed-story-view-narrow');
  165. } else {
  166. this.$s.$feed_view.removeClass('NB-feed-story-view-narrow');
  167. }
  168. },
  169. apply_resizable_layout: function(refresh) {
  170. var story_anchor = this.model.preference('story_pane_anchor');
  171. var right_pane_hidden = !$('.right-pane').is(':visible');
  172. if (refresh) {
  173. this.layout.contentLayout && this.layout.contentLayout.destroy();
  174. this.layout.rightLayout && this.layout.rightLayout.destroy();
  175. this.layout.leftCenterLayout && this.layout.leftCenterLayout.destroy();
  176. this.layout.leftLayout && this.layout.leftLayout.destroy();
  177. this.layout.outerLayout && this.layout.outerLayout.destroy();
  178. var feed_stories_bin = $.make('div').append(this.$s.$feed_stories.children());
  179. var story_titles_bin = $.make('div').append(this.$s.$story_titles.children());
  180. }
  181. $('.right-pane').removeClass('NB-story-pane-west')
  182. .removeClass('NB-story-pane-north')
  183. .removeClass('NB-story-pane-south')
  184. .addClass('NB-story-pane-'+story_anchor);
  185. this.layout.outerLayout = this.$s.$layout.layout({
  186. zIndex: 2,
  187. fxName: "slideOffscreen",
  188. fxSettings: { duration: 560, easing: "easeInOutQuint" },
  189. center__paneSelector: ".right-pane",
  190. west__paneSelector: ".left-pane",
  191. west__size: this.model.preference('feed_pane_size'),
  192. west__minSize: this.constants.MIN_FEED_LIST_SIZE,
  193. west__onresize_end: $.rescope(this.save_feed_pane_size, this),
  194. // west__initHidden: this.options.hide_sidebar,
  195. west__spacing_open: this.options.hide_sidebar ? 1 : 6,
  196. resizerDragOpacity: 0.6,
  197. resizeWhileDragging: true,
  198. enableCursorHotkey: false
  199. });
  200. if (this.model.preference('feed_pane_size') < 240) {
  201. this.layout.outerLayout.resizeAll();
  202. }
  203. this.layout.leftLayout = $('.left-pane').layout({
  204. closable: false,
  205. resizeWhileDragging: true,
  206. fxName: "slideOffscreen",
  207. fxSettings: { duration: 560, easing: "easeInOutQuint" },
  208. animatePaneSizing: true,
  209. north__paneSelector: ".left-north",
  210. north__size: 18,
  211. north__resizeable: false,
  212. north__spacing_open: 0,
  213. center__paneSelector: ".left-center",
  214. center__resizable: false,
  215. south__paneSelector: ".left-south",
  216. south__size: 31,
  217. south__resizable: false,
  218. south__spacing_open: 0,
  219. enableCursorHotkey: false
  220. });
  221. this.layout.leftCenterLayout = $('.left-center').layout({
  222. closable: false,
  223. slidable: false,
  224. resizeWhileDragging: true,
  225. center__paneSelector: ".left-center-content",
  226. center__resizable: false,
  227. south__paneSelector: ".left-center-footer",
  228. south__size: 'auto',
  229. south__resizable: false,
  230. south__slidable: true,
  231. south__spacing_open: 0,
  232. south__spacing_closed: 0,
  233. south__closable: true,
  234. south__initClosed: true,
  235. fxName: "slideOffscreen",
  236. fxSettings: { duration: 560, easing: "easeInOutQuint" },
  237. enableCursorHotkey: false
  238. });
  239. var rightLayoutOptions = {
  240. resizeWhileDragging: true,
  241. center__paneSelector: ".content-pane",
  242. spacing_open: story_anchor == 'west' ? 4 : 10,
  243. resizerDragOpacity: 0.6,
  244. enableCursorHotkey: false,
  245. fxName: "slideOffscreen",
  246. fxSettings: { duration: 560, easing: "easeInOutQuint" }
  247. };
  248. rightLayoutOptions[story_anchor+'__paneSelector'] = '.right-north';
  249. rightLayoutOptions[story_anchor+'__size'] = this.model.preference('story_titles_pane_size');
  250. rightLayoutOptions[story_anchor+'__onresize_end'] = $.rescope(this.save_story_titles_pane_size, this);
  251. rightLayoutOptions[story_anchor+'__onclose_start'] = $.rescope(this.toggle_story_titles_pane, this);
  252. rightLayoutOptions[story_anchor+'__onopen_start'] = $.rescope(this.toggle_story_titles_pane, this);
  253. this.layout.rightLayout = $('.right-pane').layout(rightLayoutOptions);
  254. var contentLayoutOptions = {
  255. resizeWhileDragging: true,
  256. center__paneSelector: ".content-center",
  257. spacing_open: 0,
  258. resizerDragOpacity: 0.6,
  259. enableCursorHotkey: false
  260. };
  261. if (story_anchor == 'west') {
  262. contentLayoutOptions['north__paneSelector'] = '.content-north';
  263. contentLayoutOptions['north__size'] = 30;
  264. } else {
  265. contentLayoutOptions[story_anchor+'__paneSelector'] = '.content-north';
  266. contentLayoutOptions[story_anchor+'__size'] = 30;
  267. }
  268. this.layout.contentLayout = this.$s.$content_pane.layout(contentLayoutOptions);
  269. if (!refresh) {
  270. $('.right-pane').hide();
  271. } else {
  272. this.$s.$feed_stories.append(feed_stories_bin.children());
  273. this.$s.$story_titles.append(story_titles_bin.children());
  274. this.resize_window();
  275. if (right_pane_hidden) {
  276. $('.right-pane').hide();
  277. }
  278. }
  279. },
  280. apply_tipsy_titles: function() {
  281. if (this.model.preference('show_tooltips')) {
  282. $('.NB-taskbar-sidebar-toggle-close').tipsy({
  283. gravity: 'se',
  284. delayIn: 375
  285. });
  286. $('.NB-taskbar-sidebar-toggle-open').tipsy({
  287. gravity: 'sw',
  288. delayIn: 375
  289. });
  290. $('.NB-task-add').tipsy({
  291. gravity: 'sw',
  292. delayIn: 375
  293. });
  294. $('.NB-task-manage').tipsy({
  295. gravity: 's',
  296. delayIn: 375
  297. });
  298. } else {
  299. $('.NB-taskbar-sidebar-toggle-close').tipsy('disable');
  300. $('.NB-taskbar-sidebar-toggle-open').tipsy('disable');
  301. $('.NB-task-add').tipsy('disable');
  302. $('.NB-task-manage').tipsy('disable');
  303. }
  304. $('.NB-module-content-account-realtime').tipsy({
  305. gravity: 'se',
  306. delayIn: 0
  307. });
  308. },
  309. save_feed_pane_size: function(w, pane, $pane, state, options, name) {
  310. var feed_pane_size = state.size;
  311. $('#NB-splash').css('left', feed_pane_size);
  312. $pane.toggleClass("NB-narrow", this.layout.outerLayout.state.west.size < 240);
  313. this.flags.set_feed_pane_size = this.flags.set_feed_pane_size || _.debounce( _.bind(function() {
  314. var feed_pane_size = this.layout.outerLayout.state.west.size;
  315. this.model.preference('feed_pane_size', feed_pane_size);
  316. this.flags.set_feed_pane_size = null;
  317. }, this), 1000);
  318. this.flags.set_feed_pane_size();
  319. },
  320. save_story_titles_pane_size: function(w, pane, $pane, state, options, name) {
  321. this.flags.scrolling_by_selecting_story_title = true;
  322. clearTimeout(this.locks.scrolling);
  323. var offset = 0;
  324. if (this.story_view == 'feed') {
  325. offset = this.$s.$feed_iframe.width();
  326. } else if (this.story_view == 'story') {
  327. offset = 2 * this.$s.$feed_iframe.width();
  328. }
  329. this.$s.$story_pane.css('left', -1 * offset);
  330. this.flags.set_story_titles_size = this.flags.set_story_titles_size || _.debounce( _.bind(function() {
  331. var story_titles_size = this.layout.rightLayout.state[this.model.preference('story_pane_anchor')].size;
  332. this.model.preference('story_titles_pane_size', story_titles_size);
  333. this.flags.set_story_titles_size = null;
  334. this.locks.scrolling = _.delay(_.bind(function() {
  335. this.flags.scrolling_by_selecting_story_title = false;
  336. }, this), 100);
  337. }, this), 1000);
  338. this.flags.set_story_titles_size();
  339. this.flags.resize_window = this.flags.resize_window || _.debounce( _.bind(function() {
  340. this.resize_window();
  341. this.flags.resize_window = null;
  342. }, this), 10);
  343. this.flags.resize_window();
  344. },
  345. add_body_classes: function() {
  346. this.$s.$body.toggleClass('NB-is-premium', NEWSBLUR.Globals.is_premium);
  347. this.$s.$body.toggleClass('NB-is-anonymous', NEWSBLUR.Globals.is_anonymous);
  348. this.$s.$body.toggleClass('NB-is-authenticated', NEWSBLUR.Globals.is_authenticated);
  349. this.$s.$body.toggleClass('NB-pref-hide-changes', !!this.model.preference('hide_story_changes'));
  350. },
  351. hide_splash_page: function() {
  352. var self = this;
  353. var resize = false;
  354. if (!$('.right-pane').is(':visible')) {
  355. resize = true;
  356. }
  357. $('.right-pane').show();
  358. $('#NB-splash,.NB-splash').hide();
  359. $('.NB-splash-info').hide();
  360. $('#NB-splash-overlay').hide();
  361. this.$s.$dashboard.addClass('NB-active');
  362. if (resize) {
  363. this.$s.$layout.layout().resizeAll();
  364. }
  365. if (NEWSBLUR.Globals.is_anonymous) {
  366. this.setup_ftux_signup_callout();
  367. }
  368. },
  369. show_splash_page: function(skip_router) {
  370. this.reset_feed();
  371. $('.right-pane').hide();
  372. $('.NB-splash-info').show();
  373. $('#NB-splash,.NB-splash').show();
  374. $('#NB-splash-overlay').show();
  375. this.$s.$dashboard.removeClass('NB-active');
  376. if (!skip_router) {
  377. NEWSBLUR.router.navigate('');
  378. }
  379. this.model.preference('dashboard_date', new Date);
  380. },
  381. add_url_from_querystring: function() {
  382. if (this.flags['added_url_from_querystring']) return;
  383. var url = $.getQueryString('url');
  384. this.flags['added_url_from_querystring'] = true;
  385. if (url) {
  386. this.open_add_feed_modal({url: url});
  387. }
  388. },
  389. animate_progress_bar: function($bar, seconds, percentage) {
  390. var self = this;
  391. percentage = percentage || 0;
  392. seconds = parseFloat(Math.max(1, parseInt(seconds, 10)), 10);
  393. if (percentage > 90) {
  394. time = seconds;
  395. } else if (percentage > 80) {
  396. time = seconds / 8;
  397. } else if (percentage > 70) {
  398. time = seconds / 16;
  399. } else if (percentage > 60) {
  400. time = seconds / 40;
  401. } else if (percentage > 50) {
  402. time = seconds / 80;
  403. } else if (percentage > 40) {
  404. time = seconds / 160;
  405. } else if (percentage > 30) {
  406. time = seconds / 200;
  407. } else if (percentage > 20) {
  408. time = seconds / 300;
  409. } else if (percentage > 10) {
  410. time = seconds / 400;
  411. } else {
  412. time = seconds / 500;
  413. }
  414. if (percentage <= 100) {
  415. this.locks['animate_progress_bar'] = setTimeout(function() {
  416. percentage += 1;
  417. $bar.progressbar({value: percentage});
  418. self.animate_progress_bar($bar, seconds, percentage);
  419. }, time * 1000);
  420. }
  421. },
  422. blur_to_page: function(options) {
  423. options = options || {};
  424. if (options.manage_menu) {
  425. $('.NB-menu-manage :focus').blur();
  426. } else {
  427. $(':focus').blur();
  428. }
  429. },
  430. // ==============
  431. // = Navigation =
  432. // ==============
  433. show_next_story: function(direction) {
  434. var story = NEWSBLUR.assets.stories.get_next_story(direction, {
  435. score: this.get_unread_view_score()
  436. });
  437. if (story) {
  438. story.set('selected', true);
  439. }
  440. },
  441. show_next_unread_story: function() {
  442. var unread_count = this.get_unread_count(true);
  443. if (unread_count) {
  444. var next_story = NEWSBLUR.assets.stories.get_next_unread_story();
  445. if (next_story) {
  446. this.counts['find_next_unread_on_page_of_feed_stories_load'] = 0;
  447. next_story.set('selected', true);
  448. } else if (this.counts['find_next_unread_on_page_of_feed_stories_load'] <
  449. this.constants.FIND_NEXT_UNREAD_STORY_TRIES &&
  450. !this.model.flags['no_more_stories']) {
  451. // Nothing up, nothing down, but still unread. Load 1 page then find it.
  452. this.counts['find_next_unread_on_page_of_feed_stories_load'] += 1;
  453. this.load_page_of_feed_stories();
  454. } else if (this.counts['find_next_unread_on_page_of_feed_stories_load'] >=
  455. this.constants.FIND_NEXT_UNREAD_STORY_TRIES) {
  456. this.open_next_unread_story_across_feeds(true);
  457. }
  458. }
  459. },
  460. open_next_unread_story_across_feeds: function(force_next_feed) {
  461. var unread_count = !force_next_feed && this.active_feed && this.get_unread_count(true);
  462. if (!unread_count) {
  463. if (this.flags.river_view && !this.flags.social_view) {
  464. var $next_folder = this.get_next_unread_folder(1);
  465. var folder = NEWSBLUR.assets.folders.get_view($next_folder);
  466. if (folder != this.active_folder) {
  467. this.open_river_stories($next_folder, folder && folder.model);
  468. }
  469. } else {
  470. // Find next feed with unreads
  471. var $next_feed = this.get_next_unread_feed(1);
  472. if (!$next_feed || !$next_feed.length) return;
  473. var next_feed_id = $next_feed.data('id');
  474. if (next_feed_id == this.active_feed) return;
  475. if (NEWSBLUR.utils.is_feed_social(next_feed_id)) {
  476. this.open_social_stories(next_feed_id, {force: true, $feed_link: $next_feed});
  477. } else {
  478. next_feed_id = parseInt(next_feed_id, 10);
  479. this.open_feed(next_feed_id, {force: true, $feed_link: $next_feed});
  480. }
  481. }
  482. }
  483. this.show_next_unread_story();
  484. },
  485. show_last_unread_story: function() {
  486. var unread_count = this.get_unread_count(true);
  487. if (unread_count) {
  488. var last_story = NEWSBLUR.assets.stories.get_last_unread_story(unread_count);
  489. if (last_story) {
  490. this.counts['find_last_unread_on_page_of_feed_stories_load'] = 0;
  491. last_story.set('selected', true);
  492. } else if (this.counts['find_last_unread_on_page_of_feed_stories_load'] < this.constants.FILL_OUT_PAGES &&
  493. !this.model.flags['no_more_stories']) {
  494. // Nothing up, nothing down, but still unread. Load 1 page then find it.
  495. this.counts['find_last_unread_on_page_of_feed_stories_load'] += 1;
  496. this.load_page_of_feed_stories();
  497. }
  498. }
  499. },
  500. select_story_in_feed: function() {
  501. var story_id = this.flags['select_story_in_feed'];
  502. var story = NEWSBLUR.assets.stories.get(story_id);
  503. // NEWSBLUR.log(['select_story_in_feed', story_id, story, this.story_view, this.counts['select_story_in_feed'], this.flags['no_more_stories']]);
  504. if (story) {
  505. this.counts['select_story_in_feed'] = 0;
  506. this.flags['select_story_in_feed'] = null;
  507. _.delay(_.bind(function() {
  508. // Even though a story_id is specified, this just means go to the comments.
  509. // Refactor when stories can be scrolled to separately from comments.
  510. story.set('selected', true, {scroll_to_comments: true});
  511. }, this), 100);
  512. } else if (this.counts['select_story_in_feed'] < this.constants.FILL_OUT_PAGES &&
  513. !this.model.flags['no_more_stories']) {
  514. // Nothing up, nothing down, but still not found. Load 1 page then find it.
  515. this.counts['select_story_in_feed'] += 1;
  516. this.load_page_of_feed_stories();
  517. } else {
  518. this.counts['select_story_in_feed'] = 0;
  519. this.flags['select_story_in_feed'] = null;
  520. }
  521. },
  522. show_previous_story: function() {
  523. NEWSBLUR.assets.stories.select_previous_story();
  524. },
  525. show_next_feed: function(direction, $current_feed) {
  526. var $feed_list = this.$s.$feed_list.add(this.$s.$social_feeds);
  527. if (this.flags.river_view && !this.flags.social_view) {
  528. return this.show_next_folder(direction, $current_feed);
  529. }
  530. var $next_feed = this.get_next_feed(direction, $current_feed, {include_selected: true});
  531. var next_feed_id = $next_feed.data('id');
  532. if (next_feed_id && next_feed_id == this.active_feed) {
  533. this.show_next_feed(direction, $next_feed);
  534. } else if (NEWSBLUR.utils.is_feed_social(next_feed_id)) {
  535. this.open_social_stories(next_feed_id, {force: true, $feed_link: $next_feed});
  536. } else {
  537. next_feed_id = parseInt(next_feed_id, 10);
  538. this.open_feed(next_feed_id, {force: true, $feed_link: $next_feed});
  539. }
  540. },
  541. show_next_folder: function(direction, $current_folder) {
  542. var $next_folder = this.get_next_folder(direction, $current_folder);
  543. var folder = NEWSBLUR.assets.folders.get_view($next_folder);
  544. this.open_river_stories($next_folder, folder && folder.model);
  545. },
  546. get_next_feed: function(direction, $current_feed, options) {
  547. options = options || {};
  548. var self = this;
  549. var $feed_list = this.$s.$feed_list.add(this.$s.$social_feeds);
  550. var $current_feed = $current_feed || $('.selected', $feed_list);
  551. var $next_feed,
  552. scroll;
  553. var $feeds = $('.feed:visible:not(.NB-empty)', $feed_list);
  554. if (!$current_feed.length) {
  555. if (options.include_selected) {
  556. $feeds = $feeds.add('.NB-feedlists .feed.NB-selected');
  557. }
  558. $current_feed = $('.feed:visible:not(.NB-empty)', $feed_list)[direction==-1?'last':'first']();
  559. $next_feed = $current_feed;
  560. } else {
  561. var current_feed = 0;
  562. $feeds.each(function(i) {
  563. if (this == $current_feed[0]) {
  564. current_feed = i;
  565. return false;
  566. }
  567. });
  568. $next_feed = $feeds.eq((current_feed+direction) % ($feeds.length));
  569. }
  570. return $next_feed;
  571. },
  572. get_next_folder: function(direction, $current_folder) {
  573. var self = this;
  574. var $feed_list = this.$s.$feed_list.add(this.$s.$social_feeds);
  575. var $current_folder = $('.folder.NB-selected', $feed_list);
  576. var $folders = $('li.folder:visible:not(.NB-empty)', $feed_list);
  577. var current_folder = 0;
  578. $folders.each(function(i) {
  579. if (this == $current_folder[0]) {
  580. current_folder = i;
  581. return false;
  582. }
  583. });
  584. var next_folder_index = (current_folder+direction) % ($folders.length);
  585. var $next_folder = $folders.eq(next_folder_index);
  586. return $next_folder;
  587. },
  588. get_next_unread_feed: function(direction, $current_feed) {
  589. var self = this;
  590. var $feed_list = this.$s.$feed_list.add(this.$s.$social_feeds);
  591. $current_feed = $current_feed || $('.selected', $feed_list);
  592. var unread_view = this.get_unread_view_name();
  593. var $next_feed;
  594. var current_feed;
  595. var $feeds = $('.feed:visible:not(.NB-empty)', $feed_list).filter(function() {
  596. var $this = $(this);
  597. if (unread_view == 'positive') {
  598. return $this.is('.unread_positive');
  599. } else if (unread_view == 'neutral') {
  600. return $this.is('.unread_positive,.unread_neutral');
  601. } else if (unread_view == 'negative') {
  602. return $this.is('.unread_positive,.unread_neutral,.unread_negative');
  603. }
  604. }).add('.NB-feedlists .feed.NB-selected');
  605. if (!$current_feed.length) {
  606. $next_feed = $feeds.first();
  607. } else {
  608. $feeds.each(function(i) {
  609. if (this == $current_feed[0]) {
  610. current_feed = i;
  611. return false;
  612. }
  613. });
  614. $next_feed = $feeds.eq((current_feed+direction) % ($feeds.length));
  615. }
  616. return $next_feed;
  617. },
  618. get_next_unread_folder: function(direction) {
  619. var self = this;
  620. var $feed_list = this.$s.$feed_list.add(this.$s.$social_feeds);
  621. var $current_folder = $('.folder.NB-selected', $feed_list);
  622. var unread_view = this.get_unread_view_name();
  623. var $next_folder;
  624. var current_folder = 0;
  625. var $folders = $('li.folder:visible:not(.NB-empty)', $feed_list);
  626. $folders = $folders.filter(function() {
  627. var $this = $(this);
  628. var folder_view = NEWSBLUR.assets.folders.get_view($current_folder);
  629. var folder_model = folder_view && folder_view.model;
  630. if (!folder_model) return false;
  631. var counts = folder_model.collection.unread_counts();
  632. if (this == $current_folder[0]) return true;
  633. if (unread_view == 'positive') {
  634. return counts.ps;
  635. } else if (unread_view == 'neutral') {
  636. return counts.ps + counts.nt;
  637. } else if (unread_view == 'negative') {
  638. return counts.ps + counts.nt + counts.ng;
  639. }
  640. });
  641. $folders.each(function(i) {
  642. if (this == $current_folder[0]) {
  643. current_folder = i;
  644. return false;
  645. }
  646. });
  647. $next_folder = $folders.eq((current_folder+direction) % ($folders.length));
  648. return $next_folder;
  649. },
  650. page_in_story: function(amount, direction) {
  651. var page_height = this.$s.$story_pane.height();
  652. var scroll_height = parseInt(page_height * amount, 10);
  653. var dir = '+';
  654. if (direction == -1) {
  655. dir = '-';
  656. }
  657. // NEWSBLUR.log(['page_in_story', this.$s.$story_pane, direction, page_height, scroll_height]);
  658. if (this.story_view == 'page' && !this.flags['page_view_showing_feed_view']) {
  659. this.$s.$feed_iframe.scrollTo({
  660. top: dir+'='+scroll_height,
  661. left:'+=0'
  662. }, 230, {queue: false});
  663. } else if (this.story_view == 'feed' || this.flags['page_view_showing_feed_view']) {
  664. this.$s.$feed_stories.scrollTo({
  665. top: dir+'='+scroll_height,
  666. left:'+=0'
  667. }, 230, {queue: false});
  668. }
  669. this.show_mouse_indicator();
  670. // _.delay(_.bind(this.hide_mouse_indicator, this), 350);
  671. },
  672. find_story_with_action_preference_on_open_feed: function() {
  673. var open_feed_action = this.model.preference('open_feed_action');
  674. if (!this.active_story && open_feed_action == 'newest') {
  675. this.show_next_unread_story();
  676. }
  677. },
  678. // =============
  679. // = Feed Pane =
  680. // =============
  681. sort_feeds: function($feeds) {
  682. $('.feed', $feeds).tsort('', {sortFunction: NEWSBLUR.Collections.Folders.comparator});
  683. $('.folder', $feeds).tsort('.folder_title_text');
  684. },
  685. load_sortable_feeds: function() {
  686. var self = this;
  687. this.$s.$feed_list.sortable({
  688. items: '.feed,li.folder',
  689. connectWith: 'ul.folder,.feed.NB-empty',
  690. placeholder: 'NB-feeds-list-highlight',
  691. axis: 'y',
  692. distance: 4,
  693. cursor: 'move',
  694. containment: '#feed_list',
  695. tolerance: 'pointer',
  696. scrollSensitivity: 35,
  697. start: function(e, ui) {
  698. self.flags['sorting_feed'] = true;
  699. ui.placeholder.attr('class', ui.item.attr('class') + ' NB-feeds-list-highlight');
  700. NEWSBLUR.app.feed_list.start_sorting();
  701. ui.item.addClass('NB-feed-sorting');
  702. ui.placeholder.data('id', ui.item.data('id'));
  703. if (ui.item.is('.folder')) {
  704. ui.placeholder.html(ui.item.children().clone());
  705. ui.item.data('previously_collapsed', ui.item.data('collapsed'));
  706. self.collapse_folder(ui.item.children('.folder_title'), true);
  707. self.collapse_folder(ui.placeholder.children('.folder_title'), true);
  708. ui.item.css('height', ui.item.children('.folder_title').outerHeight(true) + 'px');
  709. ui.helper.css('height', ui.helper.children('.folder_title').outerHeight(true) + 'px');
  710. } else {
  711. ui.placeholder.html(ui.item.children().clone());
  712. }
  713. },
  714. change: function(e, ui) {
  715. var $feeds = ui.placeholder.closest('ul.folder');
  716. self.sort_feeds($feeds);
  717. },
  718. stop: function(e, ui) {
  719. setTimeout(function() {
  720. self.flags['sorting_feed'] = false;
  721. }, 100);
  722. ui.item.removeClass('NB-feed-sorting');
  723. NEWSBLUR.app.feed_list.end_sorting();
  724. self.sort_feeds(e.target);
  725. self.save_feed_order();
  726. ui.item.css({'backgroundColor': '#D7DDE6'})
  727. .animate({'backgroundColor': '#F0F076'}, {'duration': 800})
  728. .animate({'backgroundColor': '#D7DDE6'}, {'duration': 1000});
  729. if (ui.item.is('.folder') && !ui.item.data('previously_collapsed')) {
  730. self.collapse_folder(ui.item.children('.folder_title'));
  731. self.collapse_folder(ui.placeholder.children('.folder_title'));
  732. }
  733. }
  734. });
  735. },
  736. save_feed_order: function() {
  737. var combine_folders = function($folder) {
  738. var folders = [];
  739. var $items = $folder.children('li.folder, .feed');
  740. for (var i=0, i_count=$items.length; i < i_count; i++) {
  741. var $item = $items.eq(i);
  742. if ($item.hasClass('feed')) {
  743. var feed_id = parseInt($item.data('id'), 10);
  744. if (feed_id) {
  745. folders.push(feed_id);
  746. }
  747. } else if ($item.hasClass('folder')) {
  748. var folder_title = $item.find('.folder_title_text').eq(0).text();
  749. var child_folders = {};
  750. child_folders[folder_title] = combine_folders($item.children('ul.folder').eq(0));
  751. folders.push(child_folders);
  752. }
  753. }
  754. return folders;
  755. };
  756. var combined_folders = combine_folders(this.$s.$feed_list);
  757. // NEWSBLUR.log(['Save new folder/feed order', {'combined': combined_folders}]);
  758. this.model.save_feed_order(combined_folders);
  759. },
  760. show_feed_chooser_button: function() {
  761. var self = this;
  762. var $progress = this.$s.$feeds_progress;
  763. var $bar = $('.NB-progress-bar', $progress);
  764. var percentage = 0;
  765. $('.NB-progress-title', $progress).text('Get Started');
  766. $('.NB-progress-counts', $progress).hide();
  767. $('.NB-progress-percentage', $progress).hide();
  768. $progress.addClass('NB-progress-error').addClass('NB-progress-big');
  769. $('.NB-progress-link', $progress).html($.make('div', {
  770. className: 'NB-modal-submit-button NB-modal-submit-green NB-menu-manage-feedchooser'
  771. }, ['Choose your 64 sites']));
  772. this.show_progress_bar();
  773. },
  774. hide_feed_chooser_button: function() {
  775. var $progress = this.$s.$feeds_progress;
  776. var $bar = $('.NB-progress-bar', $progress);
  777. $progress.removeClass('NB-progress-error').removeClass('NB-progress-big');
  778. this.hide_progress_bar();
  779. },
  780. open_dialog_after_feeds_loaded: function(options) {
  781. options = options || {};
  782. if (!NEWSBLUR.Globals.is_authenticated) return;
  783. if (!NEWSBLUR.assets.folders.length ||
  784. !NEWSBLUR.assets.preference('has_setup_feeds')) {
  785. if (options.delayed_import || this.flags.delayed_import) {
  786. this.setup_ftux_add_feed_callout("Check your email...");
  787. } else if (NEWSBLUR.assets.preference('has_setup_feeds')) {
  788. this.setup_ftux_add_feed_callout();
  789. } else if (!NEWSBLUR.intro || !NEWSBLUR.intro.flags.open) {
  790. _.defer(_.bind(this.open_intro_modal, this), 100);
  791. }
  792. } else if (!NEWSBLUR.assets.flags['has_chosen_feeds'] &&
  793. NEWSBLUR.assets.folders.length) {
  794. _.defer(_.bind(this.open_feedchooser_modal, this), 100);
  795. } else if (!NEWSBLUR.Globals.is_premium &&
  796. NEWSBLUR.assets.feeds.active().length > 64) {
  797. _.defer(_.bind(this.open_feedchooser_modal, this), 100);
  798. }
  799. },
  800. // ================
  801. // = Progress Bar =
  802. // ================
  803. check_feed_fetch_progress: function() {
  804. $.extend(this.counts, {
  805. 'unfetched_feeds': 0,
  806. 'fetched_feeds': 0
  807. });
  808. var counts = this.model.count_unfetched_feeds();
  809. this.counts['unfetched_feeds'] = counts['unfetched_feeds'];
  810. this.counts['fetched_feeds'] = counts['fetched_feeds'];
  811. if (this.counts['unfetched_feeds'] == 0) {
  812. this.flags['has_unfetched_feeds'] = false;
  813. this.hide_unfetched_feed_progress();
  814. } else {
  815. this.flags['has_unfetched_feeds'] = true;
  816. this.show_unfetched_feed_progress();
  817. }
  818. },
  819. show_progress_bar: function() {
  820. var $layout = this.$s.$feeds_progress.parents('.left-center').layout();
  821. if (!this.flags['showing_progress_bar']) {
  822. this.flags['showing_progress_bar'] = true;
  823. $layout.open('south');
  824. }
  825. $layout.sizePane('south');
  826. },
  827. hide_progress_bar: function(permanent) {
  828. var self = this;
  829. if (permanent) {
  830. this.model.preference('hide_fetch_progress', true);
  831. }
  832. this.flags['showing_progress_bar'] = false;
  833. this.$s.$feeds_progress.parents('.left-center').layout().close('south');
  834. },
  835. show_unfetched_feed_progress: function() {
  836. var self = this;
  837. var $progress = this.$s.$feeds_progress;
  838. var percentage = parseInt(this.counts['fetched_feeds'] / (this.counts['unfetched_feeds'] + this.counts['fetched_feeds']) * 100, 10);
  839. $('.NB-progress-title', $progress).text('Fetching your feeds');
  840. $('.NB-progress-counts', $progress).show();
  841. $('.NB-progress-counts-fetched', $progress).text(this.counts['fetched_feeds']);
  842. $('.NB-progress-counts-total', $progress).text(this.counts['unfetched_feeds'] + this.counts['fetched_feeds']);
  843. $('.NB-progress-percentage', $progress).show().text(percentage + '%');
  844. $('.NB-progress-bar', $progress).progressbar({
  845. value: percentage
  846. });
  847. if (!$progress.is(':visible') && !this.model.preference('hide_fetch_progress')) {
  848. setTimeout(function() {
  849. self.show_progress_bar();
  850. }, 1000);
  851. }
  852. this.setup_feed_refresh(true);
  853. },
  854. hide_unfetched_feed_progress: function(permanent) {
  855. if (permanent) {
  856. this.model.preference('hide_fetch_progress', true);
  857. }
  858. this.setup_feed_refresh();
  859. this.hide_progress_bar();
  860. },
  861. // ===============================
  862. // = Feed bar - Individual Feeds =
  863. // ===============================
  864. reset_feed: function(options) {
  865. options = options || {};
  866. $.extend(this.flags, {
  867. 'scrolling_by_selecting_story_title': false,
  868. 'page_view_showing_feed_view': false,
  869. 'feed_view_showing_story_view': false,
  870. 'story_titles_loaded': false,
  871. 'iframe_prevented_from_loading': false,
  872. 'pause_feed_refreshing': false,
  873. 'feed_list_showing_manage_menu': false,
  874. 'unread_threshold_temporarily': null,
  875. 'river_view': false,
  876. 'social_view': false,
  877. 'non_premium_river_view': false,
  878. 'select_story_in_feed': null,
  879. 'global_blurblogs': false
  880. });
  881. $.extend(this.cache, {
  882. 'iframe': {},
  883. 'iframe_stories': {},
  884. 'iframe_story_positions': {},
  885. 'feed_view_story_positions': {},
  886. 'iframe_story_positions_keys': [],
  887. 'feed_view_story_positions_keys': [],
  888. 'river_feeds_with_unreads': [],
  889. 'prefetch_last_story': 0,
  890. 'prefetch_iteration': 0,
  891. 'feed_title_floater_story_id': null,
  892. '$feed_in_social_feed_list': {}
  893. });
  894. $.extend(this.counts, {
  895. 'page': 1,
  896. 'page_fill_outs': 0,
  897. 'find_next_unread_on_page_of_feed_stories_load': 0,
  898. 'find_last_unread_on_page_of_feed_stories_load': 0,
  899. 'select_story_in_feed': 0
  900. });
  901. if (_.isUndefined(options.search)) {
  902. delete this.flags.search;
  903. }
  904. this.model.flags['no_more_stories'] = false;
  905. this.$s.$feed_stories.scrollTop(0);
  906. this.$s.$starred_header.removeClass('NB-selected');
  907. this.$s.$river_sites_header.removeClass('NB-selected');
  908. this.$s.$river_blurblogs_header.removeClass('NB-selected');
  909. this.$s.$river_global_header.removeClass('NB-selected');
  910. this.$s.$tryfeed_header.removeClass('NB-selected');
  911. this.model.feeds.deselect();
  912. if (_.string.contains(this.active_feed, 'social:')) {
  913. this.model.social_feeds.deselect();
  914. }
  915. if (_.string.contains(this.active_feed, 'river:')) {
  916. this.model.folders.deselect();
  917. }
  918. this.$s.$body.removeClass('NB-view-river');
  919. $('.task_view_page', this.$s.$taskbar).removeClass('NB-disabled');
  920. $('.task_view_story', this.$s.$taskbar).removeClass('NB-disabled');
  921. $('.task_view_page', this.$s.$taskbar).removeClass('NB-task-return');
  922. clearTimeout(this.flags['next_fetch']);
  923. if (this.flags['showing_feed_in_tryfeed_view'] || this.flags['showing_social_feed_in_tryfeed_view']) {
  924. this.hide_tryfeed_view();
  925. }
  926. if (NEWSBLUR.Globals.is_anonymous) {
  927. if (options.router) {
  928. this.$s.$layout.layout().show('west', true);
  929. this.$s.$layout.show();
  930. }
  931. this.hide_tryout_signup_button();
  932. }
  933. this.active_folder = null;
  934. this.active_feed = null;
  935. this.active_story = null;
  936. NEWSBLUR.assets.stories.reset();
  937. NEWSBLUR.app.feed_selector.hide_feed_selector();
  938. NEWSBLUR.app.original_tab_view.unload_feed_iframe();
  939. NEWSBLUR.app.story_tab_view.unload_story_iframe();
  940. },
  941. reload_feed: function(options) {
  942. options = options || {};
  943. if (this.active_feed == 'starred') {
  944. this.open_starred_stories(options);
  945. } else if (this.flags['social_view'] &&
  946. this.active_feed == 'river:blurblogs') {
  947. this.open_river_blurblogs_stories();
  948. } else if (this.flags['social_view'] &&
  949. this.active_feed == 'river:global') {
  950. this.open_river_blurblogs_stories({'global': true});
  951. } else if (this.flags['social_view']) {
  952. this.open_social_stories(this.active_feed);
  953. } else if (this.flags['river_view']) {
  954. this.open_river_stories(this.active_folder &&
  955. this.active_folder.folder_view &&
  956. this.active_folder.folder_view.$el,
  957. this.active_folder);
  958. } else {
  959. this.open_feed(this.active_feed, options);
  960. }
  961. },
  962. open_feed: function(feed_id, options) {
  963. options = options || {};
  964. var self = this;
  965. var $story_titles = this.$s.$story_titles;
  966. var feed = this.model.get_feed(feed_id) || options.feed;
  967. var temp = feed && (feed.get('temp') || !feed.get('subscribed'));
  968. if (!feed || (temp && !options.try_feed)) {
  969. // Setup tryfeed views first, then come back here.
  970. options.feed = options.feed && options.feed.attributes;
  971. return this.load_feed_in_tryfeed_view(feed_id, options);
  972. }
  973. this.flags['opening_feed'] = true;
  974. if (options.try_feed || feed) {
  975. this.reset_feed(options);
  976. this.hide_splash_page();
  977. if (options.story_id) {
  978. this.flags['select_story_in_feed'] = options.story_id;
  979. }
  980. this.active_feed = feed.id;
  981. this.next_feed = feed.id;
  982. feed.set('selec…

Large files files are truncated, but you can click here to view the full file