/*
	IncrementalSearch
	Copyright (c) Art. Lebedev Studio | http://www.artlebedev.ru/
	Andrew Shitov | ash@design.ru
	v. 2.0 (28.09.2005)
    
    Changes by author:
    v. 2.1 (16.08.2009) | "Gnostic" LLC | WhoYOUgle.com
*/

var KEY_LENGTH = 3;

function NumberClass ()
{
    this.AddCharactersForRegexp = "\\+\\-";
    this.indexText = '';
    this.Initialize();
    return this;
}

NumberClass.prototype.Initialize = function()
{
    this.Keys = new Array();
}

NumberClass.prototype.Match = function (oself, text)
{
    oself.Keys.length = 0;
    oself.indexText = '';
    text.replace(/^([\+\-])([\d]+([.,]\d)?[\d]*)$/, function (m, sign, num){
        oself.Keys[oself.Keys.length] = sign + num;
        oself.Keys[oself.Keys.length] = num;
        oself.indexText = sign + num;
        });
}

function IncrementalSearch (tableID, filterID, minWordLength, defaultSearch, userParams)
{
	this.Table = document.getElementById (tableID);
	this.Filter = document.getElementById (filterID);
	this.minWordLength = minWordLength ? minWordLength : 3;
	this.defaultSearch = defaultSearch ? defaultSearch : s('Quick search through the table');
    this.userParams = userParams || null;
    
	if (!this.Table || !this.Filter) return null;

	this.Initialize();

	return this;
}

IncrementalSearch.prototype.Initialize = function()
{
	this.SearchList = new Array();
	this.RowsList = new Array();

	this.WordIndex = new Array();
    this.SearchClasses = new Array();
    this.SearchClasses.push(new NumberClass());
    if (this.userParams){
        for (var i = 0; i != this.userParams.searchClasses.length; i++){
            this.SearchClasses.push(this.userParams.searchClasses[i]);
        }
    }

    this.wordCharacters = "abcdefgjhijklmnoprqstuvwxyzабвгдеёжзийклмнопрстуфхцчшщъыьэюя0-9._";

    this.addCharacters = '';
    for (var sc = 0; sc != this.SearchClasses.length; sc++)
        this.addCharacters += this.SearchClasses[sc].AddCharactersForRegexp;
    

    this.cacheTableData();

	this.Filter.IncrementalSearch = this;
	this.Filter.onkeyup = this.matchFilter;
	this.Filter.onkeydown = this.matchFilter;
    this.Filter.onuserexec = this.userFilter;
    
    _setCount (this.Filter, this.Table.rows.length - 1);

	//this.Filter.focus();
}

IncrementalSearch.prototype.cacheTableData = function()
{	
	var c = 0;
	for (var rowIndex = 0; rowIndex != this.Table.rows.length; rowIndex++)
	{
		var currentRow = this.Table.rows[rowIndex];
		var parentTagName = currentRow.parentNode.tagName.toLowerCase();
		if (parentTagName != 'tbody' && parentTagName != 'table') continue;
		
		this.RowsList[c] = currentRow;

        for (var cell = 0; cell != currentRow.cells.length; cell++){
		    var cellText = this.indexText(c, currentRow.cells[cell].innerHTML.toLowerCase());
            this.SearchList[c] += this.SearchList[c] ? cellText : ' ' + cellText;
		}

		c++;
	}
}

IncrementalSearch.prototype.matchFilter = function(event)
{
	if(window.event) event = window.event;
	if (!this.IncrementalSearch) return false;

	var search = this.IncrementalSearch;
	var filterValue = this.value.replace(this.IncrementalSearch.defaultSearch, '').toLowerCase();
	
    filterValue = filterValue.replace(/[\u002D\u02D7\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g,'-');
    
    var re = new RegExp("[" + search.wordCharacters + search.addCharacters + "]+", "ig");
    var words = filterValue.match (re) || [];
	filterValue = '';
    
    for (var c = 0; c != words.length; c++)
	{
	    var mSearchClass = null;
        for (var sc = 0; sc != search.SearchClasses.length; sc++){
            search.SearchClasses[sc].Match(search.SearchClasses[sc], words[c]);
            if (search.SearchClasses[sc].indexText != ''){
                mSearchClass = search.SearchClasses[sc];
                break;
            }
        }

        if (mSearchClass){
            filterValue += filterValue == '' ? mSearchClass.indexText : ' ' + mSearchClass.indexText;
        } else {
            var re = new RegExp("[^" + search.wordCharacters + "]+", "ig");
            words[c] = words[c].replace(re,' ').toLowerCase(); 
            filterValue += filterValue == '' ? words[c] : (' ' + words[c]);
        }
	}
    
    filterValue = filterValue.replace(/^\s+/g,"");
    filterValue = filterValue.replace(/\s+$/g,"");
    filterValue = filterValue.replace(/\s+/g," ");
	
	if (filterValue != '')
	{

		var words = filterValue.toLowerCase().split (' ');
		var selectedRows = new Array();
        var mCount = words.length;
        for (var c = 0; c != words.length; c++)
		{
			if (words[c] == '') continue;

			var key = words[c].substr (0, KEY_LENGTH);
			if (this.IncrementalSearch.WordIndex[key])
			{
				// WordIndex[key] содержит список номеров строк, номера соответствуют индексам массива RowsList, который содержит указатели на строки.
				var selected = this.IncrementalSearch.WordIndex[key];
				
				for (var s = 0; s != selected.length; s++)
					selectedRows[selected[s]] = selectedRows[selected[s]] ? selectedRows[selected[s]] + 1 : 1;
			}
		}

        var count = 0;
        
		if (filterValue.length <= KEY_LENGTH)
		{
			for (var r = 0; r != search.RowsList.length; r++)
			    if (search.RowsList[r]) 
			    {
			        if (selectedRows[r] == mCount)			        
			            search.RowsList[r].style.display = '', count++;
			        else
			            search.RowsList[r].style.display = 'none';
			    }						
		}
		else
		{
			for (var r = 0; r != search.RowsList.length; r++)
				if (search.RowsList[r]) 
				{
				    if (( selectedRows[r] == mCount ) && search.SearchList[r].indexOf (filterValue) != -1)				    
				        search.RowsList[r].style.display =  '', count++;
				    else
				        search.RowsList[r].style.display = 'none';
				}
		}		
				
		_setCount (this, count);
    	
    	if (count < 2) $(this.parentNode).find('div').remove();
	}
	else
	{
		for (var r = 0; r != search.RowsList.length; r++)
			search.RowsList[r].style.display = '';
			
		_setCount (this, search.RowsList.length);
	}
}

IncrementalSearch.prototype.userFilter = function(key)
{
    if (!this.IncrementalSearch) return false;

    var search = this.IncrementalSearch;
    var selectedRows = new Array();

    if (search.userParams && search.userParams["filters"]){
        var selected = search.userParams["filters"][key];
        
        if (selected) {
            for (var s = 0; s != selected.length; s++)
                selectedRows[selected[s]] = true;
        }
    }
    
    for (var r = 0; r != search.RowsList.length; r++)
        if (search.RowsList[r]) search.RowsList[r].style.display = selectedRows[r]||(key == '') ? '' : 'none';
}

IncrementalSearch.prototype.indexText = function (rowIndex, text)
{
    text = text.replace (/<[^>]+>/g, '');
	// U+2010   ‐   e2 80 90    HYPHEN
    // U+2011   ‑   e2 80 91    NON-BREAKING HYPHEN
    // U+2012   ‒   e2 80 92    FIGURE DASH
    // U+2013   –   e2 80 93    EN DASH
    // U+2014   —   e2 80 94    EM DASH
    // U+2015   ―   e2 80 95    HORIZONTAL BAR
    // U+002D HYPHEN-MINUS
    // U+02D7 MODIFIER LETTER MINUS SIGN
    text = text.replace(/[\u002D\u02D7\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g,'-');

    var currentText = '';
    text = text.replace('&apos;', "'");
    text = text.replace('&quote;', '"');
    text = text.replace('&amp;', '&');

    var re = new RegExp("[" + this.wordCharacters + this.addCharacters + "]+", "ig");
    var words = text.match (re) || [];
	
    for (var c = 0; c != words.length; c++)
	{
	    var mSearchClass = null;
        for (var sc = 0; sc != this.SearchClasses.length; sc++){
            this.SearchClasses[sc].Match(this.SearchClasses[sc], words[c]);
            if (this.SearchClasses[sc].indexText != ''){
                mSearchClass = this.SearchClasses[sc];
                break;
            }
        }

        if (mSearchClass){
            for (var i = 0; i != mSearchClass.Keys.length; i++)
                this.storeKey(rowIndex, mSearchClass.Keys[i]);
            currentText += currentText == '' ? mSearchClass.indexText : ' ' + mSearchClass.indexText;
        } else {
            var re = new RegExp("[^" + this.wordCharacters + "]+", "ig");
            words[c] = words[c].replace(re,' '); 
            var parts = words[c].split(' ');
            for (var i = 0; i != parts.length; i++){
                if (parts[i].length < this.minWordLength) continue;
	    	    var key = parts[i].toLowerCase();
                this.storeKey(rowIndex, key);
                currentText += currentText == '' ? key : (' ' + key);
            }
        }
	}
    return currentText;
}

IncrementalSearch.prototype.storeKey = function (rowIndex, key)
{
	for (var c = 1; c <= /*key.length*/ KEY_LENGTH; c++)
	{
		var subkey = key.substr (0, c);

		if (!this.WordIndex[subkey])
			this.WordIndex[subkey] = new Array();
		
		var uniq = true;
        for (var i = 0; i != this.WordIndex[subkey].length; i++){
            if (this.WordIndex[subkey][i] == rowIndex){
                uniq = false;
                break;
            }
        }
        if (uniq) this.WordIndex[subkey][this.WordIndex[subkey].length] = (rowIndex);
	}
}
Array.prototype.map = null; //Ha-ha, die, fucking ghost method.

function _setCount (filter, count)
{
    var $count = $(filter.parentNode.parentNode).find('.row-count');
    
    if ($count.length)
    {
        $count.text(count);
    }	    
    else
    {
        var $table = $('<table width="100%" style="margin-bottom:2em"/>')
            .append('<tr><td/><td width="1" style="padding-left:10px"/></tr>');
        
        $(filter).after($table).remove();
        $table.find('td:eq(0)').append(
            $(filter).css({margin:0, width:'99%'}).attr('size', '')
        );
        $table.find('td:eq(1)').append(
            $('<div/>')
    	        .addClass ('row-count')
    		    .text(count)
    		    .css({
    		        fontSize:'12px'
    		    })
        );      
    }
        		    
}

