c0rnerst0ne / Recortar avatar (Fórum Atelier 801)

// ==UserScript==
// @name         Recortar avatar (Fórum Atelier 801)
// @namespace    https://gist.github.com/emanuel15
// @version      0.5
// @description  Complemento que permite recortar o avatar no momento do envio.
// @author       Laagaadoo
// @require      https://code.jquery.com/jquery-2.2.4.min.js
// @require      http://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js
// @grant 		 GM.xmlHttpRequest
// @match        https://atelier801.com/profile?pr=*
// @connect      atelier801.com
// @license		 MIT
// @updateURL 	https://openuserjs.org/meta/c0rnerst0ne/Recortar_avatar_(Fórum_Atelier_801).meta.js
// @downloadURL https://openuserjs.org/src/scripts/c0rnerst0ne/Recortar_avatar_(Fórum_Atelier_801).user.js
// ==/UserScript==

'use strict';

var playerID;

var originalAvatar = "";

// cópia da img
var	$imgCopy = $('<img id="image" />');

// preview
var canvas = $('<canvas id="avtCanvas" />');
var ctx = canvas[0].getContext("2d");

// imagem que o usuário escolheu
var $imageCanvas;
var imageCanvas;
var imgCanvasCtx;

var URL_COMPLETE = window.location.href.replace(/#(.+)/ig, '');

$(document).ready(function() {

	playerID = $('input[name="pr"]').attr('value');
	originalAvatar = $('.img100').attr('src');

	var firstTime = true;
	
	// tema do jQuery UI
	$("head").prepend('<link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.12.1/themes/black-tie/jquery-ui.css" />');

	// borda preta no avatar
	$("head").prepend($('<style type="text/css">.avatar-profil:after { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; -webkit-box-shadow: inset 0px 0px 0px 2px #000; -moz-box-shadow: inset 0px 0px 0px 2px #000; box-shadow: inset 0px 0px 0px 2px #000; }</style>'));

	$(".avatar-profil").css({
		'height': '100px',
		'-webkit-box-shadow': 'inset 0px 0px 0px 2px #ff0000',
		'-moz-box-shadow': 'inset 0px 0px 0px 2px #ff0000',
		'box-shadow': 'inset 0px 0px 0px 2px #ff0000',
		'position': 'relative',
		'z-index': 1000,
	});

	$(".img100").css({
		'position': 'relative',
		'z-index': 0,
		'width': '100px',
		'height': '100px',
		'max-width': '100px',
		'max-height': '100px',
		'min-width': '100px',
		'min-height': '100px',
	})
	.load(function() {
		$(".img100").width(100).height(100);
	});

	$("#fichier").change(function(event) {

		if (event.target.files && event.target.files[0]) {

			var reader = new FileReader();

			reader.onload = function(e) {
				if (firstTime) {
					createElements();
					firstTime = false;
				}

				var image = new Image();

				image.src = e.target.result;

				image.onload = function() {
					loadImage(e.target.result);
				}
			}

			reader.readAsDataURL(event.target.files[0]);
		}
	});

	$('<div id="imageUrl" class="control-group">' +
		'<label class="control-label" for="avatarImg">Fonte (URL)</label>' +
		'<div class="controls">' +
			'<input type="url" id="imageSourceUrl" autocomplete="on"/>' +
			'<a id="loadUrlImageBtn" class="btn" style="margin-left: 5px;">Carregar</a>' +
		'</div>' +
	'</div>')
	.insertAfter('#cadre_changer_avatar_' + playerID + ' input[name="pr"]');

	var $imageUrlInput = $('#imageSourceUrl');

	$('#loadUrlImageBtn').click(function() {
		if (firstTime) {
			createElements();
			firstTime = false;
		}

		GM.xmlHttpRequest({
			method: "GET",
			url: $imageUrlInput.val(),
			onload: function(event) {
				var dataURI = 'data:image/png;base64,' + customBase64Encode(event.responseText);
				loadImage(dataURI);
			},
			overrideMimeType: 'text/plain; charset=x-user-defined'
		});
	});
});

function createElements() {
	$('<div id="cutAvatarParent" class="control-group"><label class="control-label" for="avatarImg">Recortar Avatar</label><div class="controls" id="avatarControls"><canvas id="imageCanvas" /><div id="cutArea" style="width: 100px; height: 100px;"></div></div></div>')
	.insertAfter('#cadre_changer_avatar_' + playerID + ' input[name="pr"]');

	$("#avatarControls").css('position', 'relative');

	$imageCanvas = $('#imageCanvas');
	imageCanvas = $imageCanvas[0];
	imgCanvasCtx = imageCanvas.getContext("2d");

	$imageCanvas.css('position', 'relative');

	$('#cutArea').css({
		'-webkit-box-shadow': 'inset 0px 0px 0px 2px #000',
		'-moz-box-shadow': 'inset 0px 0px 0px 2px #000',
		'box-shadow': 'inset 0px 0px 0px 2px #000'
	});

	var $submit = $("#cadre_changer_avatar_" + playerID + " .btn-post")
	$submit.attr('onclick', '');

	$submit.click(function(e) {
		//e.preventDefault();

		$submit.attr('disabled', 'on');

		var hiddenKey = $('#formulaire_deconnexion input[type="hidden"]').attr('name');
		var hiddenValue = $('#formulaire_deconnexion input[type="hidden"]').attr('value');

		var formData = new FormData();
		formData.append('pr', playerID);
		formData.append("fichier", dataURItoBlob(canvas[0].toDataURL("image/jpeg")));
		formData.append(hiddenKey, hiddenValue);

		GM.xmlHttpRequest({
			method: "POST",
			url: "https://atelier801.com/update-profile-avatar",
			data: formData,

			headers: {
				"Referer": URL_COMPLETE
			},

			onload: function(event) {
				if (event.status == 200 && (event.responseText != null || event.responseText != "")) {
					var r = JSON.parse(event.responseText);

					if (r.message != null) {

						if (r.resultat == "IMAGE_TROP_GROSSE") {
							$('body').prepend(creerPopupResultat(r.message, false));
						}
						else {
							$('body').prepend(creerPopupResultat(r.message, true));
						}

						jQuery.noConflict();
						jQuery('#popup_resultat_requete').modal('show');
					}

					if (r.redirection != null) {
						window.location.href = r.redirection;
					}

					$submit.attr('disabled', 'off');
				}
				else {
					console.log("Response (" + event.status + "): " + event.responseText);
				}
			},
		});
	});

	var $cancel = $("#cadre_changer_avatar_" + playerID + " button:eq(1)");

	$cancel.click(function(event) {
		$(".img100").attr('src', originalAvatar);
	});
}

// função do fórum
function creerPopupResultat(texte, reloadSurOk) {
	$('#popup_resultat_requete').remove();
	var popup = '<div class="modal hide fade ltr"';

	if (reloadSurOk) {
		popup += ' data-backdrop="static" data-keyboard="false" data-show="true"';
	}

	popup += ' id="popup_resultat_requete">' +
		'<form><div class="modal-header">';

	if (!reloadSurOk) {
		popup += ' <a class="close" data-dismiss="modal">×</a>';
	}

	popup += '<h3>Informação</h3>' +
		'</div>' +
		'<div class="modal-body">' +
		texte +
		'</div>' +
		'<div class="modal-footer">' +
		'<button type="submit" class="btn btn-info" data-dismiss="modal"';

	if (reloadSurOk) {
		popup += ' onclick=\"window.location.reload();\"';
	}

	popup += '>Ok</button>' +
		'</div>' +
		'</form>';

	return popup;
}

function clearCanvas(cvs) {
	var ctx = cvs.getContext("2d");

	ctx.beginPath();
	ctx.clearRect(0, 0, cvs.width, cvs.height);
}

function selectArea(moveSelector, x, y, width, height) {
	if (moveSelector) {
		$('#cutArea').css({
			'position': 'absolute',
			'top': ($imageCanvas.position().top + x) + 'px',
			'left': ($imageCanvas.position().left + y) + 'px',
			'width': width,
			'height': height,
			'z-index': 10,
		});
	}

	var img = $imgCopy[0];

	clearCanvas(imageCanvas);

	imgCanvasCtx.drawImage(img, 0, 0, img.width, img.height);
	imgCanvasCtx.stroke();

	imgCanvasCtx.fillStyle = "rgba(0, 0, 0, 0.6)";
	imgCanvasCtx.fillRect(0, 0, imageCanvas.width, imageCanvas.height);

	imgCanvasCtx.drawImage(img, x, y, width, height, x, y, width, height);
	imgCanvasCtx.stroke();
}

function loadImage(url) {
	var img = $imgCopy[0];

	$imgCopy.load(function(event) {
		clearCanvas(imageCanvas);

		imageCanvas.width = img.width;
		imageCanvas.height = img.height;

		imgCanvasCtx.drawImage(img, 0, 0, img.width, img.height);
		imgCanvasCtx.stroke();

		imgCanvasCtx.fillStyle = "rgba(0, 0, 0, 0.6)";
		imgCanvasCtx.fillRect(0, 0, imageCanvas.width, imageCanvas.height);

		selectArea(true, 0, 0, imageCanvas.width, imageCanvas.height);

		$('#cutArea').draggable({
			addClasses: false,
			containment: "#imageCanvas",

			drag: function(event, ui) {
				selectArea(false, ui.position.left - $imageCanvas.position().left, ui.position.top - $imageCanvas.position().top, $("#cutArea").width(), $("#cutArea").height());
			},

			stop: function(event, ui) {
				displayPreview();
			}
		})
		.resizable({
			handles: 'all',
			minWidth: 100,
			minHeight: 100,
			containment: '#imageCanvas',

			start: function(event, ui) {
				$("#cutArea").css('left', ($("#cutArea").css('left') + 2) + 'px');
				$("#cutArea").css('top', ($("#cutArea").css('top') + 2) + 'px');
			},

			resize: function(event, ui) {
				selectArea(false, ui.position.left - $imageCanvas.position().left, ui.position.top - $imageCanvas.position().top, $("#cutArea").width(), $("#cutArea").height());
			},

			stop: function(event, ui) {
				displayPreview();
			}
		});

		$("#cutArea .ui-resizable-se").css('background-image', 'url("")');

		displayPreview();
	})
	.error(function(event) {
		alert("Não foi possível carregar a imagem.");
	});

	$imgCopy.attr('src', url);
}

function displayPreview() {
	var cx = $("#cutArea").offset().left - $imageCanvas.offset().left;
	var cy = $("#cutArea").offset().top - $imageCanvas.offset().top;

	var sw = $("#cutArea").width();
	var sh = $("#cutArea").height();

	clearCanvas(canvas[0]);

	canvas[0].width = sw;
	canvas[0].height = sh;

	ctx.drawImage($imgCopy[0], cx, cy, sw, sh, 0, 0, sw, sh);

	$(".img100").attr('src', canvas[0].toDataURL("image/jpeg"));
}

// função feita pelo Stovie do StackOverflow
function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return new Blob([ia], {type:mimeString});
}

// StackOverflow
function customBase64Encode (inputStr) {
    var
        bbLen               = 3,
        enCharLen           = 4,
        inpLen              = inputStr.length,
        inx                 = 0,
        jnx,
        keyStr              = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
                            + "0123456789+/=",
        output              = "",
        paddingBytes        = 0;
    var
        bytebuffer          = new Array (bbLen),
        encodedCharIndexes  = new Array (enCharLen);

    while (inx < inpLen) {
        for (jnx = 0;  jnx < bbLen;  ++jnx) {
            /*--- Throw away high-order byte, as documented at:
              https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
            */
            if (inx < inpLen)
                bytebuffer[jnx] = inputStr.charCodeAt (inx++) & 0xff;
            else
                bytebuffer[jnx] = 0;
        }

        /*--- Get each encoded character, 6 bits at a time.
            index 0: first  6 bits
            index 1: second 6 bits
                        (2 least significant bits from inputStr byte 1
                         + 4 most significant bits from byte 2)
            index 2: third  6 bits
                        (4 least significant bits from inputStr byte 2
                         + 2 most significant bits from byte 3)
            index 3: forth  6 bits (6 least significant bits from inputStr byte 3)
        */
        encodedCharIndexes[0] = bytebuffer[0] >> 2;
        encodedCharIndexes[1] = ( (bytebuffer[0] & 0x3) << 4)   |  (bytebuffer[1] >> 4);
        encodedCharIndexes[2] = ( (bytebuffer[1] & 0x0f) << 2)  |  (bytebuffer[2] >> 6);
        encodedCharIndexes[3] = bytebuffer[2] & 0x3f;

        //--- Determine whether padding happened, and adjust accordingly.
        paddingBytes          = inx - (inpLen - 1);
        switch (paddingBytes) {
            case 1:
                // Set last character to padding char
                encodedCharIndexes[3] = 64;
                break;
            case 2:
                // Set last 2 characters to padding char
                encodedCharIndexes[3] = 64;
                encodedCharIndexes[2] = 64;
                break;
            default:
                break; // No padding - proceed
        }

        /*--- Now grab each appropriate character out of our keystring,
            based on our index array and append it to the output string.
        */
        for (jnx = 0;  jnx < enCharLen;  ++jnx)
            output += keyStr.charAt ( encodedCharIndexes[jnx] );
    }
    return output;
}