User:Rillke/bigChunkedUpload.js

From Wikimedia Commons, the free media repository
Revision as of 09:35, 4 May 2014 by Rillke (talk | contribs) (wikiGetlink is deprecated; getUrl is the replacement to use)
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.
// jshint valid

/*jshint curly:false*/
/*global jQuery:false, mediaWiki:false, MwJSBot:false*/

(function($, mw, undefined) {
'use strict';

var _install = function() {

	var $reuploadLink = $('#mw-imagepage-reupload-link').find('a'),
		$activationLinks;

	if ($reuploadLink.length) {
		$activationLinks = $('<a>', {
			text: " (chunked upload)"
		}).attr({
			href: '#chunked upload'
		}).insertAfter($reuploadLink);

		$activationLinks = $activationLinks.add( mw.libs.commons.ui.addEditLink('#chunked upload', "upload new version (chunked)", 'e-chunkedupload-overwrite', "Overwrite file with another one using chunked uploading") );
		$activationLinks.click(window.bigChunkedUpload);
	}
	if (mw.util.getParamValue('chunkedupload')) window.bigChunkedUpload();
};

window.bigChunkedUpload = function(e) {
	if (e) e.preventDefault();
	if (null === mw.loader.getState('mediawiki.commons.MwJSBot')) mw.loader.implement('mediawiki.commons.MwJSBot', ["//commons.wikimedia.org/w/index.php?action=raw&ctype=text/javascript&title=User:Rillke/MwJSBot.js"], { /*no styles*/
	}, { /*no messages*/
	});
	mw.loader.using(['mediawiki.commons.MwJSBot', 'jquery.ui.dialog', 'jquery.ui.progressbar', 'jquery.ui.slider', 'mediawiki.util'], _bigChunkedUpload);
};

var _bigChunkedUpload = function() {
	var instanceId = 'i' + Math.round(Math.random() * 1073741824),
		start,
		makeWikiLink = function(t, text) {
			return $('<a>').attr({
				href: mw.util.getUrl(t),
				target: '_blank',
				title: t
			}).text(text || t);
		},
		$dlg = $('<div>'),
		w = Math.min($(window).width(), 1200),
		$logInRequired = $('<div class="ui-state-highlight" style="display:none; cursor:pointer">')
			.text("To continue uploading, please log in and click here after you did so. Script will try to resume. Error reported by server is: ").appendTo($dlg),
		$progDiv = $('<div>').appendTo($dlg),
		$progBar = $('<div>').css({ 
			width: '98%', 
			padding: '2px' 
		}).progressbar({ value: 0 }).appendTo($dlg),
		$progConsole = $('<div>').css({
			'background': 'black',
			'color': 'white',
			'font-family': '\'Lucida Console\',Console,monospace',
			'overflow': 'auto',
			'width': '98%',
			'height': '200px',
			'border': '1px solid grey',
			'padding': '2px',
			'white-space': 'pre-wrap',
			'resize': 'both'
		}).text('Hi, ' + mw.config.get('wgUserName') + '! Thank you for testing the beta version of ').append(makeWikiLink('User:Rillke/bigChunkedUpload.js'), '.').appendTo($dlg),
		pad = function(digit, number, input) {
			input += '';
			return new Array(Math.max(number + 1 - input.length, 1)).join(digit) + input;
		},
		$lastLogLineEndSpan,
		log = function(what, time, color) {
			var $logline = $('<div>').text(pad('0', 5, time) + ': ' + what);
			if (color) $logline.css('color', color);
			$lastLogLineEndSpan = $('<span>').appendTo($logline);
			$progConsole.append($logline);
			$progConsole.clearQueue().animate({ scrollTop: $progConsole.scrollTop() + $logline.position().top }, 800);
		},
		logInlineProgress = function(what) {
			if ($lastLogLineEndSpan) $lastLogLineEndSpan.text(what);
		},
		$progTextDiv = $('<div>').text("Ready. Selecting a file will immediately start the upload.").appendTo($progDiv),
		$options = $('<fieldset>').appendTo($dlg),
		$optionsL = $('<legend>').text("Upload options").appendTo($options),
		$czWrap = $('<div>').appendTo($options),
		$czl = $('<label for="chunksize' + instanceId + '" style="display:block">').text("Chunk size: ").appendTo($czWrap),
		$czlz = $('<span>').appendTo($czl),
		$cz = $('<div id="chunksize' + instanceId + '">').css({ width: '98%' }).slider({
			max: 5120,
			min: 500,
			change: function(e, ui) {
				$czlz.text(ui.value + ' KiB');
			},
			slide: function(e, ui) {
				$czlz.text(ui.value + ' KiB');
			}
		}).slider('option', 'value', 4096).appendTo($czWrap),
		$fnWrap = $('<div>').appendTo($options),
		$fnl = $('<label for="filename' + instanceId + '" style="display:block">').text("File name: ").appendTo($fnWrap),
		$fn = $('<input type="text" style="width:98%" placeholder="File:Filename.ext" title="file name goes here" id="filename' + instanceId + '">').val(mw.config.get('wgPageName')).appendTo($fnWrap),
		$sumWrap = $('<div>').appendTo($options),
		$suml = $('<label for="summary' + instanceId + '" style="display:block">').text("Summary or Reason: ").appendTo($sumWrap),
		$sum = $('<input type="text" style="width:98%" placeholder="Reason/Summary" title="reason/summary go here" id="summary' + instanceId + '">').appendTo($sumWrap),
		$fsel = $('<input type="file" id="files" name="file" style="width:98%">').appendTo($options).change(function(e) {
			start = new Date();
			
			var lastdate,
				oldOnBeforeUnload = window.onbeforeunload,
				oldDocTitle = document.title,
				filename = $.ucFirst($fn.val().replace(/File:/, '').replace(/_/g, ' ')),
				lastblink,
				_blink = function($node) {
					if (lastblink) clearTimeout(lastblink);
					$node.addClass('ui-state-error');
					setTimeout(function() {
						$node.removeClass('ui-state-error');
					}, 1000);
				},
				_onLogInRequired = function(err, callWhenDone) {
					var _onNodeClick = function() {
						$logInRequired.unbind('click', _onNodeClick).hide();
						callWhenDone();
					};
					$logInRequired.find('span').first().remove();
					$logInRequired.append($('<span>').text(err)).show().click(_onNodeClick);
				},
				_onUploadProgress = function(progressCalculated, date) {
					if (!lastdate) lastdate = start;
					if ((date - lastdate) < 200 && progressCalculated !== 1) return;
					lastdate = date;
					progressCalculated = Math.round(progressCalculated, 1);
					logInlineProgress(' Upload: ' + progressCalculated + '%');
				},
				_updateProgressBar = function(progressCalculated, date) {
					if (!lastdate) lastdate = start;
					if ((date - lastdate) < 200 && progressCalculated !== 100) return;
					$progBar.progressbar('option', 'value', progressCalculated);
					document.title = Math.round(progressCalculated) + "% of " + filename + " uploaded  - Chunked upload - Wikimedia Commons";
				};
				
			if (!filename) return _blink($fn);
				
			var $def = new MwJSBot().chunkedUpload({
				maxChunkSize: $cz.slider('option', 'value') * 1024,
				title: $fn.val().replace(/File:/, ''),
				summary: $sum.val() +" (uploaded using [[User:Rillke/bigChunkedUpload.js|chunked upload script]])",
				passToAPI: {
					upload: {
						ignorewarnings: 1
					},
					finish: {
						ignorewarnings: 1
					}
				},
				callbacks: {
					loginRequired: _onLogInRequired
				}
			}, this.files[0]).progress(function(type, chunkinfo, txt) {
				var cc = chunkinfo.currentchunk,
					idIsNumber = ('number' === typeof cc.id),
					curIdPlus1 = idIsNumber ? (cc.id + 1) : cc.id,
					curId = idIsNumber ? cc.id : 0,
					l = chunkinfo.length,
					progressCalculated = ((curId)/l)*100 + (cc.progress/l),
					d = new Date(),
					ddiff = Math.round((d - start) / 1000),
					prog = '';
			
				// second term respects progress of current chunk
				if (idIsNumber) _updateProgressBar(progressCalculated);
				
				// handle these often frequently occuring events differently
				if ('uploadstatus' === type) {
					return _onUploadProgress(cc.progress, d);
				}
					
				txt = txt || cc.progressText;
				prog += "Uploaded part " + curIdPlus1 + " of " + l + ";";
				prog += " Time elapsed: " + ddiff + "s ;";
				prog += " Status: " + txt;
				$progTextDiv.text(prog);
				log(curIdPlus1 + '/' + l + '> ' + txt, ddiff, ('err' === type) ? '#E9D977' : '');
				
			}).done(function() {
				var txt = "DONE.",
					d = new Date(),
					ddiff = Math.round((d - start) / 1000);
					
				$progDiv.text(txt);
				log(txt, ddiff, '#77E9C7');
				window.onbeforeunload = oldOnBeforeUnload;
				window.location.href = "/wiki/File:" + encodeURIComponent($fn.val().replace(/^File:/i,""));
			}).fail(function(txt) {
				txt = "FAILED: " + txt;
				
				var	d = new Date(),
					ddiff = Math.round((d - start) / 1000);
					
				$progDiv.text(txt);
				log(txt, ddiff, '#E977C7');
				window.onbeforeunload = oldOnBeforeUnload;
				document.title = "FAILED! File upload failed - Chunked upload - Wikimedia Commons";
				setTimeout(function() {
					document.title = oldDocTitle;
				}, 10000);
			});
			$fn.add($sum).add($fsel).attr('disabled', 'disabled');
			
			// Prevent leaving the page accidentally
			window.onbeforeunload = function() {
				return "Upload seems to be still in progress. Do you really wish to quit?";
			};
		});

	$dlg.dialog({
		'title': "Overwrite existing files using Chunked Upload protocol",
		//'height': $(window).height(),
		'width': w
	});
	
	// Set focus to summary-field
	$sum.focus();
};

mw.loader.using(['ext.gadget.editDropdown', 'mediawiki.util'], _install);

}(jQuery, mediaWiki));