/***************************************************************************
 *                                fastengine.js
 *                            -------------------
 *   begin                : Friday, Feb 21, 2003
 *   copyright            : (C) 2003 Nicolas Grekas
 *   email                : nicolas.grekas@espci.fr
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   You can not redistribute and/or modify this program without express
 *   permission of the author ; restricted use to IntellAgence
 *
 ***************************************************************************/

/***************************************************************************
 *
 * This program has been tested and works with :
 *  - IE 5.0+
 *  - Mozilla 1.2 - Galeon
 *  - Opera 7.02
 *  - NS 4.7
 *  - Konqueror 3
 *
 * It should work on any javascript 1.2 navigator (NS et IE >=4)
 *
 ***************************************************************************/

function fastsearch(code)
{
	this.keyword = new Array();
	this.match = new Array();

	this.query = '';
	this.word = new Array();
	this.wordRegI = new Array();

	this.page = 1;
	this.result = new Array();
	this.max_result = 10000;
	this.perPage = 10;
	this.starttime = 0;

	this.data = new Array();
	this.template = new fastprinter(code);

	this.fastWin = window;
	this.outWin = window;
	this.self = new String(this.outWin.document.location).replace( /^(.*\/)+/g, '' ).replace( /\?.*$/g, '' );

	return this;
}

fastsearch.prototype.searchWord = function(word)
{
	var a = 1;
	var b = this.keyword.length - 1;
	var m = Math.floor((a+b)/2);

	if ( word==this.keyword[a]) return a;
	else if ( word==this.keyword[b]) return b;

	if (word < this.keyword[a]) m=b=a;
	else if (word > this.keyword[b]) return false;

	while ( m!=a )
	{
		if ( word > this.keyword[m] ) a = m;
		else if ( word < this.keyword[m] ) b = m;
		else  return m;
		m = Math.floor((a+b)/2);
	}

	if ( this.keyword[b].indexOf(word)==0 ) return b;
	else return false;
}

fastsearch.prototype.search = function(i, filter)
{
	this.setQuery(i);
	filter = this.setFilter(filter);
	this.result = new Array();

	if ( this.word[0]=='*' )
	{
		if ( filter )
		{
			filter = Math.min(this.data.length, this.max_result);
			for (i=0; i<filter; i++)
				if ( this.filter(i) ) this.result.push(i);
		}
		else
		{
			filter = Math.min(this.data.length, this.max_result);
			for (i=0; i<filter; i++) this.result.push(i);
		}

		return;
	}

	var found = new Array();
	var union = new Array();

	for (var word_i=0; word_i<this.word.length; word_i++)
	{
		var known = new Array();

		i = this.searchWord( this.word[word_i] );
		if ( i==false ) continue;

		var matches = '';
		while ( this.keyword[i] && this.keyword[i].indexOf(this.word[word_i])==0 ) matches += ',' + this.match[i++];
		eval('matches = new Array(0' + matches + ');');

		for (i=1; i<matches.length; i++)
		{
			if ( found[ matches[i] ]!=0 && !known[ matches[i] ] )
			{
				known[ matches[i] ] = true;

				if ( found[ matches[i] ] ) found[ matches[i] ]++;
				else if ( !filter || this.filter( matches[i] ) )
				{
					found[ matches[i] ] = 1;
					union.push( matches[i] );
					if ( this.word.length==1 && union.length==this.max_result ) break;
				}
				else found[ matches[i] ] = 0;
			}
		}
	}

	if ( this.word.length>1 )
	{
		known = new Array();
		for (i=0; i<union.length; i++)
		{
			word_i = found[ union[i] ];
			if ( word_i==this.word.length ) this.result.push( union[i] );
			else
			{
				word_i--;
				if ( !known[word_i] ) known[word_i] = new Array();
				known[word_i].push( union[i] );
			}
		}

		word_i = known.length;
		matches = this.result.length;
		while (word_i>0 && matches<=this.max_result) if (known[--word_i])
		{
			for (i=0; i<known[word_i].length && matches<=this.max_result; i++, matches++) this.result.push( known[word_i][i] );
		}
	}
	else this.result = union;

	this.result = this.result.slice(0, this.max_result);
}

fastsearch.prototype.setQuery = function(query)
{
	query = query.replace(/^\s+/g,'').replace(/\s+$/g,'');

	this.query = query;

	if (query=='*')
	{
		this.word = new Array('*');
		this.wordRegI = false;
		return;
	}

	query = query.toLowerCase();
	query = strip_accent(query);
	query = query.replace(/[^a-z0-9]+/g, '_');

	if ( query.search(/^_*(the|on|of|a|in|and|an|as|at|by|for|from|to|with)_*$/)==-1 )
	{
		query = '_'+query+'_';
		query = query.replace(/_((the|on|of|a|in|and|an|as|at|by|for|from|to|with)_)+/g,'_');
	}
	query = query.replace(/^_+/g,'').replace(/_+$/g,'');

	this.word = new Array();
	this.wordRegI = new Array();

	if (query=='') return;
	query = query.split('_');
	query.sort();

	for (var i=0; i<query.length; i++)
	{
		while ( query[i+1] && query[i+1].indexOf(query[i])==0 ) i++;
		this.word.push( query[i] );
		this.wordRegI.push( makeRegI(query[i]) );
	}
}

fastsearch.prototype.setFrame = function(fastWin, outWin)
{
	this.fastWin = fastWin;
	this.outWin = outWin;
	this.self = new String(outWin.document.location).replace( /^(.*\/)+/g, '' ).replace( /\?.*$/g, '' );
}

fastsearch.prototype.exec = function(query, page, filter)
{
	this.starttime = new Date();

	this.template.destroy();
	this.search( query, filter );
	this.make( page, this.perPage );
	this.template.pparse(this.outWin);

	this.outWin.gotoPage = this.fastWin.gotoPage;
	this.outWin.getData = this.fastWin.getData;
}

fastsearch.prototype.makePagination = function()
{
	var numPage = Math.ceil(this.result.length/this.perPage);
	var template = this.template;
	var tpldata = new Array();

	template.assign_var('NUM_PAGE', numPage);
	template.assign_var('PER_PAGE', this.perPage);

	if (numPage>1)
	{
		template.assign_block_vars('result.pagination', new Array());

		if ( this.page>1 )
		{
			template.assign_block_vars('result.pagination.previous', new Array());
			template.assign_var('U_PREVIOUS', 'javascript:gotoPage(' + (this.page-1) + ');');
		}
		else template.assign_block_vars('result.pagination.no_previous', new Array());

		if ( this.page<numPage )
		{
			template.assign_block_vars('result.pagination.next', new Array());
			template.assign_var('U_NEXT', 'javascript:gotoPage(' + (this.page+1) + ');');
		}
		else template.assign_block_vars('result.pagination.no_next', new Array());

		var tplref = template._tpldata.result[0].pagination[0].page = new Array();

		var i = 0;
		while ( i<numPage )
		{
			i++;

			tpldata = new Array();
			tpldata['U_PAGE'] = 'javascript:gotoPage('+i+');';

			if ( i==this.page ) tpldata['PAGE'] = '&nbsp;<span class="fastselected">'+i+'</span>';
			else if ( numPage>20 && i>3 && i<numPage-2 && Math.abs(this.page-i)>1 ) tpldata['PAGE'] = '.';
			else tpldata['PAGE'] = '&nbsp;'+i;

			tplref.push(tpldata);
		}
	}
}

fastsearch.prototype.make = function(i, j)
{
	this.perPage = j;
	this.page = Math.min( i, Math.ceil(this.result.length/this.perPage));
	if ( !(this.page>0) ) this.page = 1;

	this.makeFilter();

	var template = this.template;

	if ( this.result.length )
	{
		template.assign_block_vars( 'result', new Array() );
		this.makePagination();

		var numRes = Math.min(this.result.length, this.page*this.perPage) - (this.page-1)*this.perPage;

		template.assign_var('TOTAL_RESULT', this.result.length);
		template.assign_var('PAGE_RESULT', numRes);
		template.assign_var('S', numRes>1 ? 's' : '');
		template.assign_var('FIRST_RESULT', (this.page-1)*this.perPage+1);
		template.assign_var('LAST_RESULT', (this.page-1)*this.perPage+numRes);

		var tplref = template._tpldata.result[0].data = new Array();

		i = (this.page-1)*this.perPage;
		j = Math.min(this.result.length, this.page*this.perPage);

		while ( i<j )
		{
			tplref.push( this.makeData(this.result[i]) );
			i++;
		}
	}
	else if ( this.word.length ) template.assign_block_vars( 'no_result', new Array() );
	else template.assign_block_vars( 'welcome', new Array() );

	template.assign_var('TOTAL_DATA', this.data.length);
	template.assign_var('TIME', new Date - this.starttime);

	template.assign_var('ACTION', this.self.replace(/"/g, '&quot;'));
	template.assign_var('QUERY', this.query.replace(/"/g, '&quot;'));
	if (!this.f_theme) {
		template.assign_var('THEME', 'All');
	}
	else {
		template.assign_var('THEME', Theme[this.f_theme]);
	}
}

