MediaWiki:Gadget-AjaxEdit.js

From Wiktionary, the free dictionary
Jump to navigation Jump to search

Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.

From User:Dixtosa/AjaxEdit.js, compiled to ECMAScript version 5 by Babel.


"use strict";

// Description         : With a new button "Ædit" one can edit specific sections without reloading the whole page. 
//                       Supports TabbedLanguages gadget
// Dependencies: mediawiki.util, mediawiki.Uri, mediawiki.user, mediawiki.api
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true, esversion:5, esnext:false */
/* global mw, $ */

(function ajaxEditIife() {
	window.AjaxEdit = {};
	
	window.AjaxEdit.scriptLink = "";
	if (mw.config.get("wgSiteName") !== "Wiktionary")
		window.AjaxEdit.scriptLink += "wikt:";
	if (mw.config.get("wgContentLanguage") !== "en")
		window.AjaxEdit.scriptLink += "en:";
	window.AjaxEdit.scriptLink += "MediaWiki:Gadget-AjaxEdit.js";
	
	var $document = $(document);
	window.AjaxEdit.FireEvents = function() {
		// fire page load events like the [quotations ▼] thing
		// see https://github.com/wikimedia/mediawiki/blob/master/resources/src/mediawiki.action/mediawiki.action.edit.preview.js
		mw.hook("wikipage.content").fire($document);
		mw.hook("wikipage.categories").fire($document);
	};
	
	window.AjaxEdit.Click = function (ajaxEditAnchor) {
		if (ajaxEditAnchor.hasEditBox) {
			mw.notify("This AjaxEdit edit box is already open.");
			return;
		} else {
			ajaxEditAnchor.hasEditBox = true;
		}

		var elements = {};
		var _query = new mw.Uri($(ajaxEditAnchor)
				.parent()
				.find("a[href*='action=edit']")
				.attr("href")).query,
		    title = _query.title,
		    section = _query.section;
	
		section = section.replace("T-", ""); //transclusions...
		var hdr = $(ajaxEditAnchor).parent().parent();
		var h2Parent = hdr.parent();
		if (h2Parent.hasClass("mw-heading")) {
			hdr = h2Parent;
		}
		var sectionName = hdr.children(".mw-headline").first().text();
		if (window.tabbedLanguages && !hdr.is(":header")) {
			sectionName = window.tabbedLanguages[window.currentLanguageTab];
		}
	
		var data = { action: 'raw', title: title, section: section };
		$.get(mw.util.wikiScript('index'), data).then(function (wikitext) {
			var rowHeight = Math.min(15, 1 + wikitext.split("\n").length);
			elements.textarea = $("<textarea/>")
			.attr({ rows: rowHeight })
			.addClass("mw-editfont-monospace")
			.text(wikitext);
			
			elements.textareaSummary = $("<input/>")
			.attr({
				type: "text",
				maxlength: 500,
				spellcheck: true,
				name: "wpSummary",
			})
			.keydown(event, function() {
				if (event.keyCode == 13) { // on enter
					elements.saveButton.click();
				}
			});
			
			function checkboxAndLabel(id, text) {
				var checkbox = $("<input/>")
				.attr({
					"id": id,
					"type": "checkbox",
				});
				var label = $("<label/>")
				.attr("for", id)
				.text(text);
				return [checkbox, label];
			}
			
			var minorEdit = checkboxAndLabel("ajaxedit-minor-edit-checkbox", "Minor edit");
			elements.minorEditCheckbox = minorEdit[0], elements.minorEditLabel = minorEdit[1];
			
			elements.saveButton = $("<button/>")
			.text("Save")
			.click(function () {
				elements.textarea.attr('disabled', true);
				elements.textareaSummary.attr('disabled', true);
				
				window.AjaxEdit.Save(section, sectionName, elements.textarea.val(), title, elements.textareaSummary.val(), elements.minorEditCheckbox.prop("checked"))
				.then(function (apiSaveResponse) {
					if (apiSaveResponse.error) return; //if abusefilter was triggered
					new mw.Api().get({page: mw.config.get("wgPageName"), action: "parse", prop: "text|categorieshtml"})
					.then(function (newHtml) {
						$("#ajaxedit-wrapper").remove();
						$("#mw-content-text").html(newHtml.parse.text["*"]);
						$("#catlinks").replaceWith(newHtml.parse.categorieshtml["*"]);
						
						window.AjaxEdit.FireEvents();
	
						mw.loader.moduleRegistry["site"].state = "registered";
						//mw.loader.moduleRegistry["site"].version="generate-unique-guid-here";
						mw.loader.moduleRegistry["site"].script = undefined;
						mw.loader.using("site", function () {});
	
						window.AjaxEdit.Submain();
					});
				});
			})
			.css("margin-left", "3px");
			
			elements.cancelButton = $('<button/>')
			.text("Cancel")
			.click(function () {
				$("#ajaxedit-wrapper").remove();
				ajaxEditAnchor.hasEditBox = false;
			});
			elements.previewDiv = $("<div/>")
			.css({
				"border-style": "solid", "border-width": "1px 1px 1px 4px",
				"border-radius": "0.33em", "border-color": "#a3d3ff",
			})
			.hide();
			
			function timestamp() {
				return new Date().toISOString();
			}
	
			var previousText;
			var previousTextTimestamp = timestamp();
			function loadPreview() {
				var newText = elements.textarea.val();
				var textTimestamp = timestamp();
				if (!previousText || previousText != newText) {
					new mw.Api()
					.parse(
						newText,
						{ title: mw.config.get("wgPageName"), pst: "true", preview: "true", sectionpreview: "true", disableeditsection: "true" }
					)
					.done(function (html) {
						if (textTimestamp > previousTextTimestamp) {
							elements.previewDiv.html(html);
							window.AjaxEdit.FireEvents();
							previousTextTimestamp = textTimestamp;
						}
					});
				}
				previousText = newText;
			}
			
			var livePreview = checkboxAndLabel("ajaxedit-live-preview-checkbox", "Live preview");
			elements.livePreviewCheckbox = livePreview[0], elements.livePreviewLabel = livePreview[1];
			elements.livePreviewCheckbox.on("change", function () {
				if ($(this).prop("checked")) {
					elements.previewDiv.show("slow");
					loadPreview();
					this.previewIntervalId = setInterval(loadPreview, 500);
				} else {
					clearInterval(this.previewIntervalId);
					elements.previewDiv.hide("fast");
					elements.previewDiv.empty();
				}
			});
	
			var wrapper = $("<div/>")
			.attr("id", "ajaxedit-wrapper")
			.css({ width: "auto", "margin": 0, "overflow": "hidden" })
			.append(elements.textarea)
			.append(elements.textareaSummary)
			.append(elements.saveButton)
			.append(elements.livePreviewCheckbox)
			.append(elements.livePreviewLabel)
			.append(elements.minorEditCheckbox)
			.append(elements.minorEditLabel)
			.append(elements.cancelButton)
			.append(elements.previewDiv);
	
			//tabbed languages support
			if ($(ajaxEditAnchor).is("#tabstable .editlangsection a")) {
				$(".languageContainer:not(:hidden)")
				.first()
				.prepend(wrapper);
			} else {
				hdr.after(wrapper);
			}
		});
	};
	
	/*
	 * Define edit summary template with window.AjaxEditSummary. For instance:
	 window.AjaxEditSummary = "Æ $sectionlink $summary";
	 */
	window.AjaxEdit.Save = function (sectionID, sectionName, sectionText, title, summary, minorEdit) {
		var finalSummary;
		if (typeof window.AjaxEditSummary === "string") {
			var usedSummary = false;
			finalSummary = window.AjaxEditSummary.replace(
				/\$([a-zA-Z_][a-zA-Z0-9_]*)/g,
				function(fullMatch, name) {
					switch (name) {
						case "summary": {
							usedSummary = true;
							return summary;
						}
						case "scriptlink": return window.AjaxEdit.scriptLink;
						case "sectionname": return sectionText;
						case "sectionlink": return "/* " + sectionName + " */";
						default: {
							mw.notify(
								"Your edit summary template (window.AjaxEditSummary) contains an unknown variable name. Variable name: "
								+ name + ". Choose from summary, scriptlink, sectionname, sectionlink."
							);
							return "";
						}
					}
				});
			if (!usedSummary) {
				mw.notify(
					"Your edit summary template (window.AjaxEditSummary) doesn't use the edit summary you supplied, so your edit summary will be appended to the template: "
					+ window.AjaxEditSummary);
				finalSummary += " " + summary;
			}
		} else {
			finalSummary = "/* " + sectionName + " */ " + summary + " (using [[" + window.AjaxEdit.scriptLink + "|AjaxEdit]])";
		}
		var data = {
			format: 'json',
			action: 'edit',
			title: title,
			section: sectionID,
			summary: finalSummary,
			text: sectionText,
			token: mw.user.tokens.get('csrfToken'),
			minor: minorEdit,
		};
		return new mw.Api().post(data).then(function (response) {
			if (response && response.edit && response.edit.result == 'Success') {
				mw.notify("successful");
			} else if (response && response.error) {
				mw.notify('Error: API returned error code "' + response.error.code + '": ' + response.error.info);
			}
			return response;
		}).fail(function (xhr) {
			mw.notify('Error: Request failed.');
			$('#ajaxedit-wrapper *').attr('disabled', false);
		});
	};
	
	window.AjaxEdit.Submain = function()
	{
		$(".mw-body-content .mw-editsection > .mw-editsection-bracket:contains(']')").each (function(){
			var btn = $('<a/>').text("Ædit").on("click", function() { window.AjaxEdit.Click(this) });
			$(this).before(", ").before(btn);
		});
	};
	
	window.AjaxEdit.Main = function()
	{
		$(".mw-editsection > .mw-editsection-bracket:contains(']')").each (function(){
			var btn = $('<a/>').text("Ædit").on("click", function() { window.AjaxEdit.Click(this) });
			$(this).before(", ").before(btn);
		});
	};

	if (mw.config.get('wgAction') == "view")
		$(window.AjaxEdit.Main);
})();