User:Novusistic/Guidedtour-tour-tut4.js

/** * Guidedtour-tour-tut4.js * * It creates a guided tour for The Userscript Tour: Mission 4 (Novelty of OOUI). * The users are made familiar with Object-Oriented User Interface, how to easily create user * interfaces and write userscripts using OOUI. * * The following is the entire license notice for the JavaScript code in this Guided Tour. * * Copyright (C) 2021 Devyansh Chawla  and contributors * * The JavaScript/Gadget code in this page is free software: you can * redistribute it and/or modify it under the terms of the GNU * General Public License (GNU GPL) as published by the Free Software * Foundation, either version 3 of the License, or (at your option) * any later version. The code is distributed WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. * * As additional permission under GNU GPL version 3 section 7, you * may distribute non-source (e.g., minimized or compacted) forms of * that code without the copy of the GNU GPL normally required by * section 4, provided you include this license notice and a URL * through which recipients can access the Corresponding Source. * * The above is the entire license notice for the JavaScript code in this Guided Tour. */ ( function ( window, document, $, mw, gt ) {

// Defined the guided tour. var tour = new gt.TourBuilder( {			name: 'tut4',			shouldLog: true		} ), postEditButtons = [];

/**	 * Show the message dialog box. *	 * It displays a message dialog box (alert box) with relevant message if the user * is not logged-in or request to the Action API for sending yourself messages fails. *	 * @param {string} title Represents the title of the error. * @param {string} message Represents the description of the error. */	function showAlert( title, message ) { var messageDialog = new OO.ui.MessageDialog, windowManager = new OO.ui.WindowManager;

// Sets the z-index of the message dialog box to be greater than that of the 'guider' // so that the dialog box appears on top of everything. messageDialog.$element.css( { zIndex: '100000010' } );

$( 'body' ).append( windowManager.$element );

windowManager.addWindows( [ messageDialog ] );

windowManager.openWindow( messageDialog, {			title: title,			message: message,			actions: [ {				action: 'accept',				label: 'OK',				flags: 'primary'			} ]		} ); }

/**	 * Sends the message to the personal wiki page. *	 * It sends the message to the user's wiki page with API:Edit. For example, Welcome to the Tour, * Badge earned, etc.	 * * @param {string} targetPage Represents the location of the personal wiki page to send * the message to. * @param {string} msgPage Represents the location of the wiki page containing the message * to be sent. * @param {string} linkTo Represents the location of the wiki page to direct the user to, * after the message has been sent. */	function sendMessage( targetPage, msgPage, linkTo ) { var api = new mw.Api;

// Sends a GET request to the wiki page containing the message. The response contains the // message and the edit token if the Promise is resolved. api .get( {				action: 'query',				titles: msgPage,				prop: 'revisions',				rvprop: 'content',				rvslots: '*',				indexpageids: 1,				meta: 'tokens'			} ) .done( function ( result ) {				result = result.query;				var csrfToken = result.tokens.csrftoken,					page = result.pages[ result.pageids[ 0 ] ],					// Checks whether the wiki page containing the message exists or not.					text = page.revisions !== undefined ?						page.revisions[ 0 ].slots.main[ '*' ] :						'Page unavailable';

// If the page containing the message doesn't exist, simply direct the user // to the specified location. if ( text === 'Page unavailable' ) { window.location.href = linkTo; return; }

// Sends the extracted message to the personal wiki page. api .post( {						action: 'edit',						title: targetPage,						appendtext: '\n' + text,						summary: 'A badge sent as part of The Userscript Tour.',						token: csrfToken					} ) .done( function {						window.location.href = linkTo;					} ) // Should the GET request fail, a message dialog box is displayed to the user. .fail( function {						showAlert( 'Error', 'An error occured. Please try again.' );					} ); } )			// Should the GET request fail, a message dialog box is displayed to the user.			.fail( function { showAlert( 'Error', 'An error occured. Please try again.' ); } );	}

// Loads the OOUI dependencies required to display the message dialog box. mw.loader.load( [ 'oojs-ui-core', 'oojs-ui-windows' ] );

// Gracefully handles the situation when the user saves a file without editing it. // Asks the user to go back and make an edit. if ( mw.config.get( 'wgAction' ) === 'view' && !gt.isPostEdit ) { postEditButtons.push( {			name: 'Click here to go back and make an edit',			onclick: function {				window.location.href = window.location.href + '?action=edit';			}		} ); }

// Step 1 tour .firstStep( {			name: '1',			title: 'Good to see you!',			description: '  Congrats on reaching the final phase of The Userscript Tour. You must be very excited to make a difference in this Mission too.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: 'Ready to make a difference',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '2' );

// Step 2 tour .step( {			name: '2',			title: 'Object-Oriented User Interface',			description: '  OOUI (Object-Oriented User Interface) allows the creation of web user-interfaces and widgets.  The OOUI library provides the building blocks for building an object-oriented user interface.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=1'			}, {				name: 'Alright',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '3' );

// Step 3 tour .step( {			name: '3',			title: 'Features',			description: '  1) User interfaces created with OOUI generates HTML markup itself. 2) It uses ready-to-use widgets, layouts, and windows that can be instantiated directly or easily extended. 3) Elements in OOUI can be easily mixed and matched to create custom user interfaces. ',			onShow: gt.parseDescription, overlay: true, closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=2' }, {				name: 'Alright', action: 'next' } ],			allowAutomaticOkay: false } )		.next( '4' );

// Step 4 tour .step( {			name: '4',			title: 'Widgets',			description: '  The most basic component of the OOUI library is called an element. And widgets are compositions of one or more elements that users can both view and interact with.  Examples of widgets include buttons, icons, popups, etc.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=3'			}, {				name: 'Okay',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '5' );

// Step 5 tour .step( {			name: '5',			title: 'Dependencies',			description: ' Depending on which of the features you\'re going to use, you have to load one or more of the following modules in your user script: 1)   2)   3)   4)   5)  ', onShow: gt.parseDescription, overlay: true, closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=4' }, {				name: 'Got it', action: 'next' } ],			allowAutomaticOkay: false } )		.next( '6' );

// Step 6 tour .step( {			name: '6',			title: 'Hands-on',			description: '  We\'ll begin by writing a basic user script that uses button widget and window, provided by OOUI.  ',			onShow: gt.parseDescription,			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=5'			}, {				name: 'Let\'s do this',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/showAlertBox.js' ) + '?tour=tut4&step=7'			} ],			allowAutomaticOkay: false		} );

// Step 7 tour .step( {			name: '7',			title: 'Your subpage',			description: ' This user script will show an alert when a button is clicked. The location of your subpage is User:' + mw.config.get( 'wgUserName' ) + '/showAlertBox.js ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=6'			}, {				name: 'Okay',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/showAlertBox.js' ) + '?tour=tut4&step=8&action=edit&preload=User:Novusistic/TUT_showAlertBox.js&summary=Created a userscript to show an alert on clicking a button.'			} ],			allowAutomaticOkay: false		} );

// Step 8 tour .step( {			name: '8',			title: 'Sneak Peek',			description: ' Go through the above script. The comments in the script explain it all. Once done, publish the changes.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/showAlertBox.js' ) + '?tour=tut4&step=7'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '9';			}		} );

// Step 9 tour .step( {			name: '9',			title: 'The Rationale',			description: '  LOGIC:  1) The module - oojs-ui-core is loaded before the code runs. This module is required to add an OOUI button and alert dialog. 2) The button widget is created and added to the toolbox section of the Portlet area. 3) A \'click event listener\' is added to the OOUI button. The event handler shows an alert when the button is clicked. ',			onShow: gt.parseDescription, overlay: false, attachTo: '#bodyContent', position: 'bottom', closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/showAlertBox.js' ) + '?tour=tut4&step=8&action=edit' }, {				name: 'Time to go to common.js', type: 'progressive', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=10' } ],			allowAutomaticOkay: false } );

// Step 10 tour .step( {			name: '10',			title: 'Edit common.js',			description: ' Load the user script by editing common.js page. ',			onShow: gt.parseDescription,			attachTo: '#ca-edit',			position: 'bottom',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/showAlertBox.js' ) + '?tour=tut4&step=9'			} ],			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.hasQuery( { action: 'edit' } ) ) {				return '11';			}		} );

// Step 11 tour .step( {			name: '11',			title: 'Load the userscript',			description: '  Chances are your common.js looks cluttered at this point. Feel free to clear it up first :)  Next, copy and paste the following at the end of common.js: mw.loader.load( \' https:' + mw.config.get( 'wgServer' ) + '/w/index.php?title=User:' + mw.config.get( 'wgUserName' ) + '/showAlertBox.js&action=raw&ctype=text/javascript \' );  ', onShow: gt.parseDescription, attachTo: '.wikiEditor-ui-text', position: 'bottomRight', overlay: false, closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=10' }, {				name: 'Done with it', action: 'next' } ],			allowAutomaticOkay: false } )		.next( '12' );

// Step 12 tour .step( {			name: '12',			title: 'Edit summary and Publish Changes',			description: ' Good work! Before you click Publish Changes, leave a brief note about the changes you made. Click PUBLISH CHANGES when you\'re ready.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=11&action=edit'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '13';			}		} );

// Step 13 tour .step( {			name: '13',			title: 'Play around',			description: ' Did you see the button saying "Click me" on the left? In case you didn\'t, try bypassing your cache. Click it to see an alert.  ',			onShow: gt.parseDescription,			attachTo: '#tut-ooui-alert',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=12&action=edit'			}, {				name: 'Pretty neat',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=14'			} ],			allowAutomaticOkay: false		} );

// Step 14 tour .step( {			name: '14',			title: 'Custom user interface',			description: '  OOUI provides the flexibility to create user interfaces that fit your needs.  It includes many widgets and layouts that are ready to use, as well as basic elements that can be mixed and matched to create custom user interfaces.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=13'			}, {				name: 'Sounds good',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '15' );

// Step 15 tour .step( {			name: '15',			title: 'Interactive demos',			description: '  Have a look at the OOUI demos that show numerous widgets, layouts, dialogs, icons, and toolbars. The fun part is you can get to know how these are implemented, right away!  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=14'			}, {				name: 'Explore the demos',				type: 'neutral',				onclick: function  {					window.open( 'https://doc.wikimedia.org/oojs-ui/master/demos/', '_blank' );				}			}, {				name: 'Next',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '16' );

// Step 16 tour .step( {			name: '16',			title: 'A cool userscript',			description: '  Our next user script would be fun just like the OOUI demos you saw previously.  It mixes and matches various widgets to create a custom user interface.  ',			onShow: gt.parseDescription,			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=15'			}, {				name: 'Let\'s go',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/guessRandomNumber.js' ) + '?tour=tut4&step=17'			} ],			allowAutomaticOkay: false		} );

// Step 17 tour .step( {			name: '17',			title: 'Your subpage',			description: ' In this user script, you will have to guess a random number. It depicts how various OOUI elements can be composed to create user interfaces. Your subpage is User:' + mw.config.get( 'wgUserName' ) + '/guessRandomNumber.js  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/Start' ) + '?tour=tut4&step=16'			}, {				name: 'Okay',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/guessRandomNumber.js' ) + '?tour=tut4&step=18&action=edit&preload=User:Novusistic/TUT_guessRandomNumber.js&summary=Created a userscript to show a random number guessing game.'			} ],			allowAutomaticOkay: false		} );

// Step 18 tour .step( {			name: '18',			title: 'Sneak Peek',			description: ' The above script is well-documented. Try to understand the fundamental aspects of it. Once done, publish the changes.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/guessRandomNumber.js' ) + '?tour=tut4&step=17'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '19';			}		} );

// Step 19 tour .step( {			name: '19',			title: 'The Rationale',			description: ' You may go through the comments in the above user script first. The LOGIC is explained below in brief:  1) A custom widget RandomNumberWidget is created by extending OO.ui.Widget.  2) This custom widget mixes MessageWidget, LabelWidget, TextInputWidget, and ButtonWidget.  3) Methods - onBtnClick, setGameOver, and setLabels are created to define the behaviour when the user interacts with the widget. 4) Finally, the custom widget is instantiated and prepended to the DOM. ',			onShow: gt.parseDescription,			overlay: false,			attachTo: '#bodyContent',			position: 'bottom',			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/guessRandomNumber.js' ) + '?tour=tut4&step=18&action=edit'			}, {				name: 'Got it',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=20&action=edit'			} ],			allowAutomaticOkay: false		} );

// Step 20 tour .step( {			name: '20',			title: 'Load the script',			description: '  Copy and paste at the bottom:  mw.loader.load( \' https:' + mw.config.get( 'wgServer' ) + '/w/index.php?title=User:' + mw.config.get( 'wgUserName' ) + '/guessRandomNumber.js&action=raw&ctype=text/javascript \' );  ',			onShow: gt.parseDescription,			attachTo: '.wikiEditor-ui-text',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/guessRandomNumber.js' ) + '?tour=tut4&step=19'			}, {				name: 'Done',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '21' );

// Step 21 tour .step( {			name: '21',			title: 'Edit summary and Publish Changes',			description: ' Perfect! Before you click Publish Changes, leave a brief note about the changes you made. Click PUBLISH CHANGES when you\'re ready.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=20&action=edit'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '22';			}		} );

// Step 22 tour .step( {			name: '22',			title: 'A tiny game!',			description: ' There you go! You have your very own random number guessing game. Hmm, let’s see if you can win this one :) In case you can’t see it, try bypassing your cache. ',			onShow: gt.parseDescription, attachTo: '.guessRandomNumberWidget', position: 'bottomLeft', overlay: false, closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=21&action=edit' }, {				name: 'This is cool', type: 'progressive', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=23' } ],			allowAutomaticOkay: false } );

// Step 23 tour .step( {			name: '23',			title: 'Congrats!',			description: 'Final badge earned: Target reached  Bravo! You have successfully completed THE USERSCRIPT TOUR. We\'re all impressed! ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=22'			}, {				name: 'Thanks',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '24' );

// Step 24 tour .step( {			name: '24',			title: 'Save the badge',			description: '  Do you want to save this badge in your subpage?  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut4&step=23'			}, {				name: 'Don\'t save',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/4/End' ) + '?tour=tut4&step=25'			}, {				name: 'Save the badge',				onclick: function  {					if ( !mw.config.get( 'wgUserName' ) ) {						showAlert( 'Please login', 'Please login to continue on the tour.' );						return;					}					sendMessage( 'User:' + mw.config.get( 'wgUserName' ) + '/theUserscriptTourBadges', 'TUT/Badge/4template1', mw.util.getUrl( 'TUT/4/End' ) + '?tour=tut4&step=25' );				}			} ],			allowAutomaticOkay: false		} );

// Step 25 tour .step( {			name: '25',			title: 'Mission 4 complete!',			description: '  This concludes Mission 4 and with it, The Userscript Tour. You are now equipped with the fundamental constructs required to write creative user scripts.  So, what are you waiting for? Happy contributing to MediaWiki :)  ', onShow: gt.parseDescription, overlay: true, closeOnClickOutside: false, buttons: [ { name: 'Check my badges', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/theUserscriptTourBadges' ) }, {				name: 'Congrats me!', action: 'end' } ],			allowAutomaticOkay: false } );

}( window, document, jQuery, mediaWiki, mediaWiki.guidedTour ) );