MediaWiki:AjaxSubmit.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
// ajaxSubmit
//   Submit a form through Ajax. Doesn't handle file uploads yet.
//
// Parameters:
//   form                 DOM element   The form to submit
//   button      optional DOM element   If set and a submit button of 'form', is added to the
//                                      form arguments sent
//   func        optional Function      Function to call once the call has been made or the
//                                      result has arrived, if want_result === true
//   want_result optional Boolean       If true, call func with the result of the submit once
//                                      it has arrived. Otherwise, call func as soon as the
//                                      submit request has been received by the server, and
//                                      ignore any result of the submit.
//
// Notes:
//   Func should be a function (request). If func is not defined,
//   ajaxSubmit just submits the form and ignores any result.
/*global mw*/
function ajaxSubmit(form, button, func, want_result) {
"use strict";
	if (want_result && (!func || typeof(func) != 'function' || func.length < 1)) {
		/**** TODO: improve error handling: should throw an exception! */
		alert('Logic error in ajaxSubmit: func must be function (request).');
		return;
	}
	if (func && typeof(func) != 'function') {
		/**** TODO: improve error handling: should throw an exception! */
		alert('Error in ajaxSubmit: func must be a function, found a ' + typeof(func) + '.');
		return;
	}

	var is_simple = false;
	// True if it's a GET request, or if the form is 'application/x-www-form-urlencoded'
	var boundary = null;
	// Otherwise, it's 'multipart/form-data', and the multipart delimiter is 'boundary'

	function encode_entry(name, value) {
		if (!name || !name.length || !value || !value.length)
			return null;
		if (!boundary)
			return name + '=' + encodeURIComponent(value);
		else
			return boundary + '\r\n' +
			 'Content-Disposition: form-data; name="' + name + '"\r\n' +
			 '\r\n' +
			 value.replace(/\r?\n/g, '\r\n') + '\r\n'; // RFC 2046: newlines always must be represented as CR-LF
	}

	function encode_field(element) {
		var name = element.name;
		if (!name || !name.length)
			name = element.id;
		return encode_entry(name, element.value);
	}

	function form_add_argument(args, field) {
		if (!field || !field.length)
			return args;
		if (!args || !args.length)
			return field;
		if (is_simple)
			return args + '&' + field;
		else
			return args + field;
	}

	var request;
	if (window.LAPI && window.LAPI.Ajax && window.LAPI.Ajax.getRequest) {
		request = window.LAPI.Ajax.getRequest();
	} else {
		try {
			request = new window.XMLHttpRequest();
		} catch (anything) {
			if (window.ActiveXObject)
				request = new window.ActiveXObject('Microsoft.XMLHTTP');
		}
	}
	var method = form.getAttribute('method').toUpperCase();
	var uri = form.getAttribute('action');
	if (uri.length >= 2 && uri.substring(0, 2) === '//') {
		// Protocol-relative URI; can cause trouble on IE7
		uri = document.location.protocol + uri;
	} else if (uri[0] === '/') {
		// Some browsers already expand the action URI (e.g. Opera 9.26)
		uri = mw.config.get('wgServer') + uri;
		if (uri.length >= 2 && uri.substring(0, 2) === '//')
			uri = document.location.protocol + uri;
	}
	// Encode the field values

	var is_get = method === 'GET';
	var encoding = form.getAttribute('enctype');
	if (encoding) {
		encoding = encoding.toLowerCase();
		if (!encoding.length)
			encoding = null;
	}
	is_simple =
		is_get || !encoding || encoding === 'application/x-www-form-urlencoded';

	var args = '';
	var boundary_string = '----' + mw.config.get('wgArticleId') + mw.config.get('wgCurRevisionId') + 'auto_submit_by_lupo';

	boundary = null;

	if (!is_simple)
		boundary = '--' + boundary_string;

	for (var i = 0; i < form.elements.length; i++) {
		var element = form.elements[i];
		var single_select = false;
		switch (element.type) {
		case 'checkbox':
		case 'radio':
			if (!element.checked)
				break;
			else if (element.id === 'wpWatchthis' && document.getElementById('ca-unwatch')) {
				args = form_add_argument(args, encode_entry('wpWatchthis', '1'));
				break;
			}
			/* falls through */
		case 'hidden':
		case 'text':
		case 'password':
		case 'textarea':
			args = form_add_argument(args, encode_field(element));
			break;
		case 'select-one':
			single_select = true;
			/* falls through */
		case 'select-multiple':
			var name = element.name || element.id || '';
			if (!name.length)
				break;
			for (var j = 0; j < element.length; j++) {
				if (element[j].selected) {
					var value = element[j].value || element[j].text;
					args = form_add_argument(args, encode_entry(name, value));
					if (single_select)
						break; // No need to scan the rest
				}
			}
			break;
		case 'file':
			break;
		}
	}
	if (button && button.form === form && button.type === 'submit')
		args = form_add_argument(args, encode_field(button));

	// Close the multipart request
	if (!is_simple && args.length > 0)
		args += boundary;

	if (method === 'GET') {
		uri += (uri.indexOf('?') < 0 ? '?' : '&') + args;
		args = null;
	}
	// Make the request
	request.open(method, uri, true);
	if (want_result && request.overrideMimeType)
		request.overrideMimeType('application/xml');
	request.setRequestHeader('Pragma', 'cache=no');
	request.setRequestHeader('Cache-Control', 'no-transform');
	if (method === 'POST') {
		if (!encoding)
			encoding = 'application/x-www-form-urlencoded';
		if (!is_simple) {
			request.setRequestHeader(
				'Content-type', encoding + '; charset=UTF-8; boundary="' + boundary_string + '"');
		} else {
			request.setRequestHeader('Content-type', encoding);
		}
	}
	request.onreadystatechange =
	function () {
		if (want_result) {
			if (request.readyState < 4)
				return;
			func(request);
		} else {
			// Call func as soon as the request has been sent and we start getting the result.
			if (request.readyState === 3 && func)
				func(request);
		}
	};
	request.send(args);
}

// submitAndClose
//   Submit a form and close the window containing it as soon as the request has been
//   received by the server
//
// Parameters:
//   form   DOM element   The form to submit.
function submitAndClose(form) {
	ajaxSubmit(form, null, function () {
		window.close();
	});
}