미디어위키:Gadget-PluginCore.js: 두 판 사이의 차이

리버티게임(개발), 모두가 만들어가는 자유로운 게임
둘러보기로 이동 검색으로 이동
imported>Senouis
(20230604 요청 - MediawikiAPI 독자 구현에서 mw.Api 기반으로 Migration)
편집 요약 없음
 
(같은 사용자의 중간 판 7개는 보이지 않습니다)
1번째 줄: 1번째 줄:
/** [[틀:플러그인]]을 사용 가능하게 해 줍니다. 사용자의 허락을 맡고 사용자의 commonjs 편집을 허가 할 수 있는 문서입니다.
/** [[틀:플러그인]]을 사용 가능하게 해 줍니다. 사용자의 허락을 맡고 사용자의 commonjs 편집을 허가 할 수 있는 문서입니다.
    * 작성자: [[사용자:BANIP|BANIP]]
* 작성자: [[사용자:BANIP|BANIP]]
    * - [[백괴게임:관리자 요청/2018년 1월]]에서 BANIP님 요청으로 퍼왔습니다.
* - [[백괴게임:관리자 요청/2018년 1월]]에서 BANIP님 요청으로 퍼왔습니다.
    * - 2023.06.04 2.0.0 전체적인 소스 리팩토링/MediaWikiAPI의 의존성 삭제  --[[사용자:BANIP|BANIP]] ([[사용자토론:BANIP|토론]]) 2023년 6월 4일 (일) 13:52 (KST)  
* - 2023.06.04 2.0.0 전체적인 소스 리팩토링/MediaWikiAPI의 의존성 삭제  --[[사용자:BANIP|BANIP]] ([[사용자토론:BANIP|토론]]) 2023년 6월 4일 (일) 13:52 (KST)
*/
*/
    // 페이지 로드 후 실행
// 페이지 로드 후 실행
$(function(){
$(function () {
     // 구현필요
     // 구현필요
     var repo = {
     var repo = {
        PAGE_MISSING: 'The page does not exist.',
         api: new mw.Api(),
         api: new mw.Api(),
         getRawDocument: function(title) {
         getRawDocument: function (title) {
             return new Promise(function (resolve, reject){
             return new Promise(function (resolve, reject) {
                 repo.api.get({
                 repo.api
                    action: 'query',
                    .get({
                    prop: 'revisions',
                        action: 'query',
                    rvprop: 'content',
                        prop: 'revisions',
                    titles: title,
                        rvprop: 'content',
                    formatversion: 2
                        titles: title,
                }).then(function(data){
                        formatversion: 2,
                    var page = data.query.pages[0];
                    })
                    if (page.missing) {
                    .then(
                        reject("The page does not exist.");
                        function (data) {
                    } else {
                            var page = data.query.pages[0];
                        resolve(page.revisions[0].content);
                            if (page.missing) {
                    }
                                reject(repo.PAGE_MISSING);
                }, function(e){
                            } else {
                    reject(e);
                                resolve(page.revisions[0].content);
                });
                            }
                        },
                        function (e) {
                            reject(e);
                        }
                    );
             });
             });
         },
         },
   
 
         changeDocument: function(title, text, summary) {
         changeDocument: function (title, text, summary, otherOptions) {
             return new Promise(function(resolve, reject){
             return new Promise(function (resolve, reject) {
                 repo.api.postWithEditToken({
                 repo.api
                    action: 'edit',
                    .postWithEditToken(
                    title: title,
                        Object.assign(
                    text: text,
                            {
                    summary: summary,
                                action: 'edit',
                    nocreate: true
                                title: title,
                }).then(function(data){
                                text: text,
                    resolve(data);
                                summary: summary,
                }, function(e){
                                //nocreate: true,
                    reject(e);
                            },
                });
                            otherOptions
                        )
                    )
                    .then(
                        function (data) {
                            if (data.edit.result === 'Success') resolve(data);
                            else reject(data);
                        },
                        function (e) {
                            reject(e);
                        }
                    );
             });
             });
         },
         },
       
 
         getDocument: function(title) {
         getDocument: function (title) {
             return new Promise(function(resolve, reject){
             return new Promise(function (resolve, reject) {
                 repo.api.get({
                 repo.api
                    action: 'parse',
                    .get({
                    page: title,
                        action: 'parse',
                    formatversion: 2
                        page: title,
                }).then(function(data){
                        formatversion: 2,
                    resolve(data.parse.text);
                    })
                }, function(e){
                    .then(
                    reject(e);
                        function (data) {
                });
                            resolve(data.parse.text);
                        },
                        function (e) {
                            reject(e);
                        }
                    );
             });
             });
         },
         },
         getPluginMetadata: function(page) {
         getPluginMetadata: function (page) {
             var revisionPromise = repo.api.get({
             var revisionPromise = repo.api.get({
                 action: 'query',
                 action: 'query',
                 prop: 'revisions',
                 prop: 'revisions',
                 rvlimit: 1,
                 rvlimit: 1,
                 rvprop: 'timestamp|user|comment',  
                 rvprop: 'timestamp|user|comment',
                 titles: page,
                 titles: page,
                 formatversion: 2
                 formatversion: 2,
             });
             });
       
 
             var parsedPagePromise = repo.api.get({
             var parsedPagePromise = repo.api.get({
                 action: 'parse',
                 action: 'parse',
                 page: page,
                 page: page,
                 formatversion: 2
                 formatversion: 2,
             });
             });
       
            return Promise.all([revisionPromise, parsedPagePromise])
                .then(function(results) {
                    var revisionResponse = results[0];
                    var parsedPageResponse = results[1];
                   
                    var pages = revisionResponse.query.pages;
                    var pageId = Object.keys(pages)[0];
                    var revision = pages[pageId].revisions[0];
                   
                    var parsedContent = parsedPageResponse.parse.text;
       
                    return {
                        author: revision.user,
                        timestamp: revision.timestamp,
                        comment: revision.comment,
                        code: $(parsedContent).find("pre.script").text()
                    };
                });
        }


            return Promise.all([revisionPromise, parsedPagePromise]).then(function (
                results
            ) {
                var revisionResponse = results[0];
                var parsedPageResponse = results[1];
                var pages = revisionResponse.query.pages;
                var pageId = Object.keys(pages)[0];
                var revision = pages[pageId].revisions[0];
                var parsedContent = parsedPageResponse.parse.text;
                return {
                    author: revision.user,
                    timestamp: revision.timestamp,
                    comment: revision.comment,
                    code: $(parsedContent).find('pre.script').text(),
                };
            });
        },
     };
     };


113번째 줄: 135번째 줄:
         // 문서에서 사용하는 플러그인들을 체크합니다. use-script클래스를 가진 모든 돔 요소를 조사하고
         // 문서에서 사용하는 플러그인들을 체크합니다. use-script클래스를 가진 모든 돔 요소를 조사하고
         // 그 돔에 내장된 플러그인 데이터를 docPlugins에 추가합니다.
         // 그 돔에 내장된 플러그인 데이터를 docPlugins에 추가합니다.
         document: function(){
         document: function () {
             // 현재 문서에 사용되는 플러그인들의 데이터를 docPlugins에 추가합니다.
             // 현재 문서에 사용되는 플러그인들의 데이터를 docPlugins에 추가합니다.
             return $(".use-script").toArray().map(function(el){
             return $('.use-script')
                var $this = $(el);
                .toArray()
                return {
                .map(function (el) {
                    name: $this.attr("data-name"), // 플러그인 이름
                    var $this = $(el);
                    descript: $this.attr("data-descript"), // 플러그인 내용
                    return {
                    version: $this.attr("data-version"), // 플러그인 이름
                        name: $this.attr('data-name'), // 플러그인 이름
                    local: $this.attr("data-local") === "true", // 즉시 실행 여부
                        descript: $this.attr('data-descript'), // 플러그인 내용
                    creat: $this.attr("data-creat"), // 플러그인 작성자 이름
                        version: $this.attr('data-version'), // 플러그인 이름
                    state: $this.attr("data-state"),
                        local: $this.attr('data-local') === 'true', // 즉시 실행 여부
                    link: $this.attr("data-link"),
                        creat: $this.attr('data-creat'), // 플러그인 작성자 이름
                    executable: $this.attr("data-executable") === "true",
                        state: $this.attr('data-state'),
                };
                        link: $this.attr('data-link'),
            });
                        executable: $this.attr('data-executable') === 'true',
                    };
                });
         },
         },
         user: function(commonjs){
         user: function (commonjs) {
             var userplugins = [];
             var userplugins = [];
             // plugins.---가 있는지 체크하는 정규식
             // plugins.---가 있는지 체크하는 정규식
144번째 줄: 168번째 줄:


     // 문서의 서브타이틀을 플러그인들의 이름으로 설정합니다.
     // 문서의 서브타이틀을 플러그인들의 이름으로 설정합니다.
     function setSubtitleByPlugins(plugins){
     function setSubtitleByPlugins(plugins) {
         // 서브타이틀에 현재 가동중인 플러그인들의 이름을 추가합니다.
         // 서브타이틀에 현재 가동중인 플러그인들의 이름을 추가합니다.
         var pluginNames = plugins.map(function(plugin){
         var pluginNames = plugins.map(function (plugin) {
             return plugin.name;
             return plugin.name;
         });
         });
         var subTitle = " " + pluginNames.join(", ") + " 플러그인 가동중";
         var subTitle = ' ' + pluginNames.join(', ') + ' 플러그인 가동중';
         $("#siteSub").text(function(i, v) {
         $('#siteSub').text(function (i, v) {
             return v + subTitle;
             return v + subTitle;
         });
         });
159번째 줄: 183번째 줄:
         function getPluginCode(plugin) {
         function getPluginCode(plugin) {
             function entityDecode(doc) {
             function entityDecode(doc) {
            return $('<p></p>').html(doc).text();
                return $('<script></script>').html(doc).text();
             }
             }
       
 
             function getDocHead(plugin) {
             function getDocHead(plugin) {
            var docHead = "";
                var docHead = '';
            var toJSONPlugin = Object.assign({}, plugin);
                var toJSONPlugin = Object.assign({}, plugin);
            toJSONPlugin.code = undefined;
                toJSONPlugin.code = undefined;
            toJSONPlugin.link = undefined;
                toJSONPlugin.link = undefined;
            docHead += "\n";
                docHead += '\n';
            docHead += "\n/** 플러그인 " + plugin.name + "***************************\n";
                docHead +=
            docHead += "* " + plugin.descript + "\n";
                    '\n/** 플러그인 ' +
            docHead += "* 버전 => " + plugin.version + "\n";
                    plugin.name +
            docHead += "* 작성자 : [[사용자:" + plugin.creat + "|" + plugin.creat + "]] \n";
                    '***************************\n';
            docHead += "* JSON => " + plugin.name + " = " + JSON.stringify(toJSONPlugin) + "; \n";
                docHead += '* ' + plugin.descript + '\n';
            docHead += "*/ \n";
                docHead += '* 버전 => ' + plugin.version + '\n';
            docHead += "function plugin_" + plugin.name + "(){\n";
                docHead +=
            if (plugin.local) docHead += " if($(\"[data-name='" + plugin.name + "']\").length >= 1){\n";
                    '* 작성자 : [[사용자:' +
            return docHead;
                    plugin.creat +
                    '|' +
                    plugin.creat +
                    ']] \n';
                docHead +=
                    '* JSON => ' +
                    plugin.name +
                    ' = ' +
                    JSON.stringify(toJSONPlugin) +
                    '; \n';
                docHead += '*/ \n';
                docHead += 'function plugin_' + plugin.name + '(){\n';
                if (plugin.local)
                    docHead +=
                        ' if($("[data-name=\'' +
                        plugin.name +
                        '\']").length >= 1){\n';
                return docHead;
             }
             }
       
 
             function getDocFoot(plugin) {
             function getDocFoot(plugin) {
            var docFoot = "";
                var docFoot = '';
            if (plugin.local) docFoot += "\n  }\n";
                if (plugin.local) docFoot += '\n  }\n';
            docFoot += "\n}\n";
                docFoot += '\n}\n';
            if (plugin.executable) docFoot += "$( plugin_" + plugin.name + " );\n";
                if (plugin.executable)
            docFoot += "/* " + plugin.name + " 끝 */\n\n";
                    docFoot += '$( plugin_' + plugin.name + ' );\n';
            return docFoot;
                docFoot += '/* ' + plugin.name + ' 끝 */\n\n';
                return docFoot;
             }
             }
       
 
             var docHead = getDocHead(plugin);
             var docHead = getDocHead(plugin);
             var docFoot = getDocFoot(plugin);
             var docFoot = getDocFoot(plugin);
       
 
             return entityDecode(docHead + pluginMeta[plugin.name].code + docFoot);
             return entityDecode(docHead + pluginMeta[plugin.name].code + docFoot);
         }
         }
196번째 줄: 238번째 줄:
         //commonjs에서 특정 플러그인 제거
         //commonjs에서 특정 플러그인 제거
         function removePluginByDoc(pluginTitle, commonjs) {
         function removePluginByDoc(pluginTitle, commonjs) {
             var reg = new RegExp("\\/\\*\\* 플러그인 " + pluginTitle + "([\\s\\S]*)\\/\\* " + pluginTitle + " 끝 \\*\\/", "g");
             var reg = new RegExp(
             commonjs = commonjs.replace(reg, "");
                '\\/\\*\\* 플러그인 ' +
                    pluginTitle +
                    '([\\s\\S]*)\\/\\* ' +
                    pluginTitle +
                    ' 끝 \\*\\/',
                'g'
            );
             commonjs = commonjs.replace(reg, '');
             return commonjs;
             return commonjs;
         }
         }


         plugins.forEach(function(plugin) {
         plugins.forEach(function (plugin) {
             commonjs = removePluginByDoc(plugin.name, commonjs);
             commonjs = removePluginByDoc(plugin.name, commonjs);
             commonjs += getPluginCode(plugin);
             commonjs += getPluginCode(plugin);
210번째 줄: 259번째 줄:


     // 플러그인 안내 템플릿을 반환합니다.
     // 플러그인 안내 템플릿을 반환합니다.
     function getInstallPagePromise(userPlugins, uninstalledPlugins, unupdatedPlugins, pluginMeta) {
     function getInstallPagePromise(
         function getPluginCard($template,plugin, status) {
        userPlugins,
        uninstalledPlugins,
        unupdatedPlugins,
        pluginMeta
    ) {
         function getPluginCard($template, plugin, status) {
             var pluginName = plugin.name;
             var pluginName = plugin.name;
             var $box = $template.find(".cloneable.p-box").clone().removeClass("cloneable");
             var $box = $template
                .find('.cloneable.p-box')
                .clone()
                .removeClass('cloneable');
             var code = pluginMeta[pluginName].code;
             var code = pluginMeta[pluginName].code;
             $box.find(".p-status").html(status);
             $box.find('.p-status').text(status);
             $box.find(".p-code").html(code.replace(/\s{1,}$/, ""));
             $box.find('.p-code').text(code.replace(/\s{1,}$/, ''));
             $box.find(".p-name").text(pluginName);
             $box.find('.p-name').text(pluginName);
             $box.find(".p-descript").text(plugin.descript);
             $box.find('.p-descript').text(plugin.descript);
             if (status == "버전업") {
             if (status == '버전업') {
                 var prevVersion = userPlugins.find(function(userPlugin) {
                 var prevVersion = userPlugins.find(function (userPlugin) {
                     return userPlugin.name == pluginName;
                     return userPlugin.name == pluginName;
                 }).version;
                 }).version;
                 $box.find(".p-version").text(prevVersion + " => " + plugin.version);
                 $box.find('.p-version').text(prevVersion + ' => ' + plugin.version);
             } else {
             } else {
                 $box.find(".p-version").text(plugin.version);
                 $box.find('.p-version').text(plugin.version);
             }
             }
             $box.find(".p-local").text(plugin.local == true ? "일부 문서만" : "문서 전체");
             $box.find('.p-local').text(
             $box.find(".p-creat").text(pluginMeta[pluginName].author);
                plugin.local == true ? '일부 문서만' : '문서 전체'
             $box.find(".p-last").text(pluginMeta[pluginName].timestamp);
            );
             $box.find(".p-comment").text(pluginMeta[pluginName].comment);
             $box.find('.p-creat').text(pluginMeta[pluginName].author);
             $box.find('.p-last').text(pluginMeta[pluginName].timestamp);
             $box.find('.p-comment').text(pluginMeta[pluginName].comment);
             return $box;
             return $box;
         }
         }
   
 
       
         return repo.getDocument('틀:플러그인/setup').then(function (templateRaw) {
         return repo.getDocument("틀:플러그인/setup")
        .then(function(templateRaw){
             var $template = $(templateRaw);
             var $template = $(templateRaw);
             var allPlugins = uninstalledPlugins.concat(unupdatedPlugins);
             var allPlugins = uninstalledPlugins.concat(unupdatedPlugins);
             allPlugins.forEach(function(plugin){
             allPlugins.forEach(function (plugin) {
                 var status = uninstalledPlugins.includes(plugin) ? "설치" : "버전업";
                 var status = uninstalledPlugins.includes(plugin) ? '설치' : '버전업';
                 var pluginCard = getPluginCard($template,plugin, status);
                 var pluginCard = getPluginCard($template, plugin, status);
                 $template.find(".box-article").append(pluginCard);
                 $template.find('.box-article').append(pluginCard);
             });
             });


250번째 줄: 307번째 줄:


     // 플러그인 설치 관련 로직
     // 플러그인 설치 관련 로직
     (function(){
     (function () {
         var commonjsPath = "사용자:" + mw.config.get("wgUserName") + "/common.js"; // 사용자의 commonjs의 경로를 획득합니다.
         var commonjsPath = '사용자:' + mw.config.get('wgUserName') + '/common.js'; // 사용자의 commonjs의 경로를 획득합니다.
         var docPlugins = getPlugin.document(); // 문서에서 사용하는 플러그인들을 체크합니다.
         var docPlugins = getPlugin.document(); // 문서에서 사용하는 플러그인들을 체크합니다.
         if(Object.keys(docPlugins).length === 0) return; // 문서에서 사용하는 플러그인이 없으면 종료합니다.
         if (Object.keys(docPlugins).length === 0) return; // 문서에서 사용하는 플러그인이 없으면 종료합니다.


         setSubtitleByPlugins(docPlugins); // 문서의 서브타이틀을 문서에서 사용중인 플러그인들의 이름으로 설정합니다.
         setSubtitleByPlugins(docPlugins); // 문서의 서브타이틀을 문서에서 사용중인 플러그인들의 이름으로 설정합니다.


         repo.getRawDocument(commonjsPath) // 사용자의 commonjs의 문서를 획득합니다.
         repo.getRawDocument(commonjsPath) // 사용자의 commonjs의 문서를 획득합니다.
        .then(function(commonjsRaw){
            .then(
            var userPlugins = getPlugin.user(commonjsRaw); // 사용자가 가지고 있는 플러그인들을 체크합니다.
                function (commonjsRaw) {
                    var userPlugins = getPlugin.user(commonjsRaw); // 사용자가 가지고 있는 플러그인들을 체크합니다.


            var uninstalledPlugins = docPlugins.filter(function(plugin){
                    var uninstalledPlugins = docPlugins.filter(function (plugin) {
                return !userPlugins.some(function(userPlugin){
                        return !userPlugins.some(function (userPlugin) {
                    return userPlugin.name === plugin.name;
                            return userPlugin.name === plugin.name;
                });
                        });
            }); // 사용자가 가지고 있지 않은 플러그인들을 체크합니다.
                    }); // 사용자가 가지고 있지 않은 플러그인들을 체크합니다.


            var unupdatedPlugins = docPlugins.filter(function(plugin){
                    var unupdatedPlugins = docPlugins.filter(function (plugin) {
                return userPlugins.some(function(userPlugin){
                        return userPlugins.some(function (userPlugin) {
                    return userPlugin.name === plugin.name && userPlugin.version !== plugin.version;
                            return (
                });
                                userPlugin.name === plugin.name &&
            }); // 사용자가 가지고 있는 플러그인들 중 버전이 다른 플러그인들을 체크합니다.
                                userPlugin.version !== plugin.version
                            );
                        });
                    }); // 사용자가 가지고 있는 플러그인들 중 버전이 다른 플러그인들을 체크합니다.


            // 추가 플러그인 설치가 필요하지 않은 경우 로직 종료
                    return {
            if (isPluginsEmpty(uninstalledPlugins) && isPluginsEmpty(unupdatedPlugins)) return;
                        commonjsRaw: commonjsRaw,
                        userPlugins: userPlugins,
                        uninstalledPlugins: uninstalledPlugins,
                        unupdatedPlugins: unupdatedPlugins,
                    };
                },
                function (err) {
                    // 사용자 common.js 문서가 없는 경우
                    if (err === repo.PAGE_MISSING) {
                        // 모든 플러그인을 설치합니다.
                        return {
                            commonjsRaw: '',
                            userPlugins: [],
                            uninstalledPlugins: docPlugins,
                            unupdatedPlugins: [],
                        };
                    } else throw err;
                }
            )
            .then(function (results) {
                var commonjsRaw = results.commonjsRaw;
                var userPlugins = results.userPlugins;
                var uninstalledPlugins = results.uninstalledPlugins;
                var unupdatedPlugins = results.unupdatedPlugins;


            // 설치가 필요한 플러그인들의 이름을 문자열로 반환합니다.
                // 추가 플러그인 설치가 필요하지 않은 경우 로직 종료
            var needPluginNames = uninstalledPlugins.concat(unupdatedPlugins).map(function(plugin){
                if (
                return plugin.name;
                    isPluginsEmpty(uninstalledPlugins) &&
            }).join(", ");
                    isPluginsEmpty(unupdatedPlugins)
                )
                    return;


            // 플러그인 개발자/코드/마지막 수정일 등 획득, 설치버튼 클릭 시 즉시 설치 가능하도록 미리 Promise를 생성
                // 설치가 필요한 플러그인들의 이름을 문자열로 반환합니다.
            var pluginMetaPromise = Promise.all(
                var needPluginNames = uninstalledPlugins
                uninstalledPlugins.concat(unupdatedPlugins).map(function(plugin){
                    .concat(unupdatedPlugins)
                    return Promise.all([plugin.name, repo.getPluginMetadata(plugin.state)]);
                    .map(function (plugin) {
                })
                        return plugin.name;
            ).then(function(pluginMeta){
                    })
                return Object.fromEntries(pluginMeta);
                    .join(', ');
            }).catch(function(){
                alert("플러그인 정보를 불러오는데 실패했습니다. 페이지를 리로드 해주세요.");
            });


            // 플러그인 리스트 수집 끝나면 플러그인 안내 템플릿을 반환하는 Promise를 생성
                // 플러그인 개발자/코드/마지막 수정일 등 획득, 설치버튼 클릭 시 즉시 설치 가능하도록 미리 Promise를 생성
            var installPagePromise = pluginMetaPromise
                var pluginMetaPromise = Promise.all(
            .then(function(pluginMeta){
                    uninstalledPlugins
                return getInstallPagePromise(userPlugins, uninstalledPlugins, unupdatedPlugins, pluginMeta);
                        .concat(unupdatedPlugins)
            }).catch(function(){
                        .map(function (plugin) {
                alert("플러그인 설치 페이지를 불러오는데 실패했습니다. 페이지를 리로드 해주세요.");
                            return Promise.all([
            });
                                plugin.name,
                                repo.getPluginMetadata(plugin.state),
                            ]);
                        })
                )
                    .then(function (pluginMeta) {
                        return Object.fromEntries(pluginMeta);
                    })
                    .catch(function () {
                        alert(
                            '플러그인 정보를 불러오는데 실패했습니다. 페이지를 리로드 해주세요.'
                        );
                    });


            // 플러그인 설치 버튼 클릭 이벤트 핸들러
                // 플러그인 리스트 수집 끝나면 플러그인 안내 템플릿을 반환하는 Promise를 생성
            var setInstallPage = function(pluginMeta){
                var installPagePromise = pluginMetaPromise
                return installPagePromise.then(function($installPage){
                    .then(function (pluginMeta) {
                    $installPage.find(".install-button").on("click", function() {
                         return getInstallPagePromise(
                         $(".install-button").text("설치중..");
                            userPlugins,
                        $(".install-button").off("click");
                            uninstalledPlugins,
                   
                            unupdatedPlugins,
                        var commonjs = getUpdatedCommonjs(uninstalledPlugins.concat(unupdatedPlugins), commonjsRaw, pluginMeta);
                            pluginMeta
                         repo.changeDocument(commonjsPath, commonjs, "플러그인 " + needPluginNames + "설치")
                         );
                        .then(function(){
                    })
                            location.reload();
                    .catch(function () {
                        })
                        alert(
                        .catch(function(e){
                            '플러그인 설치 페이지를 불러오는데 실패했습니다. 페이지를 리로드 해주세요.'
                            alert("플러그인 설치에 실패했습니다. 다시 시도해주세요.");
                         );
                            console.error(e);
                         });
                     });
                     });


                     var $document = $("#mw-content-text");
                // 플러그인 설치 버튼 클릭 이벤트 핸들러
                    $document.html($installPage);
                var setInstallPage = function (pluginMeta) {
                });
                    function install(
            };
                        commonjsPath,
                        commonjs,
                        needPluginNames,
                        otherOptions
                    ) {
                        return repo
                            .changeDocument(
                                commonjsPath,
                                commonjs,
                                '플러그인 ' + needPluginNames + '설치',
                                otherOptions
                            )
                            .then(function () {
                                location.reload();
                            })
                            .catch(function (e) {
                                if (typeof e === 'object' && e.edit.captcha) {
                                    var captchaId = e.edit.captcha.id;
                                    var captchaAnswer = prompt(
                                        '플러그인을 설치하려면 다음 문제를 해결하십시오: \n' +
                                            e.edit.captcha.question
                                    );
                                    return install(
                                        commonjsPath,
                                        commonjs,
                                        needPluginNames,
                                        {
                                            captchaid: captchaId,
                                            captchaword: captchaAnswer,
                                        }
                                    );
                                }
 
                                alert(
                                    '플러그인 설치에 실패했습니다. 다시 시도해주세요.'
                                );
                                console.error(e);
                            });
                     }
 
                    return installPagePromise.then(function ($installPage) {
                        $installPage
                            .find('.install-button')
                            .on('click', function () {
                                $('.install-button').text('설치중..');
                                $('.install-button').off('click');
 
                                var commonjs = getUpdatedCommonjs(
                                    uninstalledPlugins.concat(unupdatedPlugins),
                                    commonjsRaw,
                                    pluginMeta
                                );
 
                                install(commonjsPath, commonjs, needPluginNames);
                            });
 
                        var $document = $('#mw-content-text');
                        $document.html($installPage);
                    });
                };


            var $loadCodeLink = $(".plugin-install");
                var $loadCodeLink = $('.plugin-install');
            // 플러그인 설치 필요 틀이 있는 페이지인 경우
                // 플러그인 설치 필요 틀이 있는 페이지인 경우
            if ($loadCodeLink.length >= 1) {
                if ($loadCodeLink.length >= 1) {
                $loadCodeLink.eq(0).closest("table").show(); // 플러그인 설치 필요 틀을 보여줍니다.
                    $loadCodeLink.eq(0).closest('.ambox').show(); // 플러그인 설치 필요 틀을 보여줍니다.
                $(".plugin-name").eq(0).text(needPluginNames); // 필요한 플러그인들의 이름을 표시합니다.
                    $('.plugin-name').eq(0).text(needPluginNames); // 필요한 플러그인들의 이름을 표시합니다.
                $loadCodeLink.on("click", function(){
                    $loadCodeLink.on('click', function () {
                    // 클릭한 버튼 이벤트 끄고 로딩중메세지 표시
                        // 클릭한 버튼 이벤트 끄고 로딩중메세지 표시
                         $loadCodeLink.off("click");
                         $loadCodeLink.off('click');
                         $loadCodeLink.text("필요한 플러그인 데이터를 가져오고 있습니다...");
                         $loadCodeLink.text(
                            '필요한 플러그인 데이터를 가져오고 있습니다...'
                        );
                         pluginMetaPromise.then(setInstallPage);
                         pluginMetaPromise.then(setInstallPage);
                     }); // 플러그인 설치 버튼을 클릭하면 플러그인 체크 로직을 실행합니다.
                     }); // 플러그인 설치 버튼을 클릭하면 플러그인 체크 로직을 실행합니다.
            } else {
                } else {
                pluginMetaPromise.then(setInstallPage);
                    pluginMetaPromise.then(setInstallPage);
            }
                }
        });
            });
     })();
     })();
});
});

2024년 11월 15일 (금) 15:32 기준 최신판

/** [[틀:플러그인]]을 사용 가능하게 해 줍니다. 사용자의 허락을 맡고 사용자의 commonjs 편집을 허가 할 수 있는 문서입니다.
 * 작성자: [[사용자:BANIP|BANIP]]
 * - [[백괴게임:관리자 요청/2018년 1월]]에서 BANIP님 요청으로 퍼왔습니다.
 * - 2023.06.04 2.0.0 전체적인 소스 리팩토링/MediaWikiAPI의 의존성 삭제  --[[사용자:BANIP|BANIP]] ([[사용자토론:BANIP|토론]]) 2023년 6월 4일 (일) 13:52 (KST)
 */
// 페이지 로드 후 실행
$(function () {
    // 구현필요
    var repo = {
        PAGE_MISSING: 'The page does not exist.',
        api: new mw.Api(),
        getRawDocument: function (title) {
            return new Promise(function (resolve, reject) {
                repo.api
                    .get({
                        action: 'query',
                        prop: 'revisions',
                        rvprop: 'content',
                        titles: title,
                        formatversion: 2,
                    })
                    .then(
                        function (data) {
                            var page = data.query.pages[0];
                            if (page.missing) {
                                reject(repo.PAGE_MISSING);
                            } else {
                                resolve(page.revisions[0].content);
                            }
                        },
                        function (e) {
                            reject(e);
                        }
                    );
            });
        },

        changeDocument: function (title, text, summary, otherOptions) {
            return new Promise(function (resolve, reject) {
                repo.api
                    .postWithEditToken(
                        Object.assign(
                            {
                                action: 'edit',
                                title: title,
                                text: text,
                                summary: summary,
                                //nocreate: true,
                            },
                            otherOptions
                        )
                    )
                    .then(
                        function (data) {
                            if (data.edit.result === 'Success') resolve(data);
                            else reject(data);
                        },
                        function (e) {
                            reject(e);
                        }
                    );
            });
        },

        getDocument: function (title) {
            return new Promise(function (resolve, reject) {
                repo.api
                    .get({
                        action: 'parse',
                        page: title,
                        formatversion: 2,
                    })
                    .then(
                        function (data) {
                            resolve(data.parse.text);
                        },
                        function (e) {
                            reject(e);
                        }
                    );
            });
        },
        getPluginMetadata: function (page) {
            var revisionPromise = repo.api.get({
                action: 'query',
                prop: 'revisions',
                rvlimit: 1,
                rvprop: 'timestamp|user|comment',
                titles: page,
                formatversion: 2,
            });

            var parsedPagePromise = repo.api.get({
                action: 'parse',
                page: page,
                formatversion: 2,
            });

            return Promise.all([revisionPromise, parsedPagePromise]).then(function (
                results
            ) {
                var revisionResponse = results[0];
                var parsedPageResponse = results[1];

                var pages = revisionResponse.query.pages;
                var pageId = Object.keys(pages)[0];
                var revision = pages[pageId].revisions[0];

                var parsedContent = parsedPageResponse.parse.text;

                return {
                    author: revision.user,
                    timestamp: revision.timestamp,
                    comment: revision.comment,
                    code: $(parsedContent).find('pre.script').text(),
                };
            });
        },
    };

    // 플러그인이 비어있는지 확인
    function isPluginsEmpty(plugins) {
        return Object.keys(plugins).length === 0;
    }

    //플러그인의 모든 키 순회
    function forEach(object, callback) {
        for (var key in object) {
            var variable = object[key];
            callback(variable, key);
        }
    }

    var getPlugin = {
        // 문서에서 사용하는 플러그인들을 체크합니다. use-script클래스를 가진 모든 돔 요소를 조사하고
        // 그 돔에 내장된 플러그인 데이터를 docPlugins에 추가합니다.
        document: function () {
            // 현재 문서에 사용되는 플러그인들의 데이터를 docPlugins에 추가합니다.
            return $('.use-script')
                .toArray()
                .map(function (el) {
                    var $this = $(el);
                    return {
                        name: $this.attr('data-name'), // 플러그인 이름
                        descript: $this.attr('data-descript'), // 플러그인 내용
                        version: $this.attr('data-version'), // 플러그인 이름
                        local: $this.attr('data-local') === 'true', // 즉시 실행 여부
                        creat: $this.attr('data-creat'), // 플러그인 작성자 이름
                        state: $this.attr('data-state'),
                        link: $this.attr('data-link'),
                        executable: $this.attr('data-executable') === 'true',
                    };
                });
        },
        user: function (commonjs) {
            var userplugins = [];
            // plugins.---가 있는지 체크하는 정규식
            var pluginreg = /JSON \=\> ([\S]+) = (\{.*\})/g;
            var nameMatch = pluginreg.exec(commonjs);
            while (nameMatch) {
                userplugins.push(JSON.parse(nameMatch[2]));
                nameMatch = pluginreg.exec(commonjs);
            }

            return userplugins;
        },
    };

    // 문서의 서브타이틀을 플러그인들의 이름으로 설정합니다.
    function setSubtitleByPlugins(plugins) {
        // 서브타이틀에 현재 가동중인 플러그인들의 이름을 추가합니다.
        var pluginNames = plugins.map(function (plugin) {
            return plugin.name;
        });
        var subTitle = ' ' + pluginNames.join(', ') + ' 플러그인 가동중';
        $('#siteSub').text(function (i, v) {
            return v + subTitle;
        });
    }

    // 플러그인을 사용자문서에 추가합니다.
    function getUpdatedCommonjs(plugins, commonjs, pluginMeta) {
        function getPluginCode(plugin) {
            function entityDecode(doc) {
                return $('<script></script>').html(doc).text();
            }

            function getDocHead(plugin) {
                var docHead = '';
                var toJSONPlugin = Object.assign({}, plugin);
                toJSONPlugin.code = undefined;
                toJSONPlugin.link = undefined;
                docHead += '\n';
                docHead +=
                    '\n/** 플러그인 ' +
                    plugin.name +
                    '***************************\n';
                docHead += '* ' + plugin.descript + '\n';
                docHead += '* 버전 => ' + plugin.version + '\n';
                docHead +=
                    '* 작성자 : [[사용자:' +
                    plugin.creat +
                    '|' +
                    plugin.creat +
                    ']] \n';
                docHead +=
                    '* JSON => ' +
                    plugin.name +
                    ' = ' +
                    JSON.stringify(toJSONPlugin) +
                    '; \n';
                docHead += '*/ \n';
                docHead += 'function plugin_' + plugin.name + '(){\n';
                if (plugin.local)
                    docHead +=
                        '  if($("[data-name=\'' +
                        plugin.name +
                        '\']").length >= 1){\n';
                return docHead;
            }

            function getDocFoot(plugin) {
                var docFoot = '';
                if (plugin.local) docFoot += '\n  }\n';
                docFoot += '\n}\n';
                if (plugin.executable)
                    docFoot += '$( plugin_' + plugin.name + ' );\n';
                docFoot += '/* ' + plugin.name + ' 끝 */\n\n';
                return docFoot;
            }

            var docHead = getDocHead(plugin);
            var docFoot = getDocFoot(plugin);

            return entityDecode(docHead + pluginMeta[plugin.name].code + docFoot);
        }

        //commonjs에서 특정 플러그인 제거
        function removePluginByDoc(pluginTitle, commonjs) {
            var reg = new RegExp(
                '\\/\\*\\* 플러그인 ' +
                    pluginTitle +
                    '([\\s\\S]*)\\/\\* ' +
                    pluginTitle +
                    ' 끝 \\*\\/',
                'g'
            );
            commonjs = commonjs.replace(reg, '');
            return commonjs;
        }

        plugins.forEach(function (plugin) {
            commonjs = removePluginByDoc(plugin.name, commonjs);
            commonjs += getPluginCode(plugin);
        });

        return commonjs;
    }

    // 플러그인 안내 템플릿을 반환합니다.
    function getInstallPagePromise(
        userPlugins,
        uninstalledPlugins,
        unupdatedPlugins,
        pluginMeta
    ) {
        function getPluginCard($template, plugin, status) {
            var pluginName = plugin.name;
            var $box = $template
                .find('.cloneable.p-box')
                .clone()
                .removeClass('cloneable');
            var code = pluginMeta[pluginName].code;
            $box.find('.p-status').text(status);
            $box.find('.p-code').text(code.replace(/\s{1,}$/, ''));
            $box.find('.p-name').text(pluginName);
            $box.find('.p-descript').text(plugin.descript);
            if (status == '버전업') {
                var prevVersion = userPlugins.find(function (userPlugin) {
                    return userPlugin.name == pluginName;
                }).version;
                $box.find('.p-version').text(prevVersion + ' => ' + plugin.version);
            } else {
                $box.find('.p-version').text(plugin.version);
            }
            $box.find('.p-local').text(
                plugin.local == true ? '일부 문서만' : '문서 전체'
            );
            $box.find('.p-creat').text(pluginMeta[pluginName].author);
            $box.find('.p-last').text(pluginMeta[pluginName].timestamp);
            $box.find('.p-comment').text(pluginMeta[pluginName].comment);
            return $box;
        }

        return repo.getDocument('틀:플러그인/setup').then(function (templateRaw) {
            var $template = $(templateRaw);
            var allPlugins = uninstalledPlugins.concat(unupdatedPlugins);
            allPlugins.forEach(function (plugin) {
                var status = uninstalledPlugins.includes(plugin) ? '설치' : '버전업';
                var pluginCard = getPluginCard($template, plugin, status);
                $template.find('.box-article').append(pluginCard);
            });

            return $template;
        });
    }

    // 플러그인 설치 관련 로직
    (function () {
        var commonjsPath = '사용자:' + mw.config.get('wgUserName') + '/common.js'; // 사용자의 commonjs의 경로를 획득합니다.
        var docPlugins = getPlugin.document(); // 문서에서 사용하는 플러그인들을 체크합니다.
        if (Object.keys(docPlugins).length === 0) return; // 문서에서 사용하는 플러그인이 없으면 종료합니다.

        setSubtitleByPlugins(docPlugins); // 문서의 서브타이틀을 문서에서 사용중인 플러그인들의 이름으로 설정합니다.

        repo.getRawDocument(commonjsPath) // 사용자의 commonjs의 문서를 획득합니다.
            .then(
                function (commonjsRaw) {
                    var userPlugins = getPlugin.user(commonjsRaw); // 사용자가 가지고 있는 플러그인들을 체크합니다.

                    var uninstalledPlugins = docPlugins.filter(function (plugin) {
                        return !userPlugins.some(function (userPlugin) {
                            return userPlugin.name === plugin.name;
                        });
                    }); // 사용자가 가지고 있지 않은 플러그인들을 체크합니다.

                    var unupdatedPlugins = docPlugins.filter(function (plugin) {
                        return userPlugins.some(function (userPlugin) {
                            return (
                                userPlugin.name === plugin.name &&
                                userPlugin.version !== plugin.version
                            );
                        });
                    }); // 사용자가 가지고 있는 플러그인들 중 버전이 다른 플러그인들을 체크합니다.

                    return {
                        commonjsRaw: commonjsRaw,
                        userPlugins: userPlugins,
                        uninstalledPlugins: uninstalledPlugins,
                        unupdatedPlugins: unupdatedPlugins,
                    };
                },
                function (err) {
                    // 사용자 common.js 문서가 없는 경우
                    if (err === repo.PAGE_MISSING) {
                        // 모든 플러그인을 설치합니다.
                        return {
                            commonjsRaw: '',
                            userPlugins: [],
                            uninstalledPlugins: docPlugins,
                            unupdatedPlugins: [],
                        };
                    } else throw err;
                }
            )
            .then(function (results) {
                var commonjsRaw = results.commonjsRaw;
                var userPlugins = results.userPlugins;
                var uninstalledPlugins = results.uninstalledPlugins;
                var unupdatedPlugins = results.unupdatedPlugins;

                // 추가 플러그인 설치가 필요하지 않은 경우 로직 종료
                if (
                    isPluginsEmpty(uninstalledPlugins) &&
                    isPluginsEmpty(unupdatedPlugins)
                )
                    return;

                // 설치가 필요한 플러그인들의 이름을 문자열로 반환합니다.
                var needPluginNames = uninstalledPlugins
                    .concat(unupdatedPlugins)
                    .map(function (plugin) {
                        return plugin.name;
                    })
                    .join(', ');

                // 플러그인 개발자/코드/마지막 수정일 등 획득, 설치버튼 클릭 시 즉시 설치 가능하도록 미리 Promise를 생성
                var pluginMetaPromise = Promise.all(
                    uninstalledPlugins
                        .concat(unupdatedPlugins)
                        .map(function (plugin) {
                            return Promise.all([
                                plugin.name,
                                repo.getPluginMetadata(plugin.state),
                            ]);
                        })
                )
                    .then(function (pluginMeta) {
                        return Object.fromEntries(pluginMeta);
                    })
                    .catch(function () {
                        alert(
                            '플러그인 정보를 불러오는데 실패했습니다. 페이지를 리로드 해주세요.'
                        );
                    });

                // 플러그인 리스트 수집 끝나면 플러그인 안내 템플릿을 반환하는 Promise를 생성
                var installPagePromise = pluginMetaPromise
                    .then(function (pluginMeta) {
                        return getInstallPagePromise(
                            userPlugins,
                            uninstalledPlugins,
                            unupdatedPlugins,
                            pluginMeta
                        );
                    })
                    .catch(function () {
                        alert(
                            '플러그인 설치 페이지를 불러오는데 실패했습니다. 페이지를 리로드 해주세요.'
                        );
                    });

                // 플러그인 설치 버튼 클릭 이벤트 핸들러
                var setInstallPage = function (pluginMeta) {
                    function install(
                        commonjsPath,
                        commonjs,
                        needPluginNames,
                        otherOptions
                    ) {
                        return repo
                            .changeDocument(
                                commonjsPath,
                                commonjs,
                                '플러그인 ' + needPluginNames + '설치',
                                otherOptions
                            )
                            .then(function () {
                                location.reload();
                            })
                            .catch(function (e) {
                                if (typeof e === 'object' && e.edit.captcha) {
                                    var captchaId = e.edit.captcha.id;
                                    var captchaAnswer = prompt(
                                        '플러그인을 설치하려면 다음 문제를 해결하십시오: \n' +
                                            e.edit.captcha.question
                                    );
                                    return install(
                                        commonjsPath,
                                        commonjs,
                                        needPluginNames,
                                        {
                                            captchaid: captchaId,
                                            captchaword: captchaAnswer,
                                        }
                                    );
                                }

                                alert(
                                    '플러그인 설치에 실패했습니다. 다시 시도해주세요.'
                                );
                                console.error(e);
                            });
                    }

                    return installPagePromise.then(function ($installPage) {
                        $installPage
                            .find('.install-button')
                            .on('click', function () {
                                $('.install-button').text('설치중..');
                                $('.install-button').off('click');

                                var commonjs = getUpdatedCommonjs(
                                    uninstalledPlugins.concat(unupdatedPlugins),
                                    commonjsRaw,
                                    pluginMeta
                                );

                                install(commonjsPath, commonjs, needPluginNames);
                            });

                        var $document = $('#mw-content-text');
                        $document.html($installPage);
                    });
                };

                var $loadCodeLink = $('.plugin-install');
                // 플러그인 설치 필요 틀이 있는 페이지인 경우
                if ($loadCodeLink.length >= 1) {
                    $loadCodeLink.eq(0).closest('.ambox').show(); // 플러그인 설치 필요 틀을 보여줍니다.
                    $('.plugin-name').eq(0).text(needPluginNames); // 필요한 플러그인들의 이름을 표시합니다.
                    $loadCodeLink.on('click', function () {
                        // 클릭한 버튼 이벤트 끄고 로딩중메세지 표시
                        $loadCodeLink.off('click');
                        $loadCodeLink.text(
                            '필요한 플러그인 데이터를 가져오고 있습니다...'
                        );
                        pluginMetaPromise.then(setInstallPage);
                    }); // 플러그인 설치 버튼을 클릭하면 플러그인 체크 로직을 실행합니다.
                } else {
                    pluginMetaPromise.then(setInstallPage);
                }
            });
    })();
});