diff options
| author | 2017-02-15 14:14:03 +0100 | |
|---|---|---|
| committer | 2017-02-15 14:14:03 +0100 | |
| commit | 5a1bb1393b4496eb35a2ffb3cc63d41c9dc1e2e5 (patch) | |
| tree | 67028e45792c575c25c92616633f64cc7a4a13eb /p/scripts/main.js | |
| parent | 7e949d50320317b5c3b5a2da2bdaf324e794b2f7 (diff) | |
| parent | 5f637bd816b7323885bfe1751a1724ee59a822f6 (diff) | |
Merge remote-tracking branch 'FreshRSS/master'
Diffstat (limited to 'p/scripts/main.js')
| -rw-r--r-- | p/scripts/main.js | 980 |
1 files changed, 689 insertions, 291 deletions
diff --git a/p/scripts/main.js b/p/scripts/main.js index 34042c945..14e0c5be5 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -1,14 +1,11 @@ "use strict"; -var $stream = null, - isCollapsed = true; - -function is_normal_mode() { - return $stream.hasClass('normal'); -} +/* globals context, i18n, shortcut, shortcuts, url */ +/* jshint globalstrict: true */ -function is_global_mode() { - return $stream.hasClass('global'); -} +var $stream = null, + isCollapsed = true, + shares = 0, + ajax_loading = false; function redirect(url, new_tab) { if (url) { @@ -31,7 +28,7 @@ function needsScroll($elem) { } function str2int(str) { - if (str == '') { + if (!str) { return 0; } return parseInt(str.replace(/\D/g, ''), 10) || 0; @@ -53,30 +50,38 @@ function numberFormat(nStr) { return x1 + x2; } -function incLabel(p, inc) { +function incLabel(p, inc, spaceAfter) { var i = str2int(p) + inc; - return i > 0 ? ' (' + numberFormat(i) + ')' : ''; + return i > 0 ? ((spaceAfter ? '' : ' ') + '(' + numberFormat(i) + ')' + (spaceAfter ? ' ' : '')) : ''; } function incUnreadsFeed(article, feed_id, nb) { //Update unread: feed - var elem = $('#' + feed_id + '>.feed').get(0), + var elem = $('#' + feed_id).get(0), feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0, feed_priority = elem ? str2int(elem.getAttribute('data-priority')) : 0; if (elem) { - elem.setAttribute('data-unread', numberFormat(feed_unreads + nb)); + elem.setAttribute('data-unread', feed_unreads + nb); + elem = $(elem).children('.item-title').get(0); + if (elem) { + elem.setAttribute('data-unread', numberFormat(feed_unreads + nb)); + } } //Update unread: category - elem = $('#' + feed_id).parent().prevAll('.category').children(':first').get(0); + elem = $('#' + feed_id).parents('.category').get(0); feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; if (elem) { - elem.setAttribute('data-unread', numberFormat(feed_unreads + nb)); + elem.setAttribute('data-unread', feed_unreads + nb); + elem = $(elem).find('.title').get(0); + if (elem) { + elem.setAttribute('data-unread', numberFormat(feed_unreads + nb)); + } } //Update unread: all if (feed_priority > 0) { - elem = $('#aside_flux .all').children(':first').get(0); + elem = $('#aside_feed .all .title').get(0); if (elem) { feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; elem.setAttribute('data-unread', numberFormat(feed_unreads + nb)); @@ -85,7 +90,7 @@ function incUnreadsFeed(article, feed_id, nb) { //Update unread: favourites if (article && article.closest('div').hasClass('favorite')) { - elem = $('#aside_flux .favorites').children(':first').get(0); + elem = $('#aside_feed .favorites .title').get(0); if (elem) { feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; elem.setAttribute('data-unread', numberFormat(feed_unreads + nb)); @@ -93,60 +98,68 @@ function incUnreadsFeed(article, feed_id, nb) { } var isCurrentView = false; - //Update unread: title - document.title = document.title.replace(/((?: \([ 0-9]+\))?)( · .*?)((?: \([ 0-9]+\))?)$/, function (m, p1, p2, p3) { + // Update unread: title + document.title = document.title.replace(/^((?:\([ 0-9]+\) )?)/, function (m, p1) { var $feed = $('#' + feed_id); if (article || ($feed.closest('.active').length > 0 && $feed.siblings('.active').length === 0)) { isCurrentView = true; - return incLabel(p1, nb) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0); + return incLabel(p1, nb, true); + } else if ($('.all.active').length > 0) { + isCurrentView = feed_priority > 0; + return incLabel(p1, feed_priority > 0 ? nb : 0, true); } else { - return p1 + p2 + incLabel(p3, feed_priority > 0 ? nb : 0); + return p1; } }); return isCurrentView; } -var pending_feeds = []; +var pending_entries = {}; function mark_read(active, only_not_read) { - if (active.length === 0 || - (only_not_read === true && !active.hasClass("not_read"))) { + if ((active.length === 0) || (!active.attr('id')) || + (only_not_read && !active.hasClass("not_read"))) { return false; } - var url = active.find("a.read").attr("href"); - if (url === undefined) { + if (pending_entries[active.attr('id')]) { return false; } + pending_entries[active.attr('id')] = true; - var feed_url = active.find(".website>a").attr("href"), - feed_id = feed_url.substr(feed_url.lastIndexOf('f_')), - index_pending = pending_feeds.indexOf(feed_id); - - if (index_pending !== -1) { - return false; - } - - pending_feeds.push(feed_id); + var url = '.?c=entry&a=read&id=' + active.attr('id').replace(/^flux_/, '') + + (active.hasClass('not_read') ? '' : '&is_read=0'); $.ajax({ type: 'POST', url: url, - data : { ajax: true } + data : { + ajax: true, + _csrf: context.csrf, + }, }).done(function (data) { var $r = active.find("a.read").attr("href", data.url), inc = 0; if (active.hasClass("not_read")) { active.removeClass("not_read"); inc--; - } else if (only_not_read !== true || active.hasClass("not_read")) { + } else { active.addClass("not_read"); + active.addClass("keep_unread"); inc++; } $r.find('.icon').replaceWith(data.icon); - incUnreadsFeed(active, feed_id, inc); + var feed_url = active.find(".website>a").attr("href"); + if (feed_url) { + var feed_id = feed_url.substr(feed_url.lastIndexOf('f_')); + incUnreadsFeed(active, feed_id, inc); + } + faviconNbUnread(); - pending_feeds.splice(index_pending, 1); + delete pending_entries[active.attr('id')]; + }).fail(function (data) { + openNotification(i18n.notif_request_failed, 'bad'); + delete pending_entries[active.attr('id')]; }); } @@ -160,20 +173,18 @@ function mark_favorite(active) { return false; } - var feed_url = active.find(".website>a").attr("href"), - feed_id = feed_url.substr(feed_url.lastIndexOf('f_')), - index_pending = pending_feeds.indexOf(feed_id); - - if (index_pending !== -1) { + if (pending_entries[active.attr('id')]) { return false; } - - pending_feeds.push(feed_id); + pending_entries[active.attr('id')] = true; $.ajax({ type: 'POST', url: url, - data : { ajax: true } + data : { + ajax: true, + _csrf: context.csrf, + }, }).done(function (data) { var $b = active.find("a.bookmark").attr("href", data.url), inc = 0; @@ -186,35 +197,34 @@ function mark_favorite(active) { } $b.find('.icon').replaceWith(data.icon); - var favourites = $('.favorites>a').contents().last().get(0); + var favourites = $('#aside_feed .favorites .title').contents().last().get(0); if (favourites && favourites.textContent) { favourites.textContent = favourites.textContent.replace(/((?: \([ 0-9]+\))?\s*)$/, function (m, p1) { - return incLabel(p1, inc); + return incLabel(p1, inc, false); }); } if (active.closest('div').hasClass('not_read')) { - var elem = $('#aside_flux .favorites').children(':first').get(0), + var elem = $('#aside_feed .favorites .title').get(0), feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; if (elem) { elem.setAttribute('data-unread', numberFormat(feed_unreads + inc)); } } - pending_feeds.splice(index_pending, 1); + delete pending_entries[active.attr('id')]; + }).fail(function (data) { + openNotification(i18n.notif_request_failed, 'bad'); + delete pending_entries[active.attr('id')]; }); } function toggleContent(new_active, old_active) { - old_active.removeClass("active"); - if (new_active.length === 0) { return; } - old_active.removeClass("current"); - - if (does_lazyload) { + if (context.does_lazyload) { new_active.find('img[data-original], iframe[data-original]').each(function () { this.setAttribute('src', this.getAttribute('data-original')); this.removeAttribute('data-original'); @@ -225,45 +235,64 @@ function toggleContent(new_active, old_active) { if (isCollapsed) { new_active.addClass("active"); } + old_active.removeClass("active current"); new_active.addClass("current"); + if (context.auto_remove_article && !old_active.hasClass('not_read')) { + auto_remove(old_active); + } + } else { + new_active.toggleClass('active'); } - var box_to_move = "html,body", - relative_move = false; - if (is_global_mode()) { - box_to_move = "#panel"; - relative_move = true; - } - - var new_pos = new_active.position().top, - old_scroll = $(box_to_move).scrollTop(); - if (hide_posts) { + var relative_move = context.current_view === 'global', + box_to_move = $(relative_move ? "#panel" : "html,body"); - new_pos = new_active.position().top; - old_scroll = $(box_to_move).scrollTop(); + if (context.sticky_post) { + var prev_article = new_active.prevAll('.flux'), + new_pos = new_active.offset().top, + old_scroll = box_to_move.scrollTop(); - if (relative_move) { - new_pos += old_scroll; + if (prev_article.length > 0 && new_pos - prev_article.offset().top <= 150) { + new_pos = prev_article.offset().top; + if (relative_move) { + new_pos -= box_to_move.offset().top; + } } - if (old_active[0] !== new_active[0]) { - new_active.children(".flux_content").first().each(function () { - $(box_to_move).scrollTop(new_pos).scrollTop(); - }); - } - } else { - if (relative_move) { - new_pos += old_scroll; - } + if (context.hide_posts) { + if (relative_move) { + new_pos += old_scroll; + } + + if (old_active[0] !== new_active[0]) { + new_active.children(".flux_content").first().each(function () { + box_to_move.scrollTop(new_pos).scrollTop(); + }); + } + } else { + if (relative_move) { + new_pos += old_scroll; + } - $(box_to_move).scrollTop(new_pos).scrollTop(); + box_to_move.scrollTop(new_pos).scrollTop(); + } } - if (auto_mark_article) { + if (context.auto_mark_article && new_active.hasClass('active')) { mark_read(new_active, true); } } +function auto_remove(element) { + var p = element.prev(); + var n = element.next(); + if (p.hasClass('day') && n.hasClass('day')) { + p.remove(); + } + element.remove(); + $('#stream > .flux:not(.not_read):not(.active)').remove(); +} + function prev_entry() { var old_active = $(".flux.current"), new_active = old_active.length === 0 ? $(".flux:last") : old_active.prevAll(".flux:first"); @@ -275,51 +304,48 @@ function next_entry() { new_active = old_active.length === 0 ? $(".flux:first") : old_active.nextAll(".flux:first"); toggleContent(new_active, old_active); - if (!auto_load_more) { - var last_active = $(".flux:last"); - if (last_active.attr("id") === new_active.attr("id")) { - load_more_posts(); - } + if (new_active.nextAll().length < 3) { + load_more_posts(); } } function prev_feed() { - var active_feed = $("#aside_flux .feeds li.active"); + var active_feed = $("#aside_feed .tree-folder-items .item.active"); if (active_feed.length > 0) { - active_feed.prev().find('a.feed').each(function(){this.click();}); + active_feed.prevAll(':visible:first').find('a').each(function(){this.click();}); } else { last_feed(); } } function next_feed() { - var active_feed = $("#aside_flux .feeds li.active"); + var active_feed = $("#aside_feed .tree-folder-items .item.active"); if (active_feed.length > 0) { - active_feed.next().find('a.feed').each(function(){this.click();}); + active_feed.nextAll(':visible:first').find('a').each(function(){this.click();}); } else { first_feed(); } } function first_feed() { - var feed = $("#aside_flux .feeds.active li:first"); + var feed = $("#aside_feed .tree-folder-items.active .item:visible:first"); if (feed.length > 0) { feed.find('a')[1].click(); } } function last_feed() { - var feed = $("#aside_flux .feeds.active li:last"); + var feed = $("#aside_feed .tree-folder-items.active .item:visible:last"); if (feed.length > 0) { feed.find('a')[1].click(); } } function prev_category() { - var active_cat = $("#aside_flux .category.stick.active"); + var active_cat = $("#aside_feed .tree-folder.active"); if (active_cat.length > 0) { - var prev_cat = active_cat.parent('li').prev().find('.category.stick a.btn'); + var prev_cat = active_cat.prevAll(':visible:first').find('.tree-folder-title .title'); if (prev_cat.length > 0) { prev_cat[0].click(); } @@ -330,10 +356,10 @@ function prev_category() { } function next_category() { - var active_cat = $("#aside_flux .category.stick.active"); + var active_cat = $("#aside_feed .tree-folder.active"); if (active_cat.length > 0) { - var next_cat = active_cat.parent('li').next().find('.category.stick a.btn'); + var next_cat = active_cat.nextAll(':visible:first').find('.tree-folder-title .title'); if (next_cat.length > 0) { next_cat[0].click(); } @@ -344,22 +370,55 @@ function next_category() { } function first_category() { - var cat = $("#aside_flux .category.stick:first"); + var cat = $("#aside_feed .tree-folder:visible:first"); if (cat.length > 0) { - cat.find('a.btn')[0].click(); + cat.find('.tree-folder-title .title')[0].click(); } } function last_category() { - var cat = $("#aside_flux .category.stick:last"); + var cat = $("#aside_feed .tree-folder:visible:last"); if (cat.length > 0) { - cat.find('a.btn')[0].click(); + cat.find('.tree-folder-title .title')[0].click(); } } function collapse_entry() { isCollapsed = !isCollapsed; - $(".flux.current").toggleClass("active"); + + var flux_current = $(".flux.current"); + flux_current.toggleClass("active"); + if (isCollapsed && context.auto_mark_article) { + mark_read(flux_current, true); + } +} + +function user_filter(key) { + var filter = $('#dropdown-query'); + var filters = filter.siblings('.dropdown-menu').find('.item.query a'); + if (typeof key === "undefined") { + if (!filter.length) { + return; + } + // Display the filter div + window.location.hash = filter.attr('id'); + // Force scrolling to the filter div + var scroll = needsScroll($('.header')); + if (scroll !== 0) { + $('html,body').scrollTop(scroll); + } + // Force the key value if there is only one action, so we can trigger it automatically + if (filters.length === 1) { + key = 1; + } else { + return; + } + } + // Trigger selected share action + key = parseInt(key); + if (key <= filters.length) { + filters[key - 1].click(); + } } function auto_share(key) { @@ -391,75 +450,78 @@ function auto_share(key) { } } -function inMarkViewport(flux, box_to_follow, relative_follow) { - var top = flux.position().top; - if (relative_follow) { - top += box_to_follow.scrollTop(); - } - var height = flux.height(), - begin = top + 3 * height / 4, - bot = Math.min(begin + 75, top + height), - windowTop = box_to_follow.scrollTop(), - windowBot = windowTop + box_to_follow.height() / 2; - - return (windowBot >= begin && bot >= windowBot); -} - -function init_lazyload() { - if ($.fn.lazyload) { - if (is_global_mode()) { - $(".flux_content img").lazyload({ - container: $("#panel") - }); - } else { - $(".flux_content img").lazyload(); - } - } +function scrollAsRead(box_to_follow) { + var minTop = 40 + (context.current_view === 'global' ? box_to_follow.offset().top : box_to_follow.scrollTop()); + $('.not_read:not(.keep_unread):visible').each(function () { + var $this = $(this); + if ($this.offset().top + $this.height() < minTop) { + mark_read($this, true); + } + }); } function init_posts() { - init_lazyload(); + var box_to_follow = context.current_view === 'global' ? $("#panel") : $(window); - var box_to_follow = $(window), - relative_follow = false; - if (is_global_mode()) { - box_to_follow = $("#panel"); - relative_follow = true; - } - - if (auto_mark_scroll) { + if (context.auto_mark_scroll) { + var lastScroll = 0, //Throttle + timerId = 0; box_to_follow.scroll(function () { - $('.not_read:visible').each(function () { - if ($(this).children(".flux_content").is(':visible') && inMarkViewport($(this), box_to_follow, relative_follow)) { - mark_read($(this), true); - } - }); + window.clearTimeout(timerId); + if (lastScroll + 500 < Date.now()) { + lastScroll = Date.now(); + scrollAsRead(box_to_follow); + } else { + timerId = window.setTimeout(function() { + scrollAsRead(box_to_follow); + }, 500); + } }); } - if (auto_load_more) { + if (context.auto_load_more) { box_to_follow.scroll(function () { var load_more = $("#load_more"); if (!load_more.is(':visible')) { return; } var boxBot = box_to_follow.scrollTop() + box_to_follow.height(), - load_more_top = load_more.position().top; - if (relative_follow) { - load_more_top += box_to_follow.scrollTop(); - } + load_more_top = load_more.offset().top; if (boxBot >= load_more_top) { load_more_posts(); } }); + box_to_follow.scroll(); + } +} + +function inject_script(name) { + var script = document.createElement('script'); + script.async = 'async'; + script.defer = 'defer'; + script.src = '../scripts/' + name; + document.head.appendChild(script); +} + +function init_sticky_column() { + if (!window.$ || !window.$.fn.stick_in_parent) { + if (window.console) { + console.log('FreshRSS waiting for Sticky-kit…'); + } + window.setTimeout(init_sticky_column, 200); + return; + } + if ($('.toggle_aside').css('display') === 'none') { + $('#aside_feed .tree').stick_in_parent({parent:'#aside_feed'}); } } function init_column_categories() { - if (!is_normal_mode()) { + if (context.current_view !== 'normal') { return; } - $('#aside_flux').on('click', '.category>a.dropdown-toggle', function () { + + $('#aside_feed').on('click', '.tree-folder>.tree-folder-title>a.dropdown-toggle', function () { $(this).children().each(function() { if (this.alt === '▽') { this.src = this.src.replace('/icons/down.', '/icons/up.'); @@ -469,17 +531,19 @@ function init_column_categories() { this.alt = '▽'; } }); - $(this).parent().next(".feeds").slideToggle(); + $(this).parent().next(".tree-folder-items").slideToggle(300 , function() { $(document.body).trigger("sticky_kit:recalc"); }); return false; }); - $('#aside_flux').on('click', '.feeds .dropdown-toggle', function () { + $('#aside_feed').on('click', '.tree-folder-items .item .dropdown-toggle', function () { if ($(this).nextAll('.dropdown-menu').length === 0) { - var feed_id = $(this).closest('li').attr('id').substr(2), + var feed_id = $(this).closest('.item').attr('id').substr(2), feed_web = $(this).data('fweb'), - template = $('#feed_config_template').html().replace(/!!!!!!/g, feed_id).replace('http://example.net/', feed_web); + template = $('#feed_config_template').html().replace(/------/g, feed_id).replace('http://example.net/', feed_web); $(this).attr('href', '#dropdown-' + feed_id).prev('.dropdown-target').attr('id', 'dropdown-' + feed_id).parent().append(template); } }); + + init_sticky_column(); } function init_shortcuts() { @@ -487,7 +551,7 @@ function init_shortcuts() { if (window.console) { console.log('FreshRSS waiting for sortcut.js…'); } - window.setTimeout(init_shortcuts, 50); + window.setTimeout(init_shortcuts, 200); return; } // Touches de manipulation @@ -500,8 +564,7 @@ function init_shortcuts() { }); shortcut.add("shift+" + shortcuts.mark_read, function () { // on marque tout comme lu - var url = $(".nav_menu a.read_all").attr("href"); - redirect(url, false); + $(".nav_menu .read_all").click(); }, { 'disable_in_input': true }); @@ -522,10 +585,22 @@ function init_shortcuts() { }, { 'disable_in_input': true }); - for(var i = 1; i < 10; i++){ - shortcut.add(i.toString(), function (e) { - auto_share(String.fromCharCode(e.keyCode)); - }, { + + shortcut.add(shortcuts.user_filter, function () { + user_filter(); + }, { + 'disable_in_input': true + }); + + function addShortcut(evt) { + if ($('#dropdown-query').siblings('.dropdown-menu').is(':visible')) { + user_filter(String.fromCharCode(evt.keyCode)); + } else { + auto_share(String.fromCharCode(evt.keyCode)); + } + } + for(var i = 1; i < 10; i++) { + shortcut.add(i.toString(), addShortcut, { 'disable_in_input': true }); } @@ -585,9 +660,9 @@ function init_shortcuts() { }); shortcut.add(shortcuts.go_website, function () { - var url_website = $(".flux.current .link a").attr("href"); + var url_website = $('.flux.current > .flux_header > .title > a').attr("href"); - if (auto_mark_site) { + if (context.auto_mark_site) { $(".flux.current").each(function () { mark_read($(this), true); }); @@ -603,18 +678,37 @@ function init_shortcuts() { }, { 'disable_in_input': true }); + + shortcut.add(shortcuts.focus_search, function () { + focus_search(); + }, { + 'disable_in_input': true + }); + + shortcut.add(shortcuts.help, function () { + redirect(url.help, true); + }, { + 'disable_in_input': true + }); + + shortcut.add(shortcuts.close_dropdown, function () { + window.location.hash = null; + }, { + 'disable_in_input': true + }); + } function init_stream(divStream) { - divStream.on('click', '.flux_header', function (e) { //flux_header_toggle - if ($(e.target).closest('.item.website, .item.link').length > 0) { + divStream.on('click', '.flux_header,.flux_content', function (e) { //flux_toggle + if ($(e.target).closest('.content, .item.website, .item.link').length > 0) { return; } var old_active = $(".flux.current"), new_active = $(this).parent(); - isCollapsed = true; + isCollapsed = true; if (e.target.tagName.toUpperCase() === 'A') { //Leave real links alone - if (auto_mark_article) { + if (context.auto_mark_article) { mark_read(new_active, true); } return true; @@ -624,6 +718,9 @@ function init_stream(divStream) { divStream.on('click', '.flux a.read', function () { var active = $(this).parents(".flux"); + if (context.auto_remove_article && active.hasClass('not_read')) { + auto_remove(active); + } mark_read(active, false); return false; }); @@ -635,20 +732,40 @@ function init_stream(divStream) { }); divStream.on('click', '.item.title > a', function (e) { + // Allow default control-click behaviour such as open in backround-tab. + return e.ctrlKey; + }); + divStream.on('mouseup', '.item.title > a', function (e) { + // Mouseup enables us to catch middle click. if (e.ctrlKey) { - return true; //Allow default control-click behaviour such as open in backround-tab + // CTRL+click, it will be manage by previous rule. + return; + } + + if (e.which == 2) { + // If middle click, we want same behaviour as CTRL+click. + var ev = jQuery.Event("click"); + ev.ctrlKey = true; + $(this).trigger(ev); + } else if(e.which == 1) { + // Normal click, just toggle article. + $(this).parent().click(); } - $(this).parent().click(); //Will perform toggle flux_content - return false; }); divStream.on('click', '.flux .content a', function () { - $(this).attr('target', '_blank'); + $(this).attr('target', '_blank').attr('rel', 'noreferrer'); }); - if (auto_mark_site) { - divStream.on('click', '.flux .link > a', function () { - mark_read($(this).parent().parent().parent(), true); + if (context.auto_mark_site) { + // catch mouseup instead of click so we can have the correct behaviour + // with middle button click (scroll button). + divStream.on('mouseup', '.flux .link > a', function (e) { + if (e.which == 3) { + return; + } + + mark_read($(this).parents(".flux"), true); }); } } @@ -666,7 +783,7 @@ function init_nav_entries() { $nav_entries.find('.up').click(function () { var active_item = $(".flux.current"), windowTop = $(window).scrollTop(), - item_top = active_item.position().top; + item_top = active_item.offset().top; if (windowTop > item_top) { $("html,body").scrollTop(item_top); @@ -677,27 +794,74 @@ function init_nav_entries() { }); } +// <actualize> +var feed_processed = 0; + +function updateFeed(feeds, feeds_count) { + var feed = feeds.pop(); + if (!feed) { + return; + } + + $.ajax({ + type: 'POST', + url: feed.url, + data : { + _csrf: context.csrf, + }, + }).always(function (data) { + feed_processed++; + $("#actualizeProgress .progress").html(feed_processed + " / " + feeds_count); + $("#actualizeProgress .title").html(feed.title); + + if (feed_processed === feeds_count) { + window.location.reload(); + } else { + updateFeed(feeds, feeds_count); + } + }); +} + function init_actualize() { var auto = false; $("#actualize").click(function () { - $.getScript('./?c=javascript&a=actualize').done(function () { - if (auto && feed_count < 1) { + if (ajax_loading) { + return false; + } + + ajax_loading = true; + + $.getJSON('./?c=javascript&a=actualize').done(function (data) { + if (auto && data.feeds.length < 1) { auto = false; + ajax_loading = false; + return false; + } + if (data.feeds.length === 0) { + openNotification(data.feedback_no_refresh, "good"); + ajax_loading = false; return; } - - updateFeeds(); + //Progress bar + var feeds_count = data.feeds.length; + $('body').after('<div id="actualizeProgress" class="notification good">' + data.feedback_actualize + + '<br /><span class="title">/</span><br /><span class="progress">0 / ' + feeds_count + + '</span></div>'); + for (var i = 10; i > 0; i--) { + updateFeed(data.feeds, feeds_count); + } }); + return false; }); - if (auto_actualize_feeds) { + if (context.auto_actualize_feeds) { auto = true; $("#actualize").click(); } } - +// </actualize> // <notification> var notification = null, @@ -712,11 +876,12 @@ function openNotification(msg, status) { notification_working = true; notification.removeClass(); + notification.addClass("notification"); notification.addClass(status); notification.find(".msg").html(msg); notification.fadeIn(300); - notification_interval = window.setInterval(closeNotification, 4000); + notification_interval = window.setTimeout(closeNotification, 4000); } function closeNotification() { @@ -739,23 +904,78 @@ function init_notifications() { if (notification.find(".msg").html().length > 0) { notification_working = true; - notification_interval = window.setInterval(closeNotification, 4000); + notification_interval = window.setTimeout(closeNotification, 4000); } } // </notification> +// <notifs html5> +var notifs_html5_permission = 'denied'; + +function notifs_html5_is_supported() { + return window.Notification !== undefined; +} + +function notifs_html5_ask_permission() { + window.Notification.requestPermission(function () { + notifs_html5_permission = window.Notification.permission; + }); +} + +function notifs_html5_show(nb) { + if (notifs_html5_permission !== "granted") { + return; + } + + var notification = new window.Notification(i18n.notif_title_articles, { + icon: "../themes/icons/favicon-256.png", + body: i18n.notif_body_articles.replace('%d', nb), + tag: "freshRssNewArticles" + }); + + notification.onclick = function() { + window.location.reload(); + }; + + if (context.html5_notif_timeout !== 0) { + setTimeout(function() { + notification.close(); + }, context.html5_notif_timeout * 1000); + } +} + +function init_notifs_html5() { + if (!notifs_html5_is_supported()) { + return; + } + + notifs_html5_permission = notifs_html5_ask_permission(); +} +// </notifs html5> + function refreshUnreads() { $.getJSON('./?c=javascript&a=nbUnreadsPerFeed').done(function (data) { - var isAll = $('.category.all > .active').length > 0; + var isAll = $('.category.all.active').length > 0, + new_articles = false; + $.each(data, function(feed_id, nbUnreads) { feed_id = 'f_' + feed_id; - var elem = $('#' + feed_id + '>.feed').get(0), + var elem = $('#' + feed_id).get(0), feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; + if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) && //Update of current view? (nbUnreads - feed_unreads > 0)) { - $('#new-article').show(); - }; + $('#new-article').attr('aria-hidden', 'false').show(); + new_articles = true; + } }); + + var nb_unreads = str2int($('.category.all .title').attr('data-unread')); + + if (nb_unreads > 0 && new_articles) { + faviconNbUnread(nb_unreads); + notifs_html5_show(nb_unreads); + } }); } @@ -774,7 +994,15 @@ function load_more_posts() { $.get(url_load_more, function (data) { box_load_more.children('.flux:last').after($('#stream', data).children('.flux, .day')); $('.pagination').replaceWith($('.pagination', data)); - $('#bigMarkAsRead').attr('href', $('#nav_menu_read_all>a').attr('href')); + if (context.display_order === 'ASC') { + $('#nav_menu_read_all .read_all').attr( + 'formaction', $('#bigMarkAsRead').attr('formaction') + ); + } else { + $('#bigMarkAsRead').attr( + 'formaction', $('#nav_menu_read_all .read_all').attr('formaction') + ); + } $('[id^=day_]').each(function (i) { var ids = $('[id="' + this.id + '"]'); @@ -784,16 +1012,25 @@ function load_more_posts() { }); init_load_more(box_load_more); - init_lazyload(); $('#load_more').removeClass('loading'); load_more = false; + $(document.body).trigger('sticky_kit:recalc'); }); } +function focus_search() { + $('#search').focus(); +} + +var freshrssLoadMoreEvent = document.createEvent('Event'); +freshrssLoadMoreEvent.initEvent('freshrss:load-more', true, true); + function init_load_more(box) { box_load_more = box; + document.body.dispatchEvent(freshrssLoadMoreEvent); + var $next_link = $("#load_more"); if (!$next_link.length) { // no more article to load @@ -816,7 +1053,7 @@ function init_load_more(box) { } //</endless_mode> -//<Web login form> +//<crypto form (Web login)> function poormanSalt() { //If crypto.getRandomValues is not available var text = '$2a$04$', base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; @@ -826,170 +1063,331 @@ function poormanSalt() { //If crypto.getRandomValues is not available return text; } -function init_loginForm() { - var $loginForm = $('#loginForm'); - if ($loginForm.length === 0) { +function init_crypto_form() { + /* globals dcodeIO */ + var $crypto_form = $('#crypto-form'); + if ($crypto_form.length === 0) { return; } + if (!(window.dcodeIO)) { if (window.console) { console.log('FreshRSS waiting for bcrypt.js…'); } - window.setTimeout(init_loginForm, 100); + window.setTimeout(init_crypto_form, 100); return; } - $loginForm.on('submit', function() { - $('#loginButton').attr('disabled', ''); + + $crypto_form.on('submit', function() { + var $submit_button = $(this).find('button[type="submit"]'); + $submit_button.attr('disabled', ''); + var success = false; $.ajax({ url: './?c=javascript&a=nonce&user=' + $('#username').val(), dataType: 'json', async: false }).done(function (data) { - if (data.salt1 == '' || data.nonce == '') { - alert('Invalid user!'); + if (!data.salt1 || !data.nonce) { + openNotification('Invalid user!', 'bad'); } else { try { var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'), s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1), - c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt()); + c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt()); $('#challenge').val(c); - if (s == '' || c == '') { - alert('Crypto error!'); + if (!s || !c) { + openNotification('Crypto error!', 'bad'); } else { success = true; } } catch (e) { - alert('Crypto exception! ' + e); + openNotification('Crypto exception! ' + e, 'bad'); } } }).fail(function() { - alert('Communication error!'); + openNotification('Communication error!', 'bad'); }); - $('#loginButton').removeAttr('disabled'); + + $submit_button.removeAttr('disabled'); return success; }); } -//</Web login form> +//</crypto form (Web login)> -//<persona> -function init_persona() { - if (!(navigator.id)) { - if (window.console) { - console.log('FreshRSS waiting for Persona…'); + + +function init_confirm_action() { + $('body').on('click', '.confirm', function () { + var str_confirmation = $(this).attr('data-str-confirm'); + if (!str_confirmation) { + str_confirmation = i18n.confirmation_default; + } + + return confirm(str_confirmation); + }); + $('button.confirm').removeAttr('disabled'); +} + +function init_print_action() { + $('.item.share > a[href="#"]').click(function () { + var content = "<html><head><style>" + + "body { font-family: Serif; text-align: justify; }" + + "a { color: #000; text-decoration: none; }" + + "a:after { content: ' [' attr(href) ']'}" + + "</style></head><body>" + + $(".flux.current .content").html() + + "</body></html>"; + + var tmp_window = window.open(); + tmp_window.document.writeln(content); + tmp_window.document.close(); + tmp_window.focus(); + tmp_window.print(); + tmp_window.close(); + + return false; + }); +} + +function init_share_observers() { + shares = $('.group-share').length; + + $('.share.add').on('click', function(e) { + var opt = $(this).siblings('select').find(':selected'); + var row = $(this).parents('form').data(opt.data('form')); + row = row.replace(/##label##/g, opt.html().trim()); + row = row.replace(/##type##/g, opt.val()); + row = row.replace(/##help##/g, opt.data('help')); + row = row.replace(/##key##/g, shares); + $(this).parents('.form-group').before(row); + shares++; + + return false; + }); +} + +function init_stats_observers() { + $('.select-change').on('change', function(e) { + redirect($(this).find(':selected').data('url')); + }); +} + +function init_remove_observers() { + $('.post').on('click', 'a.remove', function(e) { + var remove_what = $(this).attr('data-remove'); + + if (remove_what !== undefined) { + var remove_obj = $('#' + remove_what); + remove_obj.remove(); } - window.setTimeout(init_persona, 100); + + return false; + }); +} + +function init_feed_observers() { + $('select[id="category"]').on('change', function() { + var detail = $('#new_category_name').parent(); + if ($(this).val() === 'nc') { + detail.attr('aria-hidden', 'false').show(); + detail.find('input').focus(); + } else { + detail.attr('aria-hidden', 'true').hide(); + } + }); +} + +function init_password_observers() { + $('.toggle-password').on('mousedown', function(e) { + var button = $(this); + var passwordField = $('#' + button.attr('data-toggle')); + passwordField.attr('type', 'text'); + button.addClass('active'); + + return false; + }).on('mouseup', function(e) { + var button = $(this); + var passwordField = $('#' + button.attr('data-toggle')); + passwordField.attr('type', 'password'); + button.removeClass('active'); + + return false; + }); +} + +function faviconNbUnread(n) { + if (typeof n === 'undefined') { + n = str2int($('.category.all .title').attr('data-unread')); + } + //http://remysharp.com/2010/08/24/dynamic-favicons/ + var canvas = document.createElement('canvas'), + link = document.getElementById('favicon').cloneNode(true); + if (canvas.getContext && link) { + canvas.height = canvas.width = 16; + var img = document.createElement('img'); + img.onload = function () { + var ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0, canvas.width, canvas.height); + if (n > 0) { + var text = ''; + if (n < 1000) { + text = n; + } else if (n < 100000) { + text = Math.floor(n / 1000) + 'k'; + } else { + text = 'E' + Math.floor(Math.log10(n)); + } + ctx.font = 'bold 9px "Arial", sans-serif'; + ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'; + ctx.fillRect(0, 7, ctx.measureText(text).width, 9); + ctx.fillStyle = '#F00'; + ctx.fillText(text, 0, canvas.height - 1); + } + link.href = canvas.toDataURL('image/png'); + $('link[rel~=icon]').remove(); + document.head.appendChild(link); + }; + img.src = '../favicon.ico'; + } +} + +function init_slider_observers() { + var slider = $('#slider'), + closer = $('#close-slider'); + if (slider.length < 1) { return; } - $('a.signin').click(function() { - navigator.id.request(); + + $('.post').on('click', '.open-slider', function() { + if (ajax_loading) { + return false; + } + + ajax_loading = true; + var url_slide = $(this).attr('href'); + + $.ajax({ + type: 'GET', + url: url_slide, + data : { ajax: true } + }).done(function (data) { + slider.html(data); + closer.addClass('active'); + slider.addClass('active'); + ajax_loading = false; + }); + return false; }); - $('a.signout').click(function() { - navigator.id.logout(); + closer.on('click', function() { + closer.removeClass('active'); + slider.removeClass('active'); return false; }); +} - navigator.id.watch({ - loggedInUser: current_user_mail, - - onlogin: function(assertion) { - // A user has logged in! Here you need to: - // 1. Send the assertion to your backend for verification and to create a session. - // 2. Update your UI. - $.ajax ({ - type: 'POST', - url: url_login, - data: {assertion: assertion}, - success: function(res, status, xhr) { - /*if (res.status === 'failure') { - alert (res_obj.reason); - } else*/ if (res.status === 'okay') { - location.href = url_freshrss; - } - }, - error: function(res, status, xhr) { - alert("Login failure: " + res); +function init_configuration_alert() { + $(window).on('submit', function(e) { + window.hasSubmit = true; + }); + $(window).on('beforeunload', function(e) { + if (window.hasSubmit) { + return; + } + var fields = $("[data-leave-validation]"); + for (var i = 0; i < fields.length; i++) { + if ($(fields[i]).attr('type') === 'checkbox' || $(fields[i]).attr('type') === 'radio') { + // The use of != is done on purpose to check boolean against integer + if ($(fields[i]).is(':checked') != $(fields[i]).attr('data-leave-validation')) { + return false; } - }); - }, - onlogout: function() { - // A user has logged out! Here you need to: - // Tear down the user's session by redirecting the user or making a call to your backend. - // Also, make sure loggedInUser will get set to null on the next page load. - // (That's a literal JavaScript null. Not false, 0, or undefined. null.) - $.ajax ({ - type: 'POST', - url: url_logout, - success: function(res, status, xhr) { - location.href = url_freshrss; - }, - error: function(res, status, xhr) { - //alert("logout failure" + res); + } else { + if ($(fields[i]).attr('data-leave-validation') !== $(fields[i]).val()) { + return false; } - }); + } } + return; }); } -//</persona> -function init_confirm_action() { - $('.confirm').click(function () { - return confirm(str_confirmation); +function init_subscription() { + $('body').on('click', '.bookmarkClick', function (e) { + return false; }); } -function init_print_action() { - $('.print-article').click(function () { - var content = "<html><head><style>" - + "body { font-family: Serif; text-align: justify; }" - + "a { color: #000; text-decoration: none; }" - + "a:after { content: ' [' attr(href) ']'}" - + "</style></head><body>" - + $(".flux.current .content").html() - + "</body></html>"; +function parseJsonVars() { + var jsonVars = document.getElementById('jsonVars'), + json = JSON.parse(jsonVars.innerHTML); + jsonVars.outerHTML = ''; + window.context = json.context; + window.shortcuts = json.shortcuts; + window.url = json.url; + window.i18n = json.i18n; + window.icons = json.icons; +} - var tmp_window = window.open(); - tmp_window.document.writeln(content); - tmp_window.document.close(); - tmp_window.focus(); - tmp_window.print(); - tmp_window.close(); +function init_normal() { + $stream = $('#stream'); + if ($stream.length < 1) { + if (window.console) { + console.log('FreshRSS waiting for content…'); + } + window.setTimeout(init_normal, 100); + return; + } + init_column_categories(); + init_stream($stream); + init_shortcuts(); + init_actualize(); + faviconNbUnread(); +} - return false; - }); +function init_beforeDOM() { + if (!window.$) { + if (window.console) { + console.log('FreshRSS waiting for jQuery…'); + } + window.setTimeout(init_beforeDOM, 100); + return; + } + if (['normal', 'reader', 'global'].indexOf(context.current_view) >= 0) { + inject_script('jquery.sticky-kit.min.js'); + init_normal(); + } } -function init_all() { - if (!(window.$ && window.url_freshrss && ((!full_lazyload) || $.fn.lazyload))) { +function init_afterDOM() { + if (!window.$) { if (window.console) { - console.log('FreshRSS waiting for JS…'); + console.log('FreshRSS waiting again for jQuery…'); } - window.setTimeout(init_all, 50); + window.setTimeout(init_afterDOM, 100); return; } init_notifications(); - switch (authType) { - case 'form': - init_loginForm(); - break; - case 'persona': - init_persona(); - break; - } - init_confirm_action(); $stream = $('#stream'); if ($stream.length > 0) { - init_actualize(); - init_column_categories(); + init_confirm_action(); init_load_more($stream); init_posts(); - init_stream($stream); init_nav_entries(); - init_shortcuts(); init_print_action(); + init_notifs_html5(); window.setInterval(refreshUnreads, 120000); + } else { + init_subscription(); + init_crypto_form(); + init_share_observers(); + init_remove_observers(); + init_feed_observers(); + init_password_observers(); + init_stats_observers(); + init_slider_observers(); + init_configuration_alert(); } if (window.console) { @@ -997,16 +1395,16 @@ function init_all() { } } +parseJsonVars(); +init_beforeDOM(); //Can be called before DOM is fully loaded + if (document.readyState && document.readyState !== 'loading') { - if (window.console) { - console.log('FreshRSS immediate init…'); - } - init_all(); + init_afterDOM(); } else if (document.addEventListener) { document.addEventListener('DOMContentLoaded', function () { if (window.console) { console.log('FreshRSS waiting for DOMContentLoaded…'); } - init_all(); + init_afterDOM(); }, false); } |
