function tng_gallery(imageElement,thumbnailElement,json,params){
	var activeParams = {
		'title':false,
		'titleText': '',
		'titleElement': null,
		'imageArrows':false,
		'imageArrowElementPrev': null,
		'imageArrowElementNext': null,
		'thumbnails': true,
		'thumbnailsAtOnce': 3,
		'thumbnailArrowPrev': null,
		'thumbnailArrowNext': null,
		'thumbnailClass': '',
		'thumbnailSelectedClass': 'selected',
		'keyboardNavigation': true,
		'showDescription': true,
		'descriptionElement': null,
		'player': false,
		'aboutText': 'Hello World!',
		'aboutLink': 'http://www.promotim.ba',
		'uriHash': true,
		'facebox': false
	};
	
	// Initialize, and merge params and defaults
	$.extend(activeParams,params);
	var imageElementRaw = imageElement.substr(1);
	var thumbnailElementRaw = thumbnailElement.substr(1);
	var images = $.parseJSON(json);
	var totalImages = images.length;
	var imageSelected;
	var maxOffset = 0;
	var viewportWidth = 0;
	var thumbWidth;
	
	// Avoid overstretching thumbnail strip
	if(totalImages < activeParams.thumbnailsAtOnce){
		activeParams.thumbnailsAtOnce = totalImages;
	}
	if(activeParams.thumbnailsAtOnce <= 0){
		activeParams.thumbnails = false;
	}
	
	// Check for URI Hash if it's turned on
	var hash = null;
	if(activeParams.uriHash === true && window.location.hash != ''){
		hash = window.location.hash
		hash = hash.substr(1);
		if(isNaN(hash)){
			hash = null;
		} else {
			hash = hash - 1;
		}
	}
	
	// Count the number of elements
	if(totalImages <= 1){
		activeParams.thumbnails = false;
	}
	
	// Enable keyboard image scrolling if it's turned on
	if(activeParams.keyboardNavigation == true){
		$('html').bind({
			keyup: function(event){
				if(event.keyCode == '37'){
					gallery_prev();
				} else if(event.keyCode == '39'){
					gallery_next();
				}
			}
		})
	}
	
	// Append title text to the provided element if there is any
	if(activeParams.title && activeParams.titleElement != null){
		$(activeParams.titleElement).html(activeParams.titleText);
	}
	
	// At document ready, begin construction of the gallery
	$(document).ready(function(){
		construct_gallery();
	});
	
	// Initial gallery constructor
	function construct_gallery(){
		
		// If thumbnails are turned on create thumbnail viewport and container
		if(activeParams.thumbnails){
			$(thumbnailElement).html('<div class="'+thumbnailElementRaw+'_viewport"><div class="'+thumbnailElementRaw+'_content"></div></div>');
			$(thumbnailElement+'_viewport').css({'overflow':'hidden','opacity':'0'});
			$(thumbnailElement+'_content').css({'margin-left':'0px'});
			if(activeParams.thumbnailArrowPrev != null){
				$(activeParams.thumbnailArrowPrev).bind('click.thumbnailArrowHandler', function(){
					thumbs_scroll_l();
				});
			}
			if(activeParams.thumbnailArrowNext != null){
				$(activeParams.thumbnailArrowNext).bind('click.thumbnailArrowHandler', function(){
					thumbs_scroll_r();
				});
			}
		} else {
			$(activeParams.thumbnailArrowPrev).remove();
			$(activeParams.thumbnailArrowNext).remove();
		};
		
		$.each(images, function(index,value){
			
			// Pick initial image depending on URI Hash or if they're main tagged 
			if(activeParams.uriHash && hash != null && index == hash){
				$(imageElement).html('<a class="'+imageElementRaw+'_imagelink" href="'+value.source+'" rel="facebox"><img class="'+imageElementRaw+'_image" src="'+value.image+'"/></a>');
				if(activeParams.showDescription == true && value.text != null && value.text != ''){
					$(activeParams.descriptionElement).html(value.text);
				}
				imageSelected = index;
			} else if(value.main == 1) {
				$(imageElement).html('<a class="'+imageElementRaw+'_imagelink" href="'+value.source+'" rel="facebox"><img class="'+imageElementRaw+'_image" src="'+value.image+'"/></a>');
				if(activeParams.showDescription == true && value.text != null && value.text != ''){
					$(activeParams.descriptionElement).html(value.text);
				}
				imageSelected = index;
			}
			
			//If thumbnails are turned on, put them in the thumbnail container and bind respective methods
			if(activeParams.thumbnails){
				$("img[class^="+thumbnailElementRaw+"_thumb]").removeClass(activeParams.thumbnailSelectedClass);
				$(thumbnailElement+'_thumb'+imageSelected).addClass(activeParams.thumbnailSelectedClass);
				$(thumbnailElement+'_content').append('<img class="'+thumbnailElementRaw+'_thumb'+index+' '+activeParams.thumbnailClass+'" src="'+value.thumb+'"/>');
				$(thumbnailElement+'_thumb'+index).bind('click.thumbnailSwapToHandler', function(){
					if(value.type == 'image'){
						$(imageElement).html('<a class="'+imageElementRaw+'_imagelink" href="'+value.source+'" rel="facebox"><img class="'+imageElementRaw+'_image" src="'+value.image+'"/></a>');
						$(activeParams.descriptionElement).html('');
						if(activeParams.showDescription == true && value.text != null && value.text != ''){
							$(activeParams.descriptionElement).html(value.text);
						}
						if(activeParams.facebox){
							//facebox init
							//$(activeParams.imageElement+'_image').attr('rel','facebox');
							$(imageElement+'_imagelink').facebox();
						}
						imageSelected = index;
					} else {
						$(imageElement).html(show_player(value));
						$(activeParams.descriptionElement).html('');
						imageSelected = index;
					}
					$("img[class^="+thumbnailElementRaw+"_thumb]").removeClass(activeParams.thumbnailSelectedClass);
					$(thumbnailElement+'_thumb'+index).addClass(activeParams.thumbnailSelectedClass);
				});
			};
		});

		// Calculate and apply widths for viewport,content containers and maximal scrolling offset
		$(window).load(function(){
			var thumbs = $(thumbnailElement+'_content').children();
			thumbWidth = thumbs.eq(0).outerWidth(true);
			$(thumbnailElement+'_viewport').css({'width':thumbWidth * activeParams.thumbnailsAtOnce});
			viewportWidth = thumbWidth * activeParams.thumbnailsAtOnce;
			$(thumbnailElement+'_content').css({'width':thumbWidth * totalImages});
			maxOffset = (thumbWidth * activeParams.thumbnailsAtOnce) - (thumbWidth * totalImages);
			$(thumbnailElement+'_viewport').animate({'opacity':'1'},0);
		});
		
		// Activate previous and next elements on the page, if any were provided
		if(activeParams.imageArrows && activeParams.imageArrowElementPrev != null && activeParams.imageArrowElementNext != null){
			$(activeParams.imageArrowElementNext).bind('click.mainImageDirectionHandler', gallery_next);
			$(activeParams.imageArrowElementPrev).bind('click.mainImageDirectionHandler', gallery_prev);
		};
		
		if(activeParams.facebox){
			//facebox init
			//$(activeParams.imageElement+'_image').attr('rel','facebox');
			$(imageElement+'_imagelink').facebox();
		}
		
		/*if(activeParams.facebox){
			//facebox init
			//$(activeParams.imageElement+'_image').attr('rel','facebox');
			$('a[rel*=facebox]').facebox();

		}*/
	};
	
	// Calculate if a thumbnail is in the current viewport area
	function is_in_viewport(thumb,dir){
		var previousThumbs = $(thumbnailElement+'_thumb'+thumb).prevAll();
		var previousThumbsNo = previousThumbs.length;
		if(dir == 'right'){
			previousThumbsNo = previousThumbsNo + 1;
		}
		var distanceFromGZ = previousThumbsNo * thumbWidth;
		
		if(dir == 'left'){
			if(distanceFromGZ < viewport_edge('left')){
				return false;
			} else {
				return true;
			}
		} else if (dir == 'right'){
			if(distanceFromGZ > viewport_edge('right')){
				return false;
			} else {
				return true;
			}
		}
	}
	
	// Calculate at what position is a viewport edge currently at
	function viewport_edge(side){
		var currentOffset = parseFloat($(thumbnailElement+'_content').css('margin-left'));
		var edge = 0 + currentOffset;
		edge = Math.abs(edge);
		if(side == 'right'){
			edge = edge + viewportWidth;
		}
		return edge;
	}
	
	// Advance the main image by one
	function gallery_next(){
		var tImgs = totalImages - 1;
		if(imageSelected == tImgs){
			var nextImage = 0;
		} else {
			var nextImage = imageSelected + 1;
		}
		$.each(images,function(index, value){
			if(index == nextImage){
				$(activeParams.descriptionElement).html('');
				if (value.type == 'image'){
					$(imageElement).html('<a class="'+imageElementRaw+'_imagelink" href="'+value.source+'" rel="facebox"><img class="'+imageElementRaw+'_image" src="'+value.image+'"/></a>');
					if(activeParams.showDescription != false && value.text != null && value.text != ''){
						$(activeParams.descriptionElement).html(value.text);
					};
				} else {
					$(imageElement).html(show_player(value));
				}
				$("img[class^="+thumbnailElementRaw+"_thumb]").removeClass(activeParams.thumbnailSelectedClass);
				$(thumbnailElement+'_thumb'+index).addClass(activeParams.thumbnailSelectedClass);
				if(activeParams.facebox){
					//facebox init
					//$(activeParams.imageElement+'_image').attr('rel','facebox');
					$(imageElement+'_imagelink').facebox();
				}
				imageSelected = index;
			}
		});
		if(nextImage == 0){
			$(thumbnailElement+'_content').animate({'margin-left':'0'},200,function(){
				$(activeParams.thumbnailArrowNext).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_r();
				});
			});
		} else {
			if(!is_in_viewport(nextImage,'right')){
				thumbs_scroll_r();
			}
		}
	}
	
	// Backtrack the main image by one
	function gallery_prev(){
		var tImgs = totalImages - 1;
		if(imageSelected == 0){
			var nextImage = tImgs;
		} else {
			var nextImage = imageSelected - 1;
		}
		$.each(images,function(index, value){
			if(index == nextImage){
				if (value.type == 'image'){
					$(imageElement).html('<a class="'+imageElementRaw+'_imagelink" href="'+value.source+'" rel="facebox"><img class="'+imageElementRaw+'_image" src="'+value.image+'"/></a>');
					if(activeParams.showDescription != false && value.text != null && value.text != ''){
							$(activeParams.descriptionElement).html(value.text);
					};
					if(activeParams.facebox){
						//facebox init
						//$(activeParams.imageElement+'_image').attr('rel','facebox');
						$(imageElement+'_imagelink').facebox();
					}
				} else {
					$(imageElement).html(show_player(value));
				}
				$("img[class^="+thumbnailElementRaw+"_thumb]").removeClass(activeParams.thumbnailSelectedClass);
				$(thumbnailElement+'_thumb'+index).addClass(activeParams.thumbnailSelectedClass);
//				$('.pcms_gal_facebox').facebox();
				imageSelected = index;
			}
		});
		if(nextImage == tImgs){
			$(thumbnailElement+'_content').animate({'margin-left':maxOffset},200,function(){
				$(activeParams.thumbnailArrowPrev).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_l();
				});
			});
		} else {
			if(!is_in_viewport(nextImage,'left')){
				thumbs_scroll_l();
			}
		}
	}
	// Scroll thumbnail strip left
	function thumbs_scroll_l(){
		$(activeParams.thumbnailArrowPrev).unbind('.thumbnailArrowHandler');
		$(activeParams.thumbnailArrowNext).unbind('.thumbnailArrowHandler');
		var marginNow = parseFloat($(thumbnailElement+'_content').css('margin-left'));
		var marginNext = marginNow + (thumbWidth * activeParams.thumbnailsAtOnce);
		if(marginNext <= 0){
			$(thumbnailElement+'_content').animate({'margin-left':marginNext},300,function(){
				$(activeParams.thumbnailArrowPrev).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_l();
				});
				$(activeParams.thumbnailArrowNext).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_r();
				});
			});
		} else {
			$(thumbnailElement+'_content').animate({'margin-left':'0px'},300,function(){
				$(activeParams.thumbnailArrowNext).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_r();
				});
			});
		}
	}
	
	// Scroll thumbnail strip right
	function thumbs_scroll_r(){
		$(activeParams.thumbnailArrowPrev).unbind('.thumbnailArrowHandler');
		$(activeParams.thumbnailArrowNext).unbind('.thumbnailArrowHandler');
		var marginNow = parseFloat($(thumbnailElement+'_content').css('margin-left'));
		var marginNext = marginNow - (thumbWidth * activeParams.thumbnailsAtOnce);
		if(marginNext >= maxOffset){
			$(thumbnailElement+'_content').animate({'margin-left':marginNext},300,function(){
				$(activeParams.thumbnailArrowPrev).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_l();
				});
				$(activeParams.thumbnailArrowNext).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_r();
				});
			});
		} else {
			$(thumbnailElement+'_content').animate({'margin-left':maxOffset},300,function(){
				$(activeParams.thumbnailArrowPrev).bind('click.thumbnailArrowHandler',function(){
					thumbs_scroll_l();
				});
			});
		}
	}
	
	// Player embed code
	function show_player2(media){
		var string = '<embed width="308" height="308" flashvars="file='+media.source+'&amp;abouttext='+activeParams.aboutText+'&amp;aboutlink='+activeParams.aboutLink+'&amp;stretching=fill" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" quality="high" bgcolor="#ffffff" name="ply" id="ply" style="" src="'+activeParams.player+'" type="application/x-shockwave-flash">';
		var audio = '<embed width="308" height="308" flashvars="file='+media.source+'&amp;abouttext='+activeParams.aboutText+'&amp;aboutlink='+activeParams.aboutLink+'&amp;stretching=fill&amp;type=sound" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" quality="high" bgcolor="#ffffff" name="ply" id="ply" style="" src="'+activeParams.player+'" type="application/x-shockwave-flash">';
		if (media.type == 'audio') return audio; 
		if (media.type == 'embed') {
			string = media.source;
			string = string.replace(/\!/g,'"');
			alert(string);
		}
		return string;
	}
	
	function show_player(media){
		$(imageElement).html('<div id="videox">Video</div>');
		var so = new SWFObject(activeParams.player,'ply','620','225','9','#ffffff');
		so.addParam('allowfullscreen','true');
		so.addParam('allowscriptaccess','always');
		so.addParam('wmode','opaque');
		so.addVariable('file',media.video);
		so.addVariable('image',media.thumb);
		so.addVariable('logo.position','top-left');
		so.addVariable('abouttext',activeParams.aboutText);
		so.addVariable('aboutlink',activeParams.aboutLink);
		so.addVariable('stretching','fill');
		so.addVariable('backcolor','FF33FF');
		so.addVariable('frontcolor','FF99FF');
		so.addVariable('lightcolor','FFCCFF');
		so.addVariable('screencolor','660066');
		so.write('videox');
	}	
	
	// Enable external access to image navigation, in case it's needed
	this.next = gallery_next;
	this.prev = gallery_prev;
}
