// IEC16022 bar code generation library
//
// Ported to Javascript from a C implementation by 
// Adrian Kennard, Andrews & Arnold Ltd with help from Cliff Hones on the RS coding
// by Guido Sohne (JavaScript port, Firefox extension)
// 
// Copyright (C) Adrian Kennard & Cliff Hones
// Copyright (C) 2005 Guido Sohne
//
// This code is licensed under the GNU GPL Version 2.0

if (typeof(Semafox) == 'undefined') {
    Semafox = {
        E_ASCII:    0,
        E_C40:      1,
        E_TEXT:     2,
        E_X12:      3,
        E_EDIFACT:  4,
        E_BINARY:   5,
        E_MAX:      6,
        MAXBARCODE: 3116,
		Encoder: {
			Encoding: {},
			encoding: [], 
			encodingLength: 0,
			encodings: "ACTXEB",
			switchCost: 
            [
			// -> TO
			//  [A, C, T, X, E, B]			   // FROM <-
				[0, 1, 1, 1, 1, 2],            // A E_ASCII
				[1, 0, 2, 2, 2, 3],            // C E_C40
				[1, 2, 0, 2, 2, 3],            // T E_TEXT
				[1, 2, 2, 0, 2, 3],            // X E_X12
				[1, 2, 2, 2, 0, 3],            // E E_EDIFACT
				[0, 1, 1, 1, 1, 0],            // B E_BINARY
			]
		},
		ReedSolomon: {},
		Format: {},
		Firefox: {}
    }
};


// initializeGaloisField(polynomial) initialises the parameters for the Galois Field.
// The symbol size is determined from the highest bit set in poly
// This implementation will support sizes up to 30 bits (though that
// will result in very large log/antilog tables) - bit sizes of
// 8 or 4 are typical
//
// The poly is the bit pattern representing the GF characteristic
// polynomial.  e.g. for ECC200 (8-bit symbols) the polynomial is
// a**8 + a**5 + a**3 + a**2 + 1, which translates to 0x12d.

Semafox.ReedSolomon.initializeGaloisField = function(polynomial)
{
    // poly is an integer containing a bit pattern representing the polynomial
    gfpoly = polynomial;

    // Find the top bit, and hence the symbol size
    var m, b;
    for (b = 1, m = 0; b <= polynomial; b = b << 1)
        m++;
    b = b >> 1;
    m--;
    symsize = m;

    // Calculate the log/alog tables
    logmod = (1 << m) - 1;
    zlog = new Array(logmod + 1);
    alog = new Array(logmod);

    // initialize the log/alog tables
    var p, v;
    for (p = 1, v = 0; v < logmod; v++)
    {
        alog[v] = p;
        zlog[p] = v;
        p <<= 1;
        if (p & b)
            p ^= polynomial;
    }

};


// initializeEncodingParameters(chunks, polynomialIndex) 
// initialises the Reed-Solomon encoder
// chunks is the number of symbols to be generated for appending
// to the input data.  polynomialIndex is usually 1 - it is the index of
// the constant in the first term (i) of the RS generator polynomial:
// (x + 2**i)*(x + 2**(i+1))*...   [chunks terms]
// For ECC200, index is 1.

Semafox.ReedSolomon.initializeEncodingParameters = function(chunks, polynomialIndex)
{
    var i, k;
	
    rspoly = new Array(chunks + 1);
    rslen = chunks;
    rspoly[0] = 1;
	
    var i, k;
    for (i = 1; i <= chunks; i++)
    {
        rspoly[i] = 1;
        for (k = i - 1; k > 0; k--)
        {
            if (rspoly[k])
                rspoly[k] = alog[(zlog[rspoly[k]] + polynomialIndex) % logmod];
            rspoly[k] ^= rspoly[k - 1];
        }
        rspoly[0] = alog[(zlog[rspoly[0]] + polynomialIndex) % logmod];
        polynomialIndex++;
    }

};


// encodeArray returns a Reed-Solomon encoding of the
// input data (an array of bytes or integers) as a
// array of bytes or integers.

Semafox.ReedSolomon.encodeArray = function(byteArray)
{
    var i, k, m;
    var out = new Array(rslen);
    for (i = 0; i < rslen; i++)
        out[i] = 0;
    for (i = 0; i < byteArray.length; i++)
    {
        m = out[rslen - 1] ^ byteArray[i];
        for (k = rslen - 1; k > 0; k--)
        {
            if (m && rspoly[k])
                out[k] = out[k - 1] ^ alog[(zlog[m] + zlog[rspoly[k]]) % logmod];
            else
				out[k] = out[k - 1];
		}
        if (m && rspoly[0])
			out[0] = alog[(zlog[m] + zlog[rspoly[0]]) % logmod];
        else
			out[0] = 0;
    }

	return out;
};

Semafox.ReedSolomon.prototype = 
{
	gfpoly: 0,     // galois field polynomial
	symsize: 0,    // symbol size (in bits)
	logmod: 0,     // 2**symsize - 1
	zlog: 0,       // log table
	alog: 0,       // antilog table
	rspoly: 0,     // Reed Solomon polynomial
	rslen: 0,      // number of symbols to be generated using the RS polynomial
	initializeGaloisField: Semafox.ReedSolomon.initializeGaloisField,
	initializeEncodingParameters: Semafox.ReedSolomon.initializeEncodingParameters,
	encodeArray: Semafox.ReedSolomon.encodeArray
};

Semafox.ReedSolomon.makeEncoder = function(blockSize)
{
    var rs = Semafox.ReedSolomon.prototype;
    rs.initializeGaloisField(0x12d); // galois field polynomial for ECC 200
    rs.initializeEncodingParameters(blockSize, 1); // index is 1 for ECC 200
    return rs;
};

Semafox.Encoder.NewEncoding = function()
{
    var t = 
    {
		height: 0, 
		width: 0, 
		data: 0, 
		fw: 0, 
		bytes: 0, 
		datablocks: 0, 
		rsblocks: 0
    };

    return t;
};

Semafox.Encoder.UseEncoding = function(height, width, data, fw, bytes, datablocks, rsblocks)
{
    var t = Semafox.Encoder.NewEncoding();

    t.height = height;
    t.width = width;
    t.data = data;
    t.fw = fw;
    t.bytes = bytes;
    t.datablocks = datablocks;
    t.rsblocks = rsblocks;

    return t;
};

Semafox.Encoder.Encodings =
[
    Semafox.Encoder.UseEncoding(10, 10, 10, 10, 3, 3, 5),
    Semafox.Encoder.UseEncoding(12, 12, 12, 12, 5, 5, 7),
    Semafox.Encoder.UseEncoding(14, 14, 14, 14, 8, 8, 10),
    Semafox.Encoder.UseEncoding(16, 16, 16, 16, 12, 12, 12),
    Semafox.Encoder.UseEncoding(18, 18, 18, 18, 18, 18, 14),
    Semafox.Encoder.UseEncoding(20, 20, 20, 20, 22, 22, 18),
    Semafox.Encoder.UseEncoding(22, 22, 22, 22, 30, 30, 20),
    Semafox.Encoder.UseEncoding(24, 24, 24, 24, 36, 36, 24),
    Semafox.Encoder.UseEncoding(26, 26, 26, 26, 44, 44, 28),
    Semafox.Encoder.UseEncoding(32, 32, 16, 16, 62, 62, 36),
    Semafox.Encoder.UseEncoding(36, 36, 18, 18, 86, 86, 42),
    Semafox.Encoder.UseEncoding(40, 40, 20, 20, 114, 114, 48),
    Semafox.Encoder.UseEncoding(44, 44, 22, 22, 144, 144, 56),
    Semafox.Encoder.UseEncoding(48, 48, 24, 24, 174, 174, 68),
    Semafox.Encoder.UseEncoding(52, 52, 26, 26, 204, 102, 42),
    Semafox.Encoder.UseEncoding(64, 64, 16, 16, 280, 140, 56),
    Semafox.Encoder.UseEncoding(72, 72, 18, 18, 368, 92, 36),
    Semafox.Encoder.UseEncoding(80, 80, 20, 20, 456, 114, 48),
    Semafox.Encoder.UseEncoding(88, 88, 22, 22, 576, 144, 56),
    Semafox.Encoder.UseEncoding(96, 96, 24, 24, 696, 174, 68),
    Semafox.Encoder.UseEncoding(104, 104, 26, 26, 816, 136, 56),
    Semafox.Encoder.UseEncoding(120, 120, 20, 20, 1050, 175, 68),
    Semafox.Encoder.UseEncoding(132, 132, 22, 22, 1304, 163, 62),
    Semafox.Encoder.UseEncoding(144, 144, 24, 24, 1558, 156, 62),
    Semafox.Encoder.UseEncoding(0, 0, 0, 0, 0, 0, 0, 0, 0) // sentinel
]; 

// Annex M placement alogorithm low level

Semafox.Encoder.placeBit = function(array, NR, NC, r, c, p, b)
{
   if (r < 0)
   {
      r += NR;
      c += 4 - ((NR + 4) % 8);
   }
   if (c < 0)
   {
      c += NC;
      r += 4 - ((NC + 4) % 8);
   }

   array[r * NC + c] = (p << 3) + b;
}

Semafox.Encoder.placeBlock = function (array, NR, NC, r, c, p)
{
   Semafox.Encoder.placeBit(array, NR, NC, r - 2, c - 2, p, 7);
   Semafox.Encoder.placeBit(array, NR, NC, r - 2, c - 1, p, 6);
   Semafox.Encoder.placeBit(array, NR, NC, r - 1, c - 2, p, 5);
   Semafox.Encoder.placeBit(array, NR, NC, r - 1, c - 1, p, 4);
   Semafox.Encoder.placeBit(array, NR, NC, r - 1, c - 0, p, 3);
   Semafox.Encoder.placeBit(array, NR, NC, r - 0, c - 2, p, 2);
   Semafox.Encoder.placeBit(array, NR, NC, r - 0, c - 1, p, 1);
   Semafox.Encoder.placeBit(array, NR, NC, r - 0, c - 0, p, 0);
}

Semafox.Encoder.placeCornerA = function (array, NR, NC, p)
{
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, 0, p, 7);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, 1, p, 6);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, 2, p, 5);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 2, p, 4);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 1, p, 3);
   Semafox.Encoder.placeBit(array, NR, NC, 1, NC - 1, p, 2);
   Semafox.Encoder.placeBit(array, NR, NC, 2, NC - 1, p, 1);
   Semafox.Encoder.placeBit(array, NR, NC, 3, NC - 1, p, 0);
}

Semafox.Encoder.placeCornerB = function (array, NR, NC, p)
{
   Semafox.Encoder.placeBit(array, NR, NC, NR - 3, 0, p, 7);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 2, 0, p, 6);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, 0, p, 5);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 4, p, 4);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 3, p, 3);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 2, p, 2);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 1, p, 1);
   Semafox.Encoder.placeBit(array, NR, NC, 1, NC - 1, p, 0);
}

Semafox.Encoder.placeCornerC = function (array, NR, NC, p)
{
   Semafox.Encoder.placeBit(array, NR, NC, NR - 3, 0, p, 7);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 2, 0, p, 6);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, 0, p, 5);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 2, p, 4);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 1, p, 3);
   Semafox.Encoder.placeBit(array, NR, NC, 1, NC - 1, p, 2);
   Semafox.Encoder.placeBit(array, NR, NC, 2, NC - 1, p, 1);
   Semafox.Encoder.placeBit(array, NR, NC, 3, NC - 1, p, 0);
}

Semafox.Encoder.placeCornerD = function (array, NR, NC, p)
{
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, 0, p, 7);
   Semafox.Encoder.placeBit(array, NR, NC, NR - 1, NC - 1, p, 6);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 3, p, 5);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 2, p, 4);
   Semafox.Encoder.placeBit(array, NR, NC, 0, NC - 1, p, 3);
   Semafox.Encoder.placeBit(array, NR, NC, 1, NC - 3, p, 2);
   Semafox.Encoder.placeBit(array, NR, NC, 1, NC - 2, p, 1);
   Semafox.Encoder.placeBit(array, NR, NC, 1, NC - 1, p, 0);
}

// Annex M placement algorithm main function

Semafox.Encoder.place = function (array, NR, NC)
{
   var r, c, p;
   // invalidate
   for (r = 0; r < NR; r++)
      for (c = 0; c < NC; c++)
         array[r * NC + c] = 0;
   // start
   p = 1;
   r = 4;
   c = 0;
   do
   {
      // check corner
      if (r == NR && !c)
         Semafox.Encoder.placeCornerA (array, NR, NC, p++);
      if (r == NR - 2 && !c && NC % 4)
         Semafox.Encoder.placeCornerB (array, NR, NC, p++);
      if (r == NR - 2 && !c && (NC % 8) == 4)
         Semafox.Encoder.placeCornerC (array, NR, NC, p++);
      if (r == NR + 4 && c == 2 && !(NC % 8))
         Semafox.Encoder.placeCornerD (array, NR, NC, p++);
      // up/right
      do
      {
         if (r < NR && c >= 0 && !array[r * NC + c])
            Semafox.Encoder.placeBlock(array, NR, NC, r, c, p++);
         r -= 2;
         c += 2;
      }
      while (r >= 0 && c < NC);
      r++;
      c += 3;
      // down/left
      do
      {
         if (r >= 0 && c < NC && !array[r * NC + c])
            Semafox.Encoder.placeBlock(array, NR, NC, r, c, p++);
         r += 2;
         c -= 2;
      }
      while (r < NR && c >= 0);
      r += 3;
      c++;
   }
   while (r < NR || c < NC);
   // unfilled corner
   if (!array[NR * NC - 1])
      array[NR * NC - 1] = array[NR * NC - NC - 2] = 1;
}

// calculate and append ecc code, and if necessary interleave

Semafox.Encoder.rs = function (binary, bytes, datablock, rsblock)
{
   var blocks = Math.floor((bytes + 2) / datablock), b;
   var rs = Semafox.ReedSolomon.makeEncoder(rsblock);

   for (b = 0; b < blocks; b++)
   {
      var buf = [];
      var n, p = 0;
      for (n = b; n < bytes; n += blocks)
         buf.push(binary[n]);

      var enc = rs.encodeArray(buf);

      p = rsblock - 1;          // comes back reversed
      for (n = b; n < rsblock * blocks; n += blocks)
         binary[bytes + n] = enc[p--];
   }
}

Semafox.Encoder.check = function(character, range)
{
	if(character == null || character.length > 1)
		return false;
	if(range.indexOf(character) == -1)
		return false;
	return true;
}
// check if number is a digit, returns true or false

Semafox.Encoder.isDigit = function (number)
{
	return Semafox.Encoder.check(number, "1234567890");
}

// check if letter is lowercase, returns true or false

Semafox.Encoder.isLower = function (letter)
{
	return Semafox.Encoder.check(letter, "abcdefghijklmnopqrstuvwxyz");
}

// check if letter is uppercase, returns true or false

Semafox.Encoder.isUpper = function (letter)
{
	return Semafox.Encoder.check(letter, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}

// perform encoding for ecc200, source s len sl, to target t len tl, using optional encoding control string e
// return 1 if OK, 0 if failed. Does all necessary padding to tl

Semafox.Encoder.ecc200encode = function (t, tl, s, sl, e)
{
	var enc = 'a';              // start in TEXT encoding mode
   	var tp = 0, sp = 0;
   	if (e.encodinglength < sl)
   	{
      	alert("Encoding string too short");
      	return 0;
   	}
   	// do the encoding
   	while (sp < sl && tp < tl)
   	{
      	var newenc = enc;        // suggest new encoding
      	if (tl - tp <= 1 && (enc == 'c' || enc == 't') || tl - tp <= 2 && enc == 'x')
         	enc = 'a';             // auto revert to TEXT
		//debug("encoding: " + e.encoding);
		//debug("encoding[sp]: " + e.encoding[sp]);
      	newenc = e.encoding[sp].toLowerCase();
      	switch (newenc)
      	{                         // encode character
      		case 'c':                // C40
      		case 't':                // Text
      		case 'x':                // X12
         	{
            	var out = new Array(6), p = 0;
            	var en, s2 = "!\"#$%&'()*+,-./:;<=>?@[\\]_", s3 = "";
            	if (newenc == 'c')
            	{
               		en = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
               		s3 = "`abcdefghijklmnopqrstuvwxyz{|}~\177";
            	}
            	if (newenc == 't')
            	{
               		en = " 0123456789abcdefghijklmnopqrstuvwxyz";
               		s3 = "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\177";
            	}
            	if (newenc == 'x')
               		en = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\r*>";
            	do
            	{
               		var c = s.charAt(sp++), w;
               		if (c & 0x80)
               		{
                  		if (newenc == 'x')
                  		{
                     		alert("Cannot encode char in X12: " + c);
                     		return 0;
                  		}
                  	c = c & 0x7f;
                  	out[p++] = 1;
                  	out[p++] = 30;
               	}
               	w = en.indexOf(c);
               	if (w != -1)
                  	out[p++] = (w + 3) % 40;
               	else
               	{
                  	if (newenc == 'x')
                  	{
                     	alert("Cannot encode char in X12: " + c);
                     	return 0;
                  	}
                  	if (c < 32)
                  	{             // shift 1
                     	out[p++] = 0;
                     	out[p++] = c;
                  	} else
                  	{
                     	w = s2.indexOf(c);
                     	if (w != -1)
                     	{          // shift 2
                        	out[p++] = 1;
                        	out[p++] = w;
                     	} else
                     	{
                        	w = s3.indexOf(c);
                        	if (w != -1)
                        	{
                           		out[p++] = 2;
                           		out[p++] = w;
                        	} else
                        	{
                           		alert("Should not happen. Could not encode: " + c);
                           		return 0;
                        	}
                     	}
                  	}
               	}
               	if (p == 2 && tp + 2 == tl && sp == sl)
                  	out[p++] = 0; // shift 1 pad at end
               	while (p >= 3)
               	{
                  	var v = out[0] * 1600 + out[1] * 40 + out[2] + 1;
                  	if (enc != newenc)
                  	{
                     	if (enc == 'c' || enc == 't' || enc == 'x')
                        	t[tp++] = 254;  // escape C40/text/X12
                     	else if (enc == 'x')
                        	t[tp++] = 0x7C; // escape EDIFACT
                     	if (newenc == 'c')
                        	t[tp++] = 230;
                     	if (newenc == 't')
                        	t[tp++] = 239;
                     	if (newenc == 'x')
                        	t[tp++] = 238;
                     	enc = newenc;
                  	}
                  	t[tp++] = (v >> 8);
                  	t[tp++] = (v & 0xFF);
                  	p -= 3;
                  	out[0] = out[3];
                  	out[1] = out[4];
                  	out[2] = out[5];
               	}
            }
            while (p && sp < sl);
         }
         break;
      	case 'e':                // EDIFACT
        {
        	var out = new Array(4), p = 0;
            if (enc != newenc)
            {                   // can only be from C40/Text/X12
               	t[tp++] = 254;
               	enc = 'a';
            }
            while (sp < sl && e.encoding[sp].toLowerCase() == 'e' && p < 4)
               	out[p++] = s.charAt(sp++).charCodeAt(0);
            if (p < 4)
            {
               	out[p++] = 0x1F;
               	enc = 'a';
            }                   // termination
            t[tp] = ((s.charAt(0) & 0x3F) << 2);
            var ttp = tp++;
            t[ttp] = t[ttp] | ((s.charAt(1) & 0x30) >> 4);
            t[tp] = ((s.charAt(1) & 0x0F) << 4);
            if (p == 2)
               	tp++;
            else
            {
               	ttp = tp++;
               	t[ttp] = t[ttp] | ((s[2] & 0x3C) >> 2);
               	t[tp] = ((s.charAt(2) & 0x03) << 6);
               	ttp = tp++;
               	t[ttp] = t[ttp] | (s.charAt(3) & 0x3F);
            }
        }
        break;
      	case 'a':                // ASCII
        if (enc != newenc)
        {
        	if (enc == 'c' || enc == 't' || enc == 'x')
               	t[tp++] = 254;   // escape C40/text/X12
            else
               	t[tp++] = 0x7C;  // escape EDIFACT
         }
         enc = 'a';
         if (sl - sp >= 2 && Semafox.Encoder.isDigit (s.charAt(sp)) && Semafox.Encoder.isDigit (s.charAt(sp + 1)))
         {
            t[tp++] = (s.charAt(sp).charCodeAt(0) - '0'.charCodeAt(0)) * 10 + s.charAt(sp + 1).charCodeAt(0) - '0'.charCodeAt(0) + 130;
            sp += 2;
         } else if (s.charAt(sp).charCodeAt(0) > 127)
         {
            t[tp++] = 235;
            t[tp++] = s.charAt(sp++).charCodeAt(0) - 127;
         } else
            t[tp++] = s.charAt(sp++).charCodeAt(0) + 1;
         break;
      case 'b':                // Binary
         {
            var l = 0;          // how much to encode
            if (encoding)
            {
               var p;
               for (p = sp; p < sl && e.encoding[p].toLowerCase() == 'b'; p++)
                  l++;
            }
            t[tp++] = 231;      // base256
            if (l < 250)
               t[tp++] = l;
            else
            {
               t[tp++] = 249 + Math.floor(l / 250);
               t[tp++] = (l % 250);
            }
            while (l-- && tp < tl)
            {
               t[tp] = s.charAt(sp++).charCodeAt(0) + (((tp + 1) * 149) % 255) + 1;  // see annex H
               tp++;
            }
            enc = 'a';          // reverse to ASCII at end
         }
         break;
      default:
         alert("Unknown encoding: " + newenc);
         return 0;              // failed
      }
   }
   if (tp < tl && enc != 'a')
   {
      if (enc == 'c' || enc == 'x' || enc == 't')
         t[tp++] = 254;         // escape X12/C40/Text
      else
         t[tp++] = 0x7C;        // escape EDIFACT
   }
   if (tp < tl)
      t[tp++] = 129;            // pad
   while (tp < tl)
   {                            // more padding
      var v = 129 + (((tp + 1) * 149) % 253) + 1;       // see Annex H
      if (v > 254)
         v -= 254;
      t[tp++] = v;
   }
   if (tp > tl || sp < sl)
      return 0;                 // did not fit
   return tp;                 // OK
}


// Creates a encoding list (malloc)
// returns encoding string
// if lenp not null, target len stored
// if error, null returned
// if exact specified, then assumes shortcuts applicable for exact fit in target
// 1. No unlatch to return to ASCII for last encoded byte after C40 or Text or X12
// 2. No unlatch to return to ASCII for last 1 or 2 encoded bytes after EDIFACT
// 3. Final C40 or text encoding exactly in last 2 bytes can have a shift 0 to pad to make a tripple
// Only use the encoding from an exact request if the len matches the target, otherwise try again with exact=0

Semafox.Encoder.encodingList = function (l, s, exact)
{
	if (!l)
		return null;                // no length
	if (l > Semafox.MAXBARCODE)
		return null;                 // not valid

	//debug("l: " + l);
	//debug("s: " + s);
	var enc = new Array(Semafox.MAXBARCODE);
	for(var m = 0; m < enc.length; m++) {
		enc[m] = new Array(Semafox.E_MAX); 
    	for(var n = 0; n < enc[m].length; n++) {
			//enc[m][n] = new Semafox.Encoder.EncodingListItem(0, 0);
			enc[m][n] = {s:0, t:0};
		}
    }

	var elist = "", e;
	var p = l;   
	while (p--)
	{
		//debug("p: " + p);
		var b = 0, sub, sl, tl, bl, t;
		// consider each encoding from this point
      	// ASCII
      	sl = tl = 1;
      	if (Semafox.Encoder.isDigit (s.charAt(p)) && p + 1 < l && Semafox.Encoder.isDigit (s.charAt(p + 1)))
		{
			//debug("double digit");
        	sl = 2;                // double digit
		}
      	else if (s.charAt(p) & 0x80) {
			//debug("high shifted");
        	tl = 2;                // high shifted
		}
      	bl = 0;
      	if (p + sl < l)
        	for (e = 0; e < Semafox.E_MAX; e++)
            	if (enc[p + sl][e].t 
					&& ((t = enc[p + sl][e].t + Semafox.Encoder.switchCost[Semafox.E_ASCII][e]) < bl 
					|| !bl))
            	{
					//debug("1 bl: " + bl + "b: " + b);
               		bl = t;
               		b = e;
            	}
      	enc[p][Semafox.E_ASCII].t = tl + bl;
      	enc[p][Semafox.E_ASCII].s = sl;
		//debug("eascii t: " + enc[p][Semafox.E_ASCII].t + " s: " + enc[p][Semafox.E_ASCII].s)
      	if (bl && b == Semafox.E_ASCII)
        	enc[p][b].s += enc[p + sl][b].s;
      	// C40
      	sub = tl = sl = 0;
      	do
      	{
			var psl = p + sl++;
			var c = s.charAt(psl);
			//debug("s: " + s);
			//debug("psl: " + psl);
			//debug("c40: " + c);
         	if (c & 0x80)
         	{                      // shift + upper
            	sub += 2;
            	c &= 0x7F;
				//debug("shift + upper: " + c);
         	}
         	if (c != ' ' 
				&& !Semafox.Encoder.isDigit (c) 
				&& !Semafox.Encoder.isUpper (c))
			{
            	sub++;              // shift
				//debug("shift");
			}
         	sub++;
         	while (sub >= 3)
         	{
            	sub -= 3;
            	tl += 2;
         	}
      	} while (sub && p + sl < l);
      	
      	if (exact && sub == 2 && p + sl == l)
      	{                         // special case, can encode last block with shift 0 at end (Is this valid when not end of target buffer?)
        	sub = 0;
			tl += 2;
	        //debug("special case");
      	}
      	if (!sub)
      	{                         // can encode C40
			//debug("c40");
			bl = 0;
         	if (p + sl < l)
			{
            	for (e = 0; e < Semafox.E_MAX; e++)
				{
               		if (enc[p + sl][e].t 
						&& ((t = enc[p + sl][e].t + Semafox.Encoder.switchCost[Semafox.E_C40][e]) < bl 
						|| !bl))
               		{
                  		bl = t;
                  		b = e;
						//debug("2 bl: " + bl + " b: " + b);
             		}
				}
			}
         	if (exact && enc[p + sl][Semafox.E_ASCII].t == 1 && 1 < bl)
         	{                      // special case, switch to ASCII for last bytes
            	bl = 1;
            	b = Semafox.E_ASCII;
				//debug("ascii last bytes");
         	}
         	enc[p][Semafox.E_C40].t = tl + bl;
         	enc[p][Semafox.E_C40].s = sl;
         	if (bl && b == Semafox.E_C40)
            	enc[p][b].s += enc[p + sl][b].s;
			//debug("c40: b: " + b + " s: " + enc[p][b].s + " t: " + enc[p][b].t);
     	}
      	// Text
      	sub = tl = sl = 0;
      	do
      	{
			var c = s.charAt(p + sl++);
	 		//debug("text: " + c);
         	if (c & 0x80)
         	{                      // shift + upper
            	sub += 2;
            	c &= 0x7F;
		 		//debug("shift+upper: " + c);
         	}
         	if (c != ' ' 
				&& !Semafox.Encoder.isDigit (c) 
				&& !Semafox.Encoder.isLower (c))
			{
            		sub++;              // shift
			 		//debug("shift: " + sub);
			}
         	sub++;
         	while (sub >= 3)
         	{
            	sub -= 3;
            	tl += 2;
         	}
      	} while (sub && p + sl < l);
      	if (exact && sub == 2 && p + sl == l)
      	{                         // special case, can encode last block with shift 0 at end (Is this valid when not end of target buffer?)
         	sub = 0;
         	tl += 2;
	 		//debug("shift 0 at end");
      	}
      	if (!sub && sl)
      	{                         // can encode Text
	 		//debug("encode text");
        	bl = 0;
         	if (p + sl < l) {
            	for (e = 0; e < Semafox.E_MAX; e++) {
               		if (enc[p + sl][e].t 
						&& ((t = enc[p + sl][e].t + Semafox.Encoder.switchCost[Semafox.E_TEXT][e]) < bl 
						|| !bl))
               		{
                  		bl = t;
                  		b = e;
				 		//debug("3 bl: " + bl + " b: " + b);
               		}
				}
			}
         	if (exact && enc[p + sl][Semafox.E_ASCII].t == 1 && 1 < bl)
         	{                      // special case, switch to ASCII for last bytes
            	bl = 1;
            	b = Semafox.E_ASCII;
		 		//debug("ascii last bytes");
         	}
         	enc[p][Semafox.E_TEXT].t = tl + bl;
         	enc[p][Semafox.E_TEXT].s = sl;
         	if (bl && b == Semafox.E_TEXT)
            	enc[p][b].s += enc[p + sl][b].s;
			//debug("text: b: " + b + " s: " + enc[p][b].s + " t: " + enc[p][b].t);
      	}
      	// X12
      	sub = tl = sl = 0;
      	do
      	{
			var c = s.charAt(p + sl++);
			//debug("x12: " + c);
         	if (c != 13 
				&& c != '*' 
				&& c != '>' 
				&& c != ' ' 
	     		&& !Semafox.Encoder.isDigit (c) 
				&& !Semafox.Encoder.isUpper (c))
         	{
            	sl = 0;
				//debug("lowercase");
            	break;
         	}
         	sub++;
         	while (sub >= 3)
         	{
            	sub -= 3;
            	tl += 2;
         	}
      	} while (sub && p + sl < l);
      	if (!sub && sl)
      	{                         // can encode X12
			//debug("can encode x12");
         	bl = 0;
         	if (p + sl < l)
            	for (e = 0; e < Semafox.E_MAX; e++)
               		if (enc[p + sl][e].t 
						&& ((t = enc[p + sl][e].t + Semafox.Encoder.switchCost[Semafox.E_X12][e]) < bl 
						|| !bl))
               		{
                  		bl = t;
                  		b = e;
				 		//debug("4 bl: " + bl + " b: " + b);
               		}
         	if (exact && enc[p + sl][Semafox.E_ASCII].t == 1 && 1 < bl)
         	{                      // special case, switch to ASCII for last bytes
            	bl = 1;
            	b = Semafox.E_ASCII;
				//debug("ascii last bytes");
         	}
         	enc[p][Semafox.E_X12].t = tl + bl;
         	enc[p][Semafox.E_X12].s = sl;
         	if (bl && b == Semafox.E_X12)
            	enc[p][b].s += enc[p + sl][b].s;
			//debug("x12: b: " + b + " s: " + enc[p][b].s + " t: " + enc[p][b].t);
      	}
      	// EDIFACT
		//debug("edifact");
      	sl = bl = 0;
      	if (s.charAt(p + 0) >= 32 && s.charAt(p + 0) <= 94)
      	{                         // can encode 1
			//debug("can encode 1");
			var bs = 0;
         	if (p + 1 == l && (!bl || bl < 2))
         	{
            	bl = 2;
            	bs = 1;
         	} else {
            	for (e = 0; e < Semafox.E_MAX; e++) {
               		if (e != Semafox.E_EDIFACT 
						&& enc[p + 1][e].t 
						&& ((t = 2 + enc[p + 1][e].t + Semafox.Encoder.switchCost[Semafox.E_ASCII][e]) < bl 
						|| !bl))       // E_ASCII as allowed for unlatch
               		{
                  		bs = 1;
                  		bl = t;
                  		b = e;
						//debug("5 bl: " + bl + " b: " + b);
               		}
				}
			}
         	if (p + 1 < l && ss.charAt(p + 1) >= 32 && s.charAt(p + 1) <= 94)
         	{                      // can encode 2
            	//debug("can encode 2");
				if (p + 2 == l && (!bl || bl < 2))
            	{
               		bl = 3;
               		bs = 2;
            	} else {
               		for (e = 0; e < Semafox.E_MAX; e++) {
                  		if (e != Semafox.E_EDIFACT 
							&& enc[p + 2][e].t 
								&& ((t = 3 + enc[p + 2][e].t + Semafox.Encoder.switchCost[Semafox.E_ASCII][e]) < bl 
								|| !bl))    // E_ASCII as allowed for unlatch
                  		{
                     		bs = 2;
                     		bl = t;
                     		b = e;
							//debug("6 bl: " + bl + " b: " + b);
                  		}
					}
				}
            	if (p + 2 < l && s.charAt(p + 2) >= 32 && s.charAt(p + 2) <= 94)
            	{                   // can encode 3
					//debug("can encode 3");
               		if (p + 3 == l && (!bl || bl < 3))
               		{
                  		bl = 3;
                  		bs = 3;
               		} else
                  		for (e = 0; e < Semafox.E_MAX; e++)
                     		if (e != Semafox.E_EDIFACT 
								&& enc[p + 3][e].t 
								&& ((t = 3 + enc[p + 3][e].t + Semafox.Encoder.switchCost[Semafox.E_ASCII][e]) < bl 
								|| !bl)) // E_ASCII as allowed for unlatch
                     		{
                        		bs = 3;
                        		bl = t;
                        		b = e;
								//debug("7 bl: " + bl + " b: " + b);
                     		}
               		if (p + 4 < l && s.charAt(p + 3) >= 32 && s.charAt(p + 3) <= 94)
               		{                // can encode 4
						//debug("can encode 4");
                  		if (p + 4 == l && (!bl || bl < 3))
                  		{
                     		bl = 3;
                     		bs = 4;
                  		} else {
                     		for (e = 0; e < Semafox.E_MAX; e++)
                        		if (enc[p + 4][e].t 
									&& ((t = 3 + enc[p + 4][e].t + Semafox.Encoder.switchCost[Semafox.E_EDIFACT][e]) < bl 
									|| !bl))
                        		{
                           			bs = 4;
                           			bl = t;
                           			b = e;
									//debug("8 bl: " + bl + " b: " + b);
                        		}
                     		if (exact 
								&& enc[p + 4][Semafox.E_ASCII].t 
								&& enc[p + 4][Semafox.E_ASCII].t <= 2 
								&& (t = 3 + enc[p + 4][Semafox.E_ASCII].t) < bl)
                     		{          // special case, switch to ASCII for last 1 ot two bytes
                        		bs = 4;
                        		bl = t;
                        		b = Semafox.E_ASCII;
								//debug("9 bl: " + bl + " b: " + b);
                     		}
                  		}
               		}
            	}
         	}
         	enc[p][Semafox.E_EDIFACT].t = bl;
         	enc[p][Semafox.E_EDIFACT].s = bs;
         	if (bl && b == Semafox.E_EDIFACT)
            	enc[p][b].s += enc[p + bs][b].s;
			//debug("edifact: b: " + b + " s: " + enc[p][b].s + " t: " + enc[p][b].t);
      	}
      	// Binary
		//debug("binary");
      	bl = 0;
      	for (e = 0; e < Semafox.E_MAX; e++) {
        	if (enc[p + 1][e].t
            	&& ((t = enc[p + 1][e].t + Semafox.Encoder.switchCost[Semafox.E_BINARY][e] 
					+ ((e == Semafox.E_BINARY && enc[p + 1][e].t == 249) ? 1 : 0)) < bl 
				|| !bl))
         	{
            	bl = t;
            	b = e;
				//debug("10 bl: " + bl + " b: " + b);
         	}
		}
   		enc[p][Semafox.E_BINARY].t = 1 + bl;
   		enc[p][Semafox.E_BINARY].s = 1;
   		if (bl && b == Semafox.E_BINARY)
      		enc[p][b].s += enc[p + 1][b].s;
		//debug("binary: b: " + b + " s: " + enc[p][b].s + " t: " + enc[p][b].t);
	}
   	var encoder = {};
   	var encoding = new Array(l + 1);
   	p = 0;
   	{
		var cur = Semafox.E_ASCII;
      	while (p < l)
      	{
			var t, m = 0;
			var b = 0;
         	for (e = 0; e < Semafox.E_MAX; e++)
			{
				//debug("et: " + enc[p][e].t + " m: " + enc[p][b].s + " sc: " + Semafox.Encoder.switchCost[cur][e]);
            	if (enc[p][e].t 
					&& ((t = enc[p][e].t + Semafox.Encoder.switchCost[cur][e]) < m 
						|| t == m 
						&& e == cur 
						|| !m))
            	{
               		b = e;
               		m = t;
					//debug("b: " + b + " m: " + m);
            	}
			}
         	cur = b;
			//debug("m: " + enc[p][b].s);
         	m = enc[p][b].s;
         	if(!p)
            	encoder.encodingLength = enc[p][b].t;
         	while (p < l && m--)
			{
				//debug("e: " + Semafox.Encoder.encodings.charAt(b) + " b: " + b);
            	encoding[p++] =  Semafox.Encoder.encodings.charAt(b);
			}
      	}
	}
	
   	encoding[p] = 0;
   	encoder.encoding = encoding;

   	return encoder;
}

// Main encoding function
// Returns the grid containing the matrix. L corner at 0,0.
// Takes the string to be encoded

Semafox.Encoder.encode = function (barcode)
{
    var W = 0, H = 0, grid = "", binary = new Array(4096);    
    var encoded = Semafox.Encoder.NewEncoding();
    var matrix = Semafox.Encoder.NewEncoding();		
	var encoding = Semafox.Encoder.encodingList (barcode.length, barcode, false);
	
	encoded.valid = false;
	
	if(!encoding) // there's nothing to encode ...
		return encoded;
	//debug("encoding: " + encoding);
	//debug("encodingLength: " + encoding.encodingLength);
	for(var i=0; i < Semafox.Encoder.Encodings.length; i++)
	{
		matrix = Semafox.Encoder.Encodings[i];
		if(matrix.bytes > encoding.encodingLength)
			break;
	}

	if (matrix.width == 0)
	{
		alert("Cannot find suitable size, url is too long!");
		return encoded;
	}

	W = matrix.width;
	H = matrix.height;

	//alert("encoding: " + encoding.encoding + " length: " + encoding.encodingLength);
	
	if (Semafox.Encoder.ecc200encode (binary, matrix.bytes, barcode, barcode.length, encoding) == 0)
	{
		alert("URL too long for " + W + "x" + H);
		return encoded;
	}

	// add error checking using ReedSolomon
	Semafox.Encoder.rs (binary, matrix.bytes, matrix.datablocks, matrix.rsblocks);
	
	// block placement
	{                 
		var x, y, NC, NR, places;
		NC = W - 2 * Math.floor(W / matrix.fw);
		NR = H - 2 * Math.floor(H / matrix.data);
		places = new Array(NC * NR);
		Semafox.Encoder.place (places, NR, NC);
		grid = new Array(W * H);
		for (y = 0; y < H; y += matrix.data)
		{
			for (x = 0; x < W; x++)
				grid[y * W + x] = 1;
			for (x = 0; x < W; x += 2)
				grid[(y + matrix.data - 1) * W + x] = 1;
	   	}
       	for (x = 0; x < W; x += matrix.fw)
	   	{
			for (y = 0; y < H; y++)
				grid[y * W + x] = 1;
			for (y = 0; y < H; y += 2)
				grid[y * W + x + matrix.fw - 1] = 1;
		}
       	for (y = 0; y < NR; y++)
	   	{
			for (x = 0; x < NC; x++)
			{	
				var v = places[(NR - y - 1) * NC + x];
				if (v == 1 || v > 7 && (binary[(v >> 3) - 1] & (1 << (v & 7))))
			   		grid[(1 + y + 2 * Math.floor(y / (matrix.data - 2))) * W + 1 + x + 2 * Math.floor(x / (matrix.fw - 2))] = 1;
		   	}
	   	}

	}
	
   	encoded.width = W;
   	encoded.height = H;
   	encoded.data = grid;
   	encoded.encoding = encoding;
   	encoded.bytes = matrix.bytes;
   	encoded.ecc = Math.floor((matrix.bytes + 2) / matrix.datablock) * matrix.rsblock;

	return encoded;

}

//
// makes a hash table from the URL parameters
//

Semafox.Parameters = function(params)
{
	// FIXME: embedding & in urls breaks this function
	var plist = params.split('&');
	var phash = {};
	for(var i=0; i < plist.length; i++) {
		var psplit = plist[i].split('=');
		if(psplit.length != 2) continue;
		var p;
		p = unescape(psplit[1]);
		p.replace('/\+/g', ' ');
		var q;
		q = unescape(psplit[0]);
		q.replace('/\+/g', ' ');
		phash[q] = p;
	}
	if(!phash.title) phash.title = "";
	if(!phash.url) phash.url = "";
	return phash;
}

//
//	Each of the Semafox.Format functions takes a semacode 'object'. This
//	object is a hash table as follows:-
//
//		var semacode = {
//			url: "http://www.semacode.org",
//			title: "semacode.org",
//			size: 20 // used in Semafox.Format.Table and Semafox.Format.Css to determine the cell size
//			style: 'css' // used in Semafox.Format.Dom to select between SVG, CSS and HTML table based layout
//          use_images: 1 // used in Semafox.Format.Table and Semafox.Format.Css to force image based display
//		};
//
// The Semafox.Format.* functions encode a symbol for the url of the semacode and return 
// data appropriate for display in that format. Using images forces the semacode size to 16px.
//
// Semafox.Format.Css: Returns a series of nested divs, that will, in conjunction 
// with the semafox.css stylesheet, display the symbol using CSS layout
//
// Semafox.Format.Table: Returns an HTML table. The semacode object that you pass to this function
// must have the cell size set, as in the example above. If the cell size is not set, it defaults 
// to 16.
//
// Semafox.Format.Svg: Returns an SVG representation of the semacode symbol
// 
// Semafox.Format.Dom: Changes the contents of 'magic' objects within your document in order to
// place the semacode symbol and title into your layout. You need to set css as true in the 
// semacode object that you are passing to this function if you want CSS based layout. The default
// is to use a HTML table layout. The 'magic' objects are HTML tags with id of 'semacode.title' or
// 'semacode.symbol' respectively. 
//
// Each of these functions returns an 'object', which is really just another hash table, that
// contains information about the semacode it has formatted. This is what it looks like:-
//
// 		var format = { title:title, url:url, width:width, height:height, data:data}
//
// The width and height refer to the number of columns and rows of the semacode symbol that
// was generated from the url. The data is a format specific representation of the symbol.
//

Semafox.Format.Css = function(semacode)
{
    if(semacode.use_images) semacode.size = 16;
	var barcode = Semafox.Encoder.encode(semacode.url); 
	var width = barcode.width;
	var height = barcode.height;	
	var size = semacode.size;
	var repr = '<div class="semacode" style="width: ' + width * size + 'px;">\n';
	var black = "<img src='black.png' width='16' height='16'/>";
	var white = "<img src='white.png' width='16' height='16'/>";

	if(!semacode.use_images) black = white = '';

	for(var i=height; i > 0; i--) {
		repr += '\t<div class="semacodeRow">\n\t\t';
		for(var j=0; j < width; j++)
			if(barcode.data[(i - 1) * height + j])
				repr += '<div class="semacodeBlack">' + black + '<\/div>';
			else
				repr += '<div class="semacodeWhite">' + white + '<\/div>';		
		repr += '\n\t<\/div>\n';
	}
	
	repr += '<\/div>\n';
		
	return {title:semacode.title, url:semacode.url, width:width, height:height, data:repr};
}

Semafox.Format.Table = function(semacode)
{
    if(semacode.use_images) semacode.size = 16;    
	var barcode = Semafox.Encoder.encode(semacode.url); 
	var width = barcode.width;
	var height = barcode.height;	
	var blockSize = semacode.size;
	if(!blockSize) blockSize = 18;		
	var repr = '<table cellspacing="0" cellpadding="0" border="0" width="' + (width * blockSize) + '" height="' + (height * blockSize) + '">';
	var black = "<img src='/files/semafox/black.png' width='16' height='16'/>";
	var white = "<img src='/files/semafox/white.png' width='16' height='16'/>";

	if(!semacode.use_images) black = white = '';
		
	for(var i=height; i > 0; i--) {
		repr += '<tr height="' + blockSize + '">';
		for(var j=0; j < width; j++)
			if(barcode.data[(i - 1) * height + j])
				repr += '<td bgcolor="black" width="' + blockSize + '" height="' + blockSize + '">' + black + '<\/td>';
			else
				repr += '<td bgcolor="white" width="' + blockSize + '" height="' + blockSize + '">' + white + '<\/td>';		
		repr += '<\/tr>';
	}
	
	repr += '<\/table>';
	
	return {title:semacode.title, url:semacode.url, width:width, height:height, data:repr};
}

Semafox.Format.Svg = function(semacode)
{    
	var barcode = Semafox.Encoder.encode(semacode.url);
	var width = barcode.width;
	var height = barcode.height;
	var bs = 8;
	var size = bs * width;
	
	var repr = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"' 
				+ ' viewBox="0 0 ' + size + ' ' + size + '"' 
			 	+ ' preserveAspectRatio="meet">'; 
				
	for(var j=0; j < height; j++)
		for(var i=0; i < width; i++)
			if(barcode.data[j * width + i])
				repr += '<rect x="' + i * bs + '" y="' + (size - ((j + 1) * bs)) + '" width="' + bs + '" height="' + bs + '" style="fill: black; stroke: black;"\/>';
			else
				repr += '<rect x="' + i * bs + '" y="' + (size - ((j + 1) * bs)) + '" width="' + bs + '" height="' + bs + '" style="fill: white; stroke: white;"\/>';

	repr += '<\/svg>';
	
	return {title:semacode.title, url:semacode.url, width:width, height:height, data:repr};
}

Semafox.Format.Dom = function(semacode) 
{	
	if(!semacode)	
		semacode = Semafox.Parameters(location.search.substr(1));	
	
	var format;
	switch(semacode.style)
	{
		case "css": 
			format = Semafox.Format.Css(semacode);
			break;
		case "table":
			format = Semafox.Format.Table(semacode);
			break;
		case "svg":
			format = Semafox.Format.Svg(semacode);
			break;
		default:
			format = Semafox.Format.Css(semacode);
			break;			
	}
	
	document.getElementById('semacode.symbol').innerHTML = format.data;					
	
	return format;
}

//
// The Semafox.Firefox.* functions are for integration into the Firefox UI
//

Semafox.Firefox.Show = function() 
{
	var params = Semafox.Parameters(location.search.substr(1));			
	var src = "chrome://semafox/content/semafox.html?title=" 
		+ escape(params['title']) + "&url=" + escape(params['url']);	
	document.getElementById('semafox.iframe').setAttribute('src', src);
}

Semafox.Firefox.Load = function(title, url) 
{
	var src = 'chrome://semafox/content/semafox.xul?title=' + escape(title) + '&url=' + escape(url); 
	open(src,'semafox','chrome,minimizable,resizable,width=400,height=400');
}

//
// glue functions
// to help with integration into web applications
//

Semafox.Firefox.Relink = function ()
{
  var url = document.getElementById('semafox_url').value;
  var title = document.getElementById('semafox_title').value;

  document.getElementById('semafox_modern').innerHTML = 
    "\n<a href='" + '/projects/semafox/formats/xhtml-css?hide=1&amp;title=' + title + '&amp;url=' + url + "'>" + "Click here (or copy this link) for your semacode." + "<\/a>";
  document.getElementById('semafox_legacy').innerHTML = 
    "\n<a href='" + '/projects/semafox/formats/html?hide=1&amp;title=' + title + '&amp;url=' + url + "'>" + "Older" + "<\/a>";
  document.getElementById('semafox_offline').innerHTML = 
    "\n<a href='" + '/projects/semafox/formats/offline-version?hide=1&amp;title=' + title + '&amp;url=' + url + "'>" + "Offline" + "<\/a>";
}

Semafox.Firefox.Redirect = function(where)
{
  var url = document.getElementById('semafox_url').value;
  var title = document.getElementById('semafox_title').value;
  document.location = '/projects/semafox/formats/' + where + '?hide=1&amp;title=' + title + '&amp;url=' + url;
}