/**
 * @preserve jQuery Column cell selector v1.0
 *
 * Licensed under the new BSD License.
 * Copyright 2009-2012, Bram Stein
 * All rights reserved.
 */
/*global jQuery*/
(function ($) {
	var equationRegExp = /([\+\-]?\d*)[nN]([\+\-]?\d*)/,
		cache, equation,
		pseudoSelector = jQuery.expr.filter.PSEUDO;
        
	function parseEquation(str) {
		var tmp = [],
			result = {
				multiplier: 0,
				offset: 0
			};

		if (str === 'even') {
			str = '2n';
		} else if (str === 'odd') {
			str = '2n+1';
		} else if (/^\d*$/.test(str)) {
			str = '0n+' + str;
		}
		
		tmp = equationRegExp.exec(str);

		if (tmp !== null) {
			result.multiplier = tmp[1] - 0;
			result.offset = tmp[2] - 0;
		}
		return result;
	}

	function generateCache(cells, equation) {
		var currentRow, currentSection, matrix = [], first = 0, lookup = [];

		$.each(cells, function (k, cell) {
			var i = 0, j = 0, 
				rowSpan = cell.rowSpan || 1,
				colSpan = cell.colSpan || 1;

			if (cell.parentNode !== currentRow) {
				currentRow = cell.parentNode;

				if (currentRow.parentNode !== currentSection) {
					currentSection = currentRow.parentNode;
					matrix = [];
				}

				first = 0;

				if (matrix[currentRow.rowIndex] === undefined) {
					matrix[currentRow.rowIndex] = [];
				}
				
			}

			for (i = 0; i < matrix[currentRow.rowIndex].length + 1; i += 1) {
				if (matrix[currentRow.rowIndex][i] === undefined) {
					first = i;
					break;
				}
			}

			lookup[k] = first;

			for (i = currentRow.rowIndex; i < currentRow.rowIndex + rowSpan; i += 1) {
				if (matrix[i] === undefined) {
					matrix[i] = [];
				}

				for (j = first; j < first + colSpan; j += 1) {
					matrix[i][j] = true;
				}
			}
		});
		return lookup;
	}

	function nthCol(element, match, index) {
		var difference = cache[index] - (equation.offset - 1);
		if (equation.multiplier === 0) {
			return difference === 0;
		} else {
			return (difference % equation.multiplier === 0 && difference / equation.multiplier >= 0);
		}
	}

	$.extend(jQuery.fn, {
		nthCol: function (e) {
			equation = parseEquation(e);
			cache = generateCache(this);
			return $(this).filter(function (i) {
				return nthCol(this, undefined, i);
			});
		}
	});	

	$.extend(jQuery.expr.match, {
		COLUMN: new RegExp(":nth-col\\((even|odd|[\\dnN\\+\\-]*)\\)(?![\\^\\[]*\\])(?![\\^\\(]*\\))")
	});

	$.extend(jQuery.expr.leftMatch, {
		COLUMN: new RegExp("(^(?:.|\\r|\\n)*?):nth-col\\((even|odd|[\\dnN\\+\\-]*)\\)(?![\\^\\[]*\\])(?![\\^\\(]*\\))")
	});

	$.extend(jQuery.expr.preFilter, {
		COLUMN: function (match, items) {
			equation = parseEquation(match[1]);
			cache = generateCache(items);
			return match;
		}
	});

    $.extend(jQuery.expr.filter, {
        // Override the pseudo selector and go past the "unrecognized
        // expression" error message if nth-col is part of the 
        // expression. This is only necessary on jQuery >= 1.4.x.
        PSEUDO: function (elem, match, i, array) {
            var name = match[1];
            if (name !== 'nth-col') {
                return pseudoSelector(elem, match, i, array);
            }
        }
    });

	$.extend(jQuery.expr.filter, {
		COLUMN: nthCol
	});
}(jQuery));