مستخدم:TJones (WMF)/Arabic-DWIM.js

من ويكيبيديا، الموسوعة الحرة

ملاحظة: بعد الحفظ، قد يلزمك إفراغ الكاش لرؤية التغييرات.

// Target keyboard layouts from https://ar.wikipedia.org/wiki/ميدياويكي:Gadget-Dwim.js
// Javascript UI code from https://ru.wikipedia.org/wiki/Участник:Kaganer/Gadget-Dwim.js
// which is currently live and working. All DWIMs seem to be based on the Hebrew original
// https://he.wikipedia.org/wiki/מדיה ויקי:Gadget-Dwim.js
//
// Please adapt to use whatever skin and Javascript UI hooks currently make sense.
//
// Creating DWIM script Arabic & Latin keyboards presents several challenges. Like the
// Cyrillic/Russian DWIM (and unlike the Hebrew original), Arabic/Latin DWIM needs to
// account for uppercase AND lowercase Latin characters, because they map to different
// characters on the Arabic keyboard.
//
// A further complexity comes from the fact that certain characters (notably <>, [], {},
// ', and /) are present on both the Arabic and US keyboards, but in different locations,
// meaning that there isn't just one mapping value for each. (For example, q always maps
// to︎ ض, and vice versa, but / maps to ظ going from Latin to Arabic, but / maps to L
// going from Arabic to Latin.)
//
// Another difficulty is that some keys on the Arabic keyboard type two characters:
//
// Arabic - decomposition - US keyboard - US keyboard decomposition
//	لا		x	ل+ا				b			gh
//	لآ		x	ل+آ				B			gN
//	لأ		x	ل+أ				G			gH
//	لإ		x	ل+إ				T			gY
//
// While mixed-case gN, gH, and gY are generally very unlikely digraphs, gh is a
// reasonably common digraph in English ("laugh", "ghost", etc.) Unfortunately, this makes
// لا ambiguous--it could come from "b" or "gh" typed on the US keyboard. I've chosen to
// always map it to "b" because "b" is much more common than "gh" in English. This means
// that typing "laugh" or "ghost" on the Arabic keyboard will result in suggestions for
// "laub" or "bost" from the US keyboard. This is not optimal. (It also isn't pessimal--it
// could have been "e" and "th" that were conflated by the mapping!)
//
// To handle all of these complexities, we detect whether any reasonably unambiguously
// Latin or Arabic keyboard characters are present in the string. For Latin, that is a-z,
// A-Z, and ; and ?. I ignored the semicolon and question mark because they seem too
// likely to show up in another context. For Arabic, I chose a range that includes all the
// Arabic characters in the mapping, plus ×, ÷, and ~. While these three characters CAN
// show up in other contexts, they are much less likely to do so than ; and ?.
//
// If a string has both Latin and Arabic keyboard characters (or neither) then we just
// return the input unchanged, which prevents any DWIM suggestions from being generated.
//
// If the string contains Latin or Arabic (but not both), we need to first handle the
// four two-letter mappings using four calls to replaceAll(). We also need to specify
// which general mapping to use (latinMap maps Latin to Arabic, and arabicMap maps Arabic
// to Latin); this prevents us from always mapping / to ظ or always mapping / to L. Using
// the chosen map, we perform the original DWIM character-by-character swap, and return
// the result.
//
// Another problem I've seen when the Hebrew DWIM is adapted to another keyboard is that
// the magic numbers 58 and 29 get copied to the new DWIM. They are the length and half
// the length of the Hebrew-English mapping, respectively, so if the new mapping is a
// different length, they are incorrect and the new DWIM will not work correctly. Rather
// than introduce new magic numbers, I've caclulated them here. This also makes the script
// more robust to updates to the mapping (for example, if we wanted to add 0-9 and ٠-٩).
//
// I've also renamed the mapping function from hebeng() (HEBrew-ENGlish) to araeng()
// (ARAbic-ENGlish).

mw.loader.using( [ 'mediawiki.searchSuggest', 'mediawiki.util' ] ).done( function() {
	'use strict';

	$( function() {
		var latinPat = new RegExp('[a-zA-Z]'),
			arabicPat = new RegExp('[\u060c-\u0652×÷~]'),
			// map keyboard layouts [[:File:KB United States-NoAltGr.svg]] vs [[:File:KB Arabic.svg]]
			latinMap = "qwertyuiop[]asdfghjkl;'zxcvnm,./QWERYUIOP{}ASDFHJKLZXCVNM<>?ضصثقفغعهخحجدشسيبلاتنمكطئءؤرىةوزظًٌَُإ‘÷×؛<>ٍِ][أـ،/~ْ}{آ’,.؟",
			arabicMap = "ضصثقفغعهخحجدشسيبلاتنمكطئءؤرىةوزظًٌَُإ‘÷×؛<>ٍِ][أـ،/~ْ}{آ’,.؟qwertyuiop[]asdfghjkl;'zxcvnm,./QWERYUIOP{}ASDFHJKLZXCVNM<>?",
			maplen = arabicMap.length,
			rotate = maplen / 2,
			araeng = function ( str ) {
				var isLatin = latinPat.test(str);
				var isArabic = arabicPat.test(str);
				var myMap;

				if (isLatin) {
					if (isArabic) {
						return str; // mixed-script, do nothing
					}
					str = str.replaceAll('T', 'gY');
					str = str.replaceAll('G', 'gH');
					str = str.replaceAll('B', 'gN');
					str = str.replaceAll('b', 'gh');
					myMap = latinMap;
				} else if (isArabic) {
					str = str.replaceAll('لإ', 'T');
					str = str.replaceAll('لأ', 'G');
					str = str.replaceAll('لآ', 'B');
					str = str.replaceAll('لا', 'b');
					myMap = arabicMap;
				} else {
					return str; // neither Arabic nor Latin, do nothing
				}

				var res = '';
				for (var i = 0; i < str.length; i++) {
					var ic = myMap.indexOf( str.charAt( i ) );
					res += ic + 1 ? myMap.charAt( ( ic + rotate ) % maplen ) : str.charAt( i );
				}
				return res;
			};

		var $searchBoxes = $(
			'#searchInput, #searchInput2, #powerSearchText, #searchText'
		);

		$searchBoxes.suggestions( {
			fetch: function( query ) {
				var $this = $( this ),
					apiUrl = mw.util.wikiScript( 'api' );

				if ( query.length !== 0 ) {
					var params = {
						action: 'opensearch',
						search: query,
						redirects: 'return',
						suggest: true
					};

					$.ajax( {
						data: params,
						url: apiUrl,
						dataType: 'json',
						success: function( data ) {
							if ( $.isArray( data ) && 1 in data ) {
								var orig = data[1];

								$this.suggestions( 'suggestions', orig );

								if ( data[1].length < 10 ) {
									params.search = araeng( query );

									if ( params.search === query ) {
										return;
									}

									params.limit = 10 - data[1].length;

									$.ajax( {
										data: params,
										url: apiUrl,
										dataType: 'json',
										success: function( data ) {
											if ( $.isArray( data ) && 1 in data ) {
												$this.suggestions(
													'suggestions',
													orig.concat( data[1] )
												);
											}
										}
									} );
								}
							}
						}
					} );
				}
			}
		} );
	} ); // document.ready
} ); // mw.loader.using