コンテンツにスキップ

「MediaWiki:MobileUI.js」の版間の差分

提供: Vikipedia
細編集の要約なし
タグ: モバイル編集 モバイルウェブ編集
編集の要約なし
タグ: モバイル編集 モバイルウェブ編集
75行目: 75行目:
     hrefForMenuDataItemComponent(component) {
     hrefForMenuDataItemComponent(component) {
         switch (component.id) {
         switch (component.id) {
             case "mobile-ui-page-short-url":
             case MobileUIBuilder.pageShortURLID:
                 return (
                 return (
                     this.wgServer
                     this.wgServer
101行目: 101行目:
         element.href = encodeURI(href);
         element.href = encodeURI(href);
         element.id = component.id || "";
         element.id = component.id || "";
         element.classList.add(...[].concat(component.class));
         element.classList.add(...[].concat(component.class || []));
         element.setAttribute("data-event-name", component.dataEventName || "");
         element.setAttribute(MobileUIBuilder.dataEventNameAttr, component.dataEventName || "");


         return element;
         return element;
123行目: 123行目:
         const element = window.document.createElement("li");
         const element = window.document.createElement("li");
         element.id = item.id || "";
         element.id = item.id || "";
         element.classList.add(...[].concat(item.class));
         element.classList.add(...[].concat(item.class || []));
         element.append(...children);
         element.append(...children);


141行目: 141行目:
         for (const item of group.items) {
         for (const item of group.items) {
             children.push(this.menuDataItem(item));
             children.push(this.menuDataItem(item));
            Function.call
         }
         }


         const element = window.document.createElement("ul");
         const element = window.document.createElement("ul");
         element.id = group.id || "";
         element.id = group.id || "";
         element.classList.add(...[].concat(group.class));
         element.classList.add(...[].concat(group.class || []));
         element.append(...children);
         element.append(...children);


167行目: 166行目:


         return children;
         return children;
    }
    /**
    * @param {Node} node
    * @returns {HTMLElement | null}
    */
    elementIfTarget(node) {
        const element = /** @type {HTMLElement} */ (node);
        const { classList } = element;
        if (!classList) return null;
        if (!classList.contains("menu")) return null;
        if (classList.contains("menu-right")) return null;
        return element;
     }
     }


187行目: 171行目:
     * @returns {HTMLElement}
     * @returns {HTMLElement}
     */
     */
     get navElement() {
     get menuContainerElement() {
         return window.document.getElementById("mw-mf-page-left");
         return window.document.getElementById(MobileUIBuilder.menuContainerID);
     }
     }


196行目: 180行目:
     get menuElement() {
     get menuElement() {
         return /** @type {HTMLDivElement} */ (
         return /** @type {HTMLDivElement} */ (
             this.navElement.getElementsByClassName("menu").item(0)
             this.menuContainerElement.getElementsByClassName(MobileUIBuilder.menuClass).item(0)
         );
         );
     }
     }
202行目: 186行目:
     /**
     /**
     * @param {MobileUI} mobileUI
     * @param {MobileUI} mobileUI
    * @param {HTMLElement} menuElement
     */
     */
     modify(mobileUI, menuElement) {
     modify(mobileUI) {
         const navElement = this.navElement;
        const leftMenu = this.menuElement;
        if (leftMenu.id === MobileUIBuilder.leftMenuID) return;
        leftMenu.id = MobileUIBuilder.leftMenuID;
 
         const menuContainer = this.menuContainerElement;
         if (!this.wgIsArticle || this.wgIsMainPage) {
         if (!this.wgIsArticle || this.wgIsMainPage) {
             navElement.classList.add("mobile-ui-not-article");
             menuContainer.classList.add(MobileUIBuilder.notArticleID);
         }
         }


         const firstGroup = menuElement.getElementsByTagName("ul").item(0);
         const firstGroup = leftMenu.getElementsByTagName("ul").item(0);
         if (firstGroup) {
         if (firstGroup) {
             firstGroup.style.display = "none";
             firstGroup.classList.add(MobileUIBuilder.hiddenClass);
         }
         }


         const leftGroups = this.menuDataGroups(mobileUI.leftGroups);
         const customLeftGroups = this.menuDataGroups(mobileUI.leftGroups);
         menuElement.prepend(...leftGroups);
         leftMenu.prepend(...customLeftGroups);


         const rightMenuElement = window.document.createElement("div");
         const rightMenu = window.document.createElement("div");
         rightMenuElement.classList.add(...menuElement.classList, "menu-right");
         rightMenu.id = MobileUIBuilder.rightMenuID;
        rightMenu.classList.add(...leftMenu.classList);


         const rightGroups = this.menuDataGroups(mobileUI.rightGroups);
         const rightGroups = this.menuDataGroups(mobileUI.rightGroups);
         rightMenuElement.append(...rightGroups);
         rightMenu.append(...rightGroups);
         navElement.append(rightMenuElement);
         menuContainer.append(rightMenu);
     }
     }


233行目: 221行目:


         if (element) {
         if (element) {
             this.modify(mobileUI, element);
             this.modify(mobileUI);
             return;
             return;
         }
         }
240行目: 228行目:
             for (const record of mutations) {
             for (const record of mutations) {
                 for (const node of record.addedNodes) {
                 for (const node of record.addedNodes) {
                     const element = this.elementIfTarget(node);
                     const { classList } = /** @type {HTMLDivElement} */ (node);
                     if (element) {
                     if (classList && classList.contains(MobileUIBuilder.menuClass)) {
                         observer.disconnect();
                         observer.disconnect();
                         this.modify(mobileUI, element);
                         this.modify(mobileUI);
                         return;
                         return;
                     }
                     }
250行目: 238行目:
         });
         });


         observer.observe(this.navElement, { childList: true });
         observer.observe(this.menuContainerElement, { childList: true });
     }
     }
    static menuContainerID = "mw-mf-page-left";
    static menuClass = "menu";
    static dataEventNameAttr = "data-event-name";
    static leftMenuID = "mobile-ui-left-menu";
    static rightMenuID = "mobile-ui-right-menu";
    static notArticleID = "mobile-ui-not-article";
    static pageShortURLID = "mobile-ui-page-short-url";
    static hiddenClass = "mobile-ui-hidden";
}
}

2019年6月26日 (水) 22:54時点における版

// @ts-check

/**
 * @typedef MediaWiki
 * @prop {readonly Map<string, any>} config
 */

/**
 * @typedef MenuDataItemComponent
 * @prop {string} [text]
 * @prop {string} [title]
 * @prop {string} [href]
 * @prop {string} [id]
 * @prop {string | string[]} [class]
 * @prop {string} [dataEventName]
 */

/**
 * @typedef MenuDataItem
 * @prop {string} [id]
 * @prop {string | string[]} [class]
 * @prop {MenuDataItemComponent[]} components
 */

/**
 * @typedef MenuDataGroup
 * @prop {string} [id]
 * @prop {string | string[]} [class]
 * @prop {MenuDataItem[]} items
 */

/**
 * @typedef MobileUI
 * @prop {MenuDataGroup[]} leftGroups
 * @prop {MenuDataGroup[]} rightGroups
 */

class MobileUIBuilder {
    /**
     * @param {MediaWiki} mw
     */
    constructor(mw) {
        // this.mw = mw;

        /**
         * @type {string}
         */
        this.wgServer = mw.config.get("wgServer");

        /**
         * @type {string}
         */
        this.wgScriptPath = mw.config.get("wgScriptPath");

        /**
         * @type {number}
         */
        this.wgArticleId = mw.config.get("wgArticleId");

        /**
         * @type {boolean}
         */
        this.wgIsArticle = mw.config.get("wgIsArticle");

        /**
         * @type {boolean}
         */
        this.wgIsMainPage = mw.config.get("wgIsMainPage");
    }

    /**
     * @param {MenuDataItemComponent} component
     * @returns {string}
     */
    hrefForMenuDataItemComponent(component) {
        switch (component.id) {
            case MobileUIBuilder.pageShortURLID:
                return (
                    this.wgServer
                    + this.wgScriptPath
                    + "?curid="
                    + String(this.wgArticleId)
                );

            default:
                return component.title
                    ? (this.wgScriptPath + "/" + component.title)
                    : component.href;
        }
    }

    /**
     * @param {MenuDataItemComponent} component
     * @returns {HTMLAnchorElement}
     */
    menuDataItemComponent(component) {
        const href = this.hrefForMenuDataItemComponent(component);

        const element = window.document.createElement("a");
        element.text = component.text || href;
        element.href = encodeURI(href);
        element.id = component.id || "";
        element.classList.add(...[].concat(component.class || []));
        element.setAttribute(MobileUIBuilder.dataEventNameAttr, component.dataEventName || "");

        return element;
    }

    /**
     * @param {MenuDataItem} item
     * @returns {HTMLLIElement}
     */
    menuDataItem(item) {
        /**
         * @type {HTMLAnchorElement[]}
         */
        const children = [];

        for (const component of item.components) {
            children.push(this.menuDataItemComponent(component));
        }

        const element = window.document.createElement("li");
        element.id = item.id || "";
        element.classList.add(...[].concat(item.class || []));
        element.append(...children);

        return element;
    }

    /**
     * @param {MenuDataGroup} group
     * @returns {HTMLUListElement}
     */
    menuDataGroup(group) {
        /**
         * @type {HTMLLIElement[]}
         */
        const children = [];

        for (const item of group.items) {
            children.push(this.menuDataItem(item));
        }

        const element = window.document.createElement("ul");
        element.id = group.id || "";
        element.classList.add(...[].concat(group.class || []));
        element.append(...children);

        return element;
    }

    /**
     * @param {MenuDataGroup[]} groups
     * @returns {HTMLUListElement[]}
     */
    menuDataGroups(groups) {
        /**
         * @type {HTMLUListElement[]}
         */
        const children = [];

        for (const group of groups) {
            children.push(this.menuDataGroup(group));
        }

        return children;
    }

    /**
     * @returns {HTMLElement}
     */
    get menuContainerElement() {
        return window.document.getElementById(MobileUIBuilder.menuContainerID);
    }

    /**
     * @returns {HTMLDivElement | null}
     */
    get menuElement() {
        return /** @type {HTMLDivElement} */ (
            this.menuContainerElement.getElementsByClassName(MobileUIBuilder.menuClass).item(0)
        );
    }

    /**
     * @param {MobileUI} mobileUI
     */
    modify(mobileUI) {
        const leftMenu = this.menuElement;
        if (leftMenu.id === MobileUIBuilder.leftMenuID) return;
        leftMenu.id = MobileUIBuilder.leftMenuID;

        const menuContainer = this.menuContainerElement;
        if (!this.wgIsArticle || this.wgIsMainPage) {
            menuContainer.classList.add(MobileUIBuilder.notArticleID);
        }

        const firstGroup = leftMenu.getElementsByTagName("ul").item(0);
        if (firstGroup) {
            firstGroup.classList.add(MobileUIBuilder.hiddenClass);
        }

        const customLeftGroups = this.menuDataGroups(mobileUI.leftGroups);
        leftMenu.prepend(...customLeftGroups);

        const rightMenu = window.document.createElement("div");
        rightMenu.id = MobileUIBuilder.rightMenuID;
        rightMenu.classList.add(...leftMenu.classList);

        const rightGroups = this.menuDataGroups(mobileUI.rightGroups);
        rightMenu.append(...rightGroups);
        menuContainer.append(rightMenu);
    }

    /**
     * @param {MobileUI} mobileUI
     */
    build(mobileUI) {
        const element = this.menuElement;

        if (element) {
            this.modify(mobileUI);
            return;
        }

        const observer = new MutationObserver((mutations, observer) => {
            for (const record of mutations) {
                for (const node of record.addedNodes) {
                    const { classList } = /** @type {HTMLDivElement} */ (node);
                    if (classList && classList.contains(MobileUIBuilder.menuClass)) {
                        observer.disconnect();
                        this.modify(mobileUI);
                        return;
                    }
                }
            }
        });

        observer.observe(this.menuContainerElement, { childList: true });
    }

    static menuContainerID = "mw-mf-page-left";
    static menuClass = "menu";
    static dataEventNameAttr = "data-event-name";
    static leftMenuID = "mobile-ui-left-menu";
    static rightMenuID = "mobile-ui-right-menu";
    static notArticleID = "mobile-ui-not-article";
    static pageShortURLID = "mobile-ui-page-short-url";
    static hiddenClass = "mobile-ui-hidden";
}
Cookieは私達のサービスを提供するのに役立ちます。このサービスを使用することにより、お客様はCookieの使用に同意するものとします。