LLWiki正在建设中,欢迎加入我们

MediaWiki:Gadget-TalkHelper.js

来自LLWiki
跳转到导航 跳转到搜索

注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Internet Explorer:按住Ctrl的同时单击刷新,或按Ctrl-F5
  • Opera:前往菜单 → 设置(Mac为Opera → Preferences),然后隐私和安全 → 清除浏览数据 → 缓存的图片和文件
//<nowiki>
// 由ResourceLoader直接调用,不可使用ES6语法
/** 
 * @Source: [[wikipedia:user:GhostInTheMachine/TalkHelper.js]]
 * @Dependencies: mediawiki.storage, ext.gadget.CommentsInLocalTime, moment
 * @ModifiedBy: [[User:Bhsd]]
 * @Function: 上次访问(依赖localStorage)以来的新回复会高亮显示,同时屏幕右侧的滚动至底部按钮将修改为滚动至下一处新回复
 * @Issue: 与inspect小工具的原位预览不兼容
 */
"use strict";
/*global mw, $, moment, wgULS*/
const id = mw.config.get( 'wgArticleId' ),
    ns = mw.config.get( 'wgNamespaceNumber' );
// 包括更新localStorage在内,以下所有内容只应在阅读讨论页的最新版本(含历史版本与当前的差异)时生效
if (mw.config.get('wgAction') == 'view' && mw.config.get( 'wgRevisionId' ) == mw.config.get( 'wgCurRevisionId' ) &&
    ns >= 0 && id > 0 && (ns % 2 == 1 || mw.config.get( 'wgPageName' ) == 'LLWiki:互助客棧')) {
    const gadgets = mw.gadgets || {},
        talkHelper = gadgets.talkHelper || {},
        expiry = (talkHelper.expiry || 7) * 1000 * 3600 * 24, // localStorage的有效期恰好与新回复的最长追溯时限一致
        now = mw.now(),
        expired = now - expiry,
        // 更新localStorage只应该在最开始执行一次,且这不依赖于本次访问有无新回复
        hist = Object.fromEntries( (mw.storage.getObject( 'LLWiki-talkHelper' ) || [])
        .filter(function(ele) { return ele[1] > expired; }) ),
        last = moment( hist[id] || expired );
    hist[id] = now;
    // 转换为数组格式是为了方便调用filter方法,否则也可以遍历对象后使用delete语法
    mw.storage.setObject( 'LLWiki-talkHelper', Object.entries(hist) );
    // 第一部分为在ext.gadget.CommentsInLocalTime执行完后识别新回复,没有签名时间戳时不会触发
    mw.hook( 'local.comments' ).add(function($content) {
        // 排除掉Wikiplus预览的情形,但无法排除inspect小工具的预览
        if ($content.closest( '#Wikiplus-Quickedit-Preview-Output' ).length) { return; }
        // $content是页面内容,即#mw-content-text;.LocalComments是时间戳,title为CST时区的ISO时间
        const $comments = $content.find( '.LocalComments' ).filter(function() {
            // 非法时间戳也会被过滤掉,未来可能不会给非法时间戳添加LocalComments类
            return moment.utc(this.title, 'YYYY-MM-DD HH:mm').add(-8, 'hours').isAfter(last);
        });

        if ($comments.length === 0) { 
            mw.notify( wgULS('没有新回复!', '沒有新回覆!') );
            return;
        }
        console.log('Hook: local.comments, 开始标注新增回复');
        // 以下代码用于分离各人的回复
        // 首先分离同一个dd内的text节点和下一级dl,一个dd内存在多个子节点只可能是因为第一个为text或其他节点
        $content.find( 'dd:has(dl)' ).filter(function() { return $(this).contents().length > 1; }).after(function() {
            return $(this).children('dl').wrap( '<dd>' ).parent();
        });
        // 继续分离dl的不同dd子节点,但只有最后一个子节点包含签名时间戳时可以不用分离
        $content.find( 'dd:not(:last-child):has(.LocalComments)' ).parent('dl').each(function() {
            const $self = $(this),
                $parents = $self.parents('dl'),
                // 有可能自己就是最外层dl
                $ancestor = $parents.addBack().first();
            // 最后一个子节点即使没有签名时间戳也需要分离
            $self.children( ':has(.LocalComments)' ).add( $self.children().last() ).map(function() {
                return $(this).prevUntil( ':has(.LocalComments)' ).addBack().wrapAll( '<dl>' ).parent()[0];
            }).insertAfter( $ancestor ).wrap( '<dl><dd>'.repeat( $parents.length ) );
            // 注意顺序,必须现移至$ancestor外再wrap;最好还需要移除残留的空dl
        });
        // 高亮显示新回复所在的最外层容器
        $comments.each(function() {
            const $ancestor = $(this).parents('dl, p').last(),
                $siblings = $ancestor.prevUntil( ':header, :has( .LocalComments )' );
            if ($siblings.length === 0) { $ancestor.addClass( 'newLocalComments' ); }
            else { $siblings.add( $ancestor ).wrapAll( $('<div>', {class: 'newLocalComments'}) ); }
        });
        // 换个气泡样式表示标注成功
        mw.notify('共有' + $comments.length + wgULS('条新回复!', '條新回覆!'), {type: 'success'});
    });
    // 第二部分为在有新回复的情况下修改按钮功能,to.bottom总是发生在在local.comments之后
    mw.hook( 'to.bottom' ).add(function($btn) {
        const $comments = $('.newLocalComments'),
            n = $comments.length;
        // 没有新回复时什么都不用改
        if (!talkHelper.scroll || n === 0) { return; }
        
        var i = 0;
        const $body = $(document.scrollingElement || 'html'), // 兼容不同浏览器
            $doc = $(document),
            $win = $(window),
            // 用于处理粘性章节标题
            offset = mw.config.get('skin') == 'vector' ? 40 : 70;
        // 移除原有的事件处理函数
        $btn.off('click').click(function() {
            // $doc.height() - $win.height()相当于FireFox独有的window.scrollMaxY,高度不能提前计算
            $body.animate({scrollTop: i < n ? $comments.eq(i).offset().top - offset : $doc.height() - $win.height()});
            i++;
        });
        // to.bottom事件发生时回到顶部按钮一定存在
        $('.backtotop').first().click(function() { i = 0; });
        $comments.click(function() { i = $(this).index( '.newLocalComments' ) + 1; });
    });
}
//</nowiki>
// [[category:讨论工具]] [[category:作为模块的小工具]] [[category:桌面版小工具]] [[category:手机版小工具]]
// {{DEFAULTSORT:TalkHelper.js}}