ميډياويکي:Gadget-TemplateParamWizard.js

د ويکيپېډيا، وړیا پوهنغونډ له خوا

د نور تفصيل لپاره د غځول په تنۍ کلېک وکړئيادښت: د غوره توبونو د خوندي کولو وروسته، خپل د کتنمل (بروزر) ساتل شوې حافظه تازه کړی.د نور تفصيل لپاره د غځول په تنۍ کلېک وکړئ.

  • فايرفاکس/ سفري: په دې کتنمل کې د Reload د ټکوهلو په وخت د Shift تڼۍ نيولې وساتی، او يا هم Ctrl-F5 يا Ctrl-Rتڼۍ کېښکاږۍ (په Apple Mac کمپيوټر باندې ⌘-R کېښکاږۍ)
  • گووگل کروم: په دې کتنمل کې د Ctrl-Shift-R تڼۍ کېښکاږۍ (د مک لپاره ⌘-Shift-R)
  • انټرنټ اېکسپلورر: په دې کتنمل کې د Refresh د ټکوهلو په وخت کې د Ctrl تڼۍ کېښکاږلې ونيسۍ، او يا هم د Ctrl-F5 تڼۍ کېښکاږۍ
  • اوپرا: په دې کتنمل کې د خپل براوزر ساتل شوې حافظه پدې توگه سپينولی شی Tools→Preferences
لاسوند[جوړول]
// Template parameters wizard
// Written by [[User:קיפודנחש]]
'use strict';
if ( ( $.inArray( mw.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) + 1 ) && ( !$( '#wpTextbox1' ).prop( 'readonly' ) ) ) {
	$( function () {
		// template parameter is an object with the following fields:
		// desc: desciption string
		// defval: default value (optional)
		// options: object with optional fields:
		//   multiline: number of lines
		//   depends: another field's name
		//   required: boolean
		//   date: use JS date widget
		//   choices: array of legal values for the field
		//   extended: this field will not show on initial screen, and will appear once the user selects "show all fields"

		var
		// templateParams is keyed by paramName.
			templateParams,
			paramsOrder,
			// which template are we working on
			template,
			// array of pairs - [paramName, inputField]
			dialogFields,
			// table rows keyed by paramName
			rowsBypName,
			// the fields, keyed by paramName
			fieldsBypName,
			// boolean, indicating we did not find "Parameters" page, so the parameters are extracted from template page itself.
			rawTemplate,
			rtl = $( 'body' ).is( '.rtl' ),
			// test to see if a string contains wikiCode and hence needs parsing, or cen be used as is.
			wikiCodeFinder = /[\[\]\{\}\<\>]/,
			globalExplanation = '',
			extendedParamCssRule,
			anyExtended = false,
			localStorageKey = 'templateParamWizard',
			emptiesKey = 'writeEmpties';

		function addParam( name ) {
			if ( $.inArray( name, paramsOrder ) === -1 ) { paramsOrder.push( name ); }
		}

		function paramsFromSelection() {
			var selection = $( '#wpTextbox1' ).textSelection( 'getSelection' ).replace( /^\s*\{\{|\}\}\s*$/g, '' ); // scrap the first {{ and last }}
			var specials = [];
			while ( true ) { // extract inner links, inner templates and inner params - we don't want to sptit those.
				var match = selection.match( /(\{\{[^\{\}\]\[]*\}\}|\[\[[^\{\}\]\[]*\]\]|\[[^\{\}\]\[]*\])/ );
				if ( !match || !match.length ) { break; }
				specials.push( match[ 0 ] );
				selection = selection.replace( match[ 0 ], '\0' + specials.length + '\0' );
			}
			var params = selection.split( /\s*\|\s*/ );
			for ( var i in params ) {
				var param = params[ i ];
				while ( true ) {
					var match = param.match( /\0(\d+)\0/ );
					if ( !match || !match.length ) { break; }
					param = param.replace( match[ 0 ], specials[ parseInt( match[ 1 ], 10 ) - 1 ] );
				}
				var paramPair = param.split( '=' );
				var name = $.trim( paramPair.shift() );
				if ( name && paramPair.length ) {
					var tp = templateParams[ name ] = templateParams[ name ] || { options: { notInParamPage: 1 } },
						val = paramPair.join( '=' );
					addParam( name );
					$.extend( tp.options, { defval: val } );
					// next line is for the case where there are "choices"' but current value is not one of them: add it as a new choice.
					if ( typeof tp.options.choices === 'string' && tp.options.choices.indexOf( val ) < 0 ) { tp.options.choices += ( ', ' + val ); }
				}
			}
		}

		function buildParamsRaw( data ) {
			var
				paramExtractor = /{{3,}(.*?)[<|}]/mg,
				m;
			while ( ( m = paramExtractor.exec( data ) ) ) {
				var paramName = $.trim( m[ 1 ] );
				templateParams[ paramName ] = { desc: '', options: { multiline: 5 } };
				addParam( paramName );
			}
		}

		function buildParams( data ) {
			var
				lines = data.split( '\n' ),
				line;

			function extractGlobalExplanation() {
				line = line.replace( /[!\|][^\|]*\|/, '' );
				if ( wikiCodeFinder.test( line ) ) {
					$.post(
						mw.util.wikiScript( 'api' ),
						{ action: 'parse', text: line, disablepp: 1, format: 'json' },
						function ( data ) {
							var html = data.parse.text[ '*' ];
							globalExplanation = html;
							$( '#tpw_globalExplanation' ).html( html ).find( 'a' ).attr( { target: '_blank' } );
						}
					);
				} else { globalExplanation = line; }
			}

			while ( lines.length ) {
				line = lines.shift();
				// look for |- this is wikitext for table row.
				if ( !( /^\|-/.test( line ) ) ) { continue; }
				line = lines.shift();
				if ( line.indexOf( 'globalExplanation' ) + 1 ) {
					extractGlobalExplanation();
					continue;
				}
				// wikitext for column
				if ( !line || !( /^\|/.test( line ) ) ) { continue; }
				line = line.substr( 1 ); // get rid of the leading |
				var fields = line.split( '||' );
				if ( fields.length < 2 ) { continue; }
				var name = $.trim( fields[ 0 ] );
				if ( !name ) { continue; }
				var desc = $.trim( fields[ 1 ] );
				var pAttribs = { desc: desc };
				if ( fields.length > 2 ) { pAttribs.options = analyzeOptions( $.trim( fields[ 2 ] ) ); }

				templateParams[ name ] = pAttribs;
				addParam( name );

			}
		}

		function analyzeOptions( str ) {
			var res = {},
				avail = [ 'multiline', 'required', 'depends', 'defval', 'choices', 'date', 'extended' ], // maybe we'll have more in the future
				tavail = $.map( avail, i18n ),
				options = str.split( /\s*;\s*/ );
			for ( var i in options ) {
				var option = options[ i ].split( /\s*=\s*/ );
				var ind = $.inArray( option[ 0 ], tavail );
				if ( ind + 1 ) { res[ avail[ ind ] ] = option.length > 1 ? option[ 1 ] : true; }
			}
			anyExtended = anyExtended || res.extended;
			return res;
		}

		function createWikiCode() {
			var par = [ template ],
				delim = $( '#oneLineTemplate' ).prop( 'checked' ) ? '' : '\n',
				createEmpties = $( '#createEmpties' ).prop( 'checked' );
			for ( var i in dialogFields ) {
				var
					field = dialogFields[ i ],
					name = $.trim( field[ 0 ] ),
					f = field[ 1 ],
					hidden = f.parents( '.tpw_hidden' ).length,
					val = $.trim( f.val() );
				if ( !createEmpties && val === '' && f.attr( 'type' ) != 'checkbox' ) { continue; }// skip parameters with no value
				if ( f.attr( 'type' ) == 'checkbox' && !f.prop( 'checked' ) ) { val = ''; }
				par.push( name + '=' + val );
			}
			return '{{' + par.join( delim + '|' ) + delim + '}}';
		}

		function showPreview() {
			var temp = createWikiCode();
			$.post( mw.util.wikiScript( 'api' ),
				{ action: 'parse',
					title: mw.config.get( 'wgPageName' ),
					prop: 'text',
					text: temp,
					format: 'json'
				},
				function ( data ) {
					if ( data && data.parse && data.parse.text ) {
						var buttons = [ { text: i18n( 'close' ), click: function () { $( this ).dialog( 'close' ); } } ],
							div = $( '<div>' ).html( data.parse.text[ '*' ] );
						$( 'a', div ).attr( 'target', '_blank' ); // we don't want people to click on links in preview - they'll lose their work.
						$( '<div>' )
							.dialog(
								{ title: i18n( 'preview' ),
									modal: true,
									position: [ 60, 60 ],
									buttons: buttons } )
							.append( div );
						circumventRtlBug();
					}
				} );
		}

		function circumventRtlBug() {
			if ( rtl ) { $( '.ui-dialog-buttonpane button' ).css( { 'float': 'right' } ); } // jQuery has problems with rtl dialogs + ie is braindamaged.
		}

		function i18n( key, param ) {
			switch ( mw.config.get( 'wgUserLanguage' ) ) {
				case 'ar':
					switch ( key ) {
						case 'explain': return rawTemplate ?
							'قالب "' + template + '" ليس له صفحة وسائط فرعية، لذلك فما من وصف لمعطياته.' :
							'الوسائط الضرورية محددة بالأحمر والبقية اختيارية.';
						case 'wizard dialog title': return 'وسائط ' + '<a href="' + mw.util.getUrl( 'قالب:' + template ) + '" target="_blank">' + 'قالب:' + template + '</a>';
						case 'ok': return 'موافقة';
						case 'cancel': return 'إلغاء';
						case 'params subpage': return 'وسائط';
						case 'preview': return 'معاينة';
						case 'options select': return 'اختر معطى';
						case 'multiline': return 'عدد صفوف';
						case 'close': return 'أغلق';
						case 'required': return 'ضروري';
						case 'depends': return 'يلزمه';
						case 'defval': return 'غيابي';
						case 'choices': return 'خيارات';
						case 'date': return 'تاريخ';
						case 'extended': return 'Extended';
						case 'button hint': return 'معالج وسائط القالب';
						case 'able templates category name': return 'قوالب صالحة لمعالج وسائط القالب';
						case 'template selector title': return 'اكتب اسم القالب:';
						case 'notInParamPage': return 'وسيط "' + param + '" ليس من وسائط القالب';
						case 'editParamPage': return 'عدل صفحة الوسائط';
						case 'unknown error': return 'وقع خطأ.\n' + param;
						case 'please select template': return 'اسم القالب';
						case 'oneliner': return 'اجعله في صف واحد';
						case 'createempties': return 'Write empty parameters to page';
						case 'dateFormat': return 'd MM yy';
						case 'extended labels': return 'Show all parameters';
					}
					break;
				case 'ur':
					switch ( key ) {
						case 'explain': return rawTemplate;
						case 'wizard dialog title': return 'سانچہ: "' + template + '" کے لیے محددات داخل کریں۔';
						case 'ok': return 'ٹھیک ہے';
						case 'wizard dialog title': return 'מילוי הפרמטרים עבור ' + '<a href="' + mw.util.getUrl( 'תבנית:' + template ) + '" target="_blank">' + 'תבנית:' + template + '</a>';
						case 'params subpage': return 'محددات';
						case 'preview': return 'نمائش';
						case 'options select': return 'کوئی ایک منتخب کریں:';
						case 'multiline': return 'Multiline';
						case 'options select': return 'منتخب کریں';
						case 'multiline': return 'מספר שורות';
						case 'close': return 'بند کریں';
						case 'required': return 'لازمی مطلوب';
						case 'depends': return 'اس پر منحصر';
						case 'defval': return 'طے شدہ';
						case 'choices': return 'اختیارات';
						case 'date': return 'تاریخ';
						case 'button hint': return 'ساحر محدداتِ سانچہ';
						case 'button hint': return 'אשף מילוי תבניות';
						case 'able templates category name': return 'תבניות הנתמכות על ידי אשף התבניות';
						case 'template selector title': return 'براہِ کرم سانچے کا نام داخل کریں';
						case 'notInParamPage': return 'השדה "' + param + '" לא מופיע ברשימת הפרמטרים של התבנית';
						case 'editParamPage': return 'ترمیم صفحہ محددات';
						case 'unknown error': return 'نا معلوم ایرر.\n' + param;
						case 'please select template': return 'سانچہ منتخب کریں';
						case 'oneliner': return 'یک سطری سانچہ';
						case 'createempties': return 'רשום שדות ריקים';
						case 'dateFormat': return 'MM d, yy';
						case 'extended labels': return 'הראה את כל הפרמטרים';
					}
					break;
				default:
					switch ( key ) {
						case 'explain': return 'fields with red border are required, the rest are optional';
						case 'wizard dialog title': return 'Set up parameters for template: ' + template;
						case 'ok': return 'OK';
						case 'cancel': return 'Cancel';
						case 'params subpage': return 'Parameters';
						case 'preview': return 'Preview';
						case 'options select': return 'Select one:';
						case 'multiline': return 'Multiline';
						case 'close': return 'Close';
						case 'required': return 'Required';
						case 'depends': return 'Depends on';
						case 'defval': return 'Default';
						case 'choices': return 'Choices';
						case 'date': return 'Date';
						case 'extended': return 'Extended';
						case 'button hint': return 'Template parameters wizard';
						case 'able templates category name': throw ( 'Must define category name for wizard-capable templates' );
						case 'template selector title': return 'Please enter the template name';
						case 'notInParamPage': return 'field "' + param + '" does not appear in the template\'s parameters list';
						case 'editParamPage': return 'Edit paramters page';
						case 'unknown error': return 'Error occured: \n' + param;
						case 'please select template': return 'Please enter template name';
						case 'oneliner': return 'Single-line template';
						case 'createempties': return 'Write empty parameters to page';
						case 'dateFormat': return 'MM d, yy';
						case 'extended labels': return 'Show all parameters';
					}
					break;
			}
			return key;
		}

		function paramPage() { return mw.config.get( 'wgFormattedNamespaces' )[ 10 ] + ':' + $.trim( template ) + '/' + i18n( 'params subpage' ); }

		function templatePage() { return mw.config.get( 'wgFormattedNamespaces' )[ 10 ] + ':' + $.trim( template ); }

		function updateRawPreview() {
			var canOK = 'enable';
			for ( var i in dialogFields ) {
				var df = dialogFields[ i ][ 1 ];
				var opts = df.data( 'options' );
				if ( opts && opts.required && $.trim( df.val() ).length == 0 ) { canOK = 'disable'; }
				if ( opts && opts.depends ) {
					var dep = fieldsBypName[ opts.depends ];
					var depEmpty = !( ( dep && dep.val() && $.trim( dep.val() ) ) );
					var row = rowsBypName[ df.data( 'paramName' ) ];
					if ( row ) { row.toggleClass( 'tpw_hidden', depEmpty ); }
				}
			}
			$( '.ui-dialog-buttonpane button:contains(\'' + i18n( 'ok' ) + '\')' ).button( canOK );
			$( '#tpw_preview' ).text( createWikiCode() );
			localStorage.setItem( localStorageKey + '.' + emptiesKey, $( '#createEmpties' ).prop( 'checked' ) );
		}

		function createInputField( paramName ) {
			var options = templateParams[ paramName ].options || {},
				f,
				checkbox = false;

			if ( options.choices ) {
				var choices = options.choices.split( /\s*,\s*/ );
				if ( choices.length > 1 ) {
					f = $( '<select>' ).append( $( '<option>', { text: i18n( 'options select' ), value: '' } ) );
					for ( var i in choices ) { f.append( $( '<option>', { text: choices[ i ], value: choices[ i ] } ) ); }
				} else {
					checkbox = true;
					f = $( '<input>', { type: 'checkbox', value: choices[ 0 ], text: choices[ 0 ] } )
						.css( { 'float': rtl ? 'right' : 'left' } );
					f.prop( 'checked', options.defval && options.defval == choices[ 0 ] );
				}
			} else if ( options.multiline ) {
				var rows = options.multiline;
				f = $( '<textarea>', { rows: 1 } )
					.data( { dispRows: isNaN( parseInt( rows ) ) ? 5 : rows } )
					.focus( function () { this.rows = $( this ).data( 'dispRows' ); } )
					.blur( function () { this.rows = 1; } );
			} else { f = $( '<input>', { type: 'text' } ); }

			// teach the controls to autocomplete.
			if ( !checkbox && f.autoCompleteWikiText ) {
				f.autoCompleteWikiText( { positionMy: rtl ? 'left top' : 'right top' } );
			}

			f.css( { width: checkbox ? '1em' : '28em' } )
				.data( { paramName: paramName, options: options } )
				.on( 'paste cut drop input change', updateRawPreview );

			if ( options.defval ) { f.val( options.defval ); }

			if ( options.required ) { f.addClass( 'tpw_required' ).css( { border: '1px red solid' } ); }

			if ( options.date ) { f.datepicker( { dateFormat: typeof options.date === 'string' ? options.date : i18n( 'dateFormat' ) } ); }
			return f;
		}

		var
			timer = null,
			lastVisited = $( '<a>' );

		function addRow( paramName, table ) {
			var
				def = templateParams[ paramName ],
				inputField = createInputField( paramName ),
				nameColor = def.desc ? 'blue' : ( def.options.notInParamPage ? 'red' : 'black' ),
				tr = $( '<tr>' )
					.append(
						$( '<td>', { width: 120 } )
							.css( { fontWeight: 'bold', color: nameColor } )
							.text( paramName )
							.attr( 'master', 'true' )
					)
					.append( $( '<td>' ).css( { width: '30em' } ).append( inputField ) );
			dialogFields.push( [ paramName, inputField ] );
			if ( def.options.extended ) { tr.addClass( 'tpw_extended' ); }
			table.append( tr );
			rowsBypName[ paramName ] = tr;
			fieldsBypName[ paramName ] = inputField;
		}

		function injectResults() {
			$( '#wpTextbox1' ).textSelection( 'encapsulateSelection', { replace: true, peri: createWikiCode() } );
		}

		function createExtendedCheckBox() {
			return $( '<p>' )
				.text( i18n( 'extended labels' ) )
				.append( $( '<input>', { type: 'checkbox' } )
					.change( function () {
						extendedParamCssRule.disabled = $( this ).prop( 'checked' );
					} )
				);
		}

		function buildDialog( data ) {
			$( '.tpw_disposable' ).remove();
			if ( rawTemplate ) { buildParamsRaw( data ); } else { buildParams( data ); }
			paramsFromSelection();
			var	$table = $( '<table>' );
			var $dialog = $( '<div>', { 'class': 'tpw_disposable' } )
				.dialog( { height: 'auto',
					title: i18n( 'wizard dialog title', template ),
					width: 'auto',
					overflow: 'auto',
					position: [ $( 'body' ).width() * 0.2, $( 'body' ).height() * 0.1 ],
					open: function () { $( this ).css( { 'max-height': Math.round( $( 'body' ).height() * 0.7 ) } ); }
				} )
				.append( $( '<div>', { id: 'tpw_globalExplanation' } ).html( globalExplanation ) )
				.append( $( '<p>' ).html( i18n( 'explain' ) ) )
				.append( anyExtended ? createExtendedCheckBox() : '' )
				.append( $table )
				.append( $( '<p>' )
					.append( i18n( 'oneliner' ) )
					.append( $( '<input>', { type: 'checkbox', id: 'oneLineTemplate' } ).change( updateRawPreview )
					)
				)
				.append( $( '<p>' )
					.append( i18n( 'createempties' ) )
					.append( $( '<input>', { type: 'checkbox', id: 'createEmpties' } )
						.change( updateRawPreview )
						.prop( 'checked', localStorage.getItem( localStorageKey + '.' + emptiesKey ) === 'true' )
					)
				)
				.append( $( '<pre>', { id: 'tpw_preview' } )
					.css( { backgroundColor: 'lightGreen', maxWidth: '40em', maxHeight: '8em', overflow: 'auto' } ) );

			while ( paramsOrder.length ) { addRow( paramsOrder.shift(), $table ); }

			var buttons = {}; // we need to do it this way, because with literal object, the keys must be literal.
			buttons[ i18n( 'ok' ) ] = function () { injectResults(); $dialog.dialog( 'close' ); };
			buttons[ i18n( 'cancel' ) ] = function () { $dialog.dialog( 'close' ); };
			buttons[ i18n( 'preview' ) ] = showPreview;
			$dialog.dialog( 'option', 'buttons', buttons );
			circumventRtlBug();
			updateRawPreview();
		}

		function init() {
			template = null;
			templateParams = {};
			paramsOrder = [];
			dialogFields = [];
			rowsBypName = {};
			fieldsBypName = {};
			mw.util.addCSS( '.tpw_hidden{display:none;}' );
			anyExtended = false;
			extendedParamCssRule = extendedParamCssRule || mw.util.addCSS( '.tpw_extended{display:none;}' );
		}

		function reportError( a, b, error ) {
			var key;
			if ( typeof console !== 'undefined' ) {
				for ( key in a ) {
					if ( typeof a[ key ] !== 'function' ) { console.log( key + '=>' + a[ key ] ); }
				}
				console.log( b );
				console.log( error );
			}
			alert( i18n( 'unknown error', error ) );
		}

		function pickTemplate( item ) {
			function okButtonPressed( e, ui ) {
				template = ui ? ui.item.value : $selector.val();
				fireDialog();
				$templateSelector.dialog( 'close' );
			}
			var $selector = $( '<input>' )
				.css( { width: '28em' } )
				.autocomplete( {
					source: function ( request, response ) {
						$.getJSON(
							mw.util.wikiScript( 'api' ),
							{ action: 'opensearch', search: request.term, namespace: 10 },
							function ( data ) {
								if ( data[ 1 ] ) { response( $( data[ 1 ] ).map( function ( index, item ) { return item.replace( /.*:/, '' ); } ) ); }
							}
						);
					},
					select: okButtonPressed
				} );
			var $templateSelector = $( '<div>' ).dialog( {
				title: i18n( 'template selector title' ),
				height: 'auto',
				width: 'auto',
				modal: true,
				buttons: [
					{ text: i18n( 'ok' ), click: okButtonPressed },
					{ text: i18n( 'cancel' ), click: function () { $templateSelector.dialog( 'close' ); } }
				]
			} ).append( $selector );
			circumventRtlBug();
			$selector.focus();
		}

		function fireDialog() {
			rawTemplate = false;
			$.ajax( {
				url: mw.util.wikiScript(),
				data: { title: paramPage(), action: 'raw' },
				dataType: 'text',
				success: buildDialog,
				cache: false,
				error: function () {
					rawTemplate = true;
					$.ajax( {
						url: mw.util.wikiScript(),
						data: { title: templatePage(), action: 'raw' },
						dataType: 'text',
						success: buildDialog,
						error: reportError
					} );
				}
			} );
		}

		function doIt() {
			mw.loader.using( [ 'jquery.ui', 'jquery.textSelection'], function () {
				init();
				var match = $( '#wpTextbox1' ).textSelection( 'getSelection' ).match( /^\{\{([^|}]*)/ );
				template = match ? $.trim( match[ 1 ] ) : null;
				if ( template ) { fireDialog(); } else { pickTemplate(); }
			} );
		}

		if ( mw.user.options.get( 'usebetatoolbar' ) ) {
			mw.loader.using( [ 'ext.wikiEditor' ], function () {
				if ( typeof $.wikiEditor !== 'undefined' ) {
					$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
						section: 'advanced',
						groups: {
							wizards: {
								tools: {
									linkTemplatewizard: {
										label: i18n( 'button hint' ),
										type: 'button',
										icon: '//upload.wikimedia.org/wikipedia/commons/d/dd/Vector_toolbar_template_button.png',
										action: { type: 'callback', execute: doIt }
									}
								}
							}
						}
					} );
				}
			} );
		} else {
			$( 'div #toolbar' ).append( // "old style"
				$( '<img>', { src: '//upload.wikimedia.org/wikipedia/commons/e/eb/Button_plantilla.png', title: i18n( 'button hint' ), 'class': 'mw-toolbar-editbutton' } )
					.css( { cursor: 'pointer' } )
					.click( doIt )
			);
		}
	} );
}