function AJAX()	{
	var object;
	try	{
		object = new XMLHttpRequest();
	}
	catch(e)	{
		try	{
			object = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch(e)	{
			try	{
				object = new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch(e)	{}
		}
	}
	return object;
}
function NarrowingConfiguration()	{
	
	var list, pannel, pageSize, sortByPriceLink;
	var paginations = [];
	
	var filtersMultiple = [];
	var filtersRange = [];
	
	this.setList = function( DOMElement )	{
		list = DOMElement;
	};
	this.getList = function()	{
		return list;
	};
	
	this.setPannel = function( DOMElement )	{
		pannel = DOMElement;
	};
	this.getPannel = function()	{
		return pannel;
	};
	
	this.addPagination = function( DOMElement )	{
		var pagination = new NarrowingPagination( DOMElement );
		paginations.push( pagination );
	};
	this.getPagination = function()	{
		return paginations;
	};
	
	this.setPageSize = function( cols, rows )	{
		pageSize = { 'cols': cols, 'rows': rows };
	};
	this.getPageSize = function()	{
		return pageSize;
	};
	
	/*
		SET SORT BY PRICE LINK
		*** Gets a link node and sets the onclick methods for sorting by price
		Parameters:
			DOMElement - the link node
		Returns:
			Null
	*/
	this.setSortByPriceLink = function( DOMElement )	{
		sortByPriceLink = DOMElement;
	};
	this.getSortByPriceLink = function()	{
		return sortByPriceLink;
	};
	
	/*
		SET VIEW ALL LINK
		*** Gets a link node and sets the onclick methods for showing all items
		Parameters:
			DOMElement - the link node
		Returns:
			Null
	*/
	this.setShowAllLink = function( DOMElement )	{
		showAllLink = DOMElement;
	};
	this.getShowAllLink = function()	{
		return showAllLink;
	};
	
	/*
		ADD MULTIPLE
		*** Creates a new special multiple filter
		Parameters:
			name - the name of the filter that is multiple
		Returns:
			Null
	*/
	this.addMultipleFilter = function( name )	{
		var multiple = {
			'name': name
		};
		filtersMultiple.push( multiple );
	};
	
	/*
		ADD RANGE
		*** Creates a new special range filter
		Parameters:
			name - the name of the filter that is range
			interval - the intervals between is put the 
	*/
	this.addRangeFilter = function( name, interval, before, after )	{
		var range = {
			'name': name,
			'interval': interval,
			'before': before,
			'after': after
		};
		filtersRange.push( range );
	};
	
	/*
		GET MULTIPLE FILTERS
		*** Returns the array with the multiple filters
		Paramters:
			None
		Returns:
			Array
	*/
	this.getMultipleFilters = function()	{
		return filtersMultiple;
	};
	
	/*
		GET RANGE FILTERS
		*** Returns the array with the range filters
		Paramters:
			None
		Returns:
			Array
	*/
	this.getRangeFilters = function()	{
		return filtersRange;
	};
	
}
function NarrowingDOMController()	{
	
	var allItems = [];
	var appliedFilters = [];
	var selectedItems = [];
	
	/*
		ADD ITEM
		*** Adds a new item to the allItems array
		Parameters:
			newItem - Item object that will be added to the array
		Returns:
			Null
	*/
	this.addItem = function( NarrowingItem )	{
		var attributes = NarrowingItem.getAllAttributes(); // Get the attributes of the item
		allItems.push( NarrowingItem ); // Add the item to the array
		selectedItems.push( NarrowingItem ); // Also to the selected (this method only runs at app startup)
	};
	
	/*
		GET ITEMS
		*** Returns the items filtered by the array of applied filters
		Parameters:
			None
		Returns:
			Array
	*/
	this.getItems = function( sortCriteria )	{
		switch( sortCriteria )	{
			case 'id':
				return defaultSorting( selectedItems );
				break;
			case 'name':
				return sortItemsByName( selectedItems );
				break;
			case 'date':
				return sortItemsByDate( selectedItems );
				break;
			case 'price_l':
				return sortItemsByPrice_LowToHigh( selectedItems );
				break;
			case 'price_h':
				return sortItemsByPrice_HighToLow( selectedItems );
				break;
		}
		return selectedItems;
	};
	
	/*
		APPLY FILTER
		*** Adds a filter to the applied filter array. getItems will not return items that dont match this new filter
		Parameters:
			name - name of the filter to add
			value - value of the filter to add
		Returns:
			Null
	*/
	this.applyFilter = function( name, value )	{
		var filter = new NarrowingFilter( name, value ); // Create new object
		appliedFilters.push( filter ); // And add to the array
		getFilteredItems(); // Refresh the selected items
	};
	
	/*
		REMOVE FILTER
		*** Removes the filters that match the name sent in the parameters
		Parameters:
			name - the name of the filters to remove
		Returns:
			Null
	*/
	this.removeFilter = function( name, value )	{
		var newFilters = []; // This array will be storing all applied filters but the onee with the name like the parameter
		for( var i = 0; i < appliedFilters.length; i++ )	{ // Go through
			if( appliedFilters[i].getName() != name )	{ // If the name is different to the one we want to remove
				newFilters.push( appliedFilters[i] ); // Add to the new array
			}
			else if( appliedFilters[i].getValue() != value )	{
				newFilters.push( appliedFilters[i] ); // Add to the new array
			}
		}
		appliedFilters = newFilters; // Then set the appliedFilters array to the new one without the filter we wanted to remove
		getFilteredItems(); // Refresh the selected items
	};
	
	/*
		GET ALL FILTERS
		*** Return all possible filters for the currently selected items
		Parameters:
			None
		Returns:
			Array
	*/
	this.getAllFilters = function()	{
		var returnFilters = [];
		for( var i = 0; i < selectedItems.length; i++ )	{
			var attributes = selectedItems[i].getAllAttributes();
			for( var j = 0; j < attributes.length; j++ )	{
				if( !filterExists( returnFilters, attributes[j].name, attributes[j].value ) )	{
					var filter = new NarrowingFilter( attributes[j].name, attributes[j].value );
					filter.increaseItemCount();
					returnFilters.push( filter );
				}
				else	{
					var filter = getFilterFromArray( returnFilters, attributes[j].name, attributes[j].value );
					filter.increaseItemCount();
				}
			}
		}
		return returnFilters;
	};
	
	/*
		GET APPLIED FILTERS
		*** Returns the applied filters
		Parameters:
			None
		Returns:
			Array
	*/
	this.getAppliedFilters = function()	{
		return appliedFilters;
	};
	
	/*
		GET FILTERED ITEMS
		*** Gets all the items and the applied filters and returns the items that are matching the filters
		Parameters:
			None
		Returns:
			Array
	*/
	function getFilteredItems()	{
		var returnItems = []; // The array that will be filled with the items that match
		for( var i = 0; i < allItems.length; i++ )	{ // For each one of all the items
			var add = true; // Set add as true
			for( var j = 0; j < appliedFilters.length; j++ )	{ // Check every filters
				if( !appliedFilters[j].matchItem( allItems[i] ) )	{ // And if one of the filters doesnt match
					add = false; // Set add to false
				}
			}
			if( add )	{ // If at this point add is true
				returnItems.push( allItems[i] ); // Then add the item to the array
			}
		}
		selectedItems = returnItems; // Set the selected to the new
	}
	
	/*
		FILTER EXISTS
		*** Checks if the filter is already added inside the array
		Paramters:
			array - the list of filters where we will look
			name - name of the filter to be checked
			value - value of the filter to be checked
		Returns:
			Boolean
	*/
	function filterExists( array, name, value )	{
		for( var i = 0; i < array.length; i++ )	{ // Check all the filters
			if( array[i].getName() == name && array[i].getValue() == value )	{ // And if the current one matches the name and value we search
				return true; // Return true
			}
		}
		return false; // If not found return false
	}
	
	/*
		GET FILTER FROM ARRAY
		*** Returns a filter if it is inside the array snt by parameter
		Parameters:
			array - the list of filters where we will look
			name - name of the filter we are looking for
			value - value of the filter we are looking for
		Returns:
			[ NarrowingFilter || Null ]
	*/
	function getFilterFromArray( array, name, value )	{
		for( var i = 0; i < array.length; i++ )	{
			if( array[i].getName() == name && array[i].getValue() == value )	{
				return array[i];
			}
		}
	}
	
	/*
		DEFAULT SORTING
		*** Returns an array of items sorted by its id
		Parameters:
			items - the unsorted items
		Returns:
			Array
	*/
	function defaultSorting( items )	{
		for( var i = 0; i < items.length; i++ )	{
			items[i].toString = function()	{
				return this.getName();
			};
		}
		items.sort();
		return items;
	}
	
	/*
		SORT ITEM BY DATE
		*** Returns an array of items sorted by its date atrribute
		Parameters:
			items - the unsorted items
		Returns:
			Array
	*/
	function sortItemsByDate( items )	{
		for( var i = 0; i < items.length; i++ )	{
			items[i].toString = function()	{
				return this.getDate();
			};
		}
		items.sort();
		items.reverse();
		return items;
	}
	
	/*
		SORT ITEM BY PRICE (LOW TO HIGH)
		*** Returns an array of items sorted by its price
		Parameters:
			items - the unsorted items
		Returns:
			Array
	*/
	function sortItemsByPrice_LowToHigh( items )	{
		for( var i = 0; i < items.length; i++ )	{
			items[i].toString = function()	{
				return this.getPrice();
			};
		}
		items.sort();
		return items;
	}
	
	/*
		SORT ITEM BY PRICE (HIGH TO LOW)
		*** Returns an array of items sorted by its price
		Parameters:
			items - the unsorted items
		Returns:
			Array
	*/
	function sortItemsByPrice_HighToLow( items )	{
		for( var i = 0; i < items.length; i++ )	{
			items[i].toString = function()	{
				return this.getPrice();
			};
		}
		items.sort();
		items.reverse();
		return items;
	}
	
}
function NarrowingFilter( name, value )	{
	
	var itemCount = 0; // The number of items that currently match this filter
	
	/*
		GET NAME
		*** Returns the name of the filter
		Parameters:
			None
		Returns:
			String
	*/
	this.getName = function()	{
		return name;
	};
	
	/*
		GET VALUE
		*** Returns the value of the filter
		Parameters:
			None
		Returns:
			String
	*/
	this.getValue = function()	{
		return value;
	};
	
	/*
		MATCH ITEM
		*** Returns true if the attribute of the item that has the filter name has the same value as the filter does
		Parameters:
			Item
		Returns:
			Boolean
	*/
	this.matchItem = function( itemCheck )	{
		var attributes = itemCheck.getAttribute( name );
		for( var i = 0; i < attributes.length; i++ )	{
			if( attributes[i] == value )	{
				return true;
			}
		}
		return false;
	};
	
	/*
		GET ITEM COUTN
		*** Returns the items that match this filter
		Paramters:
			None
		Returns:
			Null
	*/
	this.getItemCount = function()	{
		return itemCount;
	};
	
	/*
		RESET ITEM COUTN
		*** Sets the item count to 0
		Paramters:
			None
		Returns:
			Null
	*/
	this.resetItemCount = function()	{
		itemCount = 0;
	};
	
	/*
		INCREASE ITEM COUTN
		*** Adds one to the item count
		Paramters:
			None
		Returns:
			Null
	*/
	this.increaseItemCount = function()	{
		itemCount++;
	};
	
	/*
		TO STRING
		*** Used to sort the filters when needed. Checks if the value is numeric before returning
		Parameters:
			None
		Returns:
			String
	*/
	this.toString = function()	{
		var returnValue = value;
		if( !isNaN( parseFloat( returnValue ) ) )	{
			returnValue = parseFloat( returnValue ) + '';
			var end = ( 50 - returnValue.length );
			for( var i = 0; i < end; i++ )	{
				returnValue = '0' + returnValue;
			}
		}
		else if( !isNaN( parseFloat( returnValue.substring( 1 ) ) ) )	{
			returnValue = parseFloat( returnValue.substring( 1 ) ) + '';
			var end = ( 50 - returnValue.length );
			for( var i = 0; i < end; i++ )	{
				returnValue = '0' + returnValue;
			}
		}
		return returnValue;
	};
	
}
function NarrowingItem( item, cell )	{
	
	var attributes = [];
	
	/*
		ADD ATTRIBUTE
		*** Adds and attribute to the attributes array only if it deosnt already exists
		Parameters:
			name - name of the attribute to be added
			value - value of the attribute to be added
		Returns:
			Null
	*/
	this.addAttribute = function( name, value )	{
		if( value != '' )	{
			var attribute = { 'name': name, 'value': value }; // Create the attribute object
			attributes.push( attribute ); // And add it to the array 
		}
	};
	
	/*
		GET ATTRIBUTE
		*** Returns an array of values for the name given
		Parameters:
			name - name of the attributes requested
		Returns:
			Array
	*/
	this.getAttribute = function( name )	{
		var returnArray = []; // This is the array that will be returned with the values
		for( var i = 0; i < attributes.length; i++ )	{ // Go through all the attributes
			if( attributes[i].name.toUpperCase() == name.toUpperCase() )	{ // If the name is the one we are looking for
				returnArray.push( attributes[i].value ); // Add to the array
			}
		}
		return returnArray; // Return all the values in a package
	};
	
	/*
		GET ALL ATTRIBUTES
		*** Returns an array with all the attributes
		Parameters:
			None
		Returns:
			Array
	*/
	this.getAllAttributes = function()	{
		return attributes; // All the attirubtes
	};
	
	/*
		GET CELL
		*** Returns the cell for this item
		Parameters:
			None
		Returns:
			DOMElementDiv
	*/
	this.getCell = function()	{
		return cell;
	};
	
	/*
		GET ID
		*** Returns the id for this item
		Parameters:
			None
		Returns:
			String
	*/
	this.getName = function()	{
		return item.getAttribute( 'name' );
	};
	
	/*
		GET DATE
		*** Returns date
		Parameters:
			None
		Returns:
			String
	*/
	this.getDate = function()	{
		var date = new Date( item.getAttribute( 'date' ) );
		return date.getTime();
	};

	
	/*
		GET PRICE
		*** Returns the price for this item
		Parameters:
			None
		Returns:
			String
	*/
	this.getPrice = function()	{
		var price = item.getAttribute( 'price' ).substring( 1 ) + '';
		for( var i = 0; i < ( 50 - price.length ); i++ )	{
			price = '0' + price;
		}
		return price;
	};
	
}
function NarrowingLoader( Configuration )	{
	
	var List = new NarrowingList( Configuration.getList(), Configuration.getPagination(), Configuration.getPageSize().cols, Configuration.getPageSize().rows ); // List object
	var Pannel = new NarrowingPannel( Configuration.getPannel() ); // Pannel object
	
	var DOMController = new NarrowingDOMController(); // Domain controller
	var UIController = new NarrowingUIController( DOMController, List, Pannel ); // UI controller
	
	var requestCount = -1; // Use this to count how many request I have done (is 0 when the current page is loaded)
	
	var filtersMultiple = Configuration.getMultipleFilters(); // List of special multiple filters
	var filtersRange = Configuration.getRangeFilters(); // List of special range filters
	
	/*
		INIT
		*** This method will start all the feature
		Parameters:
			None
		Returns:
			Null
	*/
	this.init = function()	{
		/* LOAD PAGES */
		UIController.changeStatus( 'Loading...' );		
		var links = getPageLinks(); // Get the links of netsuite
		savePage( document.body.innerHTML ); // Get the items from the current page
		updateStatus( links.length ); // And update
		if( window.addEventListener )	{ // For not internet explorer browsers
			NS_getPagesHTML( links );
		}
		else if( window.attachEvent )	{ // For internet explorer
			IE_getPagesHTML( links );
		}
		/* SORT AND VIEW ALL LINK INIT */
		UIController.sortByPriceLink( Configuration.getSortByPriceLink() );
		UIController.showAllLink( Configuration.getShowAllLink() );
	};
	
	/*
		GET PAGE LINKS
		*** Gets from the DOM the links that need to be revised
		Parameters:
			None
		Returns:
			Array
	*/
	function getPageLinks()	{
		var returnLinks = []; // These are the links that will be returned this page included
		var addedString = ''; // Auxiliar variable to check if the urls are already added
		var links = document.getElementsByTagName( 'a' ); // Get all the links from the DOM
		var rand = Math.random() * 100000; // Adds a random number to the end of the URL te be queried in order to disable cache
		for( var i = 0; i < links.length; i++ )	{ // Fro each link
			if( links[i].href.indexOf( 'range=' ) != -1 )	{ // If it is a pagination link, doesnt have images and is not in the aux string
				links[i].parentNode.parentNode.style.display = 'none';
				if( links[i].getElementsByTagName( 'img' ).length == 0 && addedString.indexOf( ';' + links[i].href + ';' ) == -1 )	{					
					returnLinks.push( links[i].href + '&rand=' + rand ); // Add to the array
					addedString += ';' + links[i].href + ';'; // And to the string
				}
			}
		}
		return returnLinks; // Return the links
	}
	
	/*
		NS GET PAGES HTML
		*** Runs all ajax requests at the same time and gets the code where the items are
		Parameters:
			links - the links that need to be revised
		Returns:
			Null
	*/
	function NS_getPagesHTML( links )	{
		for( var i = 0; i < links.length; i++ )	{ // For each one of the links
			var ajax = new AJAX(); // Create an AJAX object
			ajax.open( 'GET', links[i], true ); // And go search the page
			ajax.onreadystatechange = function()	{ // Set save page as callback method
				if( this.readyState == 4 )	{
					savePage( this.responseText );
					updateStatus( links.length );
				}
			};
			ajax.send( null ); // Then send
		}
	}
	
	/*
		IE GET PAGES HTML
		*** Runs one ajax request at a time. Internet Explorer only
		Parameters:
			None
		Returns:
			Null
	*/
	function IE_getPagesHTML( links )	{
		if( requestCount != links.length )	{ // If the pages retrieved quantity is less than the one we need
			var ajax = new AJAX(); // Create an AJAX object
			ajax.open( 'GET', links[ requestCount ], true ); // And go search the page
			ajax.onreadystatechange = function()	{ // Set save page as callback method
				if( this.readyState == 4 )	{
					savePage( this.responseText );
					updateStatus( links.length );
					IE_getPagesHTML( links ); // When we get a page we call the method again
				}
			};
			ajax.send( null ); // Then send
		}
	}
	
	/*
		SAVE PAGE
		*** Gets the items from a plain text page and sends them to the dom controller
		Parameters:
			html - page html that came from an ajax request
		Returns:
			Null
	*/
	function savePage( html )	{
		var temp = document.createElement( 'div' ); // Create a new DOM element to deposit the code
		temp.innerHTML = html; // Put the code
		var itemNodes = temp.getElementsByTagName( 'item' ); // Get all the item nodes inside that element
		for( var i = 0; i < itemNodes.length; i++ )	{ // Then for each item node
			
			var item = itemNodes[i];
			var name = itemNodes[i].getAttribute( 'name' );
			var cell = getElementByIdFromResponse( temp, name ); // Get the cell using the appropiate method
			var itemObject = new NarrowingItem( item, cell );
			
			var attributes = itemNodes[i].getAttribute( 'fields' ).split( ']' );
			for( var j = 0; j < ( attributes.length - 1 ); j++ )	{
				var attname = attributes[j].split( '[' )[1].split( ':' )[0];
				var attvalues = parseAttribute( attname, attributes[j].split( ':' )[1] );
				for( var k = 0; k < attvalues.length; k++ )	{
					itemObject.addAttribute( attname, attvalues[k] );
				}
			}
			
			DOMController.addItem( itemObject );
			
		}
		UIController.update(); // Update the ui
	}
	
	/*
	*/
	function updateStatus( maxRequests )	{
		requestCount++;
		if( requestCount == maxRequests )	{
			NarrowingOnLoad(); // defined on item list template
		}
		else if( DOMController.getAppliedFilters().length == 0 )	{
			UIController.changeStatus( 'Loading... ' + Math.round( ( 100 / ( maxRequests + 1 ) ) * ( requestCount + 1 ) ) + '%' );
		}
	}
	
	/*
		PARSE ATTRIBUTE
		*** Checks if the attirubte that came by parameter is a special attribute
		Parameters:
			name - name of the attribute to check
			value - value of the attribute, this will vary if the attribute is special
		Returns:
			Object
	*/
	function parseAttribute( name, value )	{
		if( isMultiple( name ) )	{
			return value.split( ';' );
		}
		else if( isRange( name ) )	{
			var range = getRange( name );
			return [ parseRange( range, value ) ];
		}
		else	{
			return [ value ];
		}
	}
	
	/*
		GET ELEMENT BY ID FROM RESPONSE
		*** Given the texte response from an ajax returns a DOM element matching the id parameter
		Parameters:
			id - the id of the element that will be returned
			response - a node outside the DOM
		Returns:
			DOM Element
	*/
	function getElementByIdFromResponse( response, id )	{
		var elements = response.getElementsByTagName( '*' );
		for( var i = 0; i < elements.length; i++ )	{
			if( elements[i].id == id )	{
				return elements[i];
			}
		}
	}
	
	/*
		GET NUMBER
		*** Given a string return a float
		Parameters:
			string - string to be parsed
		Returns:
			Float
	*/
	function getNumber( string )	{
		var valid = '.0123456789';
		var newString = '0';
		for( var i = 0; i < string.length; i++ )	{
			if( valid.indexOf( string.charAt( i ) ) != -1 )	{
				newString += string.charAt( i );
			}
		}
		return parseFloat( newString );
	}
	
	/* SPECIAL FILTER MANAGEMENT */
	
	/*
		IS MULTIPLE
		*** Returns a boolean depending if a multiple filter exists or not
		Parameters:
			name - name of the multiple value we are looking for
		Returns:
			Boolean
	*/
	function isMultiple( name )	{
		for( var i = 0; i < filtersMultiple.length; i++ )	{
			if( filtersMultiple[i].name == name )	{
				return true;
			}
		}
		return false;
	}
	
	/*
		IS RANGE
		*** Returns a boolean depending if a range filter exists or not
		Parameters:
			name - name of the range value we are looking for
		Returns:
			Boolean
	*/
	function isRange( name )	{
		for( var i = 0; i < filtersRange.length; i++ )	{
			if( filtersRange[i].name == name )	{
				return true;
			}
		}
		return false;
	}
	
	/*
		GET RANGE
		*** Given a name returns a range filter that matches
		Parameters:
			name - name of the multiple value we are looking for
		Returns:
			[ Object || Null ]
	*/
	function getRange( name )	{
		for( var i = 0; i < filtersRange.length; i++ )	{
			if( filtersRange[i].name == name )	{
				return filtersRange[i];
			}
		}
	}
	
	/*
		PARSE RANGE
		*** Transforms the value into a range formatted new value
		Parameters:
			range - range object
			value - string value like $1,300.20
		Returns:
			String
	*/
	function parseRange( range, value )	{
		value = getNumber( value );
		if( value != 0 )	{
			for( var i = 0; i < 5000; i += range.interval )	{
				if( i < value && ( i + range.interval ) >= value )	{
					value = range.before + i + ' - ' + ( i + range.interval ) + range.after;
					return value;
				}
			}
		}
		return 'Free!';
	}
	
	return this;
	
}
function NarrowingList( DOMElement, Paginations, cols, rows )	{
	
	var pages = [];
	var currentPage = 0;
	var cellCount = 0;
	
	var realRows = rows;
	
	/*
		ADD CELL
		*** Adds a new dom element to be displayed on the list
		Parameters:
			cell - dom elemen to be added to the list
		Returns:
			Null
	*/
	this.addCell = function( cell )	{
		var lastPage = pages[ pages.length - 1 ]; // Get the last page
		if( !lastPage.addCell( cell ) )	{ // If the cell cant be added is because the page is full
			var newPage = new NarrowingPage( cols, rows ); // Create a new empty page
			DOMElement.appendChild( newPage.getDOMElement() );
			newPage.addCell( cell );
			pages.push( newPage ); // Add it to the array
		}
		cellCount++;
		showLinks();
		selectPage( 0 );
	};
	
	/*
		RESET
		*** Deletes the list HTML , resets the pages array and the cell count
		Parameters:
			None
		Returns:
			Null
	*/
	this.reset = function()	{
		DOMElement.innerHTML = ''; // Removes the element html
		var newPage = new NarrowingPage( cols, rows ); // Create the first page
		DOMElement.appendChild( newPage.getDOMElement() ); // Put it into the element
		pages = [ newPage ]; // And add it to the array
		cellCount = 0; // Reset cell count
	};
	
	/*
		CHANGE STATUS
		*** Changes the text in the status bar of the pagination
		Parameters:
			code - the text that will appear in the status bar
		Returns:
			Null
	*/
	this.changeStatus = function( code )	{
		for( var i = 0; i < Paginations.length; i++ )	{
			Paginations[i].changeStatus( code );
		}
	};
	
	/*
		SET SHOW ALL
		*** Breaks the rows limit and displays all the items in the list
		Parameters:
			None
		Returns:
			Null
	*/
	this.setShowAll = function()	{
		rows = 999999;
	};
	
	/*
		REMOVE SHOW ALL
		*** Takes back the original page size
		Parameters:
			None
		Returns:
			Null
	*/
	this.removeShowAll = function()	{
		rows = realRows;
	};
	
	/*
		SHOW LINKS
		*** Set the links in the pagination for the current pages
		Parameters:
			None
		Returns:
			Null
	*/
	function showLinks()	{
		for( var i = 0; i < Paginations.length; i++ )	{
			Paginations[i].showLinks( pages.length, selectPage, nextPage, previousPage ); // Send the parameters to the pagination
		}
	}
	
	/*
		SELECT PAGE
		*** Shows the page by a given index and changes the pagination
		Parameters:
			pageNumber - number of the page to be selected
		Returns:
			Null
	*/
	function selectPage( pageNumber )	{
		for( var i = 0; i < Paginations.length; i++ )	{
			Paginations[i].selectPage( pageNumber ); // Set the selected link in the pagination
		}
		currentPage = pageNumber; // Set the current page to the number from the parameter
		for( var i = 0; i < pages.length; i++ )	{ // Pass through pages
			if( i == pageNumber )	{ // And if the page in this iteration matches the parameter
				pages[i].show(); // Show the page
			}
			else	{ // If not
				pages[i].hide(); // Hides
			}
		}
		updateStatus();
	}
	
	/*
		UPDATE STATUS
		*** Chenges the code inside the pagination's status bar to show the items that are being displayed
		Parameters:
			None
		Returns:
			Null
	*/
	function updateStatus()	{
		var pageCellCount = pages[ currentPage ].countCells();
		var from = ( ( rows * cols ) * currentPage ) + 1;
		var to = ( from + pageCellCount ) - 1;
		if( cellCount == 1 )	{
			var newStatus = 'Item 1';
		}
		else if( cellCount == 2 && ( rows * cols ) == 2 )	{
			var newStatus = 'Items 1 and 2';
		}
		else if( pageCellCount == 1 )	{
			var newStatus = 'Item ' + from + ' out of ' + cellCount;
		}
		else if( pageCellCount == 2 )	{
			var newStatus = 'Items ' + from + ' and ' + to + ' out of ' + cellCount;
		}
		else	{
			var newStatus = 'Items from ' + from + ' to ' + to + ' out of ' + cellCount;
		}
		for( var i = 0; i < Paginations.length; i++ )	{
			Paginations[i].changeStatus( newStatus );
		}
	}
	
	/*
		NEXT PAGE
		*** Select the page that corresponds to the index + 1
		Parameters:
			None
		Returns:
			Null
	*/
	function nextPage()	{
		var nextPageIndex = currentPage + 1; // Auxiliar variable for next page index
		if( nextPageIndex != pages.length )	{ // If there is a next page
			currentPage = nextPageIndex; // Set the current page to the next page
			selectPage( currentPage ); // And select the page
		}
	}
	
	/*
		PREVIOUS PAGE
		*** Select the page that corresponds to the index - 1
		Parameters:
			None
		Returns:
			Null
	*/
	function previousPage()	{
		var previousPageIndex = currentPage - 1; // Auxiliar variable for the previous page
		if( previousPageIndex != -1 )	{ // If the index is not less than 0
			currentPage = previousPageIndex; // Set the current page to the prvious
			selectPage( currentPage ); // And select the page
		}
	}
	
	// Add one page to the list when returning the object
	var newPage = new NarrowingPage( cols, rows );
	DOMElement.appendChild( newPage.getDOMElement() );
	pages.push( newPage );
	
	return this;
	
}
function NarrowingPage( cols, rows )	{
	
	var cellCount = 0;
	var DOMElement = document.createElement( 'div' );
	
	/*
		GET DOM ELEMENT
		*** Returns the page element with the table inside
		Parameters:
			None
		Returns:
			DOMElementDiv
	*/
	this.getDOMElement = function()	{
		return DOMElement;
	};
	
	/*
		ADD CELL
		*** Adds a new table cell to the pages dom element checking the number of columns and rows
		Parameters:
			cell - dom element to add inside the new table cell
		Returns:
			Boolean
	*/
	this.addCell = function( cell )	{
		if( cellCount == ( rows * cols ) )	{ // If the quantity of cells already added overpasses the number of items to display 
			return false; // Return false
		}
		else	{ // If theres room for more items
			if( tr.cells.length == cols )	{ // If the last TR element has reached the max number of columns replace it for a new empty one
				tr = table.insertRow( table.rows.length ); // Add a new TR element at the end of the table
			}
			var td = tr.insertCell( tr.cells.length ); // Add a new TD element
			td.appendChild( cell.cloneNode( true ) ); // Add a COPY of the cell to the TD element
			cellCount++; // Count one more item added
			return true;
		}
	};
	
	/*
		COUNT CELLS
		*** Returns the number of cells that are being displayed in this page
		Parameters:
			None
		Returns:
			Integer
	*/
	this.countCells = function()	{
		return cellCount;
	};
	
	/*
		SHOW
		*** Shows the page and allows having a transition effect
		Parameters:
			None
		Returns:
			Null
	*/
	this.show = function()	{
		DOMElement.className = 'list-page list-page-block';
	};
	
	/*
		HIDE
		*** Hides the page and allows having a transition effect
		Parameters:
			None
		Returns:
			Null
	*/
	this.hide = function()	{
		DOMElement.className = 'list-page list-page-none';
	};
	
	// Create, style and append the table and one row when returning the object
	var table = document.createElement( 'table' );
	table.cellPadding = 0;
	table.cellSpacing = 0;
	var tr = table.insertRow( 0 );
	DOMElement.appendChild( table );
	DOMElement.className = 'list-page';
	
	return this;
	
}
function NarrowingPagination( DOMElement )	{
	
	var linkList = [];
	var linkContainer = document.createElement( 'div' );
	var statusBar = document.createElement( 'div' );
	
	/*
		SHOW LINKS
		*** Displays links for a given number of pages. Throws the links inside linkContainer element
		Parameters:
			pageCount - number of links to display
			selectPageMethod - function to be called when a link is clicked
			nextPageMethod - function to be called when the next page link is clicked
			previousPageMethod - function to be called when the previous page link is clicked
		Returns:
			Null
	*/
	this.showLinks = function( pageCount, selectPageMethod, nextPageMethod, previousPageMethod )	{
		linkContainer.innerHTML = ''; // Reset the container HTML
		linkList = []; // Reset the link list
		
		var a = document.createElement( 'a' ); // Create link DOM element for the previous page button
		a.href = 'javascript:void(0)'; // Disable link
		a.className = 'pagination-link pagination-link-previous'; // Set class for formatting
		a.innerHTML = 'Previous'; // Set HTML
		a.onclick = previousPageMethod; // Add onclick event to change the page
		linkContainer.appendChild( a ); // Append the link to the container
		
		for( var i = 0; i < pageCount; i++ )	{
			var a = document.createElement( 'a' ); // Create link DOM element
			a.href = 'javascript:void(0)'; // Disable link
			a.className = 'pagination-link page'; // Set class for formatting
			a.innerHTML = ( i + 1 ); // Set HTML for displaying the page number
			a.pageNumber = i; // Set the number of page that the link corresponds to
			a.onclick = function()	{ // Add onclick event to change the page
				selectPageMethod( this.pageNumber );
			};
			linkContainer.appendChild( a ); // Append the link to the container
			linkList.push( a );
		}
		
		var a = document.createElement( 'a' ); // Create link DOM element for the next page button
		a.href = 'javascript:void(0)'; // Disable link
		a.className = 'pagination-link pagination-link-next'; // Set class for formatting
		a.innerHTML = 'Next'; // Set HTML
		a.onclick = nextPageMethod; // Add onclick event to change the page
		linkContainer.appendChild( a ); // Append the link to the container
		
		this.selectPage( 0 ); // Show the first link as selected
	};
	
	/*
		SELECT PAGE
		*** Changes the layout of the of the page selected
		Parameters:
			pageNumber - number of the page selected (corresponds to the page link index inside the array)
		Returns:
			Null
	*/
	this.selectPage = function( pageNumber )	{
		for( var i = 0; i < linkList.length; i++ )	{
			if( i == pageNumber )	{
				linkList[i].className = 'pagination-link pagination-link-page pagination-link-selected'; // Set class for selected
			}
			else	{
				linkList[i].className = 'pagination-link pagination-link-page'; // Set class for not selected
			}
		}
	};
	
	/*
		CHANGE STATUS
		*** Changes the HTML inside the status bar
		Parameters:
			code - HTML code that will be displayed
		Returns:
			Null
	*/
	this.changeStatus = function( code, onOffLink )	{
		statusBar.innerHTML = code;
		if( onOffLink )	{
			statusBar.appendChild( onOffLink );
		}
	};
	
	// Deploy and style the necessary dom elements when returning the element
	linkContainer.className = 'pagination-link-container';
	statusBar.className = 'pagination-status';
	DOMElement.appendChild( linkContainer );
	DOMElement.appendChild( statusBar );
	
	return this;
	
}
function NarrowingPannel( DOMElement )	{
	
	var boxes = [];
	
	/*
		SHOW FILTER BOXES
		*** Adds all the filters to the pannel and the onclick events for applying and removing
		Paramaters:
			allFilters - an array with all the available filters including applied
			appliedFitlers - an array with only the applied filters
			applyFilterMethod - the method to apply a filter
			removeFilterMethod - the method to remove a filter
		Returns:
			Null
	*/
	this.showFilterBoxes = function( allFilters, appliedFilters, applyFilterMethod, removeFilterMethod )	{
		allFilters.sort(); // Sort the items inside the filters to get them in order on the layout
		for( var i = 0; i < allFilters.length; i++ )	{ // For each one on the filters
			var box = getBox( allFilters[i].getName() ) || addBox( allFilters[i].getName() ); // Get the box for putting the link inside
			var a = document.createElement( 'a' ); // Create the link dom element
			a.filterName = allFilters[i].getName(); // Set the current filter name as an attribute for the dom element
			a.filterValue = allFilters[i].getValue(); // As well as the value
			a.href = 'javascript:void(0)'; // Disable the link
			a.innerHTML = allFilters[i].getValue() + ' <span>(' + allFilters[i].getItemCount() + ')</span>'; // Set the HTML to the filter value
			a.className = 'pannel-link'; // Set a class name to manage layout outside the scripts
			a.onclick = function()	{ // Set the onclick event to the apply filter method. If it must have the remove method it will be added in the boxLookover method
				applyFilterMethod( this.filterName, this.filterValue );
			};
			box.appendChild( a ); // Add to the box
		}
		boxLookover( appliedFilters, removeFilterMethod );
		appendBoxes();
	};
	
	/*
		RESET
		*** Deletes the pannel HTML and resets the boxes array
		Parameters:
			None
		Returns:
			Null
	*/
	this.reset = function()	{
		DOMElement.innerHTML = '';
		boxes = [];
	};
	
	/*
		BOX LOOKOVER
		*** Basically checks the boxes that have only one link and sets the remove method or a class poiting that is a came-along filter
		Parameters:
			appliedFilters - the array with applied filters in the system
			removeFilterMethod - the method to remove a filter
		Returns:
			Null
	*/
	function boxLookover( appliedFilters, removeFilterMethod )	{
		for( var i = 0; i < boxes.length; i++ )	{
			var links = boxes[i].getElementsByTagName( 'a' );
			if( links.length == 1 )	{
				boxes[i].className = 'pannel-box pannel-box-lonely';
				links[0].onclick = function()	{
					return false;
				};
			}
			for( var j = 0; j < links.length; j++ )	{
				if( filterIsApplied( appliedFilters, links[j].filterName, links[j].filterValue ) )	{
					boxes[i].className = 'pannel-box pannel-box-remove';
					links[j].className = 'pannel-link pannel-link-remove';
					links[j].onclick = function()	{
						removeFilterMethod( this.filterName, this.filterValue );
					};
				}
			}
		}
	}
	
	/*
		APPEND BOXES
		*** Puts all the boxes sorted by name into the pannel element
		Parameters:
			None
		Returns:
			Null
	*/
	function appendBoxes()	{
		boxes.sort(); // Sorts the boxes by name (NOT WORKING IN INTERNET EXPLORER TO STRING NOT BEING USED)
		for( var i = 0; i < boxes.length; i++ )	{
			DOMElement.appendChild( boxes[i] ); // Append each box
		}
	}
	
	/*
		GET BOX
		*** Returns a box if exists inside the array if not it returns false
		Parameters:
			name - box name to be found (key)
		Returns:
			[ DOMElement || false ]
	*/
	function getBox( name )	{
		for( var i = 0; i < boxes.length; i++ )	{ // Go through all the boxes
			if( boxes[i].filterName == name )	{ // If the box name matches the parameter
				return boxes[i]; // Return the box
			}
		}
		return false; // If the script gets to this point return false
	}
	
	/*
		ADD BOX
		*** Creates a dom element for a box and adds it to the box array. Also creates a heading
		Parameters:
			name - name to be assigned to the box
		Returns:
			Null
	*/
	function addBox( name )	{
		var box = document.createElement( 'div' ); // Create new dom element for a box
		var heading = document.createElement( 'h6' ); // Create a heading for the box
		heading.innerHTML = name; // Set the HTML for the heading, same as box name
		heading.className = 'pannel-box-heading'; // Set a class name to be formatted outside the script
		box.filterName = name; // Set box name as an attribute for the dom element
		box.className = 'pannel-box ' + name.replace( / /gi, '_' ).toLowerCase(); // Set a class name to be formatted outside the script
		box.appendChild( heading ); // Put the heading inside the box
		box.toString = function()	{ // Set the toString method to be used when sorting (NOT WORKING IN INTERNET EXPLORER)
			return this.filterName;
		};
		boxes.push( box ); // Add the box to the boxes array
		return box; // Return the box
	}
	
	/*
		FILTER IS APPLIED
		*** Returns a boolean depending if a filter given is inside an array given
		Parameters:
			list - array filled of filters to be checked
			filter - filter to be found inside the array
		Returns:
			Boolean
	*/
	function filterIsApplied( list, filterName, filterValue )	{
		for( var i = 0; i < list.length; i++ )	{ // Go through all the items of the array
			if( list[i].getName() == filterName && list[i].getValue() == filterValue )	{ // If the list item's name is matching the filter name
				return true; // Returns true
			}
		}
		return false; // If not returns false
	}
	
}
function NarrowingUIController( DOMController, List, Pannel )	{
	
	var currentSort = 'date';
	var paginated = true;
	
	/*
		UPDATE
		*** Gets all objects again and restarts the display
		Parameters:
			None
		Returns:
			Null
	*/
	this.update = function()	{
		updateList( currentSort );
		updatePannel();
	};
	
	/*
		CHANGE STATUS
		*** Changes the text in the status bar of the pagination
		Parameters:
			code - the text that will appear in the status bar
		Returns:
			Null
	*/
	this.changeStatus = function( code )	{
		List.changeStatus( code );
	};
	
	/*
		SORT BY PRICE
		*** Display the items sorted by price
		Parameters:
			DOMElement - link node to be assigned the sort method
		Returns:
			Null
	*/
	this.sortByPriceLink = function( DOMElement )	{
		var span = DOMElement.getElementsByTagName( 'span' )[0] || document.createElement( 'span' );
		DOMElement.href = 'javascript:void( "Sort by price" )';
		DOMElement.onclick = function()	{
			span.innerHTML = ( currentSort == 'price_l' ) ? '( Low to High )' : '( High to Low )';
			currentSort = ( currentSort == 'price_l' ) ? 'price_h' : 'price_l';
			updateList( currentSort );
			updatePannel();
			return false;
		};
	};
	
	/*
		SHOW ALL LINK
		*** Display all the itmes
		Parameters:
			DOMElement - link node to be assigned the show all method
		Returns:
			Null
	*/
	this.showAllLink = function( DOMElement )	{
		var span = DOMElement.getElementsByTagName( 'span' )[0] || document.createElement( 'span' );
		DOMElement.href = 'javascript:void( "Show all items" )';
		DOMElement.onclick = function()	{
			if( !paginated )	{
				showPaginatedItems();
				paginated = true;
				span.innerHTML = 'Off';
			}
			else	{
				showAllItems();
				paginated = false;
				span.innerHTML = 'On';
			}
			return false;
		};
	};
	
	/*
		SHOW ALL ITEMS
		*** Breaks the rows limit and displays all the items in the list
		Parameters:
			DOMElement - show all items link
		Returns:
			Null
	*/
	function showAllItems()	{
		List.setShowAll();
		updateList( currentSort );
	}
	
	/*
		SHOW PAGINATED ITEMS
		*** Resets the page size and updates
		Parameters:
			DOMElement - show all items link
		Returns:
			Null
	*/
	function showPaginatedItems()	{
		List.removeShowAll();
		updateList( currentSort );
	}
	
	/*
		UPDATE LIST
		*** Gets the cells again and restarts the list
		Parameters:
			None
		Returns:
			Null
	*/
	function updateList( sortCriteria )	{
		List.reset();
		var items = DOMController.getItems( sortCriteria );
		for( var i = 0; i < items.length; i++ )	{
			List.addCell( items[i].getCell() );
		}
	}
	
	/*
		UPDATE PANNEL
		*** Gets the filters again and restarts the pannel
		Parameters:
			None
		Returns:
			Null
	*/
	function updatePannel()	{
		Pannel.reset();
		var allFilters = DOMController.getAllFilters();
		var appliedFilters = DOMController.getAppliedFilters();
		var applyFilterMethod = applyFilter;
		var removeFilterMethod = removeFilter;
		Pannel.showFilterBoxes( allFilters, appliedFilters, applyFilterMethod, removeFilterMethod );
	}
	
	/*
		APPLY FILTER
		*** Calls the methods in the dom controller and updates the list and pannel
		Parameters:
			name - name of the filter to apply
			value - value of the filter to apply
		Returns:
			Null
	*/
	function applyFilter( name, value )	{
		DOMController.applyFilter( name, value );
		updateList( currentSort );
		updatePannel();
	}
	
	/*
		REMOVE FILTER
		*** Calls the methods in the dom controller and updates the list and pannel
		Parameters:
			name - name of the filter to remove
		Returns:
			Null
	*/
	function removeFilter( name, value )	{
		DOMController.removeFilter( name, value );
		updateList( currentSort );
		updatePannel();
	}
	
}
