|
|
Line 1: |
Line 1: |
| // Copied from [https://testwiki.wiki/wiki/User:JJBullet/script-installer-core.js] and
| | mw.loader.load('https://en.wikipedia.org/w/index.php?title=MediaWiki:Gadget-script-installer.js&action=raw&ctype=text/javascript'); |
| // [https://testwiki.wiki/wiki/User:Syunsyunminmin/script-installer-core.js]
| |
| ( function () {
| |
| // An mw.Api object
| |
| var api;
| |
| | |
| // Keep "common" at beginning
| |
| var SKINS = [ "common", "monobook", "minerva", "vector", "cologneblue", "timeless" ];
| |
| | |
| // How many scripts do we need before we show the quick filter?
| |
| var NUM_SCRIPTS_FOR_SEARCH = 3;
| |
| | |
| // The master import list. Set in buildImportList
| |
| var imports = {};
| |
| | |
| // Local scripts, keyed on name; value will be the target
| |
| var localScriptsByName = {};
| |
| | |
| // How many scripts are installed?
| |
| var scriptCount = 0;
| |
| | |
| // Goes on the end of edit summaries
| |
| var ADVERT = " ([[MediaWiki:Gadget-script-installer|script-installer]])";
| |
| | |
| /**
| |
| * Strings, for translation
| |
| */
| |
| var STRINGS = {
| |
| installSummary: "Installing $1",
| |
| uninstallSummary: "Uninstalling $1",
| |
| remoteUrlDesc: "$1, loaded from $2",
| |
| disableSummary: "Disabling $1",
| |
| enableSummary: "Enabling $1",
| |
| normalizeSummary: "Normalizing script installs",
| |
| panelHeader: "You currently have the following scripts installed",
| |
| cannotInstall: "Cannot install",
| |
| moveLinkText: "Move",
| |
| moveProgressMsg: "Moving",
| |
| insecure: "Insecure"
| |
| };
| |
| | |
| /**
| |
| * Constructs an Import. An Import is a line in a JS file that imports a
| |
| * user script. Properties:
| |
| *
| |
| * - "page" is a page name, such as "User:Foo/Bar.js".
| |
| * - "wiki" is a wiki from which the script is loaded, such as
| |
| * "en.wikipedia". If null, the script is local, on the user's
| |
| * wiki.
| |
| * - "url" is a URL that can be passed into mw.loader.load.
| |
| * - "target" is the title of the user subpage where the script is,
| |
| * without the .js ending: for example, "common".
| |
| * - "disabled" is whether this import is commented out.
| |
| * - "type" is 0 if local, 1 if remotely loaded, and 2 if URL.
| |
| *
| |
| * EXACTLY one of "page" or "url" are null for every Import. This
| |
| * constructor should not be used directly; use the factory
| |
| * functions (Import.ofLocal, Import.ofUrl, Import.fromJs) instead.
| |
| */
| |
| function Import( page, wiki, url, target, disabled ) {
| |
| this.page = page;
| |
| this.wiki = wiki;
| |
| this.url = url;
| |
| this.target = target;
| |
| this.disabled = disabled;
| |
| this.type = this.url ? 2 : ( this.wiki ? 1 : 0 );
| |
| }
| |
| | |
| Import.ofLocal = function ( page, target, disabled ) {
| |
| if( disabled === undefined ) disabled = false;
| |
| return new Import( page, null, null, target, disabled );
| |
| }
| |
| | |
| /** URL to Import. Assumes wgScriptPath is "/w" */
| |
| Import.ofUrl = function ( url, target, disabled ) {
| |
| if( disabled === undefined ) disabled = false;
| |
| var URL_RGX = /^(?:https?:)?\/\/(.+?)\.org\/w\/index\.php\?.*?title=(.+?(?:&|$))/;
| |
| var match;
| |
| if( match = URL_RGX.exec( url ) ) {
| |
| var title = decodeURIComponent( match[2].replace( /&$/, "" ) ),
| |
| wiki = match[1];
| |
| return new Import( title, wiki, null, target, disabled );
| |
| }
| |
| return new Import( null, null, url, target, disabled );
| |
| }
| |
| | |
| Import.fromJs = function ( line, target ) {
| |
| var IMPORT_RGX = /^\s*(\/\/)?\s*importScript\s*\(\s*(?:"|')(.+?)(?:"|')\s*\)/;
| |
| var match;
| |
| if( match = IMPORT_RGX.exec( line ) ) {
| |
| return Import.ofLocal( match[2], target, !!match[1] );
| |
| }
| |
| | |
| var LOADER_RGX = /^\s*(\/\/)?\s*mw\.loader\.load\s*\(\s*(?:"|')(.+?)(?:"|')\s*\)/;
| |
| if( match = LOADER_RGX.exec( line ) ) {
| |
| return Import.ofUrl( match[2], target, !!match[1] );
| |
| }
| |
| }
| |
| | |
| Import.prototype.getDescription = function () {
| |
| switch( this.type ) {
| |
| case 0: return this.page;
| |
| case 1: return STRINGS.remoteUrlDesc.replace( "$1", this.page ).replace( "$2", this.wiki );
| |
| case 2: return this.url;
| |
| }
| |
| }
| |
| | |
| /**
| |
| * Human-readable (NOT necessarily suitable for ResourceLoader) URL.
| |
| */
| |
| Import.prototype.getHumanUrl = function () {
| |
| switch( this.type ) {
| |
| case 0: return "/wiki/" + encodeURI( this.page );
| |
| case 1: return "//" + this.wiki + ".org/wiki/" + encodeURI( this.page );
| |
| case 2: return this.url;
| |
| }
| |
| }
| |
| | |
| Import.prototype.toJs = function () {
| |
| var dis = this.disabled ? "//" : "",
| |
| url = this.url;
| |
| switch( this.type ) {
| |
| case 0: return dis + "importScript('" + this.page + "'); // Backlink: [[" + this.page + "]]";
| |
| case 1: url = "//" + this.wiki + ".org/w/index.php?title=" +
| |
| this.page + "&action=raw&ctype=text/javascript";
| |
| /* FALL THROUGH */
| |
| case 2: return dis + "mw.loader.load('" + url + "');";
| |
| }
| |
| }
| |
| | |
| /**
| |
| * Installs the import.
| |
| */
| |
| Import.prototype.install = function () {
| |
| return api.postWithEditToken( {
| |
| action: "edit",
| |
| title: getFullTarget( this.target ),
| |
| summary: STRINGS.installSummary.replace( "$1", this.getDescription() ) + ADVERT,
| |
| appendtext: "\n" + this.toJs()
| |
| } );
| |
| }
| |
| | |
| /**
| |
| * Get all line numbers from the target page that mention
| |
| * the specified script.
| |
| */
| |
| Import.prototype.getLineNums = function ( targetWikitext ) {
| |
| function quoted( s ) {
| |
| return new RegExp( "(['\"])" + escapeForRegex( s ) + "\\1" );
| |
| }
| |
| var toFind;
| |
| switch( this.type ) {
| |
| case 0: toFind = quoted( this.page ); break;
| |
| case 1: toFind = new RegExp( escapeForRegex( this.wiki ) + ".*?" +
| |
| escapeForRegex( this.page ) ); break;
| |
| case 2: toFind = quoted( this.url ); break;
| |
| }
| |
| var lineNums = [], lines = targetWikitext.split( "\n" );
| |
| for( var i = 0; i < lines.length; i++ )
| |
| if( toFind.test( lines[i] ) )
| |
| lineNums.push( i );
| |
| return lineNums;
| |
| }
| |
| | |
| /**
| |
| * Uninstalls the given import. That is, delete all lines from the
| |
| * target page that import the specified script.
| |
| */
| |
| Import.prototype.uninstall = function () {
| |
| var that = this;
| |
| return getWikitext( getFullTarget( this.target ) ).then( function ( wikitext ) {
| |
| var lineNums = that.getLineNums( wikitext ),
| |
| newWikitext = wikitext.split( "\n" ).filter( function ( _, idx ) {
| |
| return lineNums.indexOf( idx ) < 0;
| |
| } ).join( "\n" );
| |
| return api.postWithEditToken( {
| |
| action: "edit",
| |
| title: getFullTarget( that.target ),
| |
| summary: STRINGS.uninstallSummary.replace( "$1", that.getDescription() ) + ADVERT,
| |
| text: newWikitext
| |
| } );
| |
| } );
| |
| }
| |
| | |
| /**
| |
| * Sets whether the given import is disabled, based on the provided
| |
| * boolean value.
| |
| */
| |
| Import.prototype.setDisabled = function ( disabled ) {
| |
| var that = this;
| |
| this.disabled = disabled;
| |
| return getWikitext( getFullTarget( this.target ) ).then( function ( wikitext ) {
| |
| var lineNums = that.getLineNums( wikitext ),
| |
| newWikitextLines = wikitext.split( "\n" );
| |
| | |
| if( disabled ) {
| |
| lineNums.forEach( function ( lineNum ) {
| |
| if( newWikitextLines[lineNum].trim().indexOf( "//" ) != 0 ) {
| |
| newWikitextLines[lineNum] = "//" + newWikitextLines[lineNum].trim();
| |
| }
| |
| } );
| |
| } else {
| |
| lineNums.forEach( function ( lineNum ) {
| |
| if( newWikitextLines[lineNum].trim().indexOf( "//" ) == 0 ) {
| |
| newWikitextLines[lineNum] = newWikitextLines[lineNum].replace( /^\s*\/\/\s*/, "" );
| |
| }
| |
| } );
| |
| }
| |
| | |
| var summary = ( disabled ? STRINGS.disableSummary : STRINGS.enableSummary )
| |
| .replace( "$1", that.getDescription() ) + ADVERT;
| |
| return api.postWithEditToken( {
| |
| action: "edit",
| |
| title: getFullTarget( that.target ),
| |
| summary: summary,
| |
| text: newWikitextLines.join( "\n" )
| |
| } );
| |
| } );
| |
| }
| |
| | |
| Import.prototype.toggleDisabled = function () {
| |
| this.disabled = !this.disabled;
| |
| return this.setDisabled( this.disabled );
| |
| }
| |
| | |
| /**
| |
| * Move this import to another file.
| |
| */
| |
| Import.prototype.move = function ( newTarget ) {
| |
| if( this.target === newTarget ) return;
| |
| var old = new Import( this.page, this.wiki, this.url, this.target, this.disabled );
| |
| this.target = newTarget;
| |
| return $.when( old.uninstall(), this.install() );
| |
| }
| |
| | |
| function getAllTargetWikitexts() {
| |
| return $.getJSON(
| |
| mw.util.wikiScript( "api" ),
| |
| {
| |
| format: "json",
| |
| action: "query",
| |
| prop: "revisions",
| |
| rvprop: "content",
| |
| rvslots: "main",
| |
| titles: SKINS.map( getFullTarget ).join( "|" )
| |
| }
| |
| ).then( function ( data ) {
| |
| if( data && data.query && data.query.pages ) {
| |
| var result = {};
| |
| prefixLength = mw.config.get( "wgUserName" ).length + 6;
| |
| Object.values( data.query.pages ).forEach( function ( moreData ) {
| |
| result[moreData.title.substring( prefixLength ).slice( 0, -3 )] =
| |
| moreData.revisions ? moreData.revisions[0].slots.main["*"] : null;
| |
| } );
| |
| return result;
| |
| }
| |
| } );
| |
| }
| |
| | |
| function buildImportList() {
| |
| return getAllTargetWikitexts().then( function ( wikitexts ) {
| |
| Object.keys( wikitexts ).forEach( function ( targetName ) {
| |
| var targetImports = [];
| |
| if( wikitexts[ targetName ] ) {
| |
| var lines = wikitexts[ targetName ].split( "\n" );
| |
| var currImport;
| |
| for( var i = 0; i < lines.length; i++ ) {
| |
| if( currImport = Import.fromJs( lines[i], targetName ) ) {
| |
| targetImports.push( currImport );
| |
| scriptCount++;
| |
| if( currImport.type === 0 ) {
| |
| if( !localScriptsByName[ currImport.page ] )
| |
| localScriptsByName[ currImport.page ] = [];
| |
| localScriptsByName[ currImport.page ].push( currImport.target );
| |
| }
| |
| }
| |
| }
| |
| }
| |
| imports[ targetName ] = targetImports;
| |
| } );
| |
| } );
| |
| }
| |
| | |
| | |
| /*
| |
| * "Normalizes" (standardizes the format of) lines in the given
| |
| * config page.
| |
| */
| |
| function normalize( target ) {
| |
| return getWikitext( getFullTarget( target ) ).then( function ( wikitext ) {
| |
| var lines = wikitext.split( "\n" ),
| |
| newLines = Array( lines.length ),
| |
| currImport;
| |
| for( var i = 0; i < lines.length; i++ ) {
| |
| if( currImport = Import.fromJs( lines[i], target ) ) {
| |
| newLines[i] = currImport.toJs();
| |
| } else {
| |
| newLines[i] = lines[i];
| |
| }
| |
| }
| |
| return api.postWithEditToken( {
| |
| action: "edit",
| |
| title: getFullTarget( target ),
| |
| summary: STRINGS.normalizeSummary,
| |
| text: newLines.join( "\n" )
| |
| } );
| |
| } );
| |
| }
| |
| | |
| function conditionalReload( openPanel ) {
| |
| if( window.scriptInstallerAutoReload ) {
| |
| if( openPanel ) document.cookie = "open_script_installer=yes";
| |
| window.location.reload( true );
| |
| }
| |
| }
| |
| | |
| /********************************************
| |
| *
| |
| * UI code
| |
| *
| |
| ********************************************/
| |
| function makePanel() {
| |
| var list = $( "<div>" ).attr( "id", "script-installer-panel" )
| |
| .append( $( "<header>" ).text( STRINGS.panelHeader ) );
| |
| var container = $( "<div>" ).addClass( "container" ).appendTo( list );
| |
|
| |
| // Container for checkboxes
| |
| container.append( $( "<div>" )
| |
| .attr( "class", "checkbox-container" )
| |
| .append(
| |
| $( "<input>" )
| |
| .attr( { "id": "siNormalize", "type": "checkbox" } )
| |
| .click( function () {
| |
| $( ".normalize-wrapper" ).toggle( 0 )
| |
| } ),
| |
| $( "<label>" )
| |
| .attr( "for", "siNormalize" )
| |
| .text( 'Show "normalize" links?' ),
| |
| $( "<input>" )
| |
| .attr( { "id": "siMove", "type": "checkbox" } )
| |
| .click( function () {
| |
| $( ".move-wrapper" ).toggle( 0 )
| |
| } ),
| |
| $( "<label>" )
| |
| .attr( "for", "siMove" )
| |
| .text( 'Show "move" links?' ) ) );
| |
| if( scriptCount > NUM_SCRIPTS_FOR_SEARCH ) {
| |
| container.append( $( "<div>" )
| |
| .attr( "class", "filter-container" )
| |
| .append(
| |
| $( "<label>" )
| |
| .attr( "for", "siQuickFilter" )
| |
| .text( "Quick filter:" ),
| |
| $( "<input>" )
| |
| .attr( { "id": "siQuickFilter", "type": "text" } )
| |
| .on( "input", function () {
| |
| var filterString = $( this ).val();
| |
| if( filterString ) {
| |
| var sel = "#script-installer-panel li[name*='" +
| |
| $.escapeSelector( $( this ).val() ) + "']";
| |
| $( "#script-installer-panel li.script" ).toggle( false );
| |
| $( sel ).toggle( true );
| |
| } else {
| |
| $( "#script-installer-panel li.script" ).toggle( true );
| |
| }
| |
| } )
| |
| ) );
| |
| | |
| // Now, get the checkboxes out of the way
| |
| container.find( ".checkbox-container" )
| |
| .css( "float", "right" );
| |
| }
| |
| $.each( imports, function ( targetName, targetImports ) {
| |
| var fmtTargetName = ( targetName === "common"
| |
| ? "common (applies to all skins)"
| |
| : targetName );
| |
| if( targetImports.length ) {
| |
| container.append(
| |
| $( "<h2>" ).append(
| |
| fmtTargetName,
| |
| $( "<span>" )
| |
| .addClass( "normalize-wrapper" )
| |
| .append(
| |
| " (",
| |
| $( "<a>" )
| |
| .text( "normalize" )
| |
| .click( function () {
| |
| normalize( targetName ).done( function () {
| |
| conditionalReload( true );
| |
| } );
| |
| } ),
| |
| ")" )
| |
| .hide() ),
| |
| $( "<ul>" ).append(
| |
| targetImports.map( function ( anImport ) {
| |
| return $( "<li>" )
| |
| .addClass( "script" )
| |
| .attr( "name", anImport.getDescription() )
| |
| .append(
| |
| $( "<a>" )
| |
| .text( anImport.getDescription() )
| |
| .addClass( "script" )
| |
| .attr( "href", anImport.getHumanUrl() ),
| |
| " (",
| |
| $( "<a>" )
| |
| .text( "Uninstall" )
| |
| .click( function () {
| |
| $( this ).text( "Uninstalling..." );
| |
| anImport.uninstall().done( function () {
| |
| conditionalReload( true );
| |
| } );
| |
| } ),
| |
| " | ",
| |
| $( "<a>" )
| |
| .text( anImport.disabled ? "Enable" : "Disable" )
| |
| .click( function () {
| |
| $( this ).text( $( this ).text().replace( /e$/, "ing" ) );
| |
| anImport.toggleDisabled().done( function () {
| |
| $( this ).toggleClass( "disabled" );
| |
| conditionalReload( true );
| |
| } );
| |
| } ),
| |
| $( "<span>" )
| |
| .addClass( "move-wrapper" )
| |
| .append(
| |
| " | ",
| |
| $( "<a>" )
| |
| .text( "Move" )
| |
| .click( function () {
| |
| var dest = null;
| |
| var PROMPT = "Destination? Enter one of: " + SKINS.join( ", " );
| |
| do {
| |
| dest = ( window.prompt( PROMPT ) || "" ).toLowerCase();
| |
| } while( dest && SKINS.indexOf( dest ) < 0 )
| |
| if( !dest ) return;
| |
| $( this ).text( "Moving" );
| |
| anImport.move( dest ).done( function () {
| |
| conditionalReload( true );
| |
| } );
| |
| } ),
| |
| )
| |
| .hide(),
| |
| ")" )
| |
| .toggleClass( "disabled", anImport.disabled );
| |
| } ) ) );
| |
| }
| |
| } );
| |
| return list;
| |
| }
| |
| | |
| function buildCurrentPageInstallElement() {
| |
| var addingInstallLink = false; // will we be adding a legitimate install link?
| |
| var installElement = $( "<span>" ); // only used if addingInstallLink is set to true
| |
| | |
| var namespaceNumber = mw.config.get( "wgNamespaceNumber" );
| |
| var pageName = mw.config.get( "wgPageName" );
| |
| | |
| // Namespace 2 is User
| |
| if( namespaceNumber === 2 &&
| |
| pageName.indexOf( "/" ) > 0 ) {
| |
| var contentModel = mw.config.get( "wgPageContentModel" );
| |
| if( contentModel === "javascript" ) {
| |
| var prefixLength = mw.config.get( "wgUserName" ).length + 6;
| |
| if( pageName.indexOf( "User:" + mw.config.get( "wgUserName" ) ) === 0 ) {
| |
| var skinIndex = SKINS.indexOf( pageName.substring( prefixLength ).slice( 0, -3 ) );
| |
| if( skinIndex >= 0 ) {
| |
| return $( "<abbr>" ).text( "Cannot install" )
| |
| .attr( "title", "This page is one of your user customization pages, and " +
| |
| ( ( skinIndex === 0 ) ? "will" : "may" ) + " already run on each page load." );
| |
| }
| |
| }
| |
| addingInstallLink = true;
| |
| } else {
| |
| return $( "<abbr>" ).text( "Cannot install (not JS)" )
| |
| .attr( "title", "Page content model is " + contentModel + ", not 'javascript'" );
| |
| }
| |
| }
| |
| | |
| // Namespace 8 is MediaWiki
| |
| if( namespaceNumber === 8 ) {
| |
| return $( "<a>" ).text( "Install via preferences" )
| |
| .attr( "href", mw.util.getUrl( "Special:Preferences" ) + "#mw-prefsection-gadgets" );
| |
| }
| |
| | |
| var editRestriction = mw.config.get( "wgRestrictionEdit" );
| |
| if( ( namespaceNumber !== 2 && namespaceNumber !== 8 ) &&
| |
| ( editRestriction.indexOf( "sysop" ) >= 0 ||
| |
| editRestriction.indexOf( "editprotected" ) >= 0 ) ) {
| |
| installElement.append( " ",
| |
| $( "<abbr>" ).append(
| |
| $( "<img>" ).attr( "src", "https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Achtung-yellow.svg/20px-Achtung-yellow.svg.png" ).addClass( "warning" ),
| |
| "(insecure)" )
| |
| .attr( "title", "Installation of non-User, non-MediaWiki"+
| |
| " protected pages is temporary and may be removed in the future." ) );
| |
| addingInstallLink = true;
| |
| }
| |
| | |
| if( addingInstallLink ) {
| |
| var fixedPageName = mw.config.get( "wgPageName" ).replace( /_/g, " " );
| |
| installElement.prepend( $( "<a>" )
| |
| .attr( "id", "script-installer-main-install" )
| |
| .text( localScriptsByName[ fixedPageName ] ? "Uninstall" : "Install" )
| |
| .click( makeLocalInstallClickHandler( fixedPageName ) ) );
| |
| return installElement;
| |
| }
| |
| | |
| return $( "<abbr>" ).text( "Cannot install (insecure)" )
| |
| .attr( "title", "Page is not User: or MediaWiki: and is unprotected" );
| |
| }
| |
| | |
| function showUi() {
| |
| var fixedPageName = mw.config.get( "wgPageName" ).replace( /_/g, " " );
| |
| $( "#firstHeading" ).append( $( "<span>" )
| |
| .attr( "id", "script-installer-top-container" )
| |
| .append(
| |
| buildCurrentPageInstallElement(),
| |
| " | ",
| |
| $( "<a>" )
| |
| .text( "Manage user scripts" ).click( function () {
| |
| if( !document.getElementById( "script-installer-panel" ) ) {
| |
| $( "#mw-content-text" ).before( makePanel() );
| |
| } else {
| |
| $( "#script-installer-panel" ).remove();
| |
| }
| |
| } ) ) );
| |
| }
| |
| | |
| function attachInstallLinks() {
| |
| // At the end of each {{Userscript}} transclusion, there is
| |
| // <span id='User:Foo/Bar.js' class='scriptInstallerLink'></span>
| |
| $( "span.scriptInstallerLink" ).each( function () {
| |
| var scriptName = this.id;
| |
| $( this ).append( " | ", $( "<a>" )
| |
| .text( localScriptsByName[ scriptName ] ? "Uninstall" : "Install" )
| |
| .click( makeLocalInstallClickHandler( scriptName ) ) );
| |
| } );
| |
| | |
| $( "table.infobox-user-script" ).each( function () {
| |
| var scriptName = $( this ).find( "th:contains('Source')" ).next().text() ||
| |
| mw.config.get( "wgPageName" );
| |
| scriptName = /user:.+?\/.+?.js/i.exec( scriptName )[0];
| |
| $( this ).children( "tbody" ).append( $( "<tr>" ).append( $( "<td>" )
| |
| .attr( "colspan", "2" )
| |
| .addClass( "script-installer-ibx" )
| |
| .append( $( "<button>" )
| |
| .addClass( "mw-ui-button mw-ui-progressive mw-ui-big" )
| |
| .text( localScriptsByName[ scriptName ] ? "Uninstall" : "Install" )
| |
| .click( makeLocalInstallClickHandler( scriptName ) ) ) ) );
| |
| } );
| |
| }
| |
| | |
| function makeLocalInstallClickHandler( scriptName ) {
| |
| return function () {
| |
| var $this = $( this );
| |
| if( $this.text() === "Install" ) {
| |
| var okay = window.sciNoConfirm || window.confirm( "Warning! All user scripts could contain malicious content capable of compromising your account. Installing a script means it could be changed by others; make sure you trust its author. If you're unsure whether a script is safe, check at the technical village pump. Install this script? (Hide this dialog next time with sciNoConfirm=true; in your common.js.)" );
| |
| if( okay ) {
| |
| $( this ).text( "Installing..." )
| |
| Import.ofLocal( scriptName, "common" ).install().done( function () {
| |
| $( this ).text( "Uninstall" );
| |
| conditionalReload( false );
| |
| }.bind( this ) );
| |
| }
| |
| } else {
| |
| $( this ).text( "Uninstalling..." )
| |
| var uninstalls = uniques( localScriptsByName[ scriptName ] )
| |
| .map( function ( target ) { return Import.ofLocal( scriptName, target ).uninstall(); } )
| |
| $.when.apply( $, uninstalls ).then( function () {
| |
| $( this ).text( "Install" );
| |
| conditionalReload( false );
| |
| }.bind( this ) );
| |
| }
| |
| };
| |
| }
| |
| | |
| function addCss() {
| |
| mw.util.addCSS(
| |
| "#script-installer-panel li.disabled a.script { "+
| |
| "text-decoration: line-through; font-style: italic; }"+
| |
| "#script-installer-panel { width:60%; border:solid lightgray 1px; "+
| |
| "padding:0; margin-left: auto; "+
| |
| "margin-right: auto; margin-bottom: 15px; overflow: auto; "+
| |
| "box-shadow: 5px 5px 5px #999; background-color: #fff; z-index:50; }"+
| |
| "#script-installer-panel header { background-color:#CAE1FF; display:block;"+
| |
| "padding:5px; font-size:1.1em; font-weight:bold; text-align:left; }"+
| |
| "#script-installer-panel .checkbox-container input { margin-left: 1.5em; }"+
| |
| "#script-installer-panel .filter-container { margin-bottom: -0.75em; }"+
| |
| "#script-installer-panel .filter-container label { margin-right: 0.35em; }"+
| |
| "#script-installer-panel .container { padding: 0.75em; }"+
| |
| "#script-installer-panel .container h2 { margin-top: 0.75em; }"+
| |
| "#script-installer-panel a { cursor: pointer; }"+
| |
| "#script-installer-main-install { font-weight: bold; }"+
| |
| "#script-installer-top-container { bottom: 5px; font-size: 70%; margin-left: 1em }"+
| |
| "body.skin-modern #script-installer-top-container a { color: inherit; cursor: pointer }"+
| |
| "body.skin-timeless #script-installer-top-container a,body.skin-cologneblue #script-installer-top-container a { cursor: pointer }"+
| |
| "#script-installer-top-container img.warning { position: relative; top: -2px; margin-right: 3px }"+
| |
| "td.script-installer-ibx { text-align: center }"
| |
| );
| |
| }
| |
| | |
| /********************************************
| |
| *
| |
| * Utility functions
| |
| *
| |
| ********************************************/
| |
| | |
| /**
| |
| * Gets the wikitext of a page with the given title (namespace required).
| |
| */
| |
| function getWikitext( title ) {
| |
| return $.getJSON(
| |
| mw.util.wikiScript( "api" ),
| |
| {
| |
| format: "json",
| |
| action: "query",
| |
| prop: "revisions",
| |
| rvprop: "content",
| |
| rvslots: "main",
| |
| rvlimit: 1,
| |
| titles: title
| |
| }
| |
| ).then( function ( data ) {
| |
| var pageId = Object.keys( data.query.pages )[0];
| |
| if( data.query.pages[pageId].revisions ) {
| |
| return data.query.pages[pageId].revisions[0].slots.main["*"];
| |
| }
| |
| return "";
| |
| } );
| |
| }
| |
| | |
| function escapeForRegex( s ) {
| |
| return s.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' );
| |
| }
| |
| | |
| function getFullTarget ( target ) {
| |
| return "User:" + mw.config.get( "wgUserName" ) + "/" +
| |
| target + ".js";
| |
| }
| |
| | |
| // From https://stackoverflow.com/a/10192255
| |
| function uniques( array ){
| |
| return array.filter( function( el, index, arr ) {
| |
| return index === arr.indexOf( el );
| |
| });
| |
| }
| |
| | |
| if( window.scriptInstallerAutoReload === undefined ) {
| |
| window.scriptInstallerAutoReload = true;
| |
| }
| |
| | |
| | |
| var jsPage = mw.config.get( "wgPageName" ).slice( -3 ) === ".js" ||
| |
| mw.config.get( "wgPageContentModel" ) === "javascript";
| |
| $.when(
| |
| $.ready,
| |
| mw.loader.using( [ "mediawiki.api", "mediawiki.util" ] )
| |
| ).then( function () {
| |
| api = new mw.Api();
| |
| addCss();
| |
| buildImportList().then( function () {
| |
| attachInstallLinks();
| |
| if( jsPage ) showUi();
| |
| | |
| // Auto-open the panel if we set the cookie to do so (see `conditionalReload()`)
| |
| if( document.cookie.indexOf( "open_script_installer=yes" ) >= 0 ) {
| |
| document.cookie = "open_script_installer=; expires=Thu, 01 Jan 1970 00:00:01 GMT";
| |
| $( "#script-installer-top-container a:contains('Manage')" ).trigger( "click" );
| |
| }
| |
| } );
| |
| } );
| |
| } )();
| |