/* jxs.js */

//V3.01.A - http://www.openjs.com/scripts/jx/
jx = {
	//Create a xmlHttpRequest object - this is the constructor. 
	getHTTPObject : function() {
		var http = false;
		//Use IE's ActiveX items to load the file.
		if(typeof ActiveXObject != 'undefined') {
			try {http = new ActiveXObject("Msxml2.XMLHTTP");}
			catch (e) {
				try {http = new ActiveXObject("Microsoft.XMLHTTP");}
				catch (E) {http = false;}
			}
		//If ActiveX is not available, use the XMLHttpRequest of Firefox/Mozilla etc. to load the document.
		} else if (window.XMLHttpRequest) {
			try {http = new XMLHttpRequest();}
			catch (e) {http = false;}
		}
		return http;
	},
	
	// This function is called from the user's script. 
	//Arguments - 
	//	url	- The url of the serverside script that is to be called. Append all the arguments to 
	//			this url - eg. 'get_data.php?id=5&car=benz'
	//	callback - Function that must be called once the data is ready.
	//	format - The return type for this function. Could be 'xml','json' or 'text'. If it is json, 
	//			the string will be 'eval'ed before returning it. Default:'text'
	//	method - GET or POST. Default 'GET'
	load : function (url,callback,format,method,opt,mimeType) {
		var http = this.init(); //The XMLHttpRequest object is recreated at every call - to defeat Cache problem in IE
		if(!http||!url) return;
		//XML Format need this for some Mozilla Browsers
		if (http.overrideMimeType) http.overrideMimeType(mimeType || 'text/xml');

		if(!method) method = "GET";//Default method is GET
		if(!format) format = "text";//Default return type is 'text'
		if(!opt) opt = {};
		format = format.toLowerCase();
		method = method.toUpperCase();
		
		//Kill the Cache problem in IE.
		var now = "uid=" + new Date().getTime();
		url += (url.indexOf("?")+1) ? "&" : "?";
		url += now;

		var parameters = null;

		if(method=="POST") {
			var parts = url.split("\?");
			url = parts[0];
			parameters = parts[1];
		}
		http.open(method, url, true);

		if(method=="POST") {
			http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			http.setRequestHeader("Content-length", parameters.length);
			http.setRequestHeader("Connection", "close");
		}

		var ths = this;// Closure
		if(opt.handler) { //If a custom handler is defined, use it
			http.onreadystatechange = function() { opt.handler(http); };
		} else {
			http.onreadystatechange = function () {//Call a function when the state changes.
				if (http.readyState == 4) {//Ready State will be 4 when the document is loaded.
					if(http.status == 200) {
						var result = "";
						if(http.responseText) result = http.responseText;
						//If the return is in JSON format, eval the result before returning it.
						if(format.charAt(0) == "j") {
							//\n's in JSON string, when evaluated will create errors in IE
							result = result.replace(/[\n\r]/g,"");
							result = eval('('+result+')');

						} else if(format.charAt(0) == "x") { //XML Return
							result = http.responseXML;
						}

						//Give the data to the callback function.
						if(callback) callback(result);
					} else {
						if(opt.loadingIndicator) document.getElementsByTagName("body")[0].removeChild(opt.loadingIndicator); //Remove the loading indicator
						if(opt.loading) document.getElementById(opt.loading).style.display="none"; //Hide the given loading indicator.
						
						if(error) error(http.status);
					}
				}
			}
		}
		http.send(parameters);
	},
	bind : function(user_options) {
		var opt = {
			'url':'', 			//URL to be loaded
			'onSuccess':false,	//Function that should be called at success
			'onError':false,	//Function that should be called at error
			'format':"text",	//Return type - could be 'xml','json' or 'text'
			'method':"GET",		//GET or POST
			'update':"",		//The id of the element where the resulting data should be shown. 
			'loading':"",		//The id of the loading indicator. This will be set to display:block when the url is loading and to display:none when the data has finished loading.
			'loadingIndicator':"" //HTML that would be inserted into the document once the url starts loading and removed when the data has finished loading. This will be inserted into a div with class name 'loading-indicator' and will be placed at 'top:0px;left:0px;'
		}
		for(var key in opt) {
			if(user_options[key]) {//If the user given options contain any valid option, ...
				opt[key] = user_options[key];// ..that option will be put in the opt array.
			}
		}
		
		if(!opt.url) return; //Return if a url is not provided

		var div = false;
		if(opt.loadingIndicator) { //Show a loading indicator from the given HTML
			div = document.createElement("div");
			div.setAttribute("style","position:absolute;top:0px;left:0px;");
			div.setAttribute("class","loading-indicator");
			div.innerHTML = opt.loadingIndicator;
			document.getElementsByTagName("body")[0].appendChild(div);
			this.opt.loadingIndicator=div;
		}
		if(opt.loading) document.getElementById(opt.loading).style.display="block"; //Show the given loading indicator.
		
		this.load(opt.url,function(data){
			if(opt.onSuccess) opt.onSuccess(data);
			if(opt.update) document.getElementById(opt.update).innerHTML = data;
			
			if(div) document.getElementsByTagName("body")[0].removeChild(div); //Remove the loading indicator
			if(opt.loading) document.getElementById(opt.loading).style.display="none"; //Hide the given loading indicator.
		},opt.format,opt.method, opt);
	},
	init : function() {return this.getHTTPObject();}
}

/* jxsf.js */

jx.generateBoundary = function(fieldsData) {
    var boundary = parseInt(Math.random()*Math.pow(10, 16)).toString(36) + '' + parseInt(Math.random()*Math.pow(10, 16)).toString(36);
    for (var i = 0; i < fieldsData.length; i++) {
        if (fieldsData[i] && fieldsData[i].indexOf(boundary) > -1) {
            // generate new boundary and check all fields again
            boundary = parseInt(Math.random()*Math.pow(10, 16)).toString(36) + '' + parseInt(Math.random()*Math.pow(10, 16)).toString(36);
            i = 0;
        }
    }
    
    return boundary;
};

jx.error = function(rcode) {
    alert('Error! Response Code: ' + rcode);
};

jx.loadImage = function(url, fileData, callback) {
    if (!fileData.match(/^data:image\/(jpeg|png);base64,(.+)$/)) {
        alert('Invalid image format!');
    }  
    this.loadFile(url, fileData, 'image', callback);
};

jx.loadFile = function(url, fileData, fileName, callback, opt) {
    var http = this.init(); //The XMLHttpRequest object is recreated at every call - to defeat Cache problem in IE
    if(!http||!url) return;

    var parts = url.split('?');
    var url = parts[0];
    var parameters = parts[1] ? parts[1].split('&') : [];
    for (var i = 0; i < parameters.length; i++) {
        parameters[i] = parameters[i].split('=');
    }
    
    var fieldsData = [fileData];
    for (var i = 0; i < parameters.length; i++) {
        fieldsData.push(parameters[i][1]);
    }
    
    var boundary = this.generateBoundary(fieldsData);

    var body = '';
    for (var i = 0; i < parameters.length; i++) {
        body += "--" + boundary + "\r\n\
Content-Disposition: form-data; name='"+parameters[i][0]+"'\r\n\
\r\n\
"+(parameters[i][1] || '')+"\r\n";
    }
        
    body += "--" + boundary + "\r\n\
Content-Disposition: form-data; name='" + fileName + "'; filename='" + fileName + "'\r\n\
Content-Type: application/octet-stream\r\n\
\r\n\
"+ fileData + "\r\n\
--" + boundary + "--\r\n";
    
    http.open("POST", url, true);
    http.setRequestHeader("Content-Type", "multipart/form-data; boundary="+boundary);
    http.setRequestHeader("Content-Length", body.length);
    http.setRequestHeader("Connection", "close");

    if (opt && opt.handler) { 
        http.onreadystatechange = function() { opt.handler(http); };
    } else {
        http.onreadystatechange = function () {//Call a function when the state changes.
            if (http.readyState == 4) {//Ready State will be 4 when the document is loaded.
                if(http.status == 200) {
                    var result = "";
                    if (http.responseText) result = http.responseText;

                    //Give the data to the callback function.
                    if (callback) callback(result);
                } else {
                    if(this.error) this.error(http.status);
                }
            }
        }
    }
    
    http.send(body);
};


/* actions.js */

MAIN_URL = "http://screenshoot.me/";
TWITTER_BUTTON = "<a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"%URL%\" data-counturl=\"%URL%\" data-count=\"none\" data-text=\"Check this out\">Tweet</a>";
TWITTER_BUTTON_SCRIPT = {type: "text/javascript", src: "http://platform.twitter.com/widgets.js"};
FB_BUTTON = "<iframe src=\"http://www.facebook.com/plugins/like.php?app_id=226662560679377&amp;href=%URL%&amp;send=false&amp;layout=button_count&amp;width=150&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font&amp;height=21\" style=\"border:none; overflow:hidden; width:150px; height:21px; margin-left: 5px\"></iframe>";

var MAX_IMAGE_SIZE = 4*1024*1024;

function getImage() {
	drawProcessingScreen(context, canvas);
	resetCrop();
	_getImage();
}

function _getImage() {
	var img = new Image();
	try {
		img.src = applet.getClipboardImage("png");
	} catch(e) {
		setTimeout(_getImage, 1000);
		return;
	}

	setTimeout(function(){ applyOnCanvas(img);}, 500);
}

function setImage() {
	applet.setClipboardImage(canvas.toDataURL());
}

function applyOnCanvas(img) {
	if (img.width == 0 || img.height == 0) {
		drawTitleScreen(context, canvas);
	} else {
		canvas.width = img.width;
		canvas.height = img.height;
		context.drawImage(img, 0, 0);
	}
	saveBitmap();
}

function cropImage() {
	if (cropDimension) {
		restoreWholeBitmap();
		var w = cropDimension[0] + cropDimension[2] + 1 > canvas.width ? canvas.width - cropDimension[0] : cropDimension[2] + 1;
		var h = cropDimension[1] + cropDimension[3] + 1 > canvas.height ? canvas.height - cropDimension[1] : cropDimension[3] + 1;
		cropData = context.getImageData(cropDimension[0], cropDimension[1], w, h);
		canvas.width = w;
		canvas.height = h;
		context.putImageData(cropData, 0, 0);
		saveBitmap();
		resetCrop();
	}
}

function saveImage() {
    var imageContent = canvas.toDataURL();
    if (imageContent.length > MAX_IMAGE_SIZE) {
        alert('This image is too big - '+(imageContent.length/(1024*1024)).toFixed(2)+'MB!\nThe maximum allowed image size is 4MB.');
        return;
    }
    
	if (bitmap) {
		restoreWholeBitmap();
		
		resetCrop();
		
		document.getElementById("content_editor").style.display = "none";
		document.getElementById("text_fields").style.display = "none";
		
		var desiredKey = "";
		if (/^\w{6,16}$/.test(lastKey) && document.getElementById("key_state_OK").style.display != "none") {
			desiredKey = "?desiredkey="+lastKey; // set the user's desired key
		}
		
		function handlerFunction(response) {
			url = MAIN_URL + response;
			document.getElementById("saving").innerHTML = "<div style=\"float: left\">Your image is here: <a href='"+url+"'>"+url+"</a></div><div id=\"new_screenshot_copy_url\" style=\"float: left; margin-left: 12px\"></div>\
			<div style=\"clear: both\"><a href='"+url+"' id=\"savingImage\"></a></div>\
            <div id=\"upload_new_one_button\" style=\"margin-top: 8px\"></div>\
            <div style=\"margin-top: 34px\"><div id='social_fb' class='fb-button'></div><div id='social_t' class='twitter-button'></div><div id='social_p1' class='plus1-button'></div></div>";
            
            generateShareButtons(url, document.getElementById('social_fb'), document.getElementById('social_t'), document.getElementById('social_p1'));
            
            var colorMap = {def: {border: "#FE9", bg: ["#FC3", "#FE9"]}, hover: {border: "#FE9", bg: ["#F90", "#FC0"]}, click: {border: "#FE9", bg: ["#F93", "#F93"]}, font: {color: "#336", style: "normal normal bold 11px 'Georgia'"}};
            var copyUrlBtn = buttonFactory(document.getElementById("new_screenshot_copy_url"), "Copy URL", 92, 24, colorMap, function(){});
            var code = clipboardButton(url, 92, 24, copyUrlBtn);
            var div = document.createElement('div');
            div.style.position = 'relative';
            div.style.top = '-32px';
            div.innerHTML = code;
            document.getElementById('new_screenshot_copy_url').appendChild(div);
            
            var uploadNewBtn = buttonFactory(document.getElementById("upload_new_one_button"), "Upload new one", 120, 24, colorMap, restartEditor);
            
			var img = new Image();
			img.src = imageContent;
			img.style.borderStyle = "none";
			img.onload = function() {
				var maxSide = Math.max(this.width, this.height);
				var SITE_LIMIT = 200;
				if (maxSide > SITE_LIMIT) {
					this.style.width = (SITE_LIMIT/maxSide*this.width)+"px";
					this.style.height = (SITE_LIMIT/maxSide*this.height)+"px";
				}
				document.getElementById("savingImage").appendChild(this);
			}

            addImageToGallery(response);
		}
		
        jx.loadImage("index.php"+desiredKey, imageContent, handlerFunction);
		
		document.getElementById("desired_key").value = "";
		_checkKey("");
		
		document.getElementById("saving").innerHTML = "Please wait a few seconds (uploading " + parseInt(imageContent.length/1024+1) + "KB)...";
	}
}

function restartEditor() {
    cropDimension = null; 
    document.getElementById("text_fields").style.display = "";
    document.getElementById("content_editor").style.display = "";
    document.getElementById("saving").innerHTML = "";
    drawTitleScreen(context, canvas); 
    saveBitmap();
}

function checkKey(key) {

	if (lastKey == key || key.length > 16 || key.length < 6) {
		if (key.length < 6) setKeyState("ERR");
		lastKey = key;
		return;
	}

	lastKey = key;
	
	if (!/^\w{6,16}$/.test(key)) {
		setKeyState("INV")
		return;
	}

	if (keyTimer) clearTimeout(keyTimer);
	keyTimer = setTimeout(function(){_checkKey(key)}, 1000);
}

function _checkKey(key) {
	if (key != document.getElementById("desired_key").value || key.length < 6) return;

	function handlerFunction(response) {
		setKeyState(/^(OK)|(USED)|(INV)$/.test(response) ? response : "ERR");
	}
	
	setKeyState("SPIN");
	jx.load("check.php?key="+key, handlerFunction, "text", "GET", null, "text/plain");
}

function setKeyState(state) {
	var states = ["ERR", "OK", "USED", "INV", "SPIN"];
	for (var i in states) {
		document.getElementById("key_state_" + states[i]).style.display = "none";
	}
	if (document.getElementById("key_state_" + state)) {
		document.getElementById("key_state_" + state).style.display = "inline";
	}
}

function saveBitmap(noUndo) {
	if (!noUndo && bitmap) undoStack.push(bitmap);
	bitmap = context.getImageData(0, 0, canvas.width, canvas.height);
}

function restoreWholeBitmap() {
	context.putImageData(bitmap, 0, 0);
}

function undo() {
	var imgData = undoStack.pop();
	if (imgData) {
		canvas.width = imgData.width;
		canvas.height = imgData.height;
		bitmap = imgData;
		context.putImageData(imgData, 0, 0);
	}
}

function restoreBitmap() {
	if (_imgData) {
		for (i in _imgData) {
			context.putImageData(_imgData[i][0], _imgData[i][1], _imgData[i][2]);
		}
	}
}

function prepare(_brushSizeSlider, _brushColorField) {

	if (!navigator.javaEnabled()) {
		alert("ScreenShootMe requires Java Runtime Environment! Please install JRE 1.5 or later and come back.");
	}
	
	applet = document.getElementById('pasteimage');
	canvas = document.getElementById('canvas');
	context = canvas.getContext('2d');
	
	undoStack = new ImageStack(25);
	
	sx = sy = null;
	mousePos = [0, 0];
	mouseDown = false;
	setMouseMode("SELECT");
	
	context.fillStyle = "#FFF";
	context.fillRect(0, 0, canvas.width, canvas.height);
	
	drawTitleScreen(context, canvas);
	
	bitmap = null;
	saveBitmap();
	mouseMoved = false;
	document.onmousemove = mouseMove;
	
	canvasOffsetLeft = document.getElementById("content_editor").offsetLeft + 120 + 12 + 3 + 1; 
	canvasOffsetTop = document.getElementById("content_editor").offsetTop + 2;
	
	resetCrop();
	
	lastKey = "";
	keyTimer = null;
	
	brushSizeSlider = _brushSizeSlider;
	brushColorField = _brushColorField;
	
}

function setMouseMode(v) {
	if (v == "SELECT") {
		mouseMode = "SELECT";
		resetCrop();
		canvas.onmousedown = function(e) {
			document.onmouseup = canvas.onmouseup;
			mouseDown = true;
			resetCrop();
			restoreWholeBitmap();
			sx = mousePos[0];
			sy = mousePos[1];
			if (sx < 0) sx = 0;
			if (sy < 0) sy = 0;
			e.preventDefault();
		} 
		canvas.onmouseup = function(e) {
			document.onmouseup = function(){};
			if (!mouseDown) return;
			if (sx != null && sy != null && mouseDown && mousePos[0] != sx && mousePos[1] != sy) {
				dimOut(sx, sy, mousePos[0]-sx, mousePos[1]-sy);
			}
			mouseDown = false;
			sx = sy = null;
		}
				
	} else {
		restoreWholeBitmap();
		mouseMode = "BRUSH";
		resetCrop();
		canvas.onmousedown = function(e) {
			document.onmouseup = canvas.onmouseup;
			if (!mouseMoved) { // Draw dot
				mousePos = mouseCoords(e);
				context.beginPath();
				context.fillStyle = getBrushColor();
				context.arc(mousePos[0], mousePos[1], getBrushSize()/2, 0, Math.PI*2, true);
				context.fill();
				context.closePath();
			}
			mouseMoved = true;
			mouseDown = true;
			cropData = null;
			sx = mousePos[0];
			sy = mousePos[1];
			if (sx < 0) sx = 0;
			if (sy < 0) sy = 0;
			e.preventDefault();
		} 
		canvas.onmouseup = function(e) {
			document.onmouseup = function(){};
			mouseMoved = false;
			mouseDown = false;
			sx = sy = null;
			saveBitmap();
		}
	}
}

function mouseMove(e) {
	e = e || window.event; 
	if (mouseDown && mouseMode == "BRUSH") {
		mouseMoved = true;
		context.beginPath();
		context.moveTo(mousePos[0], mousePos[1]);
		mousePos = mouseCoords(e);
	    context.lineTo(mousePos[0], mousePos[1]);
	    context.lineWidth = getBrushSize();
	    context.strokeStyle = getBrushColor();
		context.lineCap = "round";
		context.stroke();
		context.closePath();
	} else {
		mousePos = mouseCoords(e);
		if (mouseDown) drawSelectedArea();
	}
}

function mouseCoords(e){ 
	return [e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft - canvasOffsetLeft, 
		e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop - canvasOffsetTop]; 
}

function drawSelectedArea() {
	restoreBitmap();
	context.fillStyle = "#0000FF";
	if (mousePos[0] < 0) mousePos[0] = 0;
	if (mousePos[1] < 0) mousePos[1] = 0;
	drawSelectionArea(sx, sy, mousePos[0]-sx, mousePos[1]-sy);
}

function drawSelectionArea(x, y, w, h, interval) {

	if (w == 0 || h == 0) return;

	if (!interval) interval = 2;
	
	if (w < 0) {
		x += w;
		w = -w;
		if (x < 0) {
			w += x;
			x = 0;
		}
	} else if (x+w >= canvas.width) {
		w = canvas.width - x - 1;
	}
	
	if (h < 0) {
		y += h;
		h = -h;
		if (y < 0) {
			h += y;
			y = 0;
		}
	} else if (y+h >= canvas.height) {
		h = canvas.height - y - 1;
	}
	
	_imgData = [[context.getImageData(x, y, w, 1), x, y], [context.getImageData(x, y+h, w, 1), x, y+h], [context.getImageData(x, y, 1, h), x, y], [context.getImageData(x+w, y, 1, h), x+w, y]];
	imgData = [context.getImageData(x, y, w, 1), context.getImageData(x, y+h, w, 1), context.getImageData(x, y, 1, h), context.getImageData(x+w, y, 1, h)];
	limit = [w, h];
	
	for (l = 0; l < 4; l++) {
		for (i = 0; i < 4*limit[parseInt(l/2)]; i += 4) {
			imgData[l].data[i] = imgData[l].data[i+1] = imgData[l].data[i+2] = (~((imgData[l].data[i]+imgData[l].data[i+1]+imgData[l].data[i+2])/3)) & 0xFF;
			if (parseInt((i+1)/(4*interval)) % 2) i += 4*interval;
		}
		context.putImageData(imgData[l], _imgData[l][1], _imgData[l][2]);
	}
}

function dimOut(x, y, w, h) {

	if (w < 0) {
		x += w;
		w = -w;
	}
	
	if (h < 0) {
		y += h;
		h = -h;
	}

	context.fillStyle = "rgba(0, 0, 0, 0.5)";
	context.fillRect(0, 0, x, canvas.height);
	context.fillRect(x, 0, canvas.width-x, y);
	context.fillRect(x+w+1, y, canvas.width-x-w-1, h+1);
	context.fillRect(x, y+h+1, canvas.width-x, canvas.height-y-h-1);
	
	if (x < 0) x = 0;
	if (y < 0) y = 0;
	
	cropDimension = [x, y, w, h];
}

function resetCrop() {
	cropDimension = null;
	_imgData = null;
}

function getBrushColor() {
	return brushColorField.value;
}

function getBrushSize() {
	return brushSizeSlider.getValue() - 0.5;
}

function generateShareButtons(url, fbContainer, tContainer, p1Container) {

    fbContainer.innerHTML = FB_BUTTON.replace("%URL%", escape(url));

    tContainer.innerHTML = TWITTER_BUTTON.replace(/%URL%/g, url);
    var tScript = document.createElement("script");
	tScript.type = TWITTER_BUTTON_SCRIPT.type;
	tScript.src = TWITTER_BUTTON_SCRIPT.src;
    tContainer.appendChild(tScript);
    
    gapi.plusone.render(p1Container, {"size": "medium", "count": "false", "href": url});
}

function clipboardButton(text, w, h, btn) {
    var clip = new ZeroClipboard.Client();
    clip.setText(text);
    
    if (btn) {
        clip.addEventListener('onMouseOver', btn.button.onmouseover);
        clip.addEventListener('onMouseOut', btn.button.onmouseout);
        clip.addEventListener('onMouseDown', btn.button.onmousedown);
        clip.addEventListener('onMouseUp', btn.button.onmouseup);
    }
    
    return clip.getHTML(w, h);
}

/* button.js */

var canvasSupportsText = document.createElement("canvas").getContext("2d").fillText;

function buttonFactory(container, caption, width, height, colorMap, action) {
	if (canvasSupportsText) {
		return new Button(container, caption, width, height, colorMap, action);
	} else {
		return fallbackButton(container, caption, action);
	}
}

function Button(container, caption, width, height, colorMap, action) {

	var button = this.button = document.createElement("canvas");
	button.width = width;
	button.height = height;
	var buttonContext = button.getContext("2d");
	container.appendChild(button);
	
	var buttonAction = this.action = action;
	var caption = caption;
	var span = 2;
	
	var useImage = caption.substr(0, 7) == "http://" || caption.substr(0, 1) == "/" || caption.substr(0, 2) == "./";
	var imageLoaded = false;
	this.isMouseDown = false;
	
	if (useImage) {
		var image = new Image();
		image.src = caption;
		image.onload = function() { imageLoaded = true; button.onmouseout();};
	} else {
		var fontColor = colorMap["font"]["color"];
		var fontStyle = colorMap["font"]["style"];
	}
			
	function draw(border, background) {
		
		buttonContext.beginPath();
		buttonContext.strokeStyle = border;

		var gradient = buttonContext.createLinearGradient(10, 4, 10, 20);
		gradient.addColorStop(1, background[0]);
		gradient.addColorStop(0, background[1]);
		buttonContext.fillStyle = gradient;
		
		var r = button.height/2 - span;
		buttonContext.arc(r+span, r+span, r, Math.PI/2, 3*Math.PI/2, false);
		buttonContext.lineTo(button.width-r-span, span);
		buttonContext.arc(button.width-r-span, r+span, r, 3*Math.PI/2, Math.PI/2, false);
		buttonContext.lineTo(r+span, 2*r+span);
		buttonContext.fill();
		buttonContext.stroke();
		buttonContext.closePath();
		if (useImage) {
			if (imageLoaded) buttonContext.drawImage(image, 0, 0);
		} else {
			buttonContext.fillStyle = fontColor;
			buttonContext.font = fontStyle;
			buttonContext.textAlign = "center";
			buttonContext.textBaseline = "middle";
			buttonContext.fillText(caption, button.width / 2 - 1, button.height / 2 + 1);
		}
	}
	
	button.onmouseover = function() {
		draw(colorMap.hover.border, colorMap.hover.bg);
	}
	
	button.onmouseout = function() {
		this.isMouseDown = false;
		draw(colorMap.def.border, colorMap.def.bg);
	}
	
	button.onmousedown = function() {
		this.isMouseDown = true;
		draw(colorMap.click.border, colorMap.click.bg);
	}
	
	button.onmouseup = function(btn){ 
		return function() {
			if (!this.isMouseDown) return;
			draw(colorMap.hover.border, colorMap.hover.bg);
			if (btn.action) btn.action();
		}
	}(this);

	if (!useImage) draw(colorMap.def.border, colorMap.def.bg);
}

function fallbackButton(container, caption, action) {
	var button = document.createElement("button");
	button.onclick = action;
	button.innerHTML = caption;
	container.appendChild(button);
	
	return button;
}


/* radio.js */

function Radio(buttons, defaultButtonId) {

	for (var i in buttons) {
		buttons[i].originalAction = buttons[i].action;
		buttons[i].action = function(n) {
			return function() {
				switchStates(n);
			}
		}(i);
	}
	
	var currentButtonId = this.currentButtonId = defaultButtonId;
	var originalOnMouseOut = buttons[defaultButtonId].button.onmouseout;
	var originalOnMouseOver = buttons[defaultButtonId].button.onmouseover;
	buttons[defaultButtonId].button.onmouseout = buttons[defaultButtonId].button.onmousedown;
	buttons[defaultButtonId].button.onmouseover = buttons[defaultButtonId].button.onmousedown;
	buttons[defaultButtonId].action();
	buttons[defaultButtonId].button.onmousedown();
	
	function switchStates(newButtonId) {
		if (currentButtonId == newButtonId) return;
		
		buttons[newButtonId].originalAction();
		
		buttons[currentButtonId].button.onmouseout = originalOnMouseOut;
		buttons[currentButtonId].button.onmouseover = originalOnMouseOver;
		buttons[currentButtonId].button.onmouseout();
		
		originalOnMouseOut = buttons[newButtonId].button.onmouseout;
		originalOnMouseOver = buttons[newButtonId].button.onmouseover;
		buttons[newButtonId].button.onmouseout = buttons[newButtonId].button.onmousedown;
		buttons[newButtonId].button.onmouseover = buttons[newButtonId].button.onmousedown;
		buttons[newButtonId].button.onmousedown();

		currentButtonId = newButtonId;
	}

}

/* slider.js */

function Slider(container, min, max, step, value, lineColor, dotColor, bgColor) {
	var slider = document.createElement("canvas");
	var sliderContext = slider.getContext("2d");
	var length = max-min;
	var widthOffset = length;
	var heightOffset = length;
	
	slider.width = length*step+widthOffset;
	slider.height = length+heightOffset;
	value = length/2;
	container.appendChild(slider);
	var mouseDown = false;
	var lastMove;
	draw();
	
	this.getValue = function() {
		return value;
	}
	
	function draw() {
		sliderContext.fillStyle = bgColor;
		sliderContext.fillRect(0, 0, slider.width, slider.height);
		sliderContext.beginPath();
		sliderContext.moveTo(widthOffset/2, slider.height/2);
	    sliderContext.lineTo(slider.width-widthOffset/2, slider.height-heightOffset/2);
		sliderContext.lineTo(slider.width-widthOffset/2, heightOffset/2);
		sliderContext.lineTo(widthOffset/2, slider.height/2);
	    sliderContext.lineWidth = 1;
	    sliderContext.fillStyle = lineColor;
		sliderContext.fill();
		sliderContext.closePath();
		
		sliderContext.beginPath();
		sliderContext.fillStyle = dotColor;
		sliderContext.arc(widthOffset/2 + (value - min) * step, slider.height/2, (value - min)/2, 0, Math.PI*2, true);
		sliderContext.fill();
		sliderContext.closePath();

	}
	
	slider.onmousedown = function() { 
		mouseDown = true; 
		slider.onmousemove(lastMove); 
	}
	
	slider.onmouseup = slider.onblur = function() { 
		mouseDown = false; 
	}
	
	slider.onmousemove = function(e) { 
		lastMove = e;
		if (!mouseDown) return;
		var posX = e.pageX - this.offsetLeft;
		value = (posX-widthOffset/2) / step;
		if (value < min) {
			value = min;
		} else if (value > max) {
			value = max;
		}
		draw();
	}
	
}



/* colorpicker.js */

function ColorPicker(container, offsetLeft, offsetTop, imageURL, width ,height, outputId, defaultColor) {	
	var cp = document.createElement("canvas");	
	cp.width = width;	
	cp.height = height;	
	var cpContext = cp.getContext("2d");	
	var matrix = [];	
	var offsetLeft = offsetLeft;	
	var offsetTop = offsetTop;		
	
	this.isMouseDown = false;
	
	var cpImage = new Image();	
	cpImage.src = imageURL;	
	cpImage.onload = function() {		
		cpContext.drawImage(this, 0, 0);				
		matrix = cpContext.getImageData(0, 0, cp.width, cp.height);	
	}
	
	function toHex(n) {		
		var res = n.toString(16).toUpperCase();		
		if (n < 10) res = "0" + res;		
		return res;	
	}		
	
	cp.onmousedown = function(e) {		
		this.isMouseDown = true;	
	}
	
	cp.onmouseup = function(e) {		
		if (!this.isMouseDown) return;		
		
		var posX = e.pageX - offsetLeft;		
		var posY = e.pageY - offsetTop;		
		var base = posY*matrix.width*4+posX*4;

		if (matrix.data[base] >= 0) {			
			hexField.value = "#"+toHex(matrix.data[base]) + toHex(matrix.data[base+1]) + toHex(matrix.data[base+2]);		
		}	
	}
	
	var picker = document.createElement("div");	
	picker.style.width = width+"px";
	
	var hexField = document.createElement("input");	
	hexField.type = "text";	
	hexField.maxLength = 7;	
	hexField.style.width = width+"px";	
	hexField.style.padding = 0;	
	hexField.style.border = "none";	
	hexField.style.position = "relative";	
	hexField.style.top = "-6px";	
	hexField.id = outputId;	
	hexField.value = defaultColor;
	
	picker.appendChild(cp);	
	picker.appendChild(hexField);	
	container.appendChild(picker);
}

/* imageStack.js */

function ImageStack(maxSize) {

	var stack = [];
	var maxSize = maxSize;

	this.push = function(imgData) {
		stack.push(imgData);
		if (stack.length > maxSize) stack.shift();
	}
	
	this.pop = function() {
		return stack.pop();
	}

}

/* titleScreen.js */

function drawTitleScreen(context, canvas) {

	var width = canvas.width = 650;
	var height = canvas.height = 500;

	context.save();	

	var gradient = context.createRadialGradient(width/2, height/2, width*5/6, width/2-60, height/2, width/6);
	gradient.addColorStop(0.2, "#F90");
	gradient.addColorStop(0.9, "#FE9");
	context.fillStyle = gradient;
	context.fillRect(0, 0, width, height);
	
	context.rotate(-0.2);
	context.font = "32px Comic Sans, Comic Sans MS, cursive";
	context.fillStyle = "#369";
	var base = 220;
	context.fillText("Share your screenshot in 3 clicks...", 15, base);	
	context.fillText("1. Print Screen", 115, base+60);	
	context.fillText("2. Paste", 140, base+105);	
	context.fillText("3. Save", 165, base+150);	
	
	context.restore();
}

function drawProcessingScreen(context, canvas) {

	var width = canvas.width = 650;
	var height = canvas.height = 500;

	context.save();	

	var gradient = context.createRadialGradient(width/2, height/2, width*5/6, width/2-60, height/2, width/6);
	gradient.addColorStop(0.2, "#F90");
	gradient.addColorStop(0.9, "#FE9");
	context.fillStyle = gradient;
	context.fillRect(0, 0, width, height);
	
	context.font = "32px Comic Sans, Comic Sans MS, cursive";
	context.fillStyle = "#369";
	context.textAlign = "center";
	context.fillText("Please wait! Processing...", width/2, height/2);	
	
	context.restore();

}




/* auth.js */

function authWindow(provider) {
    window.open('auth.php?provider='+provider, 'auth', 'width=800,height=600');
}

function logout() {
    jx.load('auth.php?logout', signedOut, "text", "GET", null, "text/plain");
}

function signedIn(displayName, profileUrl, provider) {
    document.getElementById('login').style.display = 'none';
    document.getElementById('profile').style.display = '';
    document.getElementById('profile_greeting').innerHTML = 'Greetings, <a href="'+profileUrl+'" target="_blank">'+displayName.toLowerCase()+'</a>.&nbsp;\
    Your <a href="javascript: void(0)" onclick="javascript: showGallery()"><em>gallery</em></a> awaits you...';
}

function signedOut(response) {
    if (response == 'OK') {
        document.getElementById('login').style.display = '';
        document.getElementById('profile').style.display = 'none';
        document.getElementById('gallery_scroll_view').innerHTML = '';
        clearGalleryPane();
    }
}


/* gallery.js */

var galleryData = [];
var galleryCurrentScreenshotKey = null;
var gallery_screenshotUrlCopyBtn = null;

function initGallery(data) {
    galleryData = [];
    for (var i = 0; i < data.length; i++) {
        if (data[i]) {
            galleryData.push(data[i].split('@'));
        }
    }
}

function addLeadingZero(n) {
    return (n < 10 ? '0' : '') + n;
}

function addImageToGallery(key) {

    var date = new Date();
    var formatedDate = date.getFullYear() + '-' + addLeadingZero(date.getMonth()+1) + '-' + addLeadingZero(date.getDate());
    galleryData.splice(0, 0, [key, formatedDate]);
    
    var container = document.getElementById('gallery_scroll_view');
    if (galleryData.length <= 0 || container.innerHTML.trim() != '') {
        container.innerHTML = thumbCode(key, formatedDate) + container.innerHTML;
        document.getElementById('thumb_'+key).style.backgroundImage = "url(thumbs/"+key+".jpg)";
    }
}

function thumbCode(key, data) {
    return '<div style="margin: 3px; margin-bottom: 12px">\
    <div id="thumb_'+key+'" class="thumb" style="background: #000 url(\'images/thumbs_spinner.gif\') no-repeat center center;" onclick="javascript: showScreenshot(\''+key+'\');">\
    </div>\
    <div>'+data+'</div>\
    </div>';
}

function renderGallery() {
    var res = '';
    for (var i = 0; i < galleryData.length; i++) {
        res += thumbCode(galleryData[i][0], galleryData[i][1]);
    }
    
    return res;
}

function showGallery() {
    var container = document.getElementById('gallery_scroll_view');
    if (container.innerHTML.trim() == '') {
        container.innerHTML = renderGallery();
        galleryScroll(container);
        setHintMessage();
    }

    document.body.style.overflow = 'hidden';
    document.getElementById('gallery').style.display = '';
    setTimeout(function(){container.focus()}, 100);

}

function hideGallery() {
    document.body.style.overflow = 'visible';
    document.getElementById('gallery').style.display = 'none';
}

function toggleGallery() {
    if (document.getElementById('gallery').style.display == 'none') {
        showGallery();
    } else {
        hideGallery();
    }
}

function galleryScroll(element) {
    var thumbsHeight = 174+15;
    var perView = parseInt(window.innerHeight / thumbsHeight)+3;
    var start = parseInt(element.scrollTop / thumbsHeight);
    var thumbs = element.childNodes; 
    
    for (var i = start; i < Math.min(thumbs.length, start+perView); i++) {
        var thumb = thumbs[i].childNodes[1];

        if (thumb.style.backgroundImage.indexOf('thumbs_spinner.gif') > -1) {
            thumb.style.backgroundImage = "url(thumbs/"+(thumb.id.split('_')[1])+".jpg)";
        }        
    }
}

function showScreenshot(key) {
    galleryCurrentScreenshotKey = key;
    document.getElementById('gallery_screenshot').style.display = 'none';
    document.getElementById('gallery_screenshot_image').innerHTML = '<a href="'+MAIN_URL + key+'" target="_blank">\
    <img id="screenshot_image" src="'+key+'" class="screenshot-image" onload="javascript: galleryResize(); document.getElementById(\'gallery_screenshot\').style.display=\'\';" />\
    </a>';
    document.getElementById('gallery_screenshot_image_url').href = MAIN_URL + key;
    document.getElementById('gallery_screenshot_image_url').innerHTML = MAIN_URL + key;
    generateShareButtons(MAIN_URL + key, document.getElementById('share_screenshot_fb'), document.getElementById('share_screenshot_t'), document.getElementById('share_screenshot_p1'));
    document.getElementById('gallery_navigation').style.display = '';
    
    if (document.getElementById('share_screenshot_copy_url_flash')) {
        document.getElementById('share_screenshot_copy_url').removeChild(document.getElementById('share_screenshot_copy_url_flash'));
    }
    var code = clipboardButton(MAIN_URL + galleryCurrentScreenshotKey, 90, 24, gallery_screenshotUrlCopyBtn);
    var div = document.createElement('div');
    div.id = 'share_screenshot_copy_url_flash';
    div.style.position = 'relative';
    div.style.top = '-32px';
    div.innerHTML = code;
    document.getElementById('share_screenshot_copy_url').appendChild(div);
}

function deleteCurrentScreenshot() {
    if (galleryCurrentScreenshotKey && confirm('Are you sure?')) {
        jx.load("index.php?delete="+galleryCurrentScreenshotKey, removeThumb, "text", "POST", null, "text/plain");
    }
}

function cloneCurrentScreenshot() {
    restartEditor();
    
    var img = new Image();
    img.onload = function() {
        applyOnCanvas(this);
        hideGallery();
    }
    img.src = MAIN_URL + galleryCurrentScreenshotKey;
}

function removeThumb(key) {
    if (key != 'ERR') {
        var newGalleryData = [];
        for (var i = 0; i < galleryData.length; i++) {
            if (galleryData[i][0] != key) {
                newGalleryData.push(galleryData[i]);
            }
        }
        galleryData = newGalleryData;
    
        document.getElementById('thumb_'+key).parentNode.parentNode.removeChild(document.getElementById('thumb_'+key).parentNode);
        clearGalleryPane();
        
        galleryScroll(document.getElementById('gallery_scroll_view'));
    }
}

function clearGalleryPane() {
    document.getElementById('gallery_navigation').style.display = 'none';
    document.getElementById('share_screenshot_fb').innerHTML = '';
    document.getElementById('share_screenshot_t').innerHTML = '';
    document.getElementById('share_screenshot_p1').innerHTML = '';
    setHintMessage();
}

function galleryResize() {
    if (!document.getElementById('gallery')) {
        return;
    }

    if (document.getElementById('gallery').style.display != 'none') {
        document.getElementById('gallery_screenshot').style.width = (window.innerWidth - 200 - 30) + 'px';
    }

    var screenshot = document.getElementById('screenshot_image');
    if (screenshot) {

        if (!screenshot.originalWidth) {
            screenshot.originalWidth = screenshot.width;
        }
        if (!screenshot.originalHeight) {
            screenshot.originalHeight = screenshot.height;
        }
        
        var wSize = window.innerWidth - 200 - 40;
        var hSize = window.innerHeight - 150;
        
        var k = Math.max(screenshot.originalWidth/wSize, screenshot.originalHeight/hSize);
        if (k > 1) {
            screenshot.style.width = (screenshot.originalWidth/k)+'px';
            screenshot.style.height = (screenshot.originalHeight/k)+'px';
        }
    }
}

function setHintMessage() {
    if (document.getElementById('gallery_scroll_view').innerHTML.trim() == '') {
        document.getElementById('gallery_screenshot_image').innerHTML = '<div class="gallery-hint">Do some screenshots first!</div>';
    } else {
        document.getElementById('gallery_screenshot_image').innerHTML = '<div class="gallery-hint">Select a screenshot from the left panel!</div>';
    }
}

window.onresize = galleryResize;



