
/**
 * gettext.js ( http://code.google.com/p/gettext-js/ )
 *
 * @author     Maxime Haineault, 2007 (max@centdessin.com)
 * @version    0.1.0
 * @licence    M.I.T
 * @example:
 *
 *   Gettext.lang = 'de';
 *   Gettext.gettext('hello %s','world');
 *   _('hello %s','world');
 *
 */
 
 
if(!Array.indexOf){
	Array.prototype.indexOf = function(obj){
		for(var i=0; i<this.length; i++){
			if(this[i]==obj){
				return i;
			}
		}
		return -1;
	}
}

/**
sprintf() for JavaScript 0.6

Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of sprintf() for JavaScript nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

**/

function str_repeat(i, m) {
	for (var o = []; m > 0; o[--m] = i);
	return o.join('');
}

function sprintf() {
	var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
	while (f) {
		if (m = /^[^\x25]+/.exec(f)) {
			o.push(m[0]);
		}
		else if (m = /^\x25{2}/.exec(f)) {
			o.push('%');
		}
		else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
			if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
				throw('Too few arguments.');
			}
			if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
				//throw('Expecting number but found ' + typeof(a));
			}
			switch (m[7]) {
				case 'b': a = a.toString(2); break;
				case 'c': a = String.fromCharCode(a); break;
				case 'd': 
					b = parseInt(a);
					if(b.toString() != 'NaN'){
						a = b	
					}
					break;
				case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
				case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
				case 'o': a = a.toString(8); break;
				case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
				case 'u': a = Math.abs(a); break;
				case 'x': a = a.toString(16); break;
				case 'X': a = a.toString(16).toUpperCase(); break;
			}
			a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
			c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
			x = m[5] - String(a).length - s.length;
			p = m[5] ? str_repeat(c, x) : '';
			o.push(s + (m[4] ? a + p : p + a));
		}
		else {
			throw('Huh ?!');
		}
		f = f.substring(m[0].length);
	}
	return o.join('');
}
 
function AssertException(message) { this.message = message; }
AssertException.prototype.toString = function () {
  return 'AssertException: ' + this.message;
};

function assert(exp, message) {
  if (!exp) {
    throw new AssertException(message);
  }
}


Function.prototype.binder = function(scope) {
  var proxied = this;
  return function() { proxied.apply(scope, arguments); };
}; 

if (typeof window.XMLHttpRequest == "undefined"){
  var XMLHttpRequest = function () {
    try { 
    	return new window.ActiveXObject("Msxml2.XMLHTTP.6.0"); 
    }
    catch (e1) {}
    try { 
    	return new window.ActiveXObject("Msxml2.XMLHTTP.3.0"); 
    }
    catch (e2) {}
    try {
    	return new window.ActiveXObject("Msxml2.XMLHTTP"); 
    }
    catch (e3) {}
    //Microsoft.XMLHTTP points to Msxml2.XMLHTTP.3.0 and is redundant
    throw new Error("This browser does not support XMLHttpRequest.");
  };
}

var langs = {};

langs.en = {};
langs.en.nplurals = 2;
langs.en.plural = function(n){ return (n != 1) ? 1 : 0};

langs.hr = {};
langs.hr.nplurals = 3;
langs.hr.plural = function(n){ return (n % 10 == 1 && n % 100 != 11) ? 0: ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) ? 1 : 2); };

assert(langs.hr.plural(1) === 0,'Failure 1');
assert(langs.hr.plural(2) == 1,'Failure 2');
assert(langs.hr.plural(5) == 2,'Failure 3');
//nplurals=3; 
//plural=
//n % 10 == 1 && n % 100 != 11 ?					// if n mod 10 == 1 && n mod 100 != 11: 
//0 								//   return 0
//:								// else:
//n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? //   if n mod 10 >= 2 && n mod 10 <= 4 && (n mod 100 < 10 || n mod 100 >=20 ):
//1 								//     return 1
//: 								//   else:
//2;								//     return 2

var JSGettext = function(){};
JSGettext.prototype = {
	
	initialize: function(lang) {
		this.lang         = lang || 'en';
		this.debug        = true;
		this.LCmessages   = {};
		this.links        = [];
		this.linksPointer = 0;
		this.currentFetch = false;
		
		this.logMessages = [];
		var linkels = document.getElementsByTagName('link');
		for(var i=0; i < linkels.length; i++){
			var link = linkels[i];
			if (link && link.rel == 'gettext' && link.href && link.lang){ 
				this.links = [link.lang, link.href];
				return;
			}
		}
		/* new PeriodicalExecuter(function(pe) {
  			if (Gettext.linksPointer == Gettext.links.length) pe.stop();
  			else {
  				Gettext.include.apply(Gettext, Gettext.links[Gettext.linksPointer])
  				Gettext.linksPointer++;
  			}
		}, 0.5); */
		
	},

	log: function() {
		if (this.debug) {
			if(typeof console != 'undefined' && console.log && this.debug && console.log.apply){
				console.log.apply(this, arguments);
			}
		}
	},
	
	include: function(lang, href) {
		if (arguments[1].substring(0,7) == 'http://' || arguments[1].substring(0,1) == '/') {
			this.currentFetch  = lang;
			this.log('Loading language: ', lang.toUpperCase(), ' (',href,')');
			var xmlhttp = new XMLHttpRequest();
			 xmlhttp.open("GET", arguments[1],false);
			 var th = this;
			 xmlhttp.send(null);
			  if (xmlhttp.readyState==4) {
			   this.include_complete(xmlhttp);
			  }

			//goog.net.XhrIo.send(arguments[1], this.include_complete.bind(this));
			/* new Ajax.Request(arguments[1], {
			        method:'GET',
				onSuccess: onCompleteCallback,
				asynchronous: false
			}); */
		}
		/* else {
			this.LCmessages[lang] = new jsGettext.Parse(str);
			Gettext.log('Loading language: ', lang.toUpperCase(), ' (',str,')');
		}
		 */
	},
	
	include_complete: function (t) {
			var responseText = t.responseText;
			this.log('Fetched language: ', this.currentFetch.toUpperCase(), ' ('+ responseText.length +' bytes)');
			try {
			    this.LCmessages[this.currentFetch] = this.parse(responseText);
			}catch(e){
			    this.log(e.message);
			}
			this.log(_('Parsed: %d msgids', this.LCmessages[this.currentFetch].message_ids.length));
				//, %d msgids-plurals, %d msgstrs, %d obsoletes, %d contexts, %d references %d flags, %d previous untranslateds, %d previous untranslated-plurals
			/* this.LCmessages[this.currentFetch].msgidplurals.length,
				this.LCmessages[this.currentFetch].msgstr.length,
				this.LCmessages[this.currentFetch].obsoletes.length,
				this.LCmessages[this.currentFetch].contexts.length,
				this.LCmessages[this.currentFetch].references.length,
				this.LCmessages[this.currentFetch].flags.length,
				this.LCmessages[this.currentFetch].previousUntranslateds.length,
				this.LCmessages[this.currentFetch].previousUntranslatedsPlurals.length) */
			this.currentFetch = false;
			
			this.log(this);
	},
	
	// This function based on public domain code. Feel free to take a look the original function at http://jan.moesen.nu/
	// ---
	// Changes made by Maxime Haineault (2007):
	// - The function is now extended to Strings instead (using prototype)
	// - The function now accept arrays as arguments, much easier to handle (using prototype)
	// - The function throw error if argument lenght doesn't equal matches count (as specified in gettex file format documentation)	
	// - Translations lookups
	// Reference: http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
	gettextn: function(untranslated_string, untranslated_string_plural, count) {
		var str = untranslated_string;
		
		if(!this.LCmessages[this.lang]){
			return str;
		}
		
		var plural_index = langs[this.lang].plural(count);
		var index = this.LCmessages[this.lang].message_ids.indexOf(str);
		
		if(index != -1){
			// found a match
			str = this.LCmessages[this.lang].messages[index].string;
			
			if(plural_index > str.length){ 
				throw "Plural index to big for translation";
			}
			str = str[plural_index];
		}
		else {
			// determine plural index using standard us english
			str = arguments[langs.en.plural(count)];
		}
		
		var hasTokens = str.match(/%[\d\w\-]+/g,'g');
		if (hasTokens && hasTokens.length != arguments.length-3) {
			this.log('Gettext error: Arguments count ('+ (arguments.length-3) +') does not match replacement token count ('+str.match(/%[\d\w\-]+/g,'g').length +').');
			return;
		}
		
		var args = [str];
		for(var i=3; i < arguments.length; i++){
		    args.push(arguments[i]);
		}
		
		try{
			return sprintf.apply(this, args);
		}
		catch(e){
			this.log('Gettext error:' + e);
			return str;
		}
	},
	gettext: function(untranslated_string) {
		var str = untranslated_string;
		
		if(!this.LCmessages[this.lang]){
			return str;
		}
		
		var index = this.LCmessages[this.lang].message_ids.indexOf(str);
		
		if(index != -1){
			str = this.LCmessages[this.lang].messages[index].string[0];
			
			
			if(typeof this.LCmessages[this.lang].messages[index].id_plural != 'undefined'){
				// TODO: check if there is any more indexes
				str = untranslated_string;
			}
			
		}
		
		var hasTokens = str.match(/%[\d\w\-]+/g,'g');
		if (hasTokens && hasTokens.length > arguments.length-1) {
			this.log('Gettext error: Arguments count ('+ (arguments.length-1) +') is less than replacement token count ('+str.match(/%[\d\w\-]+/g,'g').length +'). ' +str);
			return;
		}
		
		var args = [str];
		for(var i=1; i < arguments.length; i++){
		    args.push(arguments[i]);
		}
	
		try{
			return sprintf.apply(this, args);
		}
		catch(e){
			this.log('Gettext error:' + e);
			return str;
		}
	},
	
	parse: function(str) {
		// #  translator-comments
		// #. extracted-comments
		// #: reference...
		// #, flag...
		// #| msgid previous-untranslated-string
		// msgid untranslated-string
		// msgstr translated-string

		function cleanup(string){
			var string_len = string.length;
			var re_start = /^("+)/;
			var re_end = /("+)$/;
			string = string.replace(re_start,'');
			string = string.replace(re_end,'');
			/* if(string[0] == '"' || string[0] == "'"){
				if(string[string_len] == '"' || string[string_len] == "'"){
					string = string.substring(1, string_len-1);
				}
			} */
			return string;
		}
		
		var po        = str.split("\n");
		var output    = { };
		//header: [], contexts: [], msgid: [], msgidplurals: [], references: [], flags: [], msgstr: [], obsoletes: [], previousUntranslateds: [], previousUntranslatedsPlurals: [] 
		output.message_ids = {};
		
		output.messages = [];
		var STATE_NONE = 0;
		var STATE_COMMENT = 1;
		var STATE_MSGID = 2;
		var STATE_MSGID_PLURAL = 3;
		var STATE_MSGSTR = 4;
		
		var state = STATE_NONE;
		var current_message = {};
		current_message.string = [];
		var string = '';
		var current_state = 0;
		var currentstring = null;
		var re_msgid = /msgid\s+("[^"\\]*(\\.[^"\\]*)*")$/;
		var re_msgid_plural = /msgid_plural\s+("[^"\\]*(\\.[^"\\]*)*")$/;
		var re_msgstr = /msgstr(?:\[(\d+)\])?\s+("[^"\\]*(\\.[^"\\]*)*")$/;
		var re_multilinequote = /^\s*"(.*?)"$/;
		
		for(var x=0, cnt=po.length; x<cnt; ++x) {
			if (po[x].substring(0,1) == '#') {
				var prefix = po[x].substring(1,2);
				/* switch (prefix) {
					// translator-comments
						case ' ':
						// top comments
						if (curMsgid == 0) output.header.push(po[x]);
						break;

					// references
					case ':':
						if(typeof output.references[curMsgid] == 'undefined') output.references[curMsgid] = [];
						output.references[curMsgid].push(clean(po[x]));
						break;

					// msgid previous-untranslated-string
					case '|':
						if(typeof output.previousUntranslateds[curMsgid] == 'undefined') output.previousUntranslateds[curMsgid] = [];
						// previous-untranslated-string-plural
						if (po[x].substring(3,15) == 'msgid_plural') {
							output.previousUntranslateds[curMsgid].push(clean(po[x]));
						}
						else {
							output.previousUntranslatedsPlurals[curMsgid].push(clean(po[x]));
						}
						break;

					// flags
					case ',':
						if(typeof output.flags[curMsgid] == 'undefined') output.flags[curMsgid] = [];
						output.flags[curMsgid].push(clean(po[x]));
						break;

					// obsoletes
					case '~':
						if (po[x].substring(3,9) == 'msgid ') {
							curMsgid++;
							if(typeof output.msgid[curMsgid] == 'undefined') output.msgid[curMsgid] = [];
							output.msgid[curMsgid].push(clean(po[x]));
							output.obsoletes.push(curMsgid);
						}
						else if (po[x].substring(3,10) == 'msgstr ') {
							if(typeof output.msgstr[curMsgid] == 'undefined')    output.msgstr[curMsgid] = [];
							output.msgstr[curMsgid].push(clean(po[x]));
						}
						break;
				} */
				continue;
			}
			// blank line.. matches break
			// empty line signifies the end of a translation string
			if(po[x] === ""){
				// join an previous messages
				if(current_message.state == STATE_MSGSTR){
					current_message.string.push(current_message[STATE_MSGSTR].join(''));
				}
				// push out the message object
				if(typeof current_message.id != 'undefined'){
					delete current_message.state;
					delete current_message[STATE_NONE];
					delete current_message[STATE_MSGID];
					delete current_message[STATE_MSGSTR];
					delete current_message[STATE_MSGID_PLURAL];
				
					output.messages.push(current_message);
				}
				current_message = {'state':STATE_NONE};
				current_message.string = [];
			}
			
			// handle message ids
			if(po[x].match(re_msgid)){
				//this.log('matched msgid');
				string = cleanup(po[x].match(re_msgid)[1]);
				current_message.state = STATE_MSGID;
				current_message[STATE_MSGID] = [string];
			}
			
			// handle plural ids
			if(po[x].match(re_msgid_plural)){
				//this.log('matched msgid plural');
				// if previous state was msgid join it
				if(current_message.state == STATE_MSGID){
					var current_string = current_message[STATE_MSGID].join('');
					current_message.id = current_message[STATE_MSGID].join('');
					//this.log('Last message id: ---'+current_string+'---')
				}
				string = cleanup(po[x].match(re_msgid_plural)[1]);
				current_message.state = STATE_MSGID_PLURAL;
				current_message[STATE_MSGID_PLURAL] = [string];
			}
			
			// handle msgstr for singular and plural messages
			if(po[x].match(re_msgstr)){
				//this.log('matched msgstr', match);
				current_state = current_message.state;
				if(current_state == STATE_MSGID){
					current_string = current_message[STATE_MSGID].join('');
					current_message.id = current_message[STATE_MSGID].join('');
					//this.log('Last message id: ---'+current_string+'---')
				}
				else if(current_state == STATE_MSGID_PLURAL){
					current_string = current_message[STATE_MSGID_PLURAL].join('');
					current_message.id_plural = current_message[STATE_MSGID_PLURAL].join('');
					//this.log('Last message id plural: ---'+current_string+'---')
				} 
				else if(current_state == STATE_MSGSTR){
					current_message.string.push(current_message[STATE_MSGSTR].join(''));
					//this.log('Last message str: ---'+current_message[STATE_MSGSTR].join('')+'---')
				}
				/* 
				if(parseInt(match[1]) >= 0){
					this.log('handling msgstr with index number');
				}
				 */
				var match = po[x].match(re_msgstr);
				string = cleanup(match[2]);
				current_message.state = STATE_MSGSTR;
				current_message[STATE_MSGSTR] = [string];
			}
			
			// handle multiple lines
			if(po[x].match(re_multilinequote) && current_message.state !== 0){
				///this.log('matched multiline '+current_state);
				string = cleanup(po[x].match(re_multilinequote)[1]);
				current_state = current_message.state;
				current_message[current_state].push(string);
			}

			// context
			//if (po[x].substring(0,7) == 'msgctxt ') {
			//	if (!output.contexts[curMsgid]) output.contexts[curMsgid] = [];
			//	output.contexts[curMsgid].push(clean(po[x]));
			//}
		}
		
		output.message_ids = [];
		for(var i=0; i < output.messages.length; i++){
			output.message_ids.push(output.messages[i].id);
		}
		return output;
	}
};
var gettext = new JSGettext();

function _() {
    return gettext.gettext.apply(gettext,arguments); 
}

function _n() {
    return gettext.gettextn.apply(gettext,arguments); 
}
