Benutzer:Perhelion/sectionSummary.js

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
  • Opera: Strg+F5
/** AUTOCOMPLETE for CORRECT SECTION in Edit-Summary
*** aut. section summary / (automatische Abschnittserkennung für die Zusammenfassung) ***
* @description [[w:de:Benutzer:Perhelion/sectionSummary]]
* @created 2012-01-07
* @author [[User:Perhelion]], 2012–2017
* @revision 15:17, 15. Aug. 2018 (CEST)
* @license Dual-licensed under the terms of the GFDL v1.2 or the GPL v2.
* @ToDo FIXME: Remove tags from headline!? Inlude existing code?
* @required modules: jquery, mediawiki.util, user.options
**/
/* global jQuery, mediaWiki */
(function ($, mw) {
'use strict';
var libs = mw.libs;
if (libs.autoSectiontId instanceof Object)
	return;

libs.autoSectiontId = {
	version: '0.55t',
	exec: function () {
		var $editform = $('#editform'),
			name = 'autoSectiontId';
		if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) < 0 || !$editform.length)
			return; // only if editing // || !mw.config.get('wgCurRevisionId')
		var sumM = { // default lang en.
			'new': 'new section ',
			'rem': 'removed section – ',
			'top': 'top', // phab:T20004; same name from Gadget-edittop.js
			'cancel': name + ' canceled: more than one section was changed'
		};
		 // Local translations
		if (mw.config.get('wgContentLanguage') === 'de')
			sumM = {
				'new': 'Neuer Abschnitt ',
				'rem': 'Entfernter Abschnitt – ',
				'top': 'Einleitung', // same name from Gadget-Einleitung-bearbeiten.js
				'cancel': name + ' Abbruch: mehr als ein Abschnitt wurde verändert'
			};

		var $textbox = $editform.find('#wpTextbox1'),
			txtOld = $.trim($textbox.val()), // original text container
			lrL = txtOld.lastIndexOf('\n'),
			lnL, // last line (new & old)
			$sum = $editform.find('#wpSummary'),
			headline = /^=+.+=+.*$/gm,
			trimHeadline = /^=+(.+[^=])=+\s*$/m; // headline text only
		// console.log("Last old line: ", txtOld.slice(lrL)); // The last line
		txtOld = txtOld.slice(0, lrL) + txtOld.slice(lrL).replace(/\n[:*# ]*$/, ''); // Trimmed last line indent, could  be changed by another script.

		function forceSummary() {
			var newS = (/&section=new/.test(location.search) || $editform.children('input[name|=\'wpSection\']').val() === 'new') ? 1 : 0; // new section
			// console.log(newS,$sum.val())
			if (newS && $sum.val())
				return; // on new section, if there is already a summary, because there is no comment supported
			/** Get difference from arrays, equvialent to jQuery .not()
			 * @return {string}
			 */
			function _not(s, s2) {
				return $.grep(s, function (x) {
/*					for (var i = 0; i < s2.length; i++) {
	 					// lesser exact
						if (s2[i].indexOf(x) !== -1) {
							s2.splice(i, 1);
				        	return false;
						}
					}
					return true;*/
					return $.inArray(x, s2) < 0; // too exact
				})[0]; // Only first?
			}
			var txt = $.trim($textbox.val()),
				remS, // Boolean section removed
				sectNamesOld = txtOld.match(headline) || [], // Array
				sectNames = txt.match(headline) || [], // Array
				// lastHeadlineOld = trimHeadline.exec(RegExp.lastMatch)[1],
				// lastHeadline = trimHeadline.exec(RegExp.lastMatch)[1],
				sections_len = sectNames.length + 1,
				sections_lenOld = sectNamesOld.length + 1,
				section = '',
				comp = 0, // compare counter
				sumTxt = '',
				$wpAS = $editform.find('input[name|=\'wpAutoSummary\']'),
				sumOld = /^( *\/\*.+\*\/ *)?(.*) *$/.exec($sum.val())[2];

	// console.log("Headlines: ",sectNamesOld, sectNames,"length", sectNamesOld.length, sectNames.length);

			// egalize temporary different whitespaces
			function _uniSectNames(arr) {
				return $.map(arr, function (x) {
					return x.replace(/^(==+) *([^\n]+[^ ]) *\1 *$/m, '$1 $2 $1');
				});
			}
			/** Get difference from arrays, equvialent to jQuery .not()
			 * @para b {boolean} (for compare)
			 * @return array
			 */
			function doComp(b) { // made headline compare
				var uSectNamesOld = _uniSectNames(sectNamesOld);
				var uSectNames = _uniSectNames(sectNames);
				if (!comp)
					comp = new Array(sections_len);
				for (i = 0; i < sections_len; i++) {
					comp[i] = comp[i] || 0;
					if ($.inArray(uSectNames[i], uSectNamesOld) < 0) {
						b = true;
						comp[i] = b;
					}
				}
				if (!b)
					comp = 0; // reset for fail
				return comp;
			}
			/* check section level, get parent section and made unique */
			function uniqueSect(c) {
				// console.log("uniqueSect before: ",c,sections_len,sectLvl);
				var s = [[0, 8]], // sections change counter [section id, heading level]
					hl,
					h,
					i;
				for (i = sections_len - 1; i > 0; i--) { // try c compare unique
					hl = sectLvl[i]; // level of current section
					h = s[0][1]; // level of last changed section
					// console.log(i, c[i], "current & last section hl, h", hl, h, s, sectNames[i]);
					if (c[i]) // compare
						if (hl < h) { s = [[i, hl]]; } else {
							if (hl > h)
								sectLvl[i] = h;
							s.unshift([i, h]);
						}
					 else if (hl < h && s.length > 1)
						s = [[i, hl]]; // unique

				}
				// console.log("uniqueSect End: ", s,s.length,c,sectNames,s[0][1]);
				return (s.length < 2 && s[0][1] !== 8 && !c[0]) ? sectNames[s[0][0]] : s;
			}
	// console.log('sections_lenOld, sections_len ', sections_lenOld, sectNamesOld, sections_len, sectNames);
			if (sections_len + sections_lenOld === 2)
				return; // console.log( ' canceled: no subject/headline found ', sections_len, sections_lenOld );

			/* Made headlines level */
			var sectLvl = [0],
				i;
			for (i = 1; i < sections_len; i++)
				sectLvl[i] = sectNames[i - 1].match(/^=+/m)[0].length;

			/** New headline **/
			if (sections_lenOld < sections_len) {
				if (sections_lenOld + 1 === sections_len) { // only single add
					sumTxt = sumM['new']; // the keyword new is reserved
					section = _not(_uniSectNames(sectNames), _uniSectNames(sectNamesOld));
				} else // try find unique
				{ section = uniqueSect(doComp()); }
			}
			/** Removed headline **/
			else if (sections_lenOld > sections_len) {
				if (sections_lenOld === sections_len + 1) { // only single remove support
					sumTxt = sumM.rem;
					section = _not(sectNamesOld, sectNames);
					remS = 1;
				} else { section = [0, 0]; } // for fail
			}
			/** Same headline count **/
			else if (sections_lenOld === sections_len) {
				if ($('#wpMinoredit').is(':checked'))
					return; // Minoredit is not supported, because it needs string compare
				// check first all section lines, after them also if a headline is changed
				/** find the right section **/
				var linesOld = txtOld.match(/^.*$/gm), // all lines  txt.match(/(.*?\n/g),
					lines = txt.match(/^.*$/gm), //  with empty lines: txt.match(/(.+/g),
					line = 0, // line counter
					lineOld = 0,
					lines_l = lines.length,
					lines_lOld = linesOld.length,
					lines_max = Math.max(lines_l, lines_lOld),
					sIdOld = 1, // old line numbers
					sId = 1, // new -
					s,
					sectionsOld = [[0, 0]], // +section 0
					sections = [[0, 0]]; // +section 0
				sectNamesOld.unshift(''); // +section 0
				sectNames.unshift(''); // +section 0
				i = 0;
				comp = doComp(1); // made empty array
				// console.log("sectionsOld ", sectNamesOld, sectNamesOld.length, "\nsections ", sectNames, sectNames.length)
				// do count lines to sections
				for (var l = 0; l < lines_max; l++) { // do sections indexed
					// console.log("Do count lines", l, sId, lines[l])
					if (sectNames[sId] && sectNames[sId] === lines[l]) {
						// console.log(l, sId, "new headline: "+sectNames[sId])
						line = l - 1 - line;
						sections[sId] = [l];
						sections[sId - 1][1] = line;
						sId++;
						line = l;
					}
					if (sectNamesOld[sIdOld] && sectNamesOld[sIdOld] === linesOld[l]) {
						// console.log(l, sIdOld, "old headline: "+sectNamesOld[sIdOld])
						lineOld = l - 1 - lineOld;
						sectionsOld[sIdOld] = [l];
						sectionsOld[sIdOld - 1][1] = lineOld;
						sIdOld++;
						lineOld = l;
					}
				}
				// console.log(lines_l,lines_lOld, "last section: ", sectionsOld[sections_lenOld-1], " sections ", sections_lenOld, sectionsOld, sections_len, sections)
				// last section length
				sections[sections_len - 1][1] = lines_l - sections[sections_len - 1][0] - 1;
				sectionsOld[sections_lenOld - 1][1] = lines_lOld - sectionsOld[sections_lenOld - 1][0] - 1;
				// sections_len++;
				// console.log(" sections old: ",sectionsOld," len ",sections_lenOld, "\n sections new: ",sections," len ",sections_len)
				// console.log("All lines_l: "+ lines_l+" lines_lOld: "+lines_lOld)
				/** * Line count was _not changed, compare all lines ***/
				// if (lines_l === lines_lOld) { // we can't take here a dependency
				for (i = 0; i < sections_len; i++) {
					// console.log("* check every line * section: ",i, sectionsOld[i][0] + " lines " + sectionsOld[i][1]);
					if (sections[i][1] !== sectionsOld[i][1]) { // compare only line quantity (performance reason)
						comp[i] = Math.abs(sections[i][1] - sectionsOld[i][1]);
						// console.log("* line quantity was changed *", i, comp[i], sections[i]);
						continue;
					}
					for (s = 1; s < sectionsOld[i][1] + 1; s++) {
						lineOld = sectionsOld[i][0] + s;
						line = sections[i][0] + s;
						// console.log(sections[i], " line: ", lineOld , linesOld[lineOld]);
						if (linesOld[lineOld].length !== lines[line].length) { // String compare is not yet supported
							comp[i]++;
							// console.log("section: ",i, sections[i] ," line ≠: ", lineOld, "\nOld: "+linesOld[lineOld]+"\nNew: "+lines[line])
							break;
						}
					}
				}
				// console.log("comp lines: ", comp)
				section = uniqueSect(comp);
				// console.log("section len, last comp ", section.length,section, comp[sections_len-1]);
				if ($.isArray(section) && section.length > 1 && comp[sections_len - 1] === 1) { // properly only last line changed (tolerance)
					lrL = txtOld.slice(txtOld.lastIndexOf('\n'));
					lnL = txt.slice(txt.lastIndexOf('\n'));
					if (lnL !== lrL) {
						// console.log(name + " last line change ignored: ", lrL, lnL);
						comp[sections_len - 1] = 0;
						section = uniqueSect(comp); // so try again
					}
				}
			}
			if (!section)
				return; // console.log( name + ' canceled: no modified section found', comp ); //DEBUG

			if ($.isArray(section)) //  more than one section changed
				if (section.length === 1 && comp[0] && section[0][1] === 8) {
					section = '=' + sumM.top + '='; // section 0
				} else {
					// console.warn("FAIL: ", '"' + sumOld + '"', $wpAS.val().indexOf(name), $wpAS.val()); // DEBUG
					if ($wpAS.val().indexOf(name) !== -1) // if already ran, remove old forceSummary
						$sum.val(sumOld);
					// $wpAS.val( $wpAS.val().replace(name,'') );

					return console.log(sumM.cancel); // to many changes
				}

	// console.log("before trimHeadline: ", section);
			var sect2 = trimHeadline.exec(section);
			if (!sect2 || !sect2[1]) return;
			sect2 = $.trim(sect2[1]);
	// console.log("after trimHeadline: '%s'", sect2);

			/* trim wiki links (to link text) FIXME?: trimLink1 & trimLink2 could be better one regexp? */
			var trimLink1 = /\[\[[\s_]*?:?[\s_]*?[^[]*\]\]/.exec(sect2);
			var trimLink2 = /[^[\]*?\[\[[\s_]*?:?[\s_]*?([^[|]*|[^]]*\|([^|]]*))\]\]/g.exec(sect2);
	// console.log("trimLink: "+ trimLink1 +" , "+ trimLink2);
			if (trimLink1 && trimLink2) // if link found trim
				sect2 = sect2.replace(trimLink1[0], trimLink2[1]);
			// trim external links (to link text) because external wiki links are not provided in the edit summary and can be also to long
			sect2 = sect2.replace(/\[https?:\/\/[^\s[\]|]*? +([^\n[\]|]*)?\]/gi, '$1');
			if (!sect2)
				return;
			// throw Error(name + ' failed: section is non compliant!?'); // DEBUG;
			if (newS) { // Remove headline from text and put it on the right field
				// newS = new RegExp('^=+ *' + section + ' *=+\\s+$','m'); // FIXME section need to convert to regexp
				txt = txt.replace(section, '');
				// console.log("newS", section, newS, txt);
				$sum.val(sect2);
				return $textbox.val($.trim(txt)); // save test before
			}
			sect2 = (remS) ? sumTxt + sect2 : sumTxt + '/* ' + sect2 + ' */';
			var wpAS = $wpAS.val().indexOf(name); // save old changes of Sum in wpAutoSummary, if changes remove this
			if (wpAS < 0) { $wpAS[0].value += name + sect2; }
			// only needed without LIVEPREVIEW
			else { // On Preview, Diff remove own old Sum and delete unfortunately actual section
				// console.log("sect2, sumOld", sect2, sumOld);
				sumOld = sumOld.replace(sect2, '');
				if (!mw.user.options.get('uselivepreview') && sect2 !== $wpAS.val().slice(wpAS + name.length))
					sect2 = ''; // FIXME: without LIVEPREVIEW / Ajax we can't be SURE
				// console.log(sumM.cancel + " and LIVEPREVIEW not activated!"); // to many changes

			}
			$sum.val(sect2 + ' ' + $.trim(sumOld));
		} // event forceSummary // FIXME: old wpTextbox1.value get lost? Works for sure only with LIVEPREVIEW?
		$('#wpSave,#wpPreview,#wpDiff').click(forceSummary);
	}
};

$(function () {
	if (mw.config.get('wgPageContentModel') === 'wikitext')
		mw.loader.using('user.options', libs.autoSectiontId.exec);
});
}(jQuery, mediaWiki));