/*
The contents of this file are (C) Paul Stephens 2005. All Rights reserved. 
You may use this software free of charge on non profit-making websites, provided that this
copyright notice is left intact.

This software must not be used on revenue-generating websites, or distributed with a commerical
package, except by agreement with the copyright holder.

For details contact paul@paulspages.co.uk.

Version 1.2.1 29th March 2005
*/

function bgd_parseMat(y, matFileName, gameNo) {
if (gameNo == null) {gameNo = 0}
if (matFileName == null || matFileName == "") {
	alert("bgd_parseMat error - no .mat file name supplied")
	return
}
var i, gameLines = new Array(), op = "", gameCount = 0, matchData = ""
var p1, p2, n1 = "", n2 = "", x, m, gameBlock, blockStart, blockEnd, nameLine

rawdata = new Array()
rawdata = y.split("\n")
for (i = 0; i < rawdata.length; i++) {
	rawdata[i] = bg_replace(rawdata[i], "\r", "")
}
// remove blank lines and ; commment lines
lines = new Array()
for (i = 0; i < rawdata.length; i++) {
	if (rawdata[i].substr(0,1) != ";" && bg_btrim(rawdata[i]) != "")  {
		lines[lines.length] = rawdata[i]
	}
}

gameLines[0] = 0
for (i = 0; i < lines.length; i++) {
	if (lines[i].substr(0,6) == " Game ") {
		gameLines[gameLines.length] = i
	} 
}
gameCount = (gameLines.length - 1)
if (gameCount == 1) {gameNo = 1} // Skip menu for 1-game matches
if (gameNo < 1 || gameNo > gameCount) {gameNo = 0}
i = bgd_findLine(lines, ":") // Contains player names
if (i > 0) { 
	m = lines[i]
	p1 = bg_btrim(m.substr(0, 31))
	p2 = bg_btrim(m.substring(32))
	if (p1.indexOf(":") >= 0) { // .mat file - name field contains match score
		n1 = bg_btrim(p1.substr(0, p1.indexOf(":")))
	} 
	if (p2.indexOf(":") >= 0) { // .mat file - name field contains match score
		n2 = bg_btrim(p2.substr(0, p2.indexOf(":")))
	} 
}

// When the gameNo parameter is zero, generate a menu page
// When the gameNo parameter is non-zero, extract the relevant game and 
// construct a script for it.

/* In either case, precede the menu/game with a matchdata element, as follows:
0 - game no. (1 to matchlength) to play in this page OR 0 = use this match fle as a menu
1 - match file name
2 - parent menu file name
3 - match title
4 - Match description (e.g. "Match to 7 points"
5 onwards - game file names
*/
matchData = gameNo.toString() + "::" +	// Game no
			matFileName + "::" +		// match file name
			"::" +						// parent menu file name (not used for .mat files)
			n1 + " vs " + n2 + "::" +	// match title
			bg_btrim(lines[0])			// match description
for (i = 1; i <= gameCount; i++) {
	matchData += "::" + matFileName
}
op = "[game][matchdata]" + matchData + "[/matchdata]\n"
if (gameNo == 0) {
	// Nothing more to do - parseData will generate the menu page script
	op += "[/game]"
	return op
}

// OK - find the gameplay for this game, generate the opening game tags, then extract the gameplay
op += "[pagetitle " + n1 + " vs " + n2 + "]\n"
i = gameLines[gameNo]  // Find the "Game" line for this game
nameLine = bgd_findLine(lines, ":", null, i)
if (nameLine > 0 ) {
	m = lines[nameLine]
	p1 = bg_btrim(m.substr(0, 31))
	p1 = p1.substr(0, p1.indexOf(":")) + " " + p1.substring(p1.indexOf(":") + 1)
	p2 = bg_btrim(m.substring(32))
	p2 = p2.substr(0, p2.indexOf(":")) + " " + p2.substring(p2.indexOf(":") + 1)
	op += "[gametitle " + p1 + ", " + p2 + "]\n"
}
op += "[playername1 " + n1 + "][playername2 " + n2 + "]\n"
blockStart = bgd_findLine(lines, ")", null, i)
if (blockStart < 1) {
	alert("Error in parseMat - no gameplay lines found after line " + i)
	return 
}
blockEnd = (gameNo < gameCount) ? gameLines[gameNo + 1] : lines.length
gameBlock = ""  
for (i = blockStart; i < blockEnd; i++) {
	gameBlock += lines[i] + "\n"
}
return op + bgd_parseGam(gameBlock) + "[game]"
}


function bgd_parseGam(y, addpre, addpos){
addpre = (addpre==null) ? false : addpre 
addpos = (addpos==null) ? false : addpos 
var i, s, e, j, k, l, m, p1, p2
var opa = ""
var lines = new Array()
lines = y.split("\n")
if (lines.length == 0) {
	alert("Error in parseGam() - empty source data")
	return -1
}
// remove any carriage return characters

for (i = 0; i < lines.length; i++) {
	lines[i] = bg_replace(lines[i], "\r", "")
}

// Get player names from 1st line (if available)
i = bgd_findLine(lines, ")")
if (i > 0) { // This is a ''real' .gam file (not a block sent by the .mat file parser)
	i--  // Line before 1st ")" contains player names
	m = lines[i]
	p1 = bg_btrim(m.substr(0, 31))
	p2 = bg_btrim(m.substring(32))
	if (p1.indexOf(":") >= 0) { // .mat file - name field contains match score
		p1 = bg_btrim(p1.substr(0, p1.indexOf(":")))
	} 
	if (p2.indexOf(":") >= 0) { // .mat file - name field contains match score
		p2 = bg_btrim(p2.substr(0, p2.indexOf(":")))
	} 
	opa += "[playername1 " + p1 + "][playername2 " + p2 + "][sourcetype GAM/MAT converter]\n"	
}

// Find first gameplay line
i = 0
while (i < lines.length) {
	if (lines[i].substr(2,2) == "1)") {break}
	i++
}
if (i == lines.length) {
	alert("Error in parseGam() - source data contains no gameplay lines")
	return -1
}
// Now create serialised plays array
plays = new Array()
var lcount = 0
while (i < lines.length) {
	plays[plays.length] = lines[i].substring(5, 33)
	if (lines[i].length >= 33) {
		plays[plays.length] = (lines[i].substr(lines[i].length -1, 1) == "\n") ? lines[i].substring(33, lines[i].length -1) : lines[i].substring(33)		
	} else {
		plays[plays.length] = ""
	}
	i++
	lcount++
}
//alert("Input lines: " + lcount + ", output lines: " + plays.length)


// Now generate the script from the plays[] elements
i = 0
var s1, s2, s3, s4
while (i < plays.length) {
	m = ""
	j = (i % 2 == 0) ? 1 : 2  // Player no for this element
	k = plays[i]
	e = k.indexOf(":")
	if (e >= 0) { // This is a dice play
		m = "[play][player" + j + "][dice " + k.substr(e-2,2) + "][move " + bg_replace(bg_btrim(k.substring(e+1)),"\r", "") + "]"
	//	m+= "[precomment][/precomment][postcomment][/postcomment][/play]"
	} else {
		k = bg_btrim(k)
		if (k != "") {
			s1 = k.toLowerCase()
			if (s1.indexOf("double") >= 0) {
				// Cube doubling - special process
				s2 = parseInt(s1.substring(s1.lastIndexOf(" ")))
				//alert(s1.length + " = *" + s1 + "*" + s1.indexOf("\r"))
				// Check that next command is a take or drop
				s3 = plays[i+1].toLowerCase()
				if (s3.indexOf("take") < 0 && s3.indexOf("drop") < 0) {
					alert("Error in data around play " + Math.floor(i/2) + ": Double must be followed by Take or Drop")
					return
				} else {
					m = "[play][player" + j + "][command double " + s2 + "]"	
					s4 = (j == 1) ? 2 : 1
					if (s3.indexOf("take") >= 0) {
						plays[i+1] = "take " + s2 
					} else {
						plays[i+1] = "drop"
					}
				}
			
			} else {
				// Other command
				m = "[play][player" + j + "][command " + bg_btrim(k) + "]"	
			}
		}	
	}
	//if (!confirm(m)) {break}
	if (m != "") {
	/*
		if (addpre) {
			m += "[precomment][/precomment]"
		}
		if (addpos) {
			m += "[postcomment][/postcomment]"
		}		
	*/
		m += "[/play]"
		opa += (m + "\n")
	}
	i++
}
return opa
}

function bgd_inArray(srch, ary) {
var i
for (i=0; i< ary.length; i++) {
	if (ary[i] == srch) {
		return true
	}
}
return false
}




function bgd_parseXML (y) {
var n = "]\n"
var op = ""
var i, s=y.indexOf("<game"), e, tag, j, k, l, m, r
var outerTags = new Array("game", "play", "steps", "/game", "/play", "/steps")
var containerTags = new Array("step", "precomment", "postcomment", 
	"analysemove", "analysecube", "gamestats", "gameintro")
var tightTags = new Array("horiz", "povplr", "povclr", "player")
var integralTags = new Array("dice", "move", "cube", "title", "take", "pass", 
	"pagetitle", "gametitle", "sourcetype", "menuscript", "playername1", "playername2",
	"posid", "lockpos", "noprompts", "wait", "command")

while (s < y.length) {
	s = y.indexOf("<", s)
	if (s < 0) {break}
	e = y.indexOf(">", s)
	tag = bg_btrim(y.substring(s+1, e))
	if (tag.indexOf(" ") > 0) { // remove any tag attributes (not used by this system)
		tag = tag.substring(0, tag.indexOf(" "))	
	}
	k = tag.toLowerCase()
	r = "[" + tag
	if ( bgd_inArray(k, outerTags))  {
		// Outer container tags - just convert tag to [] format
		op += r + "]\n"
		s = e +1
	} else if (bgd_inArray(k, containerTags)) {
		// Text containers - wrap text in [tag] [/tag] pairs
		j = y.indexOf("</" + tag, e)
		op += r + "]" + unXML(y.substring(e+1, j)) + "[/" + tag + "]\n"
		s = j+1
	} else if ( bgd_inArray(k, tightTags) ) {
		// Tight format tags (e.g. [povclrw])
		j = y.indexOf("</" + tag, e)
		op += r + y.substring(e+1, j) + "]\n"
		s = j+1	
	} else if (  bgd_inArray(k, integralTags)) {
		// single tags containing text (e.g. [playername1 Paul]
		j = y.indexOf("</" , e)
		op += r + " " + unXML(y.substring(e+1, j)) + "]\n"
		s = j+1	
	} else {
		// Unrecognised tag - ignore
		// Check if it's a closed tag
		m = bg_btrim(y.substring(s+1, e))
//		alert("unrecognised tag " + tag + ", m is " + m)

		if (m.substr(m.length - 1, 1) == "/") { //Closed tag)
			s = e
		} else {
			s = y.indexOf("</" + tag, s) +1
		}		
	}
	//if (!confirm(tag + ":\n" + op)) {return op}

}
return op
}

function unXML (s) {
s = bg_replace(s, "&amp;", "&")
return bg_replace(s, "&lt;", "<")
}


function bgd_parseBgt(y) {
// this function parses a GNU backgammon text format game record (stored as a .bgt file)
// into a bgg script.
var doma = true // include move analysis blocks in output script
var domc = true // include move analysis blocks in output script
// Remove any carriage returns left in the source block by IE
y = bg_replace(y, "\r", "")
var s1,s2,s3,s4,s5,s6,s9
var curplr, curcube, curPosId, curmove, curcommand, opp, annoline
var curMoveAnalysis, curCubeAnalysis, curprecomment, curpostcomment
var sd_a = new Array(), sd_i, sd_x, sd_y, sd_j
var stype = ""
var cubeOfferedAt = 0
var cubeTakenBy = 0
var pnos = ":OX"
var oScript = "[povplr2]"
iPlays = new Array()
iPlays = y.split("Move number")
if (iPlays.length == 0) {
	alert("Error in parseBgt() - no move records found in source file")
	return
}

//alert(iPlays.length)
curPlay = new Array()
curPlay = iPlays[0].split("\n")
i = bgd_findLine(curPlay, "The score")
if (i < 0) {
	alert("Error in parseBgt() - no header line found in source file")
	return
}
s1 = curPlay[i]
playerNames = new Array()
playerNames[1] = s1.substring( s1.indexOf("is:") + 4, s1.lastIndexOf(" ", s1.indexOf(",")) )

if (s1.indexOf("(", s1.indexOf(":")) >= 0) {  // match game with "(match to)" comment
	playerNames[2] = s1.substring( s1.indexOf(",") + 2, s1.lastIndexOf(" ", s1.lastIndexOf("(")-2) )
	stype = "GNU match"
} else {  // money game wth no "(math to)" comment
	playerNames[2] = s1.substring( s1.indexOf(",") + 2, s1.lastIndexOf(" "))
	stype = "GNU money"
}

if (stype != "") {
	oScript += "[sourcetype " + stype + "]\n"
}
oScript += "[playername1 " + playerNames[1] + "]"
oScript += "[playername2 " + playerNames[2] + "]\n"
oScript += "[pagetitle " + playerNames[1] + " vs " + playerNames[2] + "]\n"
oScript += "[gametitle " + s1 + "]\n"

/*
i = bgd_findLine(curPlay, "Date:")
if (i >= 0) {
	s1 = curPlay[i]
	oScript += "[gametitle " + s1.substring(s1.indexOf("Date:")+6) + "]\n"
}
*/

// Extract other game header info here


// Now work through the real plays, converting to bgg script
for (i = 1; i < iPlays.length; i++ ) {
	cubeOfferedAt = cubeTakenBy = 0
	curMoveAnalysis = curCubeAnalysis = curprecomment = curpostcomment = ""
	curPlay = iPlays[i].split("\n")
	
	// Check for annotation block so it can be excluded from keyword searches
	annoline = bgd_findLineAtPos(curPlay, "Annotation:", 0)

	
	s1 = curPlay[0]
	s2 = parseInt(s1.substr(0, s1.indexOf(":")))
	if (s2 != i) {
		alert("Error in parseBgt() - move no. " + s2 + " does not match index " + i)
		return
	}
	curplr = (s1.indexOf("X") > 0 ) ? 2 : 1
	opp = (curplr == 1) ? 2 : 1
	s2 = s1.indexOf("to play")
	curDice = (s2 >= 0) ? bg_btrim(s1.substring(s2 + 7)) : ""
	curmove = ""; curcommand = ""
	if (curDice != "") { // dice rolled, so expect a move
		s9 = bgd_findLine(curPlay, playerNames[curplr] + " moves", annoline)
		if (s9 >=0) {
			s1 = curPlay[s9]
			curmove = bg_btrim(s1.substring(s1.indexOf("moves") + 5))
		} else {
			curmove = ""
		}
	} else {
		// This is a non-move play (e.g. a cube double)	
		s2 = s1.indexOf("doubles to")
		if (s2 >= 0) {
			cubeOfferedAt = parseInt(s1.substring(s1.lastIndexOf(" ")))
			curcommand = "double " + cubeOfferedAt
			s3 = bgd_findLine(curPlay, playerNames[opp] + " accepts", annoline)
			cubeTakenBy = (s3 < 0) ? 0 : opp
			//alert("Double on play " + i + ": cubeOfferedAt = " + cubeOfferedAt + ", cubeTakenBy = " + cubeTakenBy)

		}	
	}
	
	// Extract position ID
	
	s1 = curPlay[bgd_findLine(curPlay, "Position ID:", annoline)]
	s2 = bg_btrim(s1.substring(s1.indexOf(":") + 1))
	//if (!confirm("s2 is " + s2)) {return}
	s3 = bgd_findLine(curPlay, "Cube:")
	if (s3 >= 0) {
		s4 = curPlay[s3]	
		curcube = bg_btrim( s4.substring(s4.lastIndexOf(" "), s4.length -1) )
	}
	if (s3 <= 0) {
		if (cubeOfferedAt < 4 ) {
			curcube = ""		
		} else {
			curcube = curplr.toString() + (cubeOfferedAt/2).toString()
		}
	} else if (s3 < 7) {
		curcube = "1" + curcube
	} else if (s3 < 14) {
		curcube = ""
	} else {	
		curcube = "2" + curcube
	}
	
	// Collect move and cube analysis blocks if present	
	if (doma) {
		s1 = bgd_findLineAtPos(curPlay, "Rolled", 0)
		if (s1 >= 0) {
			if (curPlay[s1].indexOf("Cube") >= 0) {
				// GNU funny - sometimes forgets 1st CR  	
				curMoveAnalysis += curPlay[s1].substring(0, curPlay[s1].indexOf(":")+1) + "\n"
				curPlay[s1] = curPlay[s1].substring(curPlay[s1].indexOf(":")+1)
			} 
			while (curPlay[s1].length > 1 && s1 < curPlay.length) {
				curMoveAnalysis += curPlay[s1] + "\n"
				s1++		
			}			
		}
	}
	
	if (domc) {
		s1 = bgd_findLineAtPos(curPlay, "Cube analysis", 0)
		if (s1 >= 0) {
			// Firefox strips newlines out of split() data, IE doesn't,
			// So check has to be for !="" and !="\n"
			while (curPlay[s1].length > 1 && s1 < curPlay.length) {
				//if (!confirm(curPlay[s1].length + " *" + curPlay[s1] + "* " + (curPlay[s1]=="\n")+ " " + curPlay[s1].charCodeAt(0) + " " + "\n".charCodeAt(0))) {return}
				curCubeAnalysis += curPlay[s1] + "\n"
				s1++		
			}			
		}
	}
	
	
	
	
	if (annoline >= 0) {
		s5 = annoline + 1
		while (s5 < curPlay.length) {
				curpostcomment += curPlay[s5]
				s5++		
		}
		s6 = curpostcomment.toLowerCase()			
		if (s6.indexOf("[split]") >= 0) {
			curprecomment = curpostcomment.substring(0, s6.indexOf("[split]"))
			curpostcomment = curpostcomment.substring(s6.indexOf("[split]") + 7) 		
		}
	
	}

	
	// GNU makes it hard here - when this move's player is 1, the posID is for player 2, and vice-versa
	curPosId = (curplr == 1) ? "2" : "1"
	curPosId += s2 + curcube

	oScript += "[play][player" + curplr + "]"
	oScript += "[posID " + curPosId + "]"

	if (curDice != "") {
		oScript += "\n[dice " + curDice + "][move " + curmove + "]" 		
	}
	if (curcommand != "") {		
		oScript += "\n[command " + curcommand + "]" 		
	}
	if (curMoveAnalysis != "") {
		oScript += "\n[analysemove]\n" + curMoveAnalysis + "[/analysemove]"	
	}
	if (curCubeAnalysis != "") {
		oScript += "\n[analysecube]\n" + curCubeAnalysis + "[/analysecube]"	
	}
	/*
	if (curprecomment == "" && curMoveAnalysis != "") {
		// No precomment supplied, but move analysis data exists.
		// Construct precomment containing [demo] for each possible move 
		// suggested by GNU
		sd_a = curMoveAnalysis.split("\n")
		curprecomment = "GNU's possible moves (click for demonstrations):<br>"
		sd_j = 1
		for (sd_i = 0; sd_i < sd_a.length; sd_i++) {
			sd_x = sd_a[sd_i]
			sd_y = sd_x.indexOf("-ply")
			if (sd_y >= 0) {
				curprecomment += sd_j + ". [demo]" + bg_btrim(sd_x.substring(sd_y + 4, sd_x.indexOf("MWC"))) + "[/demo]<br>"			
				sd_j++
			}		
		}
	
	}
	*/
	if (curprecomment != "") {
		oScript += "\n[precomment]" + curprecomment + "[/precomment]"	
	} 
	if (i == (iPlays.length -1) && curcommand == "") { // last play processing
		s1 = bgd_findLine(curPlay, playerNames[curplr] + " wins", annoline)
		if (s1 >= 0) {
			curpostcomment += "<br><br>" + curPlay[s1]
		}
	}

	
	if (curpostcomment != "") {
		oScript += "\n[postcomment]" + curpostcomment + "[/postcomment]"	
	}




	oScript += "\n[/play]\n"
	if (cubeOfferedAt > 0 ) {
		// GNU does a double and take/pass in one play
		// .bgg format does it in 2 plays
		oScript += "[play][player" + opp + "]"
		oScript += "[posID " + curPosId + "]"
		if (cubeTakenBy > 0) {
			oScript += "\n[command take " + cubeOfferedAt + "]" 	
		} else	{
			oScript += "\n[command drop ]"		
		}
		oScript += "\n[/play]\n"	
	}		

	if (i == (iPlays.length -1)) { // last play processing
		s1 = bgd_findLineAtPos(curPlay, "Game statistics", 0)
		if (s1 >= 0) {
			curpostcomment = ""
			s1++
			while (s1 < curPlay.length) {
				curpostcomment += curPlay[s1] + "<br>"
				s1++		
			}
			oScript += "\n[gamestats]" + curpostcomment + "[/gamestats]"	
		}
	}



}
return oScript
}


function bgd_bgg2mat() {
// This function generates a single-game .mat file from the current game
var op = "", cp, j = "", i, ix, k, m, n, rc = 33, newline, curcommand
op = "1 point match\n\nGame 1\n " 
m = (this.playerNames[1] == "") ? 'Player 1' : this.playerNames[1]
op += bgd_padStr(m + " : 0", rc)
m = (this.playerNames[2] == "") ? 'Player 2' : this.playerNames[2]
op += m + " : 0\n"
ix=0; i = -1

newline = true
while (i < this.plays.length -1) {
	if (newline) {	
		ix++
		k = ix.toString()
		op += "     ".substr(0, 3 - k.length) + k + ") "
	}
		
	do {
		i++
		cp=this.plays[i]
	} while (cp.command == "steps" && i < this.plays.length -1)
	if (cp.player == 2) {
		op += bgd_padStr("", rc - 5)
		newline = true	
		if (cp.dice != "") {
			op += cp.dice + ": " + bgd_padStr(bgd_cleanupMoveStr(cp.moves), rc - 9)
		} else {
			op += "  " + bgd_padStr(cp.command, rc - 7)		
		}
			op += "\n"
	} else {
		if (cp.dice != "") {
			op += cp.dice + ": " + bgd_padStr(bg_btrim(bgd_cleanupMoveStr(cp.moves)), rc - 9)
		} else {
			op += "  " + bgd_padStr(bgd_convCommand(cp.command), rc - 7)		
		}
		do {
			i++
			cp=this.plays[i]
		} while (cp.command == "steps" && i < this.plays.length -1)
		if (cp.dice != "") {
			op += cp.dice + ": " + bgd_padStr(bg_btrim(bgd_cleanupMoveStr(cp.moves)), rc - 9)
		} else {				
			op += "  " + bgd_padStr(bgd_convCommand(cp.command), rc - 7)		
		}
		op += "\n"

	}
	
	
}

return op
}


function bgd_convCommand(str) {
var x = bg_btrim(str)
if (str.indexOf("double") >= 0) {
	str = "Doubles => " + parseInt(x.substr(x.lastIndexOf(" ") + 1))			
}
if (str.indexOf("take") >= 0) {
	str = "Takes"	
}
if (str.indexOf("drop") >= 0) {
	str = "Drops"	
}
return str
}

function bgd_padStr(str, len) {
var x = "                                                                               "
if (str.length < len) {
	return str + x.substr(0, len - str.length)
} else {
	return str
}
}

function bgd_cleanupMoveStr(str) {
// This function takes a move string (e.g. "24/18 18/8") and converts special GNU and other
// constructs to plain moves, e.g.:
// "6/off" to "6/0"
// "bar/23" to "25/23"
// "24/22(2)" to "24/22 24/22"
// "24/22*/18" to "24/22* 22/18"
var i, j, k
str = bg_replace(str, "off", "0")
str = bg_replace(str, "bar", "25")
// Conver "x/y(2)" format to "x/y x/y"
while (str.indexOf("(") >= 0) {
	i = str.indexOf("(")
	p = str.indexOf(")", i)
	j = parseInt(str.substring(i+1, p))
	m = str.lastIndexOf(" ", i)
	n = str.substring(m+1, i) // move string
	q = " "
	for (k = 0; k < j; k++) {
		q += n + " "
	}
	str = str.substring(0, m) + q + str.substring(p+1)
}
// string may now contain more than one move

// convert "24/18*/16*" format
return str
}


function bgd_findLine(ary, str, annoline, startLine) {
if (startLine == null) {startLine = 0}
if (annoline == null) {annoline = -1}
var i, found = false
for (i = startLine; i < ary.length; i++) {
	if (ary[i].indexOf(str) >= 0) {
		found = true
		break
	}
}
if (annoline >= 0 && i >= annoline) {found = false}
return (found) ? i : -1
}

function bgd_findLineAtPos(ary, str, pos, startLine) {
if (startLine == null) {startLine = 0}
var i, found = false
for (i = startLine; i < ary.length; i++) {
	if (ary[i].substr(pos, str.length) == str) {
		found = true
		break
	}
}
return (found) ? i : -1
}

