gnm.FACETED_SEARCH_IS_ACTIVE = true

gnm.ID_DELIMITER = '!!!'

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Master list of all products in the current category
gnm.products = []

// Filtration control
gnm.filteredProducts = null         // List of all products currently visible based on filtration state
gnm.numFilteredProductPages = 0     // Number of pages of products given current filtration and pagination states
gnm.currPage = 0                    // Pagination state (currently-displayed page)
gnm.nonFilteredKeys =               // Product fields we ignore for purposes of filtration
    [ 'id','price','name','teaser','thumb_html' ]

// Image preloading control
gnm.preloadedImageMap = {}          // Mapping of urls to Images that have already preloaded
gnm.preloadingImageMap = {}         // Mapping of urls to Images that we are currently preloading

// Pagination display control
gnm.productListFadesIn = true       // Whether to fade in the product list or display it immediately
gnm.numProductsPerPage = 10         // Number of products to display on each page
gnm.maxPaginationLinks = 5          // Number of explicit pagination links (2,3,4 etc) displayed
gnm.numHtmlsPerTimeslice = 5        // Number of HTML fragments to generate in each timeslice

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Initialization

// Called by client code (presumably on DOM load), kicks off initialization
function initFacetedSearch()
{
    // Get product and configuration data from the server
    // GNM_ACTION: need an on error handler
    elapsed()
    new Ajax.Request('ajax_faceted_search.php?c=' + gnm.selectedSubCatId,{method:'get',onSuccess: onReceiveDataFromServer})
}

// Called when we receive data back from the server
function onReceiveDataFromServer(transport)
{
    traceTime('Server response')
    
    // Check for server error message
    if (transport.responseText.startsWith('ERROR:'))
    {
        // No good way to handle an ajax error so bail to home page
        // GNM_ACTION: redirect to reset page on grounds that it represents an internal logic error?
        //alert(transport.responseText)   // GNM_DEBUG: uncomment to see error message before redirect
        document.location.href = '/';
    }
    
    // Instantiate the json object
    var json = (transport.responseText == '') ? {} : eval('(' + transport.responseText + ')')  // transport.responseText.evalJSON()
    traceTime('JSON evaluation')
    
    // Output any server trace strings
    if (!gnm.SUPPRESS_SERVER_TRACE) json.traceStrs.each(function(s) { trace('Server message: ' + s) })
    
    // Extract configuration variables
    gnm.productListFadesIn = json.config.fadesIn
    gnm.numProductsPerPage = parseInt(json.config.limit)
    gnm.maxPaginationLinks = parseInt(json.config.maxLinks)
    gnm.numHtmlsPerTimeslice = parseInt(json.config.numHtmls)

    // Initialize the product data
    initProducts(json.products)
    if (gnm.products.length == 0)
    {
        _writeNoProductsHtml()
        return
    }
    traceTime('Product initialization')

    // Initialize the product filter data
    initFilters(json.filters)
    displayFilterControls()
    traceTime('Filter initialization');
    
    updateFilteredProductMap()
    displayFilteredProductPage()
}

function _writeNoProductsHtml()
{
    // No products in this category: display a message and bail
    var divElement = $('faceted-search-product-list')
    divElement.update()
    divElement.insert('<div id="pageNote">Nothing to display</div>')    // GNM_ACTION: style this
}

// Initialize the product data.  Each record contains all the data necessary to display one product's
// entry in the faceted search category list
function initProducts(productData)
{
    if (productData == '') return   // Didn't get any products from the server
    
    // Create a product record for each product
    var productKeys = $H(productData).keys()
    var i = 0
    productKeys.each(function(productKey)
    {
        var fieldMap = productData[productKey]
        var requiresCustomization = fieldMap['requires_customization']
        gnm.products.push({fieldMap:fieldMap,html:null,index:i,imageUrl:fieldMap['image_url'],id:'faceted-search-product-' + i,requiresCustomization:requiresCustomization})
        i++
    })
}

// Initialize the filter data
function initFilters(filtrationData)
{
    // Initialize the search filters
    gnm.filterMap = filtrationData
                                     
     $H(gnm.filterMap).keys().each(function(filterKey)
     {
         var filter = gnm.filterMap[filterKey]
         
         trace('filter key:',filterKey,'(' + filter.type + ')')
         
         switch (filter.type)
         {
             case 'select':
                 filter.values = _getUniqueFieldValues(filterKey)
                 m0 = {}
                 m1 = {}
                 filter.values.each(function(value)
                 {
                     var mungedValue = value.toLowerCase().replace(/ |"/g,'-')
                     m0[value] = mungedValue
                     m1[mungedValue] = value
                 })
                 filter.valueToMungedValueMap = m0
                 filter.mungedValueToValueMap = m1
                 
                 filter.currValue = 'any'
                 
                 break
             case 'multi':
                 filter.values = _getUniqueFieldValues(filterKey)
                 m0 = {}
                 m1 = {}
                 filter.values.each(function(value)
                 {
                     var mungedValue = value.toLowerCase().replace(/ |"/g,'-')
                     m0[value] = mungedValue
                     m1[mungedValue] = value
                 })
                 filter.valueToMungedValueMap = m0
                 filter.mungedValueToValueMap = m1
                 
                 filter.currValues = filter.values.collect(function(value) { return filter.valueToMungedValueMap[value] })
                 break
             case 'search':
                 filter.currValue = ''
                 break
             default:
                 break       // GNM_ACTION
         }
     })
}

// Return the list of unique values for a given product field
function _getUniqueFieldValues(fieldKey)
{
    var uniqueValueMap = {}
    gnm.products.each(function(productMap)
    {
        var fields = productMap.fieldMap[fieldKey]
        if (fields === undefined) fields = []               // GNM_ACTION
        if (fields.constructor !== Array) fields = [fields]
        fields.each(function(field)
        {
            var value = field.strip()
            if (value.length > 0) uniqueValueMap[value] = true
        })
    })
    return $H(uniqueValueMap).keys().sort()
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Populate gnm.filteredProducts with all products which should be displayed
// given the current filter state
function updateFilteredProductMap()
{
    elapsed()
    
    // Accumulate all products which are not rejected by one of the filters
    var fieldNames = $H(gnm.filterMap).keys()
    
    gnm.filteredProducts = gnm.products.select(function(productMap)
    {
        // For each product, return true if all filters accept it, false if any reject it
        return undefined === fieldNames.detect(function(field)
        {
            // Quick out if the product doesn't have the field
            if (productMap.fieldMap[field] === undefined) return false      // GNM_ACTION
            
            // For each field, the product may have one value or an array of values.  Force to array
            var values = productMap.fieldMap[field]
            if (values.constructor !== Array) values = [values]
            
            // Each filter type has its own accept logic
            var filterMap = gnm.filterMap[field]
            switch (filterMap.type)
            {
                case 'select':
                    // Quick out if the special 'any' value is selected
                    if (filterMap.currValue == 'any') return false
                    
                    // Test the filter value against each product value.  If none of the product values match
                    // the filter rejects the product
                    return undefined === values.detect(function(value) { return filterMap.valueToMungedValueMap[value] == filterMap.currValue })
                case 'multi':
                    // Quick out if all possible multi options are selected
                    if (filterMap.currValues.length == filterMap.values.length) return false
                    
                    // Test each filter value against each product value.  If no product value matches a
                    // filter value the filter rejects the product
                    return undefined === filterMap.currValues.detect(function(filterValue)
                    {
                        // Test each product value against the current filter value.
                        return undefined !== values.detect(function(value)
                        {
                            var mungedValue = filterMap.valueToMungedValueMap[value]
                            return mungedValue == filterValue
                        })
                    })
                case 'search':
                    // Quick out if the search box is empty
                    var currValue = filterMap.currValue.toLowerCase()
                    if (currValue == '') return false

                    // Test the filter value against each product value.  If none of the product values match
                    // the filter rejects the product
                    return undefined === values.detect(function(value) { return value.toLowerCase().include(currValue) })    // GNM_ACTION: cache lower-cased values in gnm.products
                default:
                    // GNM_ACTION: internal error!
                    return true
            }
        })
    })
    
    // Calculate the number of pages needed
    gnm.numFilteredProductPages = Math.floor(gnm.filteredProducts.length / gnm.numProductsPerPage)
    if (gnm.filteredProducts.length % gnm.numProductsPerPage) gnm.numFilteredProductPages++

    // Force the page to zero to guarantee it's legal.  A bit of hacky overkill
    gnm.currPage = 0
    
    traceTime('update filtered product map')
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Initialize the search controls
function displayFilterControls()
{
    var template = new Template( [ '<div>',
                                     '<table border="0">',
                                       '<tr height="60">',
                                         '#{cell_0_0} #{cell_0_1} #{cell_0_2} #{cell_0_3}',
                                       '</tr>',
                                       '<tr height="60">',
                                         '#{cell_1_0} #{cell_1_1} #{cell_1_2} #{cell_1_3}',
                                       '</tr>',
                                     '</table>',
                                   '</div>'
                                 ].join('') )
    var cellTemplate = new Template('<td width="137" rowSpan="#{numRows}" colSpan="#{numCols}"  valign="top">#{content}</td>')
               
    var emptyCellStr = '<td width="137"  valign="top"></td>'
    var missingCellStr = '';
    
    var templateObj = { cell_0_0: emptyCellStr, cell_0_1: emptyCellStr, cell_0_2: emptyCellStr, cell_0_3: emptyCellStr,
                        cell_1_0: emptyCellStr, cell_1_1: emptyCellStr, cell_1_2: emptyCellStr, cell_1_3: emptyCellStr }
                        
    var keyTemplate = new Template('cell_#{row}_#{col}')
                                 
    $H(gnm.filterMap).keys().each(function(filterKey)
    {
        var filterMap = gnm.filterMap[filterKey]
        
        var numRows = 1
        var numCols = 1
        
        var a = filterMap.location.split(',')
        aa = a[0].split(':')
        var row = aa[0]
        if (aa.length > 1) numRows = aa[1]
        
        aa = a[1].split(':')
        var col = aa[0]
        if (aa.length > 1) numCols = aa[1]
        
        var key = keyTemplate.evaluate({row:row,col:col})
        if (templateObj[key] != emptyCellStr) trace('WARNING: filter control location conflict')        // GNM_ACTION
        
        switch (filterMap.type)
        {
            case 'select':
                var s = makeSelectHtml(filterKey,filterMap.prettyName,filterMap.values)
                break
            case 'multi':
                var s = makeMultiSelectHtml(filterKey,filterMap.prettyName,filterMap.values)
                break
            case 'search':
                var s = makeSearchHtml(filterKey,filterMap.prettyName)
                break
            default:
                var s = ''      // GNM_ACTION: internal error
                break
        }
        
        templateObj[key] = cellTemplate.evaluate({content:s,numRows:numRows,numCols:numCols})
        
        for (var i = 0; i < numRows; i++)
        {
            for (var j = 0; j < numCols; j++)
            {
                key = keyTemplate.evaluate({row:i,col:j})
                if (templateObj[key] == emptyCellStr) templateObj[key] = missingCellStr
            }
        }
    })
                                 
    var s = template.evaluate(templateObj)
    
    var divElement = $('faceted-search-controls')
    divElement.update()
    divElement.insert(s)
    
    $$('.faceted-search-single-select').each(function(element) { element.observe('change',onSelectChange) })
    $$('.faceted-search-multi-select-option').each(function(element) { element.observe('click',onMultiSelectClick) })
    $$('.faceted-search-typable').each(function(element) { element.observe('keypress',onSearchKeyPress) })
}

// Display the current product list page given current filter state and pagination state
function displayFilteredProductPage()
{
    elapsed()   // Reset the timestamp
    
    var divElement = $('faceted-search-product-list')       // GNM_ACTION: move to constant
    
    // If nothing returned, display notice and bail
    if (gnm.numFilteredProductPages == 0)
    {
        divElement.update()
        divElement.insert('<div id="pageNote">Nothing to display</div>')
        return
    }
    
    // Figure out first and last indices based on current page and pagination settings
    gnm.firstIndex = gnm.currPage * gnm.numProductsPerPage
    gnm.lastIndex = gnm.firstIndex + gnm.numProductsPerPage - 1
    if (gnm.lastIndex >= gnm.filteredProducts.length) gnm.lastIndex = gnm.filteredProducts.length - 1

    // Display the loading image
    divElement.update()
    divElement.insert('<p style="text-align:center;margin-top:2em;"><img src="images/ajax-loader.gif"></p>')

    // Make sure we're scrolled to the top of the window
    window.scrollTo(0,0)
    
    traceTime('display prep')
    
    gnm.preloadIsDone = false
    preloadImages()

    gnm.htmlIsDone = false
    new PeriodicalExecuter(prepFilteredProductHtml,0.001)
}

function prepFilteredProductHtml(periodicalExecutor)
{
    var count = 0
    for (var i = gnm.firstIndex; i <= gnm.lastIndex; i++)
    {
        var product = gnm.filteredProducts[i]
        if (product.html === null)
        {
            product.html = makeProductListingHtml(product)
            
            if (++count >= gnm.numHtmlsPerTimeslice) return
        }
    }
    
    periodicalExecutor.stop()
    trace('Done preparing HTML')
    
    if (gnm.preloadIsDone) _writeFilteredProductListHtml()
    else gnm.htmlIsDone = true
}

// Preload all the images so they're in-cache when we draw the product list (smoothes out
// the animation)
function preloadImages()
{
    // Accumulate all the images we need to preload for the current filtered search page
    var preloadingImages = {}
    for (var i = gnm.firstIndex; i <= gnm.lastIndex; i++)
    {
        var product = gnm.filteredProducts[i]
        var imageUrl = product.imageUrl
        if ( gnm.preloadedImageMap[imageUrl] !== undefined || gnm.preloadingImageMap[imageUrl] !== undefined) continue
        
        var image = new Image()
        preloadingImages[imageUrl] = image
        gnm.preloadingImageMap[imageUrl] = image
    }
    
    // Kick off the preload
    $H(preloadingImages).keys().each(function(key) { preloadingImages[key].src = key })
    
    // Set a watchdog to observe the images as they preload
    new PeriodicalExecuter(onPreloadCheckTimer,0.001)
}

// Called periodically while images are pre-loading
function onPreloadCheckTimer(periodicalExecutor)
{
    var keys = $H(gnm.preloadingImageMap).keys()
    keys.each(function(key)
    {
        var image = gnm.preloadingImageMap[key]
        if (image.complete)
        {
            // Move the image from the preloading list to the preloaded list
            var imageUrl = image.src
            gnm.preloadedImageMap[imageUrl] = gnm.preloadingImageMap[imageUrl]
            delete gnm.preloadingImageMap[imageUrl]
        }
    })
    
    // When there's nothing left to do, we're done
    var numPreloading = $H(gnm.preloadingImageMap).keys().length
    if (numPreloading == 0)
    {
        periodicalExecutor.stop()
        trace('Done preloading images')
        
        if (gnm.htmlIsDone) _writeFilteredProductListHtml()
        else gnm.preloadIsDone = true
    }
}

// Generate the product list html
function _writeFilteredProductListHtml()
{
    traceTime('fuck 1')
    traceTime('HTML generation')
    traceTime('fuck 2')
    
    var divElement = $('faceted-search-product-list')       // GNM_ACTION: move to constant
    
    var htmlFragments = []
    var ids = []
    for (var i = gnm.firstIndex; i <= gnm.lastIndex; i++)
    {
        htmlFragments.push(gnm.filteredProducts[i].html)
        ids.push(gnm.filteredProducts[i].id)
    }
    var tableHtml =  htmlFragments.join('')
    
    var linkStr = makePaginationLinkHtml(gnm.currPage)

    var a = [   '<div class="pagination">',
                  'Page: ' + linkStr,
                '</div>'
            ]
    var paginationHtml = a.join('')

    // Stop observing pagination links
    $$('.pagination_link').each(function(element) { element.stopObserving('click',onPaginationClick) })
     
    // Rebuild the product list
    divElement.update()
    divElement.insert('<div id="productList"><table>' + tableHtml + '</table><div id="btmOptions">' + paginationHtml + '</div></div>')

    // Observe pagination links
    $$('.pagination_link').each(function(element) { element.observe('click',onPaginationClick) })
    
    // Kick off the fade-in effect
    if (gnm.productListFadesIn)
    {
        for (var i = 0; i < ids.length; i++)
        {
            var element = $(ids[i])
            element.appear({duration:0.3,from:0.0,to:1.0,delay:0.1 * i})
        }
    }
    
    traceTime('DOM display');
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Event handlers

// Called when a faceted search select control is modified
function onSelectChange(event)
{
    var selectElement = event.element()
    
    // Find the selected option
    optionElement = selectElement.childElements().detect(function(element) { if (element.selected) return true })
    var id = optionElement.id
    var a = id.split(gnm.ID_DELIMITER)
    var field = a[1]
    var value = a[2]
    var filter = gnm.filterMap[field]
    trace('Single select:',field,'changed to',value,'from',filter.currValue)
    
    if (value != filter.currValue)
    {
        filter.currValue = value
    
        updateFilteredProductMap()
        displayFilteredProductPage()
    }
}

// Called when the user clicks in a faceted search multi-select control
function onMultiSelectClick(event)
{
    var clickedElement = event.element()
    var id = clickedElement.id
    var a = id.split(gnm.ID_DELIMITER)
    var filterKey = a[1]
    var value = a[2]
    var isChecked = clickedElement.checked
    trace('multi-select:',filterKey,value,isChecked)
    
    var filterMap = gnm.filterMap[filterKey]
    filterMap.currValues.each(function(v) { trace(v) })
    if (isChecked) filterMap.currValues.push(value)
    else filterMap.currValues = filterMap.currValues.without(value)
    filterMap.currValues.each(function(v) { trace(v) })
    
    updateFilteredProductMap()
    displayFilteredProductPage()
}

// Called when the user types in a faceted search text control
function onSearchKeyPress(event)
{
    var keyCode = event.keyCode
    if (keyCode == Event.KEY_RETURN)
    {
        event.stop()

        var focusElement = event.element()
        var value = focusElement.value
        var id = focusElement.id
        var a = id.split(gnm.ID_DELIMITER)
        var filterKey = a[1]
        var filterMap = gnm.filterMap[filterKey]
        
        trace('search:',filterKey,value,filterMap.currValue)
        
        if (value != filterMap.currValue)
        {
            filterMap.currValue = value
            
            updateFilteredProductMap()
            displayFilteredProductPage()
        }
    }
}

// Called when the user clicks a pagination link
function onPaginationClick(event)
{
    event.stop()

    var clickedElement = event.element()
    var s = clickedElement.innerHTML

    if (s == '&lt;&lt;') gnm.currPage = 0
    else if (s == '&gt;&gt;') gnm.currPage = gnm.numFilteredProductPages - 1
    else gnm.currPage = parseInt(s) - 1
    
    displayFilteredProductPage()
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Helpers

// Generate the HTML for a single product listing
function makeProductListingHtml(product)
{
    var o = product.fieldMap
    var rowNum = product.index
    var price = (o['price'])
    var pricePerFoot = (o['price_per_foot'])
    if (o['sold_by'] == 'each')
    {
        if (parseFloat(price) == 0.0) price = 'Call for price'
        else price = '$' + price
    }
    else price = '$' + pricePerFoot + '/ft'
    
    var productUrl = 'cart.php?m=product_detail&amp;p=#{id}'
    if (true)
    {
        var regExp = /["'|\/\\#%&*? ]/g                         // GNM_SEO: convert to SEO URL.  Keep consistent with Product.class.php
        productUrl = '/Products/' + o['name'].replace(regExp,'-') + '.html'
    }
    
    var addHtml
    if (price == 'Call for price')
    {
        addHtml = '<div class="detailBtn">' + 
                    '<a href="cart.php?m=content&amp;page=5">Contact Us</a>' +
                  '</div>'
    }
    else if (product.requiresCustomization)
    {
        addHtml = '<div class="detailBtn">' + 
                    '<a href="#{product_url}">Customize</a>' +
                  '</div>'
    }
    else
    {
        addHtml = '<div class="addToCartBtn">' +
                    '<a href="cart.php?m=add&amp;productID=#{id}&amp;quantity=1" class="sublink">Add to Cart</a>' +
                    '</div>'
    }
    
    var t = new Template([  '<tr>',
                             (gnm.productListFadesIn ?
                              '<td id="faceted-search-product-#{row_num}" class="content-odd1" style="zoom:1;filter:alpha(opacity=0);opacity:0.0;">' :
                              '<td id="faceted-search-product-#{row_num}" class="content-odd1">'
                             ),
                                '<div class="photo">',
                                  '<a href="#{product_url}">',
                                    '#{image_html}',
                                  '</a>',
                                '</div>',
                                '<div class="title">',
                                  '<a href="#{product_url}">#{name}</a>',
                                '</div> ',
                                '<div class="description">#{teaser}</div>',
                                '<div class="details">',
                                  '<div class="sprice">#{price}</div>',
                                  '<div class="buttons">',
                                    addHtml,
                                  '</div>',
                                '</div>',
                              '</td>',
                            '</tr>'
                         ].join(' '))
                          
    return t.evaluate({name:o['name'],id:o['id'],product_url:productUrl,price:price,row_num:rowNum,teaser:o['teaser'],image_html:o['thumb_html']})
}

// Generate the HTML for a native select control
// GNM_ACTION: make sure "any" is not in the list!
function makeSelectHtml(name,legend,values)
{
    var template = new Template( [ '<form class="select-input">',
                                     '<fieldset>',
                                       '<legend>#{legend}</legend>',
                                       '<select id="#{selectId}" name="#{name}" class="faceted-search-single-select">',
                                         '#{optionStr}',
                                       '</select>',
                                     '</fieldset>',
                                   '</form>'
                                 ].join(''))
    var optionTemplate = new Template( [ '<option value="#{value}" ',
                                                 'id="#{optionId}" ',
                                                 'class="faceted-search-single-select-option" ',
                                                 '#{selectedStr}>',
                                           '#{text}',
                                         '</option>'
                                       ].join(''))
                                       
    var selectId = ['select',gnm.ID_DELIMITER,name].join('')
    
    var optionStrs = values.collect(function(value)
    {
        var mungedValue = value.toLowerCase().replace(/ |"/g,'-')
        var optionId = ['select',gnm.ID_DELIMITER,name,gnm.ID_DELIMITER,mungedValue].join('')
        return optionTemplate.evaluate({value:mungedValue,text:value,optionId:optionId,selectedStr:''})
        
        return '<option value="' + mungedValue + '" id="' + optionId + '" class="faceted-search-single-select-option">' + value + '</option>'
    })
    
    // Prepend special-case 'Any' option
    var anyOptionId = ['select',gnm.ID_DELIMITER,name,gnm.ID_DELIMITER,'any'].join('')
    optionStrs.unshift(optionTemplate.evaluate({value:'any',text:'Any',optionId:anyOptionId,selectedStr:'selected="selected"'}))
    
    return template.evaluate({name:name,legend:legend,selectId:selectId,optionStr:optionStrs.join('')})
}

// Generate the HTML for a custom multi-select control
function makeMultiSelectHtml(name,legend,values)
{
    var template = new Template( [ '<form class="multi-select-input">',
                                   '  <fieldset><legend>#{legend}</legend>',
                                   '    <div>',
                                   '      #{rowStr}',
                                   '    </div>',
                                   '  </fieldset>',
                                   '</form>'
                                 ].join('') )
    var rowTemplate = new Template( [ '<p class="#{rowClass}">',
                                      '  <input type="checkbox" ',
                                      '         id="#{id}" ',
                                      '         name="#{name}" ',
                                      '         class="faceted-search-multi-select-option" ',
                                      '         value="#{value}" ',
                                      '         checked="checked" >',
                                      '  <label for="#{id}">',
                                      '    #{value}',
                                      '  </label>',
                                      '</p>'
                                    ].join('') )

    var rowStrs = values.collect(function(value)
    {
        var mungedValue = gnm.filterMap[name].valueToMungedValueMap[value]
        var id = ['multi',gnm.ID_DELIMITER,name,gnm.ID_DELIMITER,mungedValue].join('')
        var rowClass = (this.i++ % 2) ? 'odd' : 'even'
        return rowTemplate.evaluate({name:name,value:value,id:id,rowClass:rowClass})
    },{i:0})
    
    return template.evaluate({legend:legend,rowStr:rowStrs.join('')})
}

// Generate the HTML for a native text input field
function makeSearchHtml(name,legend)
{
    var template = new Template([ '<form class="search-input">',
                                    '<fieldset>',
                                      '<legend>#{legend}</legend>',
                                      '<input type="text" ',
                                              'name="#{name}" ',
                                              'value="" ',
                                              'id="#{id}" ',
                                              'class="faceted-search-typable" ',
                                              'size="15" ',
                                              'maxlength="20" />',
                                    '</fieldset>',
                                  '</form>'
                                ].join(''))
                                
    var id = ['search',gnm.ID_DELIMITER,name].join('')
    
    return template.evaluate({name:name,legend:legend,id:id})
}

// Generate the HTML for the pagination links
function makePaginationLinkHtml(pageNum)
{
    var maxPageLinksEitherSide = (gnm.maxPaginationLinks - 1) / 2
    var low = pageNum - maxPageLinksEitherSide
    if (low < 0) low = 0
    var high = pageNum + maxPageLinksEitherSide
    if (high - low < gnm.maxPaginationLinks - 1) high = low + gnm.maxPaginationLinks - 1
    if (high >= gnm.numFilteredProductPages) high = gnm.numFilteredProductPages - 1
    
    var a = []
    if (low > 0) a.push('<a href=""  class="link pagination_link">&lt;&lt;</a>')
    for (var i = low; i <= high; i++)
    {
        var dispI = i + 1
        if (i == pageNum) a.push('' + dispI)
        else a.push('<a href=""  class="link pagination_link">' + dispI + '</a>')
    }
    if (high < gnm.numFilteredProductPages - 1) a.push('<a href=""  class="link pagination_link">&gt;&gt;</a>')
    
    return a.join('&nbsp;&nbsp;')
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Generate the HTML for a native multi-select control
function makeMultiSelectHtml_native(name,legend,values)
{
    var a = [ '<form style="float:left;width:25%">',
                '<fieldset><legend>#{legend}</legend>',
                  '<select name="foo" multiple="multiple" size="5">' ]
    a.push(         '<option value="1">Option 1</option>')
    a.push(         '<option value="2">Option 2</option>')
    a.push(         '<option value="3">Option 3</option>')
    a.push(         '<option value="4">Option 4</option>')
    a.push(       '</select>')
    a.push(     '</fieldset>')
    a.push(   '</form>')
    
    var t = new Template(a.join(''))
    return t.evaluate({legend:legend})
}

/*
<li><a href="#">Brand 1</a></li>
<li><a href="#">Brand 2</a></li>
<li><a href="#">Brand 3</a></li>
<li><a href="#">Brand 4</a></li>
<li><a href="#">Brand 5</a></li>
<li><a href="#">More Brands</a></li>

cart.php?m=search_results&catID=&search=black+master&venID=
*/


