From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/**

 * @module tinfoboxUtil

 * Common util functions.

 * Usage: see User:Tokenzero/infoboxJournal.js for how to load a module.

 *  import * as util from '/?title=User:Tokenzero/tinfoboxUtil.js&action=raw&ctype=text%2Fjavascript';

 *  (async function() {

 *      console.log(await util.getWikitext('Foo'));

 *  })();

 */



/**

 * Escape HTML special characters.

 *

 * @param {string} s

 * @returns {string}

 */

export function escapeHTML(s) {

    const encoding = {

        '&': '&',

        '<': '&lt;',

        '>': '&gt;',

        '"': '&quot;',

        "'": '&#39;',

        '`': '&#96;'

    };

    return String(s).replace(/[&<>"'`]/g, (s) => encodings]);

}



/**

 * Return whether s is false-y or pure-whitespace string.

 *

 * @param {string} s

 * @returns {boolean}

 */

export function isTrivialString(s) {

    return !s || !s.trim();

}



/**

 * Create an object from a list of entries.

 * Polyfills ECMAScript2019 Object.fromEntries() (not currently supported in Edge).

 *

 * @param {Iterable<[string, *]>} entries - Output of Object.entries(obj) or Map.entries().

 * @returns {object}

 */

export function objectFromEntries(entries) {

    const result = {};

    for (const key, value of entries)

        resultkey = value;

    return result;

}



/**

 * Return the wikitext of given [[pageTitle]].

 *

 * @param {string} pageTitle

 * @returns {Promise<string>}

 */

export async function getWikitext(pageTitle) {

    return $.ajax({

        url: mw.util.getUrl(pageTitle, { action: 'raw' }),

        data: 'text'

    });

}



/**

 * Return whether [[pageTitle]] exists.

 *

 * @param {string} pageTitle

 * @returns {Promise<boolean>}

 */

export async function pageExists(pageTitle) {

    const data = await (new mw.Api()).get({

        formatversion: 2,

        prop: 'info',

        titles: pageTitle

    });

    return !data.query.pages0].missing;

}



/**

 * Return a list of subcategory titles. Not recursive.

 *

 * @param {string} categoryTitle

 * @returns {Promise<Array<string>>}

 */

export async function getSubcategories(categoryTitle) {

    const data = await (new mw.Api()).get({

        formatversion: 2,

        list: 'categorymembers',

        cmtitle: 'Category:' + categoryTitle,

        cmtype: 'subcat',

        cmlimit: 'max' // The default max is 500.

    });

    return data.query.categorymembers.map((c) => c.title);

}



/**

 * Parse and return all categories in given wikitext.

 * Category links are included (useful for testing, drafts).

 * Namespace prefix and sortkey is cut out.

 * So parseCategories('[[:Category:Foo|Foo]]') returns ['Foo'].

 *

 * @param {string} wikitext

 * @returns {Array<string>}

 */

export function parseCategories(wikitext) {

    // In general, use mw.config.get('wgFormattedNamespaces')[14] to get localized name,

    // mw.config.get('wgNamespaceIds') to find aliases, check case-sensitivity settings,

    // see also HotCat for more on whitespace transformations.

    const result = [];

    const catRegex = /\[\[\s*:?\s*[Cc]ategory\s*:\s*([^|\]]+)(|[^\]]+)?\s*\]\]/g;

    wikitext = wikitext.replace(/<!--.*?-->/g, '').replace(/<nowiki>.*?<\/nowiki>/g, '');

    let match;

    while ((match = catRegex.exec(wikitext)) !== null)

        result.push(match1]);

    return result;

}



/**

 * Check if category has a parent category matching some regex.

 * Filters intermediate ancestors to reduce number of api calls.

 *

 * @param {string} categoryTitle - category to start from

 * @param {RegExp} ancestorRegex - the final ancestor should test positively

 * @param {RegExp} interRegex - tested on all intermediate ancestors

 *  (including the final one, excluding the starting categoryTitle)

 * @param {number} maxDepth - depth 0 compares categoryTitle directly with ancestorRegex

 * @returns {Promise<boolean>}

 */

export async function isCategoryChildOf(categoryTitle, ancestorRegex, interRegex, maxDepth) {

    categoryTitle = categoryTitle.replace('Category:', '').replace(/_/g, ' ');

    console.log(maxDepth, categoryTitle);

    if (maxDepth === 0)

        return ancestorRegex.test(categoryTitle);

    if (ancestorRegex.test(categoryTitle))

        return true;

    const parents = await (new mw.Api()).getCategories('Category:' + categoryTitle);

    for (const parentData of parents) {

        const parent = parentData.title;

        if (interRegex.test(parent)) {

            if (await isCategoryChildOf(parent, ancestorRegex, interRegex, maxDepth - 1))

                return true;

        }

    }

    return false;

}



/**

 * Redirect browser to execute specified POST action.

 *

 * @param {string} url

 * @param {Map<string,string>} data

 */

export function redirectPost(url, data) {

    const form = $('<form>', {

        method: 'POST',

        action: url

    });

    for (const k of data.keys()) {

        form.append($('<input>', {

            type: 'hidden',

            name: k,

            value: data.get(k)

        }));

    }

    form.appendTo('body').submit();

}



/**

 * Redirect to diff-preview view with modified wikitext.

 *

 * @param {string} wikitext

 * @param {string} summary

 */

export async function redirectToPreviewDiff(wikitext, summary) {

    const r = await (new mw.Api()).get({

        prop: 'revisions',

        rvprop: 'timestamp',

        revids: mw.config.get('wgRevisionId')

    });

    const wgEdittime = r.query.pagesmw.config.get('wgArticleId')]

        .revisions0].timestamp.replace(/[^0-9]/gi, '').slice(0, 12);

    const wgStarttime = new Date(window.performance.timing.requestStart)

        .toISOString().replace(/[^0-9]/gi, '').slice(0, 12);

    redirectPost(

        mw.util.getUrl(mw.config.get('wgPageName'), { action: 'edit' }),

        new Map([

            'editRevId', mw.config.get('wgRevisionId')],

            'baseRevId', mw.config.get('wgRevisionId')],

            'wpSection', ''],

            'wpStarttime', wgStarttime],

            'wpEdittime', wgEdittime],

            'parentRevId', mw.config.get('wgRevisionId')],

            'format', 'text/x-wiki'],

            'model', 'wikitext'],

            'wpTextbox1', wikitext],

            'wpSummary', summary],

            'wpAutoSummary', 'd41d8cd98f00b204e9800998ecf8427e'], // this is md5('')

            'wpDiff', 'Show changes'], // ['wpPreview', 'yes'],

            'wpEditToken', mw.user.tokens.get('csrfToken')],

            'mode', 'preview'],

            'wpUltimateParam', 1 // A weird mediawiki safety check.

        ])

    );

}
From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/**

 * @module tinfoboxUtil

 * Common util functions.

 * Usage: see User:Tokenzero/infoboxJournal.js for how to load a module.

 *  import * as util from '/?title=User:Tokenzero/tinfoboxUtil.js&action=raw&ctype=text%2Fjavascript';

 *  (async function() {

 *      console.log(await util.getWikitext('Foo'));

 *  })();

 */



/**

 * Escape HTML special characters.

 *

 * @param {string} s

 * @returns {string}

 */

export function escapeHTML(s) {

    const encoding = {

        '&': '&amp;',

        '<': '&lt;',

        '>': '&gt;',

        '"': '&quot;',

        "'": '&#39;',

        '`': '&#96;'

    };

    return String(s).replace(/[&<>"'`]/g, (s) => encodings]);

}



/**

 * Return whether s is false-y or pure-whitespace string.

 *

 * @param {string} s

 * @returns {boolean}

 */

export function isTrivialString(s) {

    return !s || !s.trim();

}



/**

 * Create an object from a list of entries.

 * Polyfills ECMAScript2019 Object.fromEntries() (not currently supported in Edge).

 *

 * @param {Iterable<[string, *]>} entries - Output of Object.entries(obj) or Map.entries().

 * @returns {object}

 */

export function objectFromEntries(entries) {

    const result = {};

    for (const key, value of entries)

        resultkey = value;

    return result;

}



/**

 * Return the wikitext of given [[pageTitle]].

 *

 * @param {string} pageTitle

 * @returns {Promise<string>}

 */

export async function getWikitext(pageTitle) {

    return $.ajax({

        url: mw.util.getUrl(pageTitle, { action: 'raw' }),

        data: 'text'

    });

}



/**

 * Return whether [[pageTitle]] exists.

 *

 * @param {string} pageTitle

 * @returns {Promise<boolean>}

 */

export async function pageExists(pageTitle) {

    const data = await (new mw.Api()).get({

        formatversion: 2,

        prop: 'info',

        titles: pageTitle

    });

    return !data.query.pages0].missing;

}



/**

 * Return a list of subcategory titles. Not recursive.

 *

 * @param {string} categoryTitle

 * @returns {Promise<Array<string>>}

 */

export async function getSubcategories(categoryTitle) {

    const data = await (new mw.Api()).get({

        formatversion: 2,

        list: 'categorymembers',

        cmtitle: 'Category:' + categoryTitle,

        cmtype: 'subcat',

        cmlimit: 'max' // The default max is 500.

    });

    return data.query.categorymembers.map((c) => c.title);

}



/**

 * Parse and return all categories in given wikitext.

 * Category links are included (useful for testing, drafts).

 * Namespace prefix and sortkey is cut out.

 * So parseCategories('[[:Category:Foo|Foo]]') returns ['Foo'].

 *

 * @param {string} wikitext

 * @returns {Array<string>}

 */

export function parseCategories(wikitext) {

    // In general, use mw.config.get('wgFormattedNamespaces')[14] to get localized name,

    // mw.config.get('wgNamespaceIds') to find aliases, check case-sensitivity settings,

    // see also HotCat for more on whitespace transformations.

    const result = [];

    const catRegex = /\[\[\s*:?\s*[Cc]ategory\s*:\s*([^|\]]+)(|[^\]]+)?\s*\]\]/g;

    wikitext = wikitext.replace(/<!--.*?-->/g, '').replace(/<nowiki>.*?<\/nowiki>/g, '');

    let match;

    while ((match = catRegex.exec(wikitext)) !== null)

        result.push(match1]);

    return result;

}



/**

 * Check if category has a parent category matching some regex.

 * Filters intermediate ancestors to reduce number of api calls.

 *

 * @param {string} categoryTitle - category to start from

 * @param {RegExp} ancestorRegex - the final ancestor should test positively

 * @param {RegExp} interRegex - tested on all intermediate ancestors

 *  (including the final one, excluding the starting categoryTitle)

 * @param {number} maxDepth - depth 0 compares categoryTitle directly with ancestorRegex

 * @returns {Promise<boolean>}

 */

export async function isCategoryChildOf(categoryTitle, ancestorRegex, interRegex, maxDepth) {

    categoryTitle = categoryTitle.replace('Category:', '').replace(/_/g, ' ');

    console.log(maxDepth, categoryTitle);

    if (maxDepth === 0)

        return ancestorRegex.test(categoryTitle);

    if (ancestorRegex.test(categoryTitle))

        return true;

    const parents = await (new mw.Api()).getCategories('Category:' + categoryTitle);

    for (const parentData of parents) {

        const parent = parentData.title;

        if (interRegex.test(parent)) {

            if (await isCategoryChildOf(parent, ancestorRegex, interRegex, maxDepth - 1))

                return true;

        }

    }

    return false;

}



/**

 * Redirect browser to execute specified POST action.

 *

 * @param {string} url

 * @param {Map<string,string>} data

 */

export function redirectPost(url, data) {

    const form = $('<form>', {

        method: 'POST',

        action: url

    });

    for (const k of data.keys()) {

        form.append($('<input>', {

            type: 'hidden',

            name: k,

            value: data.get(k)

        }));

    }

    form.appendTo('body').submit();

}



/**

 * Redirect to diff-preview view with modified wikitext.

 *

 * @param {string} wikitext

 * @param {string} summary

 */

export async function redirectToPreviewDiff(wikitext, summary) {

    const r = await (new mw.Api()).get({

        prop: 'revisions',

        rvprop: 'timestamp',

        revids: mw.config.get('wgRevisionId')

    });

    const wgEdittime = r.query.pagesmw.config.get('wgArticleId')]

        .revisions0].timestamp.replace(/[^0-9]/gi, '').slice(0, 12);

    const wgStarttime = new Date(window.performance.timing.requestStart)

        .toISOString().replace(/[^0-9]/gi, '').slice(0, 12);

    redirectPost(

        mw.util.getUrl(mw.config.get('wgPageName'), { action: 'edit' }),

        new Map([

            'editRevId', mw.config.get('wgRevisionId')],

            'baseRevId', mw.config.get('wgRevisionId')],

            'wpSection', ''],

            'wpStarttime', wgStarttime],

            'wpEdittime', wgEdittime],

            'parentRevId', mw.config.get('wgRevisionId')],

            'format', 'text/x-wiki'],

            'model', 'wikitext'],

            'wpTextbox1', wikitext],

            'wpSummary', summary],

            'wpAutoSummary', 'd41d8cd98f00b204e9800998ecf8427e'], // this is md5('')

            'wpDiff', 'Show changes'], // ['wpPreview', 'yes'],

            'wpEditToken', mw.user.tokens.get('csrfToken')],

            'mode', 'preview'],

            'wpUltimateParam', 1 // A weird mediawiki safety check.

        ])

    );

}

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook