User:Irukaza/common.js:修订间差异

H萌娘,万物皆可H的百科全书!
跳到导航 跳到搜索
imported>=海豚=
无编辑摘要
imported>=海豚=
无编辑摘要
(未显示同一用户的1个中间版本)
第1行: 第1行:
//<nowiki>
// <pre>
// 由[[mediawiki:gadget-definitions.js]]调用,可以使用ES6语法
/**
* @Function: 使用表格界面显示和编辑小工具定义
* @Dependencies: mediawiki.api, jquery.tablesorter, oojs-ui-widgets
* @Author: [[User:Bhsd]]
* @Warning: 未添加繁体中文
*/
"use strict";
"use strict";
/* global OO */
$(() => (async () => {
$(() => {
   if (mw.config.get("wgCanonicalSpecialPageName") !== "Recentchanges" || !mw.config.get("wgUserGroups").includes("autoconfirmed")) {
   // 1. 最基本的工具函数,用于text、HTML、data三种状态间转换
      return;
   const Keys = ['name', 'type', 'default', 'peers', 'dependencies', 'rights', 'targets', 'hidden', 'pages'],
   }
     dictionary = { type: {general: '通常', styles: '纯CSS'},
   await mw.loader.using("mw.Api");
     targets: {desktop: '桌面版', mobile: '手机版', 'desktop,mobile': '通用'}
    const uuidv4 = () => {
   },
      let result = "";
     text2data = (text) => {
      while (result === "" || $(`#${result}, [name="${result}"]`).length > 0) {
     const parts = text.match( /^(.+)\[(.+)]\|(.+)$/ ),
        result = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
       params = $.extend({name: parts[1], pages: parts[3], type: 'general', targets: 'desktop'},
          (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16),
       Object.fromEntries( parts[2].split( '|' ).map(s => s.includes( '=' ) ? s.split( '=' ) : [s, true]) ));
       );
      delete params.ResourceLoader;
     ['peers', 'dependencies', 'rights', 'pages'].forEach(key => {
       params[key] = params[key] ? params[key].split(key == 'pages' ? '|' : ',').sort() : [];
     });
     return params;
    },
     data2html = (params, key) => {
      const val = params[key];
      switch(key) {
       case 'name':
         return $('<span>', {id: val, html: [ val,
           $('<a>', {href: '#', title: '删除', html: $('<i>', {class: 'far fa-minus-square'})})
         ]});
        case 'type':
       case 'targets':
         return dictionary[key][val];
       case 'default':
       case 'hidden':
          return val ? '是' : '否';
       case 'peers':
         return val.map(ele => $('<div>', {html: $('<a>', {text: ele, href: `#${ele}`})}));
       case 'dependencies':
       case 'rights':
         return val.map(ele => $('<div>', {text: ele}));
       case 'pages':
         return val.map(ele => $('<div>', {html: $('<a>', {text: ele,
           href: mw.util.getUrl( `mediawiki:gadget-${ele}` )})}));
      }
      }
    },
     return result;
     data2text = (params) => {
    };
     return `* ${ params.name }[${ params.type == 'general' ? 'ResourceLoader' : 'type=styles' }` +
   const api = new mw.Api();
       ['default', 'hidden'].map(key => params[key] ? `|${key}` : '').join( '' ) +
   $('.tagfilterForm .mw-input input[type="submit"]').appendTo(".namespaceForm .mw-input").css("margin-left", ".75em");
       ['peers', 'dependencies', 'rights'].map(key =>
    const container = $("<table/>");
         params[key].length ? `|${key}=${ params[key].join(',') }` : '').join( '' ) +
   container.html("<td> 加载中……</td>").css("border-collapse", "collapse");
       (params.targets == 'desktop' ? '' : `|targets=${params.targets}`) +
    $(".tagfilterForm .mw-input").empty().append(container);
       `]|${ params.pages.join( '|' ) }`;
    const tags = await (async () => {
    },
      const result = [];
   // 2. 使用表格显示小工具定义
      try {
     insertRow = function() {
       const cache = JSON.parse(localStorage.getItem("AnnTools-tags"));
     const params = text2data( this.textContent );
       if (!$.isPlainObject(cache)
     return $('<tr>', {class: 'defTr', html: Keys.map(key => $('<td>', {html: data2html(params, key)}))})
         || typeof cache.timestamp !== "number" || cache.timestamp < new Date().getTime() - 24 * 60 * 60 * 1000
       .data('params', params)[0];
         || !Array.isArray(cache.tags)) {
    },
         throw new Error();
   // 3. 使用表格修改小工具定义
       } else {
     api = new mw.Api(),
         for (const tag of cache.tags) {
     btns = [ new OO.ui.ButtonWidget({label: '保存', flags: ['primary', 'progressive']}),
           if (!("name" in tag && "displayname" in tag && "description" in tag)) {
     new OO.ui.ButtonWidget({label: '添加', flags: 'progressive'})
             throw new Error();
    ],
           }
     $tr = $('<tr>', {html: $('<td>', {colspan: 9, html: btns.map(ele => ele.$element)})}),
         }
      boolWidget = new OO.ui.DropdownInputWidget({options: [{data: 'true', label: '是'}, {data: '', label: '否'}]}),
       }
      freeWidget = new OO.ui.TagMultiselectWidget({allowArbitrary: true}),
        result.push(...cache.tags);
     getOptions = (key) => Object.entries( dictionary[key] ).map(ele => ({data: ele[0], label: ele[1]})),
     } catch {
     widgets = [],
        const eol = Symbol();
     endEdit = (widget) => {
        let tgcontinue = undefined;
     if (!document.body.contains( widget.$element[0] )) { return; }
        while (tgcontinue !== eol) {
     const $oldTd = widget.$element.closest( 'td' ),
         const _result = await api.post({
       params = $oldTd.closest( '.defTr' ).data( 'params' ),
           action: "query",
       key = Keys[ $oldTd.index() ];
           list: "tags",
     params[key] = widget.getValue();
           tglimit: "max",
     widget.$element.detach();
           tgprop: "displayname|description|active",
     $oldTd.html( data2html(params, key) );
           tgcontinue,
   },
         });
     save = () => {
         if (_result.continue) {
     btns[0].setDisabled( true );
           tgcontinue = _result.continue.tgcontinue;
     widgets.forEach( endEdit );
          } else {
     const $table = $tr.closest( 'table' ),
           tgcontinue = eol;
        revid = mw.config.get( 'wgCurRevisionId' ),
          }
       pageid = mw.config.get( 'wgArticleId' ),
          result.push(..._result.query.tags.filter((tag) => "active" in tag).map((tag) => {
       section = $table.index( '.defTable' ) + 1,
           delete tag.active;
       // 注意可能标题自动编号
           return tag;
        sectionName = $( '.mw-headline' ).eq( section - 1 ).contents().last().text().trim(),
         }));
        text = `==${ sectionName }==\n` +
       }
        [...$table.find( '.defTr' )].map(ele => data2text( $(ele).data( 'params' ) )).join( '\n' );
       localStorage.setItem("AnnTools-tags", JSON.stringify({
     mw.safeEdit(api, revid, {pageid, section, text, summary: `/*${sectionName}*/ 使用definitions小工具编辑`})
         timestamp: new Date().getTime(),
       .then(() => { location.reload(); },
          tags: result,
       reason => { btns[0].setDisabled( reason == 'editConflict' ); });
        }));
   },
     edit = function(e) {
     if (widgets.length === 0) {
       widgets.push(...[ new OO.ui.TextInputWidget(),
          new OO.ui.DropdownInputWidget({options: getOptions( 'type' )}),
          boolWidget,
          new OO.ui.MenuTagMultiselectWidget({allowArbitrary: true, options: [...$('.defTr')].map(ele =>
           $(ele).data( 'params' )).filter(ele => ele.type == 'styles').map(ele => ({data: ele.name}))}),
         freeWidget,
         freeWidget,
         new OO.ui.DropdownInputWidget({options: getOptions( 'targets' )}),
          boolWidget,
         freeWidget
        ]);
      }
      }
      const $td = $(this),
      return result;
       i = $td.index(),
   })();
       params = $td.closest( '.defTr' ).data( 'params' ),
   const select = $("<select/>");
       widget = widgets[i],
   let defaultValue = uuidv4();
       table = e.delegateTarget;
   while (tags.includes(defaultValue)) {
      if (!params) { return; }
     defaultValue = uuidv4();
      if (!table.contains( $tr[0] )) { $tr.appendTo( table ); }
   }
     if ($td[0].contains( widget.$element[0] )) { return; }
   select.html(`<option selected value="${defaultValue}">未选择(可多选)</option>`).attr({
      endEdit( widget );
     autocomplete: "none",
      widget.setValue( params[ Keys[i] ] || '' );
     multiple: "multiple",
     $td.html( widget.$element );
   });
    },
   tags.forEach(({ displayname, description, name }) => {
      deleteRow = function(e) {
      const option = $("<option/>");
     e.preventDefault();
      option.text(displayname).attr({
     $(this).closest( '.defTr' ).detach();
       value: name,
    };
       title: `${description} (${name})`,
    $('h2:has( .mw-editsection )').next().children( 'ul' ).addBack( 'ul' ).replaceWith(function() {
      });
      return $('<table>', {class: 'wikitable sortable defTable', html: $('<tbody>', {html: $('<tr>', {html:
      select.append(option);
       ['名称', '类型', '默认', 'Peers', 'Dependencies', '权限', '范围', ' 隐藏', '链接']
   });
         .map(ele => $('<th>', {text: ele}))
   const selectTd = $("<td/>");
     }).add( $(this).children().map( insertRow ) )})}).tablesorter()
    selectTd.css({
       .on('dblclick', 'td', edit).on('click', 'i', deleteRow);
     "vertical-align": "middle",
      padding: "0",
   });
   selectTd.append(select);
   container.empty().append(selectTd);
    const typeCheckbox = $("<input/>");
    const typeId = uuidv4();
   typeCheckbox.attr({
     type: "checkbox",
     id: typeId,
     title: "勾选以隐藏未勾选标签",
   });
   const typeLabel = $("<label/>");
   typeLabel.text("隐藏未勾选标签").attr({
      "for": typeId,
     title: "勾选以隐藏未勾选标签",
   });
   const methodCheckbox = $("<input/>");
   const methodId = uuidv4();
   methodCheckbox.attr({
     type: "checkbox",
     id: methodId,
     title: "勾选以隐藏/显示只包含指定标签的",
   });
   const methodLabel = $("<label/>");
   methodLabel.text("隐藏/显示只包含指定标签的").attr({
     "for": methodId,
     title: "勾选以 隐藏/显示只包含指定标签的",
   });
   const checkboxTd = $("<td/>");
   checkboxTd.css({
     "vertical-align": "middle",
     padding: "0",
   });
   checkboxTd.append(typeCheckbox);
   checkboxTd.append(typeLabel);
   checkboxTd.append(methodCheckbox);
   checkboxTd.append(methodLabel);
   checkboxTd.append('<div style="padding-left: .25em">在 Chrome 和新版 Edge 等一众 Chromium 系浏览器中,按住 Ctrl 键即可多选,<br>按住 Shift 键或按右键可以批量选择已选到当前选定之间的所有项。</div>');
   container.append(checkboxTd);
   $(".mw-changeslist-line").each((_, ele) => {
     ele.dataset.tags = Array.from(ele.classList).filter((cls) => cls.startsWith("mw-tag-")).map((cls) => cls.replace("mw-tag-", "")).join("|");
    });
    });
    btns[0].on('click', save);
    mw.loader.addStyleTag(".hidden { display: none !important; }");
    btns[1].$element.click(function() {
    select.add(typeCheckbox).add(methodCheckbox).on("change", () => {
      const params = {name: '', type: 'general', peers: [], dependencies: [], rights: [], targets: 'desktop',
      const type = typeCheckbox.prop("checked");
       pages: []};
     const method = methodCheckbox.prop("checked");
      $('<tr>', {class: 'defTr', html: Keys.map(key => $('<td>', {html: data2html(params, key)}))})
      const tags = select.val().map((tag) => tag.replace(/ /g, "_"));
        .data('params', params).insertBefore( $(this).closest( 'tr' ) );
     Array.from(document.getElementsByClassName("mw-changeslist-line")).forEach((ele) => {
       const eleTags = ele.dataset.tags.split("|");
       if (method) {
         if (eleTags.filter((tag) => !tags.includes(tag)).length > 0) {
           ele.classList[type ? "add" : "remove"]("hidden");
         } else {
           ele.classList[type ? "remove" : "add"]("hidden");
         }
        } else {
         if (eleTags.filter((tag) => tags.includes(tag)).length > 0) {
           ele.classList[type ? "remove" : "add"]("hidden");
         } else {
           ele.classList[type ? "add" : "remove"]("hidden");
         }
       }
     });
    });
    });
});
})());
//</nowiki>
// </pre>

2021年7月29日 (四) 11:05的版本

// <pre>
"use strict";
$(() => (async () => {
    if (mw.config.get("wgCanonicalSpecialPageName") !== "Recentchanges" || !mw.config.get("wgUserGroups").includes("autoconfirmed")) {
        return;
    }
    await mw.loader.using("mw.Api");
    const uuidv4 = () => {
        let result = "";
        while (result === "" || $(`#${result}, [name="${result}"]`).length > 0) {
            result = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
                (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16),
            );
        }
        return result;
    };
    const api = new mw.Api();
    $('.tagfilterForm .mw-input input[type="submit"]').appendTo(".namespaceForm .mw-input").css("margin-left", ".75em");
    const container = $("<table/>");
    container.html("<td>加载中……</td>").css("border-collapse", "collapse");
    $(".tagfilterForm .mw-input").empty().append(container);
    const tags = await (async () => {
        const result = [];
        try {
            const cache = JSON.parse(localStorage.getItem("AnnTools-tags"));
            if (!$.isPlainObject(cache)
                || typeof cache.timestamp !== "number" || cache.timestamp < new Date().getTime() - 24 * 60 * 60 * 1000
                || !Array.isArray(cache.tags)) {
                throw new Error();
            } else {
                for (const tag of cache.tags) {
                    if (!("name" in tag && "displayname" in tag && "description" in tag)) {
                        throw new Error();
                    }
                }
            }
            result.push(...cache.tags);
        } catch {
            const eol = Symbol();
            let tgcontinue = undefined;
            while (tgcontinue !== eol) {
                const _result = await api.post({
                    action: "query",
                    list: "tags",
                    tglimit: "max",
                    tgprop: "displayname|description|active",
                    tgcontinue,
                });
                if (_result.continue) {
                    tgcontinue = _result.continue.tgcontinue;
                } else {
                    tgcontinue = eol;
                }
                result.push(..._result.query.tags.filter((tag) => "active" in tag).map((tag) => {
                    delete tag.active;
                    return tag;
                }));
            }
            localStorage.setItem("AnnTools-tags", JSON.stringify({
                timestamp: new Date().getTime(),
                tags: result,
            }));
        }
        return result;
    })();
    const select = $("<select/>");
    let defaultValue = uuidv4();
    while (tags.includes(defaultValue)) {
        defaultValue = uuidv4();
    }
    select.html(`<option selected value="${defaultValue}">未选择(可多选)</option>`).attr({
        autocomplete: "none",
        multiple: "multiple",
    });
    tags.forEach(({ displayname, description, name }) => {
        const option = $("<option/>");
        option.text(displayname).attr({
            value: name,
            title: `${description} (${name})`,
        });
        select.append(option);
    });
    const selectTd = $("<td/>");
    selectTd.css({
        "vertical-align": "middle",
        padding: "0",
    });
    selectTd.append(select);
    container.empty().append(selectTd);
    const typeCheckbox = $("<input/>");
    const typeId = uuidv4();
    typeCheckbox.attr({
        type: "checkbox",
        id: typeId,
        title: "勾选以隐藏未勾选标签",
    });
    const typeLabel = $("<label/>");
    typeLabel.text("隐藏未勾选标签").attr({
        "for": typeId,
        title: "勾选以隐藏未勾选标签",
    });
    const methodCheckbox = $("<input/>");
    const methodId = uuidv4();
    methodCheckbox.attr({
        type: "checkbox",
        id: methodId,
        title: "勾选以隐藏/显示只包含指定标签的",
    });
    const methodLabel = $("<label/>");
    methodLabel.text("隐藏/显示只包含指定标签的").attr({
        "for": methodId,
        title: "勾选以隐藏/显示只包含指定标签的",
    });
    const checkboxTd = $("<td/>");
    checkboxTd.css({
        "vertical-align": "middle",
        padding: "0",
    });
    checkboxTd.append(typeCheckbox);
    checkboxTd.append(typeLabel);
    checkboxTd.append(methodCheckbox);
    checkboxTd.append(methodLabel);
    checkboxTd.append('<div style="padding-left: .25em">在 Chrome 和新版 Edge 等一众 Chromium 系浏览器中,按住 Ctrl 键即可多选,<br>按住 Shift 键或按右键可以批量选择已选到当前选定之间的所有项。</div>');
    container.append(checkboxTd);
    $(".mw-changeslist-line").each((_, ele) => {
        ele.dataset.tags = Array.from(ele.classList).filter((cls) => cls.startsWith("mw-tag-")).map((cls) => cls.replace("mw-tag-", "")).join("|");
    });
    mw.loader.addStyleTag(".hidden { display: none !important; }");
    select.add(typeCheckbox).add(methodCheckbox).on("change", () => {
        const type = typeCheckbox.prop("checked");
        const method = methodCheckbox.prop("checked");
        const tags = select.val().map((tag) => tag.replace(/ /g, "_"));
        Array.from(document.getElementsByClassName("mw-changeslist-line")).forEach((ele) => {
            const eleTags = ele.dataset.tags.split("|");
            if (method) {
                if (eleTags.filter((tag) => !tags.includes(tag)).length > 0) {
                    ele.classList[type ? "add" : "remove"]("hidden");
                } else {
                    ele.classList[type ? "remove" : "add"]("hidden");
                }
            } else {
                if (eleTags.filter((tag) => tags.includes(tag)).length > 0) {
                    ele.classList[type ? "remove" : "add"]("hidden");
                } else {
                    ele.classList[type ? "add" : "remove"]("hidden");
                }
            }
        });
    });
})());
// </pre>