// jQuery Translate plugin and related components

/* 
 * jQuery Translate plugin 
 * 
 * Version: 1.2.5
 * 
 * http://code.google.com/p/jquery-translate/
 * 
 * Copyright (c) 2008 Balazs Endresz (balazs.endresz@gmail.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * 
 * This plugin uses the 'Google AJAX Language API' (http://code.google.com/apis/ajaxlanguage/)
 * You can read the terms of use at http://code.google.com/apis/ajaxlanguage/terms.html
 * 
 */
;(function($){

function $fn(){}
function isSet(a){	return typeof a!='undefined'; }
function isString(a){ return typeof a=='string'; }
function same(a, b){ return a.toLowerCase() == b.toLowerCase(); }

function isInput(e){
	return $.nodeName(e[0],'input') ? !!e.attr('type').match(/text|button|submit/i) : false;
}

function args(a, b, c, method){
	var from='', to, o={};
	if(typeof a=='object'){o=a;}else{
		if(!b && !c) to=a;
		if(!c && b){ if(typeof b=='object'){ to=a; o=b; }else{ from=a; to=b; } }
		if(a && b && c){ from=a; to=b; o=c;}
		o.from=from || o.from || '';
		o.to=to || o.to || '';
	}
	if(o.fromOriginal) o.toggle=true;
	if(o.toggle) o.data=true;
	if(o.async===true) o.async=2;
	
	return $.extend({}, defaults, (isSet(method) ? $.fn.translate.defaults : $.translate.defaults), o);
}

function T(){}

T.fn=T.prototype={
	version: "1.2.5",
	
	translateInit: function(t, a, b, c){ 
		var that=this, from, to, o;
		this.options=o=args(a, b, c);
		o.from=this.toLanguageCode(o.from) || '';
		o.to=this.toLanguageCode(o.to) || '';
		
		if(o.fromOriginal && o.nodes[0]){
			o.nodes.each(function(i){
				var data=that.getData(this, o.from)
				if( !data ) return false;
				t[i]=data;
			})
		}
		
		if(isString(t)){
			if(o.comments===false)
				t=this.stripComments(t);
			this.rawSource='<div>'+t+'</div>';
			this.isString=true;
		}else{
			if(o.comments===false)
				for(var i=0, l=t.length; i<l; i++)
					t[i]=this.stripComments(t[i]);
			this.rawSource='<div>'+t.join('</div><div>')+'</div>';
			this.isString=false;
		}

		this.from=o.from;
		this.to=o.to;
		this.source=t;
		this.elements=o.nodes;
		this.rawTranslation='';
		this.translation=[];
		this.startPos=0;
		this.i=0;
		this.numberOfCalls=0;
		this.stopped=false;
		
		o.start.call(this, o.nodes[0] ? o.nodes : t , o.from, o.to, o);
		
		if(o.timeout>0){
			this.timeout=setTimeout(function(){
				o.onTimeout.call(that, o.nodes[0] ? o.nodes : t, o.from, o.to, o);
			}, o.timeout);
		}
		
		if(o.toggle && o.nodes[0])
			this.toggle();
		
		return this.translate();
	},
	
	translate: function(){
		if(this.stopped)
			return;
		var that=this, o=this.options;
	
		this.rawSourceSub=this.truncate( this.rawSource.substring(this.startPos, this.startPos+o.limit) );
		this.startPos+=this.rawSourceSub.length;//set next start position
		
		//---------handle each callbacks as transl arrived----------
		var i=this.rawTranslation.length;
		while(this.rawTranslation.lastIndexOf('</div>',i)>-1){
			i=this.rawTranslation.lastIndexOf('</div>',i)-1;
			var subst=this.rawTranslation.substr(0,i),			
				divst=subst.match(/<div(>|( style=";))/gi),
				divcl=subst.match(/<\/div>/gi);
			
			divst=divst ? divst.length : 0;
			divcl=divcl ? divcl.length : 0;
				
			if(divst!=divcl+1) continue;
			var divscompl=$(this.rawTranslation.substr(0,(i+7))), divlen=divscompl.length, l=this.i;
			if(l==divlen) break;//if no new elements have been translated
			
			divscompl.slice(l, divlen).each(function(j, e){ (function(){
				if(this.stopped)
					return false;
				var tr=$(e).html(), i=l+j, src=this.source //set det.source lang, if unset:
					from=this.from.length<2 && this.detectedSourceLanguage || this.from;
				this.translation[i]=tr;//create an array for complete callback

				if(!o.nodes[0]){//called from function
					if(this.isString)
						this.translation=tr;
					else
						src=this.source[i]
					o.each.call(this, i, tr, src, from, this.to, o, this.numberOfCalls);
				}else{//called from method
					this.each(i, this.elements[i], tr, this.source[i], from, this.to, o);
					o.each.call(this, i, this.elements[i], tr, this.source[i], from, this.to, o, this.numberOfCalls);
				}
				this.i++;
			}).call(that); });
			
			break;
		}

		//---------translate one part of text-----------
		if(this.rawSourceSub.length>0){
			GL.translate(this.rawSourceSub, this.from, this.to, function(result){ (function(){
				if(result.error)
					return o.error.call(this, result.error, this.rawSourceSub, this.from, this.to, o, this.numberOfCalls);
		
				this.rawTranslation+=result.translation;
				this.detectedSourceLanguage=result.detectedSourceLanguage;
				if( /[\.\?\!;:]$/.exec(this.rawTranslation) )
					this.rawTranslation+=' ';
					
				this.translate();
			}).call(that); });
			this.numberOfCalls++;
			
			if(!o.nodes[0])
				return this;
		}else{
		
		//------------translation complete------------
			
			if(!this.rawTranslation)
				return;
	
			this.rawTranslation=this.rawTranslation.replace(/\s*$/,'');
			var from=this.from.length<2 && this.detectedSourceLanguage || this.from;
			
			if(this.timeout)
				clearTimeout(this.timeout);
	
			if(!o.nodes[0]){//called from function
				o.complete.call(this, this.translation, this.source, from, this.to, o, this.numberOfCalls);
			}else
				o.complete.call(this, this.elements.end(), this.elements, this.translation, this.source, from, this.to, o, this.numberOfCalls);
		}
	},

	
	
	each: function(i, el, t, s, from, to, o){
		var that=this, e=$(el), d;
		
		if(o.data===true){
			if(isString(o.subject))
				d=o.subject;
			else{
				if(o.altAndVal && $.nodeName(el,'img')) d='alt';
				else if(o.altAndVal && isInput(e)) d='value';
				else if($.nodeName(el,'textarea')) d='value';
				else d='html';
			}
			$.data(el, 'translation.'+from+'.'+d, s);
			$.data(el, 'translation.'+to+'.'+d, t);
		}
		
		this.setLangAttr(e, to, o);
		this.replace(e, t, o);
	},
	
	getData: function(el, lang, o){
		var o=o || this.options || {subject:true}, d='translation.'+lang+'.';
		return isString(o.subject) && $.data(el, d+o.subject) ||		
			o.subject===true && $.data(el, d+'html') ||
			o.altAndVal && $.data(el, d+'value') ||
			o.altAndVal && $.data(el, d+'alt');
	},
	
	toggle: function(){
		var that=this, translation=[], o=this.options, el=o.nodes, to=o.to, stop=false;
		
		el.each(function(i){
			var e=$(this), tr=that.getData(this, to);
			
			if(isSet(tr))
				that.stop();
			else{
				stop=true;
				return false;
			}
			that.setLangAttr(e, to, o);
			that.replace(e, tr, o);

			translation.push(tr);
			o.each.call(that, i, that.elements[i], tr, that.source[i], that.from, to, o);
			//'from' will be undefined, if it wasn't set
		});
		
		if(!stop)
			o.complete.call(this, el.end(), el, translation, this.source, this.from, this.to, o);
	},
	
	replace: function(e, t, o){
		if(o.replace===true){
			if(isString(o.subject))
				e.attr(o.subject, t);
			else{
				if(o.from=='ar' || o.to=='ar'){
					if(o.from=='ar')	e.css('direction','ltr');
					if(o.to=='ar')	e.css('direction','rtl');
					if(e.css('text-align')=='right')	e.css('text-align','left');
					if(e.css('text-align')=='left')		e.css('text-align','right');
				}
				
				if(o.altAndVal && $.nodeName(e[0],'img'))
					e.attr('alt', t);
				else if(o.altAndVal && isInput(e))
					e.val(t);
				else if($.nodeName(e[0],'textarea'))
					e.val(t);
				else{
					if(o.rebind===true){
						var k=e.find('*').not('script').clone(true);
						e.html(t);
						this.copyEvents(k, e.find('*'));
					}else
						e.html(t);
				}
			}
		}
	},
	
	setLangAttr: function(e, l, o){
		if(o.setLangAttr===true)
			e.attr('lang', l);
		if(isString(o.setLangAttr))
			e.attr(o.setLangAttr, l);
	},
	
	stripComments: function(t){ return t.replace(/<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)>/g,''); },
	
	truncate: function(t){
		var m1=/<(?![^<]*>)/.exec(t);
		if(!m1){//if no broken tag present
			var m2=/>\s*$/.exec(t);
			if(!m2){//if doesn't end with '>'
				var m3=/[\.\?\!;:](?![^\.\?\!;:]*[\.\?\!;:])/.exec(t); 
				if(m3){//if broken sentence present
					var m4=/>(?![^>]*<)/.exec(t);
					if(m4){
						if(m3.index > m4.index){
							t=t.substring(0, m3.index+1);
						}else t=t.substring(0, m4.index+1);
					}else t=t.substring(0, m3.index+1);
				}else t=t;
			}else t=t;
		}else t=t.substring(0, m1.index);
		
		return t;
	},

	copyEvents: function(from, to){
		to.each(function(i){
			var events=$.data(from[i], 'events');
			if(!events)
				return false;
			for(var type in events)
				for(var handler in events[type])
					$.event.add(this, type, events[type][handler], events[type][handler].data);
		});
	},
	
	stop: function(){
		if(this.stopped)
			return this;
		this.stopped=true;
		this.options.error.call(this, {message:'stopped'});
		return this;
	},
	
	ready: function(fn, that){//sth like $(document).ready()
		if(isReady)//if the API is loaaded
			return fn();
		readyList.push(fn);//else execute it later
		if(isSet(that))//and return the object provided for chaining
			return that;
	},
	
	getLanguages: function(translatable){
		if(!isSet(translatable))
			return GLL;
		translatable={};
		for (var l in GLL)
			if(GL.isTranslatable(GLL[l]))
				translatable[l]=GLL[l];
		return translatable;
	},
	
	toLanguage: function(langCode, format){//accepts both full language and language code
		var lang='UNKNOWN';
		for(l in GLL)
			if( same(GLL[l], langCode) || same(l, langCode) )
				{lang=l; break;}
		if(format=='lowercase')
			lang=lang.toLowerCase();
		if(format=='uppercase')
			lang=lang.toUpperCase();
		if(format=='capitalize')
			lang=lang.charAt(0).toUpperCase()+lang.substring(1).toLowerCase();
		return lang;
	},
	
	toLanguageCode: function(a){//accepts both full language and language code
		for(l in GLL)
			if( same(GLL[l], a) || same(l, a) )
				return GLL[l];
	},
		
	isTranslatable: function(l){//accepts both full language and language code
		return GL.isTranslatable(this.toLanguageCode(l));
	},
	
	getBranding: function(a, b){ return $( GL.getBranding(a, b) ); }
	
}

//assign functions to jquery and delay execution if necessary (r=T.fn.ready)
$.fn.translate=function(a, b, c){
	var o=args(a, b, c, true), 
		ncto=$.extend({}, defaults, $.fn.translate.defaults, o,
		{complete:function(e,t){ o.nodes=e; $.translate(t, o); }, each:$fn } );

	if(isSet($.fn.nodesContainingText))
		return this.nodesContainingText(ncto);
	o.nodes=this.get();
	return $.translate(o);
}

$.translate=function(t, a, b, c){
	if($.isFunction(t))
		return r(t);
	var that=new T();
	if(!isSet(t))
		return that;
	return r(function(){ return that.translateInit(t, a, b, c);  }, that);
}

var defaults={
	limit: 1000,
	comments: false,
	start: $fn,
	error: $fn,
	each: $fn,
	complete: $fn,
	onTimeout: $fn,
	timeout: 0,
	from: '',
	to: '',
	nodes: [],
	walk: true,
	returnAll: false,
	replace: true,
	rebind: true,
	data: true,
	setLangAttr: false,
	subject: true,
	not: '',
	altAndVal:true,
	async: false,
	toggle: false,
	fromOriginal: false
}, GL, GLL, isReady=false, readyList=[], r=T.fn.ready;

$.translate.defaults=$.fn.translate.defaults=defaults;

$.translate.fn=$.translate.prototype=T.fn;

function loaded(){
	GL=T.prototype.GL=google.language;
	GLL=GL.Languages;
	isReady=true;
	$.each(readyList, function(){ this(); });
}

function load(){ google.load('language', '1', {'callback' : loaded}); }

function jsapi(key){
	if(typeof google!='undefined' && typeof google.load!='undefined')
		return load();
	$.getScript('http://www.google.com/jsapi?'+(key ? 'key='+key : ''), load);
}

jsapi();//you can put your Google API key here as the argument, or load it manually before this file

})(jQuery);

/* 
 * Language detection extension for the jQuery Translate plugin 
 * Version: 1.2.5
 * http://code.google.com/p/jquery-translate/
 */
;(function($){

function $fn(){}

$.fn.language=function(o){
	o=o || {};
	var ncto=$.extend( {}, defaults, $.fn.language.defaults, o,
		{complete:function(e,t){ o.nodes=e; $.language(t, o); }, each:$fn, start:$fn, returnAll:true } );
	
	if(typeof $.fn.nodesContainingText!='undefined')
		return this.nodesContainingText(ncto);
	o.nodes=this;
	return $.language(that, o);

}

$.language=function(t, o){
	o=o || {};
	var that=new $.translate();
	if($.isFunction(t))
		return that.ready(t);
	if(typeof t=='undefined')
		return that;
	return that.ready(function(){ return that.detect(t, o); }, that);
}

var defaults={
	start: $fn,
	each: $fn,
	complete: $fn,
	error: $fn,
	setLangAttr: true,
	subject: true,
	limit: 500,
	walk: true,
	returnAll: false,
	not: '',
	altAndVal: false,
	async: false,
	nodes: []
}

$.language.defaults=$.fn.language.defaults=defaults;

$.language.fn=$.language.prototype=$.extend($.translate.fn, {
	detect: function(t, o){
		if(typeof t=='undefined')
			return this;
		this.options=o=$.extend({}, defaults, (o.nodes[0] ? $.fn.language.defaults : $.language.defaults), o);
		
		var that=this, i=0, languageCodes=[], languages=[], results=[], elements=o.nodes, isString;
		if(typeof t=='string'){ t=[t]; isString=true; }
		var len=t.length;
		this.elements=elements;
		this.text=t;
		this.stopped=false;
		this.languageCodes=languageCodes;
		this.languages=languages;
		this.results=results;
		
		o.start.call(this, o.nodes[0] ? o.nodes : t , o);
		
		$.each(t, function(i, v){
			that.GL.detect(v.substring(0, o.limit*1), function(result){ (function(){
				if(this.stopped)
					return;
				if(result.error){
					if(elements[0])
						return o.error.call(this, result.error, elements[i], v, i, o);
					else
						return o.error.call(this, result.error, v, i, o);
				}
				
				var languageCode=result.language, language=this.toLanguage(languageCode);
				
				languageCodes.push(languageCode);
				languages.push(language);
				results.push(result);
				
				//each:
				if(elements[0]){
					var e=elements[i];
					this.setLangAttr($(e), languageCode, o);
					o.each.call(this, i, e, v, languageCode, language, result, o);
				}else
					o.each.call(this, i, v, languageCode, language, result, o);
				
				//complete:
				if(i==len-1){
					if(isString)
						return o.complete.call(this, t[0], languageCodes[0], languages[0],  results[0], o);
					else if(elements[0])
						return o.complete.call(this, elements, t, languageCodes, languages, results, o);
					else
						return o.complete.call(this, t, languageCodes, languages,  results, o);
				}
			}).call(that); });
		});
		
		return this;
	}
});

})(jQuery);

/* 
 * Simple user interface extension for the jQuery Translate plugin 
 * Version: 1.2.5
 * http://code.google.com/p/jquery-translate/
 */
;(function($){
$.translate.fn.ui=function(a, b, c){
	var str='', cs='', cl='';
	if(c){ cs='<'+c+'>'; cl='</'+c+'>'; }
	$.each( this.getLanguages(true), function(l, lc){
		str+=('<'+b+'>'+cs+l.charAt(0)+l.substring(1).toLowerCase()+cl+'</'+b+'>');
	});
	return $('<'+a+' id="jq-translate-ui">'+str+'</'+a+'>');
}
})(jQuery);

/* 
 * Progress indicator extension for the jQuery Translate plugin 
 * Version: 1.2.5
 * http://code.google.com/p/jquery-translate/
 */

;(function($){
$.translate.fn.progress=function(selector, options){
	if(!this.i) this.pr=0;
	this.pr+=this.source[this.i].length;
	var progress= 100 * this.pr / ( this.rawSource.length - (11*(this.i+1)) );

	if(selector){
		var e=$(selector);
		if( !this.i && !e.hasClass("ui-progressbar") )
			e.progressbar(options)
		e.progressbar( "progress", progress );
	}
	
	return progress;
}
})(jQuery);

/* 
 * jQuery nodesContainingText plugin 
 * 
 * Version: 1.0.0
 * 
 * http://code.google.com/p/jquery-translate/
 * 
 * Copyright (c) 2008 Balazs Endresz (balazs.endresz@gmail.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * 
 */
 
;(function($){

function is(el, name){ return el.nodeName && el.nodeName.toUpperCase()==name.toUpperCase(); }

function isInput(e){
	if(!is(e[0],'input'))
		return false;
	var t=e.attr('type');
	return t=='text' || t=='button' || t=='submit';
}

function Nct(){}

Nct.prototype={
	init: function(jq, o){
		this.textArray=[];
		this.elements=[];
		this.options=o;
		this.jquery=jq;
		this.n=-1;
		if(o.async===true) o.async=2;
		
		jq=jq.not('script, ' + o.not);
		jq=jq.add( jq.find('*').not('script, ' + o.not) );//add all contents to jq
		if(o.not)
			jq=jq.not( $(o.not).find('*') );
		
		this.jq=jq;
		this.jql=this.jq.length;
		return this.process();

	},

	process: function(){
		this.n++;
		var that=this, o=this.options, text='', hasTextNode=false, hasChildNode=false, el=this.jq[this.n], e, c, ret;
		
		if(this.n==this.jql){
			ret=this.jquery.pushStack(this.elements);
			o.complete.call(ret, ret, this.textArray);
			
			if(o.returnAll===false && o.walk===false)
				return this.jquery;
			return ret;
		}
		
		if(!el)
			return this.process();
		e=$(el);

		if(typeof o.subject=='string')
			text=e.attr(o.subject);
		else{
			
			if(o.altAndVal && is(el,'img'))
				text=e.attr('alt');
			else if(o.altAndVal && isInput(e))
				text=e.val();
			else if(is(el,'textarea'))
				text=e.val();
			else{
				//check childNodes:
				c=el.firstChild;
				if(o.walk!==true)
					hasChildNode=true;
				else{
					while(c){
						if(c.nodeType==1){
							hasChildNode=true;
							break;
						}
						c=c.nextSibling;
					}
				}

				if(!hasChildNode)
					text=e.text();
				else{//check textNodes:
					if(o.walk!==true)
						hasTextNode=true;
					
					c=el.firstChild;
					while(c){
						if(c.nodeType==3 && c.nodeValue.match(/\S/) !== null){//textnodes with text
							if(c.nodeValue.match(/<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)>/) !== null){
								if(c.nodeValue.match(/(\S+(?=.*<))|(>(?=.*\S+))/) !== null){
									hasTextNode=true;
									break;
								}
							}else{
								hasTextNode=true;
								break;
							}
						}
						c=c.nextSibling;
					}

					if(hasTextNode){//remove child nodes from jq
						text=e.html().replace(/<script[^<]+<[^<]+>/,'');//removes scripts
						this.jq=this.jq.not(e.find('*'));
					}
				}
			}
		}

		if(!text)
			return this.process();
		this.elements.push(el);
		if(o.comments===false)
			text=this.stripComments(text);
		this.textArray.push(text);

		o.each.call(el, this.elements.length-1, el, text);
		
		if(o.async){
			setTimeout(function(){ that.process(); }, o.async);
			return this.jquery;
		}else
			return this.process();
		
	},

	stripComments: function(t){ return t.replace(/<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)>/g,''); }

}

$.fn.nodesContainingText=function(o){
	o=$.extend({}, defaults, $.fn.nodesContainingText.defaults, o);
	return new Nct().init(this, o);
}

var defaults={
	not: '',
	async: false,
	each: function(){},
	complete: function(){},
	comments: false,
	returnAll: true,
	walk: true,
	altAndVal:false,
	subject:true
}
	
$.fn.nodesContainingText.defaults=defaults;

})(jQuery);