function demoHackernews(runner) { const { $, _, prefab } = runner; let allCommentsCount, $commentsWithoutLinks; prefab.clearReport(); prefab.userSettings.addInput('How many rows of stats would you like to display?', { name: 'maxRows', default: 8, type: 'number' }); prefab.userSettings.addSelect('Modify the page to display highlights?', [ 'yes', 'no' ], { name: 'modifyDom', default: 'yes' }); if (location.host !== 'news.ycombinator.com' || location.pathname !== '/item') { prefab.reportHtml('
Tip: visit Hacker News /best
'); // or use prefab.sendAlertMessage } else { runOnDiscussionPage(); } function runOnDiscussionPage() { allCommentsCount = $('tr.comtr').length; // Count total # comments on page load, when this script runs, before they are toggled/hidden $commentsWithoutLinks = $('.comment:visible > span:not(:has(a[rel=nofollow]))').closest('tr.comtr'); prefab.mainMenu.addButton('Toggle Comments Without Links', () => { $commentsWithoutLinks.toggle(); updateUi(); }); runner.on(prefab.Event.UserSettingsChanged, updateUi); // this event fires when the user updates their settings. The listener is passed the updated data (though it is ignored in this example for simplicity) updateUi(); } function updateUi() { prefab.userSettings.snapshot().then(userSettings => { // re-fetches userSettings each time, again for simplicity const stats = _commenterStats(userSettings.maxRows || 8); const areAllVisible = $('tr.comtr').length === allCommentsCount; prefab.reportTable(stats, { segmentId: 'commenterStatistics', sortScore: 20 }); // Set segmentId option to replace an existing report if present, rather than append a new report prefab.reportHtml('' + (allCommentsCount - $commentsWithoutLinks.length) + ' of ' + allCommentsCount + ' posts contain external links. ' + '' + (areAllVisible ? 'Not hiding' : 'Hiding ' + $commentsWithoutLinks.length) + ' posts.
' , { segmentId: 'myCustomHtmlIntro', sortScore: 10 }); // Use sortScore to explicitly position rows in the report (else it uses order they are sent) _doSomethingWithTheDom(stats, userSettings.modifyDom === 'no'); }); } function _commenterStats(maxRows) { // Does not use Wranggle APIs. Gathers post statistics from the discussion page. let res = {}; $('a.hnuser:visible').each((i, el) => { const $el = $(el); const username = $el.text(); res[username] = res[username] || { name: username, posts: 0, chars: 0 }; res[username].posts += 1; res[username].chars += $el.closest('td').text().length; }); return _.orderBy(Object.values(res), [ 'posts', 'chars' ], [ 'desc', 'desc' ]).slice(0, maxRows); } function _doSomethingWithTheDom(stats, skipDomModification) { // Does not use Wranggle APIs. Modifies the DOM to display post count and to auto-scroll on click to the user's next comment $('.inserted-posts-count').remove(); if (skipDomModification) { return; } const gemStyle = 'padding: 3px 8px; margin: 0 4px 0 6px; color: #ffcb05; background-color: #00274c; border-radius: 2px;'; stats.forEach((userInfo) => { const postCount = userInfo.posts; const userPosts = $(`a[href="user?id=${ userInfo.name }"]`); $(`${ postCount } comment${ postCount > 1 ? 's' : '' }`) .click(evt => { const clickedEl = $(evt.target).prev('a')[0]; const nextEl = userPosts[(_.findIndex(userPosts, (el) => el === clickedEl) + 1) % userPosts.length]; $('body').animate({ scrollTop: $(nextEl).offset().top }); }) .css({ cursor: 'pointer' }) .insertAfter(userPosts); }); } }