	/**
	 * 2i API
	 * 
	 * This API enables to upload images to 2i.cz using JavaScript.
	 * 
	 * When an object of class "upload2i" is instanced, these variable enables further configuration:
	 * 		- cfgCodeType (string)				- enables to additionaly change code type; defaults to "XHTML"
	 * 		- cfgCrop (bool)					- enables to additionaly change crop settings; defaults to FALSE
	 * 		- cfgLarge (bool)					- enables to additionaly change large settings; defaults to FALSE
	 * 		- cfgCustomCode (string)			- enabled to set the customCode for rendering; custom code is enabled only when cfgCodeType is set to "custom"; defaults to NULL
	 * 		- cfgResetFormAfterUpload (bool)	- whether the dynamically created FORM for uploading image should be reset after upload; defaults to TRUE
	 * 		- cfgBackURLPath (string)			- URI relative to client's domain to use for back redirection after upload; it has to exist; if NULL, current URI is used
	 * 
	 * Many of these configuration parameters may be specified directly in the constructor (as described in the constructor's doc).
	 * 
	 * All methods with names beginning with underscore are not meant for public use or overloading. These are internal functions.
	 * All other methods are meant for public use and following methods are meant for overloading for target use:
	 * 		- handleResultCode()	- "handles" the result code, by default fills the code into the TEXTAREA given in the constructor
	 * 		- displayAlert()		- displays alert, by default using window.alert() function
	 * 
	 * Possible code types are:
	 * 		- id
	 * 		- BBCode
	 * 		- BBCode+thumb
	 * 		- HTML
	 * 		- HTML+thumb
	 * 		- XHTML
	 * 		- XHTML+thumb
	 * 		- custom
	 * 
	 * @author Petr "Don" Kačer, Aleš Krajník
	 */

	/**
	 *	2i API constructor 
	 *
	 *	@param button	an element (div) to host the upload button
	 *	@param target 	a textarea where link should be inserted
	 *	@param language	language code; currently supported languages are "cz", "en"; default is "en"
	 *	@param codeType	code type to render
	 *	@param crop		whether to crop image (default is false)
	 *	@param large	whether to use 100px thumbnails instead of 75px (default is false)
	 */
	function upload2i ( button, target, language, codeType, crop, large )
	{
		// setup URLs
		this.baseURL	= 'http://www.2i.cz/';
		this.uploadURL 	= this.baseURL + 'uploadAPI'; // 2i upload URL
		this.thumbURL 	= 'http://2i.cz/2i/t/'; // 2i thumbnail URL
		this.showURL 	= this.baseURL + 'show/'; // 2i show URL

		// setup 2i params - these are possible to update from outside
		this.cfgCodeType 				= ( codeType != undefined ) ? codeType : 'XHTML'; // inserted code syntax, default XHTML
		this.cfgCrop					= ( crop != undefined ) && ( crop == true ); // whether to crop the picture to a square
		this.cfgLarge					= ( large != undefined ) && ( large == true ); // whether to use 100px instead of 75px thumbnails
		this.cfgCustomCode				= null; // when not set, custom format is not available
		this.cfgResetFormAfterUpload	= true; // auto resets form after successful upload
		
		/**
		 * Back URL path is a relative path from clients URI's domain
		 * 
		 * This is to prevent interference with existing application (redirects sent back does not fire real URL).
		 * 
		 * If it is specified (that is not NULL), it is used as a relative path from client's domain.
		 * If it is not specified, this value is ignored and client's full URL is used as backURL.
		 * 
		 * In order to get this work in IE, it has to be a valid URL (may not return 404 header); Firefox seems to handle 404 pages as well.
		 */
		this.cfgBackURLPath = null;
		
		// set language
		this.setLanguage( language );
		
		// init - elements
		this.target			= this._getElement ( target );
		this.backURL		= null;
		this.form			= null;
		this.frame			= null;
		this.button			= null;
		this.cropEl			= null;
		this.largeEl		= null;
		
		// init - runtime variables
		this.lock			= null; // prevent doubleclicks and also store classname of the button
		this.lastID			= null;
		
		if ( button )
			this._bind ( button );
	}
	
	/**************************** "PUBLIC" methods ***********************************/

	/**
	 * This method sets translations to given language; when no language is given, default ("en") is used
	 * 
	 * Accepted languages at the moment are "en", "cz".
	 */
	upload2i.prototype.setLanguage = function ( language )
	{
		// set up language texts & settings
		switch ( language != undefined ? language : 'en' )
		{
			default:
			case 'en':
				this.tUploadError	= 'An error occured while uploading your image.';
				this.tBadType		= 'This image format is not supported.';
				this.tLinkToImage	= '2i.cz - image hosting for free';
				break;
			
			case 'cs':
			case 'cz':
				this.tUploadError	= 'Uložení obrázku se nezdařilo.';
				this.tBadType		= 'Nepodporovaný typ obrázku.';
				this.tLinkToImage	= '2i.cz - image hosting zdarma';
				break;
		}
	};

	/**
	 * Returns code for the given type and last upload
	 * @param codeType
	 * @return string
	 */
	upload2i.prototype.getLastUploadedCode = function ( codeType )
	{
		if ( this.lastID == null )
			return null;
		
		var codetxt, thumblink = this.thumbURL + this.lastID, showlink = this.showURL + this.lastID;
		
		switch ( codeType )
		{
			case 'custom':
				if ( this.cfgCustomCode != null )
					return this.cfgCustomCode
						.replace( /#thumb#/g, thumblink )
						.replace( /#show#/g, showlink )
						.replace( /#text#/g, this.tLinkToImage );
				// no break here - switch to default when no custom code set
				
			default:
			case 'id':
				return '[2i:' + this.lastID + ']';
				
			case 'BBCode':
				return '[url=' + showlink + '] ' + this.tLinkToImage + ' [/url]';
				
			case 'BBCode+thumb':
				return '[url=' + showlink + '] [img]' + thumblink + '[/img] [/url]';
				
			case 'HTML':
				return '<a href="'
					+ this._htmlspecialchars( showlink ) + '" target="_blank">'
					+ this._htmlspecialchars( this.tLinkToImage ) + '</a>';
				
			case 'XHTML':
				return '<a href="'
					+ this._htmlspecialchars( showlink ) + '" onclick="return ! window.open(this.href);">'
					+ this._htmlspecialchars( this.tLinkToImage ) + '</a>';
				
			case 'HTML+thumb':
				return '<a href="'
					+ this._htmlspecialchars( showlink ) + '" target="_blank"><img src="'
					+ this._htmlspecialchars( thumblink ) + '" alt="'
					+ this._htmlspecialchars( this.tLinkToImage ) + '" title="'
					+ this._htmlspecialchars( this.tLinkToImage ) + '"></a>';
				
			case 'XHTML+thumb':
				return '<a href="'
					+ this._htmlspecialchars( showlink ) + '" onclick="return ! window.open(this.href);"><img src="'
					+ this._htmlspecialchars( thumblink ) + '" alt="'
					+ this._htmlspecialchars( this.tLinkToImage ) + '" title="'
					+ this._htmlspecialchars( this.tLinkToImage ) + '" /></a>';
		}
	};

	/**************************** methods for overloading ***********************************/
	/************************ these are meant for overloading *******************************/
	
	/**
	 * Handles the result code - by default it sets the code to the given TEXTAREA
	 * @param codetxt
	 */
	upload2i.prototype.handleResultCode = function ( codetxt )
	{
		this.target.value += codetxt;
	};

	/**
	 * Displays alert
	 */
	upload2i.prototype.displayAlert = function ( text )
	{
		window.alert ( text );
	};

	/******************************* "PRIVATE" methods **************************************/
	/**************** these are NOT meant for overloading or public use *********************/

	/**
	 * Finds object by text ID; if object is passed to it, it is simply returned back.
	 * 
	 * @param target
	 * @return target
	 */
	upload2i.prototype._getElement = function( target )
	{
		if ( typeof target == 'object' )
			return target;
		
		if ( typeof target == 'string' )
		{
			var el;
			if ( el = document.getElementById ( target ) )
				return el;
		}
		
		return null;
	};
	
	/**
	 * Parses returned ID and calls code insertion method
	 */
	upload2i.prototype._frameload = function()
	{
		try
		{
			var framedoc	= ( this.frame.contentWindow ) ? this.frame.contentWindow.document : this.frame.contentDocument;
			var loc			= framedoc.location.href;
		}
		catch ( e )
		{
			return;
		}

		if ( ( loc != 'about:blank' ) && ( loc.indexOf('#') != -1 ) )
		{
			this.button.className	= this.lock ? this.lock : '';
			this.lock				= null;
			var id					= decodeURIComponent(loc.substring(loc.indexOf('#')+1, loc.length).replace(/\+/gi, ' ') );
			
			if ( id == 'error' )
				this.displayAlert ( this.tUploadError );
			
			else if ( id == 'badtype' )
				this.displayAlert( this.tBadType );
			
			else
			{
				this.lastID = id;
				this.handleResultCode ( this.getLastUploadedCode( this.cfgCodeType ) );
			}
			
			framedoc.location = 'about:blank';
		}
	};

	/**
	 * Sanitizes output for HTML
	 * 
	 * @param origString
	 * @param alsoQuotes
	 * @return string
	 */
	upload2i.prototype._htmlspecialchars = function ( origString, alsoQuotes )
	{
		if ( origString == null )
			return null;
		
		var newString = origString
			. replace ( /&/g, '&amp;' )
			. replace ( /</g, '&lt;' )
			. replace ( />/g, '&gt;' )
			. replace ( /"/g, '&quot;' )
			;
	
		if ( ( alsoQuotes != undefined ) && ( alsoQuotes == true ) )
			newString = newString.replace ( /'/g, '&#039;' );
		
		return newString;
	};
	
	/**
	 * Form submit handler - sets the backurl, sends and resets the form afterwards
	 */
	upload2i.prototype._submit = function()
	{
		// just a safety brake
		if ( ! this.target || this.lock )
			return false;
		this.lock = true
		
		// get backURL
		var backURL = document.location.href;
		
		if ( this.cfgBackURLPath != null )
		{
			var slashPOS	= backURL.indexOf ( '/', backURL.substring ( 0, 6 ) == 'https:' ? 8 : 7 );
			backURL			= ( slashPOS != -1 ? backURL.substring ( 0, slashPOS ) : backURL ) + '/' + this.cfgBackURLPath;
		}
		
		this.backURL.value	= backURL;
		this.lock			= this.button.className;

		// set crop & large
		this.cropEl.value	= ( this.cfgCrop ) ? '1' : '0';
		this.largeEl.value	= ( this.cfgLarge ) ? '1' : '0';
		
		// set the CSS class
		this.button.className += ' uploading2i';
		
		// submit & reset aftewards
		this.form.submit();
		
		if ( this.cfgResetFormAfterUpload )
			this.form.reset();
		
		return true;
	};

	/**
	 * Wrapper for settings event handlers to objects
	 * 
	 * @param obj
	 * @param event
	 * @param handler
	 * @return bool
	 */
	upload2i.prototype._setEvent = function (obj, event, handler)
	{
		if (obj.addEventListener)
			obj.addEventListener(event, handler, false);
		else if (obj.attachEvent)
			obj.attachEvent('on'+event, handler);
		else
			return false;
		
		return true;
	};
	
	/**
	 * Binds upload button to given element
	 */
	upload2i.prototype._bind = function ( el )
	{
		var element;
		if ( ! ( element = this._getElement ( el ) ) )
			return false;
		
		// init
		this.button	= element;
		var self	= this;
		
		// prepare element
		element.style.overflow	= 'hidden';
		element.style.position	= 'relative';
		
		// create file input
		var nFile = document.createElement('input');
		nFile.setAttribute('type', 'file');
		nFile.setAttribute('name', '_2i_file');
		nFile.setAttribute('size', '1');
		nFile.style.fontSize	= '200px';
		nFile.style.position	= 'absolute';
		nFile.style.top			= '0';
		nFile.style.right		= '0';
		nFile.style.bottom		= '0';
		nFile.style.zIndex		= '9999';
		nFile.style.opacity		= '0';
		nFile.style.filter		= 'alpha(opacity=0)'; // IE 6+7
		nFile.style.borderWidth	= '0';
		
		this._setEvent ( nFile, 'change', function() { self._submit(); } );
	
		// create hidden input
		var nHidden = this.backURL = document.createElement('input');
		nHidden.setAttribute('type', 'hidden');
		nHidden.setAttribute('name', '_2i_backurl');
		nHidden.setAttribute('value', '');
	
		// create IFRAME
		var frameName = '_2i_frm_' + Math.floor(Math.random() * 10000).toString();
		var nFrame;
		try
		{
			// this is for IE6/7 unable to set IFRAME NAME
			nFrame = document.createElement ( '<iframe name="' + frameName + '">' );
		}
		catch (ex)
		{
			nFrame = document.createElement ( 'iframe' );
		}
		
		this.frame = nFrame;
		nFrame.setAttribute('src', 'about:blank');
		nFrame.setAttribute('name', frameName);
		nFrame.style.width			= '0px';
		nFrame.style.height			= '0px';
		nFrame.style.borderWidth	= '0px';
		
		// set event using advanced event operations => for it to work under IE 6/7
		this._setEvent ( nFrame, 'load', function() { return self._frameload(); } );
	    
		// create hidden FORM
		var nForm = this.form = document.createElement('form');
		nForm.setAttribute('method', 'post');
		nForm.setAttribute('target', frameName);
		nForm.setAttribute('enctype', 'multipart/form-data');
		nForm.setAttribute('encoding', 'multipart/form-data'); // for IE6/7
		nForm.setAttribute('action', this.uploadURL);

		// create hidden max file size field
		var nMax = this.maxEl = document.createElement ( 'input' );
		nMax.setAttribute('type', 'hidden');
		nMax.setAttribute('name', 'MAX_FILE_SIZE');
		nMax.setAttribute('value', '10000000');
		
		// create hidden CROP when necessary
		var nCrop = this.cropEl = document.createElement ( 'input' );
		nCrop.setAttribute('type', 'hidden');
		nCrop.setAttribute('name', '_2i_crop');
		nCrop.setAttribute('value', '');
		
		// create hidden LARGE when necessary
		var nLarge = this.largeEl = document.createElement ( 'input' );
		nLarge.setAttribute('type', 'hidden');
		nLarge.setAttribute('name', '_2i_large');
		nLarge.setAttribute('value', '');
		
		// append all children
		nForm.appendChild(nMax);
		nForm.appendChild(nFile);
		nForm.appendChild(nHidden);
		nForm.appendChild(nFrame);
		nForm.appendChild(nCrop);
		nForm.appendChild(nLarge);
		element.appendChild(nForm);
	};
	
