跳转到内容

MediaWiki:Gadget-PrintOptions.js:修订间差异

勤求古训,博采众方
无编辑摘要
标签已被回退
无编辑摘要
标签已被回退
第15行: 第15行:
             'hideImages': mw.message('printdialog-opt-hide-images').plain() || '隐藏图片',
             'hideImages': mw.message('printdialog-opt-hide-images').plain() || '隐藏图片',
             'hideRefs': mw.message('printdialog-opt-hide-refs').plain() || '隐藏参考文献'
             'hideRefs': mw.message('printdialog-opt-hide-refs').plain() || '隐藏参考文献'
            // 可继续扩展其他选项...
         },
         },
         'help': {
         'help': {
             'hideImages': mw.message('printdialog-help-hide-images').plain() || '不打印页面中的图片和缩略图'
             'hideImages': mw.message('printdialog-help-hide-images').plain() || '不打印页面中的图片和缩略图',
            'hideRefs': mw.message('printdialog-help-hide-refs').plain() || '不打印参考文献和引用部分'
         }
         }
     };
     };
第28行: 第28行:
             dependencies: ['oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'],
             dependencies: ['oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'],
             hotkeys: {
             hotkeys: {
                 print: 'ctrl+enter', // 打印快捷键
                 print: 'ctrl+enter',
                 cancel: 'esc'       // 取消快捷键
                 cancel: 'esc'
             }
             }
         },
         },
第36行: 第36行:
         init: function () {
         init: function () {
             if (!this.shouldInit()) return;
             if (!this.shouldInit()) return;
           
            // 预加载依赖
            if (!this.isDepsLoaded()) {
                mw.loader.load(this.config.dependencies);
            }
              
              
             // 绑定打印按钮
             // 绑定打印按钮
第43行: 第48行:
                 .on('click.print', this.onPrintClick.bind(this));
                 .on('click.print', this.onPrintClick.bind(this));
              
              
             // 预加载资源
             this.registerHotkeys();
             if (!this.isDepsLoaded()) {
           
                 mw.loader.load(this.config.dependencies);
             if (this.config.debug) {
                 console.log('PrintDialog initialized');
             }
             }
           
            // 注册全局快捷键(需确保唯一性)
            this.registerHotkeys();
         },
         },
          
          
第72行: 第75行:
         },
         },
          
          
         // 🏗️ 创建对话框
         // 🏗️ 创建对话框类
         createDialog: function () {
         createDialogClass: function () {
             if (this.dialog) {
             var self = this;
                this.windowManager.openWindow(this.dialog);
                return;
            }
              
              
            // 对话框构造函数
             function Dialog(config) {
             function Dialog(config) {
                 Dialog.super.call(this, config);
                 Dialog.super.call(this, config);
第85行: 第84行:
             OO.inheritClass(Dialog, OO.ui.ProcessDialog);
             OO.inheritClass(Dialog, OO.ui.ProcessDialog);
              
              
             // 静态配置(使用本地化文本)
             // 静态配置
             Dialog.static.name = 'printDialog';
             Dialog.static.name = 'printDialog';
             Dialog.static.title = i18n.title;
             Dialog.static.title = i18n.title;
第107行: 第106行:
                 Dialog.super.prototype.initialize.apply(this, arguments);
                 Dialog.super.prototype.initialize.apply(this, arguments);
                 this.buildForm();
                 this.buildForm();
               
                if (self.config.debug) {
                    console.log('PrintDialog: Dialog initialized');
                }
             };
             };
              
              
             // 构建表单(本地化选项)
             // 构建表单
             Dialog.prototype.buildForm = function () {
             Dialog.prototype.buildForm = function () {
                 var panel = new OO.ui.PanelLayout({ padded: true });
                 var panel = new OO.ui.PanelLayout({ padded: true });
                 var fieldset = new OO.ui.FieldsetLayout();
                 var fieldset = new OO.ui.FieldsetLayout({
                    label: mw.message('printdialog-options-title').plain() || '打印选项'
                });
                  
                  
                 PrintDialog.options.forEach(function (opt) {
                 // 初始化选项部件存储
                self.optionWidgets = {};
               
                self.options.forEach(function (opt) {
                     if (opt.type === 'checkbox') {
                     if (opt.type === 'checkbox') {
                         var widget = new OO.ui.CheckboxInputWidget({
                         var widget = new OO.ui.CheckboxInputWidget({
第120行: 第128行:
                         });
                         });
                          
                          
                         fieldset.addItems([
                         // 保存部件引用
                            new OO.ui.FieldLayout(widget, {
                        self.optionWidgets[opt.id] = widget;
                                label: i18n.options[opt.id] || opt.id,
                                align: 'inline',
                                help: i18n.help[opt.id] ?
                                    new OO.ui.LabelWidget({ label: i18n.help[opt.id] }) : null
                            })
                        ]);
                          
                          
                         opt.widget = widget; // 保存引用
                         var fieldLayout = new OO.ui.FieldLayout(widget, {
                            label: i18n.options[opt.id] || opt.id,
                            align: 'inline',
                            help: i18n.help[opt.id] ?
                                new OO.ui.LabelWidget({
                                    label: i18n.help[opt.id]
                                }) : null
                        });
                       
                        fieldset.addItems([fieldLayout]);
                     }
                     }
                 });
                 });
第135行: 第146行:
                 panel.$element.append(fieldset.$element);
                 panel.$element.append(fieldset.$element);
                 this.$body.append(panel.$element);
                 this.$body.append(panel.$element);
               
                if (self.config.debug) {
                    console.log('PrintDialog: Form built with options', self.options);
                }
             };
             };
              
              
             // 处理动作(含快捷键支持)
             // 处理动作
             Dialog.prototype.getActionProcess = function (action) {
             Dialog.prototype.getActionProcess = function (action) {
                 if (action === 'print') {
                 if (action === 'print') {
第147行: 第162行:
             };
             };
              
              
             // 执行打印(拆分为独立方法)
             // 执行打印
             Dialog.prototype.executePrintAction = function () {
             Dialog.prototype.executePrintAction = function () {
                var process = new OO.ui.Process();
               
                 // 保存设置
                 // 保存设置
                 PrintDialog.options.forEach(function (opt) {
                 if (self.optionWidgets) {
                    PrintDialog.settings[opt.id] = opt.widget.isSelected();
                    Object.keys(self.optionWidgets).forEach(function (key) {
                 });
                        self.settings[key] = self.optionWidgets[key].isSelected();
                    });
                 }
               
                if (self.config.debug) {
                    console.log('PrintDialog: Settings saved', self.settings);
                }
                  
                  
                 // 关闭窗口并处理打印
                 // 关闭窗口
                 return this.close({ action: 'print' })
                 return this.close({ action: 'print' })
                    .then(PrintDialog.applyPrintStyles)
                     .then(function () {
                     .then(function () {
                         if (typeof window.print === 'function') {
                         return self.applyPrintStyles();
                            window.print();
                    })
                        } else {
                    .then(function () {
                            mw.notify(i18n['no-print-support'], { type: 'warning' });
                        // 延迟确保样式已应用
                         }
                        return new Promise(function (resolve) {
                            setTimeout(function () {
                                if (typeof window.print === 'function') {
                                    window.print();
                                    resolve();
                                } else {
                                    throw new Error(i18n['no-print-support']);
                                }
                            }, 100);
                         });
                     })
                     })
                     .catch(function (err) {
                     .catch(function (err) {
                         mw.notify(i18n['print-error'].replace('$1', err.message), { type: 'error' });
                         mw.notify(
                            i18n['print-error'].replace('$1', err.message),  
                            { type: 'error', tag: 'printdialog' }
                        );
                        throw err; // 重新抛出以保持错误链
                     });
                     });
             };
             };
           
            return Dialog;
        },
       
        // 🏗️ 创建对话框实例
        createDialog: function () {
            if (this.dialog && this.windowManager) {
                this.windowManager.openWindow(this.dialog);
                return Promise.resolve();
            }
              
              
             // 初始化窗口管理器
             // 初始化窗口管理器
第173行: 第218行:
                 this.windowManager = new OO.ui.WindowManager();
                 this.windowManager = new OO.ui.WindowManager();
                 $('body').append(this.windowManager.$element);
                 $('body').append(this.windowManager.$element);
            }
           
            // 创建对话框类(如果不存在)
            if (!this.DialogClass) {
                this.DialogClass = this.createDialogClass();
             }
             }
              
              
             // 创建对话框实例
             // 创建对话框实例
             this.dialog = new Dialog({ size: 'medium' });
             this.dialog = new this.DialogClass({  
                size: 'medium'  
            });
           
             this.windowManager.addWindows([this.dialog]);
             this.windowManager.addWindows([this.dialog]);
             this.windowManager.openWindow(this.dialog);
             return this.windowManager.openWindow(this.dialog);
         },
         },
          
          
第185行: 第238行:
             var style = [];
             var style = [];
              
              
             if (PrintDialog.settings.hideImages) {
            // 隐藏图片
                 style.push('img, .thumb { display:none !important; }');
             if (this.settings.hideImages) {
                 style.push(
                    'img, .thumb, .gallery, .image, .thumbinner, .thumbimage { ' +
                    'display:none !important; }'
                );
             }
             }
              
              
             // 清理旧样式并添加新样式
             // 隐藏参考文献
            if (this.settings.hideRefs) {
                style.push(
                    '.references, .ref-list, #References, .reflist, .citation { ' +
                    'display:none !important; }'
                );
            }
           
            // 清理旧样式
             $('style.print-styles').remove();
             $('style.print-styles').remove();
             if (style.length) {
           
            // 添加新样式
             if (style.length > 0) {
                 $('<style>')
                 $('<style>')
                     .addClass('print-styles')
                     .addClass('print-styles')
                     .attr('media', 'print')
                     .attr('media', 'print')
                    .attr('type', 'text/css')
                     .text(style.join('\n'))
                     .text(style.join('\n'))
                     .appendTo('head');
                     .appendTo('head');
                   
                if (this.config.debug) {
                    console.log('PrintDialog: Applied print styles', style);
                }
             }
             }
              
              
             return Promise.resolve();
             return Promise.resolve();
        },
       
        // ✅ 工具方法
        shouldInit: function () {
            return mw.config.get('wgNamespaceNumber') >= 0;
        },
       
        isDepsLoaded: function () {
            return this.config.dependencies.every(function (dep) {
                return mw.loader.getState(dep) === 'ready';
            });
         },
         },
          
          
第217行: 第278行:
             e.preventDefault();
             e.preventDefault();
              
              
             var notification = mw.notify(
             if (this.config.debug) {
                new OO.ui.ProgressBarWidget({ progress: false }).$element,  
                console.log('PrintDialog: Print button clicked');
                 {
            }
                    title: i18n.loading,
           
                    autoHide: false
            // 创建加载通知
                 }
            var progressBar = new OO.ui.ProgressBarWidget({ progress: false });
             );
            var notification = mw.notify(progressBar.$element, {
                 title: i18n.loading,
                autoHide: false,
                 tag: 'printdialog-loading'
             });
              
              
            // 确保依赖已加载并创建对话框
             mw.loader.using(this.config.dependencies)
             mw.loader.using(this.config.dependencies)
                 .then(this.createDialog.bind(this))
                 .then(this.createDialog.bind(this))
                 .catch(function (err) {
                 .catch(function (err) {
                     mw.notify(i18n['print-error'].replace('$1', err.message), { type: 'error' });
                    console.error('PrintDialog: Error creating dialog', err);
                     mw.notify(
                        i18n['print-error'].replace('$1', err.message),  
                        { type: 'error', tag: 'printdialog' }
                    );
                 })
                 })
                 .always(function () {
                 .always(function () {
                     notification.close();
                     notification.close();
                 });
                 });
        },
       
        // ✅ 工具方法
        shouldInit: function () {
            // 只在内容命名空间显示
            return mw.config.get('wgNamespaceNumber') >= 0 &&
                  $('#t-print').length > 0;
        },
       
        isDepsLoaded: function () {
            return this.config.dependencies.every(function (dep) {
                var state = mw.loader.getState(dep);
                return state === 'ready' || state === 'loaded';
            });
         },
         },
          
          
第240行: 第324行:
                 id: 'hideImages',
                 id: 'hideImages',
                 type: 'checkbox',
                 type: 'checkbox',
                 default: false
                 default: false,
                description: 'Hide images and thumbnails in print'
             },
             },
             {
             {
                 id: 'hideRefs',
                 id: 'hideRefs',
                 type: 'checkbox',
                 type: 'checkbox',
                 default: false
                 default: false,
                description: 'Hide references and citations in print'
             }
             }
         ],
         ],
第251行: 第337行:
         // 🗃️ 状态存储
         // 🗃️ 状态存储
         settings: {},
         settings: {},
        optionWidgets: null,
          
          
         // ♻️ 清理方法
         // ♻️ 清理方法
         destroy: function () {
         destroy: function () {
             $(document).off('keydown.printdialog');
             $(document).off('keydown.printdialog');
           
             if (this.windowManager) {
             if (this.windowManager) {
                 this.windowManager.destroy();
                 this.windowManager.destroy();
                 this.windowManager = null;
                 this.windowManager = null;
             }
             }
           
            $('style.print-styles').remove();
           
             this.dialog = null;
             this.dialog = null;
            this.DialogClass = null;
            this.optionWidgets = null;
           
            if (this.config.debug) {
                console.log('PrintDialog: Destroyed');
            }
         }
         }
     };
     };
第265行: 第362行:
     // 📅 延迟初始化
     // 📅 延迟初始化
     mw.hook('wikipage.content').add(function () {
     mw.hook('wikipage.content').add(function () {
         setTimeout(PrintDialog.init.bind(PrintDialog), 300);
         // 等待页面完全加载后再初始化
        if (document.readyState === 'loading') {
            $(document).ready(function () {
                setTimeout(PrintDialog.init.bind(PrintDialog), 300);
            });
        } else {
            setTimeout(PrintDialog.init.bind(PrintDialog), 300);
        }
     });
     });
      
      
     // 🖇️ 暴露到全局(可选)
     // 🖇️ 暴露到全局
     window.PrintDialog = PrintDialog;
     window.PrintDialog = PrintDialog;
   
    if (PrintDialog.config.debug) {
        console.log('PrintDialog: Module loaded');
    }
})(mediaWiki, jQuery);
})(mediaWiki, jQuery);

2025年11月1日 (六) 22:26的版本

/*! PrintDialog v3.0 - 支持多语言&快捷键 */
(function (mw, $) {
    'use strict';
    
    // 🌐 本地化文本库
    var i18n = {
        'title': mw.message('printdialog-title').plain() || '打印选项',
        'print-btn': mw.message('printdialog-print-btn').plain() || '打印',
        'cancel-btn': mw.message('printdialog-cancel-btn').plain() || '取消',
        'loading': mw.message('printdialog-loading').plain() || '准备打印中...',
        'print-error': mw.message('printdialog-error').plain() || '打印失败:$1',
        'no-print-support': mw.message('printdialog-no-support').plain() || '您的浏览器不支持直接打印',
        // 选项文本
        'options': {
            'hideImages': mw.message('printdialog-opt-hide-images').plain() || '隐藏图片',
            'hideRefs': mw.message('printdialog-opt-hide-refs').plain() || '隐藏参考文献'
        },
        'help': {
            'hideImages': mw.message('printdialog-help-hide-images').plain() || '不打印页面中的图片和缩略图',
            'hideRefs': mw.message('printdialog-help-hide-refs').plain() || '不打印参考文献和引用部分'
        }
    };

    var PrintDialog = {
        // ⚙️ 配置项
        config: {
            debug: false,
            dependencies: ['oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'],
            hotkeys: {
                print: 'ctrl+enter',
                cancel: 'esc'
            }
        },
        
        // 🚀 初始化
        init: function () {
            if (!this.shouldInit()) return;
            
            // 预加载依赖
            if (!this.isDepsLoaded()) {
                mw.loader.load(this.config.dependencies);
            }
            
            // 绑定打印按钮
            $('#t-print a').first()
                .text(mw.message('printdialog-link-text').plain() || '可打印版本')
                .off('click.print')
                .on('click.print', this.onPrintClick.bind(this));
            
            this.registerHotkeys();
            
            if (this.config.debug) {
                console.log('PrintDialog initialized');
            }
        },
        
        // 🔥 快捷键支持
        registerHotkeys: function () {
            $(document).off('keydown.printdialog')
                .on('keydown.printdialog', function (e) {
                    if (!PrintDialog.dialog || !PrintDialog.windowManager) return;
                    
                    // ESC - 取消
                    if (e.key === 'Escape') {
                        PrintDialog.dialog.executeAction('cancel');
                        e.preventDefault();
                    }
                    
                    // Ctrl+Enter - 打印
                    if (e.ctrlKey && e.key === 'Enter') {
                        PrintDialog.dialog.executeAction('print');
                        e.preventDefault();
                    }
                });
        },
        
        // 🏗️ 创建对话框类
        createDialogClass: function () {
            var self = this;
            
            function Dialog(config) {
                Dialog.super.call(this, config);
            }
            OO.inheritClass(Dialog, OO.ui.ProcessDialog);
            
            // 静态配置
            Dialog.static.name = 'printDialog';
            Dialog.static.title = i18n.title;
            Dialog.static.actions = [
                { 
                    action: 'print', 
                    label: i18n['print-btn'],
                    flags: ['primary', 'progressive'],
                    icon: 'printer'
                },
                { 
                    action: 'cancel', 
                    label: i18n['cancel-btn'], 
                    flags: 'safe',
                    icon: 'close'
                }
            ];
            
            // 初始化内容
            Dialog.prototype.initialize = function () {
                Dialog.super.prototype.initialize.apply(this, arguments);
                this.buildForm();
                
                if (self.config.debug) {
                    console.log('PrintDialog: Dialog initialized');
                }
            };
            
            // 构建表单
            Dialog.prototype.buildForm = function () {
                var panel = new OO.ui.PanelLayout({ padded: true });
                var fieldset = new OO.ui.FieldsetLayout({
                    label: mw.message('printdialog-options-title').plain() || '打印选项'
                });
                
                // 初始化选项部件存储
                self.optionWidgets = {};
                
                self.options.forEach(function (opt) {
                    if (opt.type === 'checkbox') {
                        var widget = new OO.ui.CheckboxInputWidget({
                            selected: !!opt.default
                        });
                        
                        // 保存部件引用
                        self.optionWidgets[opt.id] = widget;
                        
                        var fieldLayout = new OO.ui.FieldLayout(widget, {
                            label: i18n.options[opt.id] || opt.id,
                            align: 'inline',
                            help: i18n.help[opt.id] ? 
                                new OO.ui.LabelWidget({ 
                                    label: i18n.help[opt.id] 
                                }) : null
                        });
                        
                        fieldset.addItems([fieldLayout]);
                    }
                });
                
                panel.$element.append(fieldset.$element);
                this.$body.append(panel.$element);
                
                if (self.config.debug) {
                    console.log('PrintDialog: Form built with options', self.options);
                }
            };
            
            // 处理动作
            Dialog.prototype.getActionProcess = function (action) {
                if (action === 'print') {
                    return new OO.ui.Process(function () {
                        return this.executePrintAction();
                    }.bind(this));
                }
                return Dialog.super.prototype.getActionProcess.call(this, action);
            };
            
            // 执行打印
            Dialog.prototype.executePrintAction = function () {
                var process = new OO.ui.Process();
                
                // 保存设置
                if (self.optionWidgets) {
                    Object.keys(self.optionWidgets).forEach(function (key) {
                        self.settings[key] = self.optionWidgets[key].isSelected();
                    });
                }
                
                if (self.config.debug) {
                    console.log('PrintDialog: Settings saved', self.settings);
                }
                
                // 关闭窗口
                return this.close({ action: 'print' })
                    .then(function () {
                        return self.applyPrintStyles();
                    })
                    .then(function () {
                        // 延迟确保样式已应用
                        return new Promise(function (resolve) {
                            setTimeout(function () {
                                if (typeof window.print === 'function') {
                                    window.print();
                                    resolve();
                                } else {
                                    throw new Error(i18n['no-print-support']);
                                }
                            }, 100);
                        });
                    })
                    .catch(function (err) {
                        mw.notify(
                            i18n['print-error'].replace('$1', err.message), 
                            { type: 'error', tag: 'printdialog' }
                        );
                        throw err; // 重新抛出以保持错误链
                    });
            };
            
            return Dialog;
        },
        
        // 🏗️ 创建对话框实例
        createDialog: function () {
            if (this.dialog && this.windowManager) {
                this.windowManager.openWindow(this.dialog);
                return Promise.resolve();
            }
            
            // 初始化窗口管理器
            if (!this.windowManager) {
                this.windowManager = new OO.ui.WindowManager();
                $('body').append(this.windowManager.$element);
            }
            
            // 创建对话框类(如果不存在)
            if (!this.DialogClass) {
                this.DialogClass = this.createDialogClass();
            }
            
            // 创建对话框实例
            this.dialog = new this.DialogClass({ 
                size: 'medium' 
            });
            
            this.windowManager.addWindows([this.dialog]);
            return this.windowManager.openWindow(this.dialog);
        },
        
        // 🎨 应用打印样式
        applyPrintStyles: function () {
            var style = [];
            
            // 隐藏图片
            if (this.settings.hideImages) {
                style.push(
                    'img, .thumb, .gallery, .image, .thumbinner, .thumbimage { ' +
                    'display:none !important; }'
                );
            }
            
            // 隐藏参考文献
            if (this.settings.hideRefs) {
                style.push(
                    '.references, .ref-list, #References, .reflist, .citation { ' +
                    'display:none !important; }'
                );
            }
            
            // 清理旧样式
            $('style.print-styles').remove();
            
            // 添加新样式
            if (style.length > 0) {
                $('<style>')
                    .addClass('print-styles')
                    .attr('media', 'print')
                    .attr('type', 'text/css')
                    .text(style.join('\n'))
                    .appendTo('head');
                    
                if (this.config.debug) {
                    console.log('PrintDialog: Applied print styles', style);
                }
            }
            
            return Promise.resolve();
        },
        
        // 🖨️ 主入口处理
        onPrintClick: function (e) {
            e.preventDefault();
            
            if (this.config.debug) {
                console.log('PrintDialog: Print button clicked');
            }
            
            // 创建加载通知
            var progressBar = new OO.ui.ProgressBarWidget({ progress: false });
            var notification = mw.notify(progressBar.$element, { 
                title: i18n.loading,
                autoHide: false,
                tag: 'printdialog-loading'
            });
            
            // 确保依赖已加载并创建对话框
            mw.loader.using(this.config.dependencies)
                .then(this.createDialog.bind(this))
                .catch(function (err) {
                    console.error('PrintDialog: Error creating dialog', err);
                    mw.notify(
                        i18n['print-error'].replace('$1', err.message), 
                        { type: 'error', tag: 'printdialog' }
                    );
                })
                .always(function () {
                    notification.close();
                });
        },
        
        // ✅ 工具方法
        shouldInit: function () {
            // 只在内容命名空间显示
            return mw.config.get('wgNamespaceNumber') >= 0 && 
                   $('#t-print').length > 0;
        },
        
        isDepsLoaded: function () {
            return this.config.dependencies.every(function (dep) {
                var state = mw.loader.getState(dep);
                return state === 'ready' || state === 'loaded';
            });
        },
        
        // ⚙️ 可配置选项
        options: [
            {
                id: 'hideImages',
                type: 'checkbox',
                default: false,
                description: 'Hide images and thumbnails in print'
            },
            {
                id: 'hideRefs',
                type: 'checkbox',
                default: false,
                description: 'Hide references and citations in print'
            }
        ],
        
        // 🗃️ 状态存储
        settings: {},
        optionWidgets: null,
        
        // ♻️ 清理方法
        destroy: function () {
            $(document).off('keydown.printdialog');
            
            if (this.windowManager) {
                this.windowManager.destroy();
                this.windowManager = null;
            }
            
            $('style.print-styles').remove();
            
            this.dialog = null;
            this.DialogClass = null;
            this.optionWidgets = null;
            
            if (this.config.debug) {
                console.log('PrintDialog: Destroyed');
            }
        }
    };
    
    // 📅 延迟初始化
    mw.hook('wikipage.content').add(function () {
        // 等待页面完全加载后再初始化
        if (document.readyState === 'loading') {
            $(document).ready(function () {
                setTimeout(PrintDialog.init.bind(PrintDialog), 300);
            });
        } else {
            setTimeout(PrintDialog.init.bind(PrintDialog), 300);
        }
    });
    
    // 🖇️ 暴露到全局
    window.PrintDialog = PrintDialog;
    
    if (PrintDialog.config.debug) {
        console.log('PrintDialog: Module loaded');
    }
})(mediaWiki, jQuery);