Example #1
0
 public function load_facets()
 {
     if ('off' == $this->facetedmenus) {
         return;
     }
     $output = '';
     $this->filters();
     $Storefront = ShoppStorefront();
     if (!$Storefront) {
         return;
     }
     $CategoryFilters =& $Storefront->browsing[$this->slug];
     $Filtered = new ProductCategory($this->id);
     $filtering = array_merge($Filtered->facetsql(array()), array('ids' => true, 'limit' => 1000));
     $Filtered->load($filtering);
     $ids = join(',', $Filtered->worklist());
     // Load price facet filters first
     if ('disabled' != $this->pricerange) {
         $Facet = $this->facets['price'];
         $Facet->link = add_query_arg(array('s_ff' => 'on', urlencode($Facet->slug) => ''), shopp('category', 'get-url'));
         if (!$this->loaded) {
             $this->load();
         }
         if ('auto' == $this->pricerange) {
             $ranges = auto_ranges($this->pricing->average, $this->pricing->max, $this->pricing->min, $this->pricing->uniques);
         } else {
             $ranges = $this->priceranges;
         }
         if (!empty($ranges)) {
             $casewhen = '';
             foreach ($ranges as $index => $r) {
                 $minprice = $r['max'] > 0 ? " AND minprice <= {$r['max']}" : "";
                 $casewhen .= " WHEN (minprice >= {$r['min']}{$minprice}) THEN {$index}";
             }
             $sumtable = ShoppDatabaseObject::tablename(ProductSummary::$table);
             $query = "SELECT count(*) AS total, CASE {$casewhen} END AS rangeid\n\t\t\t\t\tFROM {$sumtable}\n\t\t\t\t\tWHERE product IN ({$ids}) GROUP BY rangeid";
             $counts = sDB::query($query, 'array', 'col', 'total', 'rangeid');
             foreach ($ranges as $id => $range) {
                 if (!isset($counts[$id]) || $counts[$id] < 1) {
                     continue;
                 }
                 $label = money($range['min']) . ' &mdash; ' . money($range['max']);
                 if ($range['min'] == 0) {
                     $label = sprintf(__('Under %s', 'Shopp'), money($range['max']));
                 }
                 if ($range['max'] == 0) {
                     $label = sprintf(__('%s and up', 'Shopp'), money($range['min']));
                 }
                 $FacetFilter = new ProductCategoryFacetFilter();
                 $FacetFilter->label = $label;
                 $FacetFilter->param = urlencode($range['min'] . '-' . $range['max']);
                 $FacetFilter->count = $counts[$id];
                 $Facet->filters[$FacetFilter->param] = $FacetFilter;
             }
         }
         // END !empty($ranges)
     }
     // Identify facet menu types to treat numeric and string contexts properly @bug #2014
     $custom = array();
     foreach ($this->facets as $Facet) {
         if ('custom' == $Facet->type) {
             $custom[] = sDB::escape($Facet->name);
         }
     }
     // Load spec aggregation data
     $spectable = ShoppDatabaseObject::tablename(Spec::$table);
     $query = "SELECT spec.name,spec.value,\n\t\t\tIF(0 >= FIND_IN_SET(spec.name,'" . join(",", $custom) . "'),IF(spec.numeral > 0,spec.name,spec.value),spec.value) AS merge, count(DISTINCT spec.value) AS uniques,\n\t\t\tcount(*) AS count,avg(numeral) AS avg,max(numeral) AS max,min(numeral) AS min\n\t\t\tFROM {$spectable} AS spec\n\t\t\tWHERE spec.parent IN ({$ids}) AND spec.context='product' AND spec.type='spec' AND (spec.value != '' OR spec.numeral > 0) GROUP BY merge";
     $specdata = sDB::query($query, 'array', 'index', 'name', true);
     foreach ($this->specs as $spec) {
         if ('disabled' == $spec['facetedmenu']) {
             continue;
         }
         $slug = sanitize_title_with_dashes($spec['name']);
         if (!isset($this->facets[$slug])) {
             continue;
         }
         $Facet =& $this->facets[$slug];
         $Facet->link = add_query_arg(array('s_ff' => 'on', urlencode($Facet->slug) => ''), shopp('category', 'get-url'));
         // For custom menu presets
         switch ($spec['facetedmenu']) {
             case 'custom':
                 $data = $specdata[$Facet->name];
                 $counts = array();
                 foreach ($data as $d) {
                     $counts[$d->value] = $d->count;
                 }
                 foreach ($spec['options'] as $option) {
                     if (!isset($counts[$option['name']]) || $counts[$option['name']] < 1) {
                         continue;
                     }
                     $FacetFilter = new ProductCategoryFacetFilter();
                     $FacetFilter->label = $option['name'];
                     $FacetFilter->param = urlencode($option['name']);
                     $FacetFilter->count = $counts[$FacetFilter->label];
                     $Facet->filters[$FacetFilter->param] = $FacetFilter;
                 }
                 break;
             case 'ranges':
                 foreach ($spec['options'] as $i => $option) {
                     $matches = array();
                     $format = '%s-%s';
                     $next = 0;
                     if (isset($spec['options'][$i + 1])) {
                         if (preg_match('/(\\d+[\\.\\,\\d]*)/', $spec['options'][$i + 1]['name'], $matches)) {
                             $next = $matches[0];
                         }
                     }
                     $matches = array();
                     $range = array("min" => 0, "max" => 0);
                     if (preg_match('/^(.*?)(\\d+[\\.\\,\\d]*)(.*)$/', $option['name'], $matches)) {
                         $base = $matches[2];
                         $format = $matches[1] . '%s' . $matches[3];
                         if (!isset($spec['options'][$i + 1])) {
                             $range['min'] = $base;
                         } else {
                             $range = array("min" => $base, "max" => $next - 1);
                         }
                     }
                     if ($i == 1) {
                         $href = add_query_arg($slug, urlencode(sprintf($format, '0', $range['min'])), $link);
                         $label = __('Under ', 'Shopp') . sprintf($format, $range['min']);
                         $list .= '<li><a href="' . $href . '">' . $label . '</a></li>';
                     }
                     $href = add_query_arg($slug, urlencode(sprintf($format, $range['min'], $range['max'])), $link);
                     $label = sprintf($format, $range['min']) . ' &mdash; ' . sprintf($format, $range['max']);
                     if ($range['max'] == 0) {
                         $label = sprintf($format, $range['min']) . ' ' . __('and up', 'Shopp');
                     }
                     $list .= '<li><a href="' . $href . '">' . $label . '</a></li>';
                 }
                 break;
             default:
                 if (!isset($specdata[$Facet->name])) {
                     break;
                 }
                 $data = $specdata[$Facet->name];
                 if (!is_array($data)) {
                     $data = array($data);
                 }
                 if ($data[0]->min + $data[0]->max + $data[0]->avg == 0) {
                     // Generate facet filters from text values
                     foreach ($data as $option) {
                         $FacetFilter = new ProductCategoryFacetFilter();
                         $FacetFilter->label = $option->value;
                         $FacetFilter->param = urlencode($option->value);
                         $FacetFilter->count = $option->count;
                         $Facet->filters[$FacetFilter->param] = $FacetFilter;
                     }
                 } else {
                     $data = reset($data);
                     $format = '%s';
                     if (preg_match('/^(.*?)(\\d+[\\.\\,\\d]*)(.*)$/', $data->value, $matches)) {
                         $format = $matches[1] . '%s' . $matches[3];
                     }
                     $ranges = auto_ranges($data->avg, $data->max, $data->min, $data->uniques);
                     if (!empty($ranges)) {
                         $casewhen = '';
                         foreach ($ranges as $index => $r) {
                             $max = $r['max'] > 0 ? " AND spec.numeral <= {$r['max']}" : "";
                             $casewhen .= " WHEN (spec.numeral >= {$r['min']}{$max}) THEN {$index}";
                         }
                         $query = "SELECT count(*) AS total, CASE {$casewhen} END AS rangeid\n\t\t\t\t\t\t\t\tFROM {$spectable} AS spec\n\t\t\t\t\t\t\t\tWHERE spec.parent IN ({$ids}) AND spec.name='{$Facet->name}' AND spec.context='product' AND spec.type='spec' AND spec.numeral > 0 GROUP BY rangeid";
                         $counts = sDB::query($query, 'array', 'col', 'total', 'rangeid');
                         foreach ($ranges as $id => $range) {
                             if (!isset($counts[$id]) || $counts[$id] < 1) {
                                 continue;
                             }
                             $label = sprintf($format, $range['min']) . ' &mdash; ' . sprintf($format, $range['max']);
                             if ($range['min'] == 0) {
                                 $label = __('Under ', 'Shopp') . sprintf($format, $range['max']);
                             } elseif ($range['max'] == 0) {
                                 $label = sprintf($format, $range['min']) . ' ' . __('and up', 'Shopp');
                             }
                             $FacetFilter = new ProductCategoryFacetFilter();
                             $FacetFilter->label = $label;
                             $FacetFilter->param = urlencode($range['min'] . '-' . $range['max']);
                             $FacetFilter->count = $counts[$id];
                             $Facet->filters[$FacetFilter->param] = $FacetFilter;
                         }
                     }
                     // END !empty($ranges)
                 }
         }
         // END switch
     }
 }
Example #2
0
	/**
	 * ecart('category','...') tags
	 * 
	 * @since 1.0
	 * @version 1.1
	 *
	 * @param string $property The property to handle
	 * @param array $options (optional) The tag options to process
	 * @return mixed
	 **/
	function tag ($property,$options=array()) {
		global $Ecart;
		$db = DB::get();

		switch ($property) {
			case "link":
			case "url":
				return ecarturl(ECART_PRETTYURLS?'category/'.$this->uri:array('ecart_category'=>$this->id));
				break;
			case "feed-url":
			case "feedurl":
				$uri = 'category/'.$this->uri;
				if ($this->slug == "tag") $uri = $this->slug.'/'.$this->tag;
				return ecarturl(ECART_PRETTYURLS?"$uri/feed":array('ecart_category'=>urldecode($this->uri),'src'=>'category_rss'));
			case "id": return $this->id; break;
			case "parent": return $this->parent; break;
			case "name": return $this->name; break;
			case "slug": return urldecode($this->slug); break;
			case "description": return wpautop($this->description); break;
			case "total": return $this->loaded?$this->total:false; break;
			case "has-products":
			case "loadproducts":
			case "load-products":
			case "hasproducts":
				if (empty($this->id) && empty($this->slug)) return false;
				if (isset($options['load'])) {
					$dataset = explode(",",$options['load']);
					$options['load'] = array();
					foreach ($dataset as $name) $options['load'][] = trim($name);
				 } else {
					$options['load'] = array('prices');
				}
				if (!$this->loaded) $this->load_products($options);
				if (count($this->products) > 0) return true; else return false; break;
			case "products":
				if (!isset($this->_product_loop)) {
					reset($this->products);
					$Ecart->Product = current($this->products);
					$this->_pindex = 0;
					$this->_rindex = false;
					$this->_product_loop = true;
				} else {
					$Ecart->Product = next($this->products);
					$this->_pindex++;
				}

				if (current($this->products) !== false) return true;
				else {
					unset($this->_product_loop);
					$this->_pindex = 0;
					return false;
				}
				break;
			case "row":
				if (!isset($this->_rindex) || $this->_rindex === false) $this->_rindex = 0;
				else $this->_rindex++;
				if (empty($options['products'])) $options['products'] = $Ecart->Settings->get('row_products');
				if (isset($this->_rindex) && $this->_rindex > 0 && $this->_rindex % $options['products'] == 0) return true;
				else return false;
				break;
			case "has-categories":
			case "hascategories":
				if (empty($this->children)) $this->load_children();
				return (!empty($this->children));
				break;
			case "is-subcategory":
			case "issubcategory":
				return ($this->parent != 0);
				break;
			case "subcategories":
				if (!isset($this->_children_loop)) {
					reset($this->children);
					$this->child = current($this->children);
					$this->_cindex = 0;
					$this->_children_loop = true;
				} else {
					$this->child = next($this->children);
					$this->_cindex++;
				}

				if ($this->child !== false) return true;
				else {
					unset($this->_children_loop);
					$this->_cindex = 0;
					$this->child = false;
					return false;
				}
				break;
			case "subcategory-list":
				if (isset($Ecart->Category->controls)) return false;

				$defaults = array(
					'title' => '',
					'before' => '',
					'after' => '',
					'class' => '',
					'exclude' => '',
					'orderby' => 'name',
					'order' => 'ASC',
					'depth' => 0,
					'childof' => 0,
					'parent' => false,
					'showall' => false,
					'linkall' => false,
					'linkcount' => false,
					'dropdown' => false,
					'hierarchy' => false,
					'products' => false,
					'wraplist' => true,
					'showsmart' => false
					);

				$options = array_merge($defaults,$options);
				extract($options, EXTR_SKIP);

				if (!$this->children) $this->load_children(array('orderby'=>$orderby,'order'=>$order));
				if (empty($this->children)) return false;

				$string = "";
				$depthlimit = $depth;
				$depth = 0;
				$exclude = explode(",",$exclude);
				$classes = ' class="ecart_categories'.(empty($class)?'':' '.$class).'"';
				$wraplist = value_is_true($wraplist);

				if (value_is_true($dropdown)) {
					$count = 0;
					$string .= $title;
					$string .= '<select name="ecart_cats" id="ecart-'.$this->slug.'-subcategories-menu" class="ecart-categories-menu">';
					$string .= '<option value="">'.__('Select a sub-category&hellip;','Ecart').'</option>';
					foreach ($this->children as &$category) {
						if (!empty($show) && $count+1 > $show) break;
						if (value_is_true($hierarchy) && $depthlimit && $category->depth >= $depthlimit) continue;
						if ($category->products == 0) continue; // Only show categories with products
						if (value_is_true($hierarchy) && $category->depth > $depth) {
							$parent = &$previous;
							if (!isset($parent->path)) $parent->path = '/'.$parent->slug;
						}
						$padding = str_repeat("&nbsp;",$category->depth*3);

						$category_uri = empty($category->id)?$category->uri:$category->id;
						$link = ECART_PRETTYURLS?ecarturl("category/$category->uri"):ecarturl(array('ecart_category'=>$category_uri));

						$total = '';
						if (value_is_true($products)) $total = '&nbsp;&nbsp;('.$category->products.')';

						$string .= '<option value="'.htmlentities($link).'">'.$padding.$category->name.$total.'</option>';
						$previous = &$category;
						$depth = $category->depth;
						$count++;
					}
					$string .= '</select>';
				} else {
					if (!empty($class)) $classes = ' class="'.$class.'"';
					$string .= $title.'<ul'.$classes.'>';
					$count = 0;
					foreach ($this->children as &$category) {
						if (!isset($category->total)) $category->total = 0;
						if (!isset($category->depth)) $category->depth = 0;
						if (!empty($category->id) && in_array($category->id,$exclude)) continue; // Skip excluded categories
						if ($depthlimit && $category->depth >= $depthlimit) continue;
						if (value_is_true($hierarchy) && $category->depth > $depth) {
							$parent = &$previous;
							if (!isset($parent->path)) $parent->path = $parent->slug;
							$string = substr($string,0,-5); // Remove the previous </li>
							$active = '';

							if (isset($Ecart->Category) && !empty($parent->slug)
									&& preg_match('/(^|\/)'.$parent->path.'(\/|$)/',$Ecart->Category->uri)) {
								$active = ' active';
							}

							$subcategories = '<ul class="children'.$active.'">';
							$string .= $subcategories;
						}

						if (value_is_true($hierarchy) && $category->depth < $depth) {
							for ($i = $depth; $i > $category->depth; $i--) {
								if (substr($string,strlen($subcategories)*-1) == $subcategories) {
									// If the child menu is empty, remove the <ul> to avoid breaking standards
									$string = substr($string,0,strlen($subcategories)*-1).'</li>';
								} else $string .= '</ul></li>';
							}
						}

						$category_uri = empty($category->id)?$category->uri:$category->id;
						$link = ECART_PRETTYURLS?
							ecarturl("category/$category->uri"):
							ecarturl(array('ecart_category'=>$category_uri));

						$total = '';
						if (value_is_true($products) && $category->total > 0) $total = ' <span>('.$category->total.')</span>';

						$current = '';
						if (isset($Ecart->Category) && $Ecart->Category->slug == $category->slug)
							$current = ' class="current"';

						$listing = '';
						if ($category->total > 0 || isset($category->smart) || $linkall)
							$listing = '<a href="'.$link.'"'.$current.'>'.$category->name.($linkcount?$total:'').'</a>'.(!$linkcount?$total:'');
						else $listing = $category->name;

						if (value_is_true($showall) ||
							$category->total > 0 ||
							isset($category->smart) ||
							$category->children)
							$string .= '<li'.$current.'>'.$listing.'</li>';

						$previous = &$category;
						$depth = $category->depth;
						$count++;
					}
					if (value_is_true($hierarchy) && $depth > 0)
						for ($i = $depth; $i > 0; $i--) {
							if (substr($string,strlen($subcategories)*-1) == $subcategories) {
								// If the child menu is empty, remove the <ul> to avoid breaking standards
								$string = substr($string,0,strlen($subcategories)*-1).'</li>';
							} else $string .= '</ul></li>';
						}
					if ($wraplist) $string .= '</ul>';
				}
				return $string;
				break;
			case "section-list":
				if (empty($this->id)) return false;
				if (isset($Ecart->Category->controls)) return false;
				if (empty($Ecart->Catalog->categories))
					$Ecart->Catalog->load_categories(array("where"=>"(pd.status='publish' OR pd.id IS NULL)"));
				if (empty($Ecart->Catalog->categories)) return false;
				if (!$this->children) $this->load_children();

				$defaults = array(
					'title' => '',
					'before' => '',
					'after' => '',
					'class' => '',
					'classes' => '',
					'exclude' => '',
					'total' => '',
					'current' => '',
					'listing' => '',
					'depth' => 0,
					'parent' => false,
					'showall' => false,
					'linkall' => false,
					'dropdown' => false,
					'hierarchy' => false,
					'products' => false,
					'wraplist' => true
					);

				$options = array_merge($defaults,$options);
				extract($options, EXTR_SKIP);

				$string = "";
				$depthlimit = $depth;
				$depth = 0;
				$wraplist = value_is_true($wraplist);
				$exclude = explode(",",$exclude);
				$section = array();

				// Identify root parent
				if (empty($this->id)) return false;
				$parent = '_'.$this->id;
				while($parent != 0) {
					if (!isset($Ecart->Catalog->categories[$parent])) break;
					if ($Ecart->Catalog->categories[$parent]->parent == 0
						|| $Ecart->Catalog->categories[$parent]->parent == $parent) break;
					$parent = '_'.$Ecart->Catalog->categories[$parent]->parent;
				}
				$root = $Ecart->Catalog->categories[$parent];
				if ($this->id == $parent && empty($this->children)) return false;

				// Build the section
				$section[] = $root;
				$in = false;
				foreach ($Ecart->Catalog->categories as &$c) {
					if ($in && $c->depth == $root->depth) break; // Done
					if ($in) $section[] = $c;
					if (!$in && isset($c->id) && $c->id == $root->id) $in = true;
				}

				if (value_is_true($dropdown)) {
					$string .= $title;
					$string .= '<select name="ecart_cats" id="ecart-'.$this->slug.'-subcategories-menu" class="ecart-categories-menu">';
					$string .= '<option value="">'.__('Select a sub-category&hellip;','Ecart').'</option>';
					foreach ($section as &$category) {
						if (value_is_true($hierarchy) && $depthlimit && $category->depth >= $depthlimit) continue;
						if (in_array($category->id,$exclude)) continue; // Skip excluded categories
						if ($category->products == 0) continue; // Only show categories with products
						if (value_is_true($hierarchy) && $category->depth > $depth) {
							$parent = &$previous;
							if (!isset($parent->path)) $parent->path = '/'.$parent->slug;
						}
						$padding = str_repeat("&nbsp;",$category->depth*3);

						$category_uri = empty($category->id)?$category->uri:$category->id;
						$link = ECART_PRETTYURLS?ecarturl("category/$category->uri"):ecarturl(array('ecart_category'=>$category_uri));

						$total = '';
						if (value_is_true($products)) $total = '&nbsp;&nbsp;('.$category->total.')';

						$string .= '<option value="'.htmlentities($link).'">'.$padding.$category->name.$total.'</option>';
						$previous = &$category;
						$depth = $category->depth;

					}
					$string .= '</select>';
				} else {
					if (!empty($class)) $classes = ' class="'.$class.'"';
					$string .= $title;
					if ($wraplist) $string .= '<ul'.$classes.'>';
					foreach ($section as &$category) {
						if (in_array($category->id,$exclude)) continue; // Skip excluded categories
						if (value_is_true($hierarchy) && $depthlimit &&
							$category->depth >= $depthlimit) continue;
						if (value_is_true($hierarchy) && $category->depth > $depth) {
							$parent = &$previous;
							if (!isset($parent->path) && isset($parent->slug)) $parent->path = $parent->slug;
							$string = substr($string,0,-5);
							$string .= '<ul class="children">';
						}
						if (value_is_true($hierarchy) && $category->depth < $depth) $string .= '</ul></li>';

						$category_uri = empty($category->id)?$category->uri:$category->id;
						$link = ECART_PRETTYURLS?ecarturl("category/$category->uri"):ecarturl(array('ecart_category'=>$category_uri));

						if (value_is_true($products)) $total = ' <span>('.$category->total.')</span>';

						if ($category->total > 0 || isset($category->smart) || $linkall) $listing = '<a href="'.$link.'"'.$current.'>'.$category->name.$total.'</a>';
						else $listing = $category->name;

						if (value_is_true($showall) ||
							$category->total > 0 ||
							$category->children)
							$string .= '<li>'.$listing.'</li>';

						$previous = &$category;
						$depth = $category->depth;
					}
					if (value_is_true($hierarchy) && $depth > 0)
						for ($i = $depth; $i > 0; $i--) $string .= '</ul></li>';

					if ($wraplist) $string .= '</ul>';
				}
				return $string;
				break;
			case "pagination":
				if (!$this->paged) return "";

				$defaults = array(
					'label' => __("Pages:","Ecart"),
					'next' => __("next","Ecart"),
					'previous' => __("previous","Ecart"),
					'jumpback' => '&laquo;',
					'jumpfwd' => '&raquo;',
					'show' => 1000,
					'before' => '<div>',
					'after' => '</div>'
				);
				$options = array_merge($defaults,$options);
				extract($options);

				$_ = array();
				if (isset($this->alpha) && $this->paged) {
					$_[] = $before.$label;
					$_[] = '<ul class="paging">';
					foreach ($this->alpha as $alpha) {
						$link = $this->pagelink($alpha->letter);
						if ($alpha->total > 0)
							$_[] = '<li><a href="'.$link.'">'.$alpha->letter.'</a></li>';
						else $_[] = '<li><span>'.$alpha->letter.'</span></li>';
					}
					$_[] = '</ul>';
					$_[] = $after;
					return join("\n",$_);
				}

				if ($this->pages > 1) {

					if ( $this->pages > $show ) $visible_pages = $show + 1;
					else $visible_pages = $this->pages + 1;
					$jumps = ceil($visible_pages/2);
					$_[] = $before.$label;

					$_[] = '<ul class="paging">';
					if ( $this->page <= floor(($show) / 2) ) {
						$i = 1;
					} else {
						$i = $this->page - floor(($show) / 2);
						$visible_pages = $this->page + floor(($show) / 2) + 1;
						if ($visible_pages > $this->pages) $visible_pages = $this->pages + 1;
						if ($i > 1) {
							$link = $this->pagelink(1);
							$_[] = '<li><a href="'.$link.'">1</a></li>';

							$pagenum = ($this->page - $jumps);
							if ($pagenum < 1) $pagenum = 1;
							$link = $this->pagelink($pagenum);
							$_[] = '<li><a href="'.$link.'">'.$jumpback.'</a></li>';
						}
					}

					// Add previous button
					if (!empty($previous) && $this->page > 1) {
						$prev = $this->page-1;
						$link = $this->pagelink($prev);
						$_[] = '<li class="previous"><a href="'.$link.'">'.$previous.'</a></li>';
					} else $_[] = '<li class="previous disabled">'.$previous.'</li>';
					// end previous button

					while ($i < $visible_pages) {
						$link = $this->pagelink($i);
						if ( $i == $this->page ) $_[] = '<li class="active">'.$i.'</li>';
						else $_[] = '<li><a href="'.$link.'">'.$i.'</a></li>';
						$i++;
					}
					if ($this->pages > $visible_pages) {
						$pagenum = ($this->page + $jumps);
						if ($pagenum > $this->pages) $pagenum = $this->pages;
						$link = $this->pagelink($pagenum);
						$_[] = '<li><a href="'.$link.'">'.$jumpfwd.'</a></li>';
						$_[] = '<li><a href="'.$link.'">'.$this->pages.'</a></li>';
					}

					// Add next button
					if (!empty($next) && $this->page < $this->pages) {
						$pagenum = $this->page+1;
						$link = $this->pagelink($pagenum);
						$_[] = '<li class="next"><a href="'.$link.'">'.$next.'</a></li>';
					} else $_[] = '<li class="next disabled">'.$next.'</li>';

					$_[] = '</ul>';
					$_[] = $after;
				}
				return join("\n",$_);
				break;

			case "has-faceted-menu": return ($this->facetedmenus == "on"); break;
			case "faceted-menu":
				if ($this->facetedmenus == "off") return;
				$output = "";
				$CategoryFilters =& $Ecart->Flow->Controller->browsing[$this->slug];
				$link = $_SERVER['REQUEST_URI'];
				if (!isset($options['cancel'])) $options['cancel'] = "X";
				if (strpos($_SERVER['REQUEST_URI'],"?") !== false)
					list($link,$query) = explode("?",$_SERVER['REQUEST_URI']);
				$query = $_GET;
				$query = http_build_query($query);
				$link = esc_url($link).'?'.$query;

				$list = "";
				if (is_array($CategoryFilters)) {
					foreach($CategoryFilters AS $facet => $filter) {
						$href = add_query_arg('ecart_catfilters['.urlencode($facet).']','',$link);
						if (preg_match('/^(.*?(\d+[\.\,\d]*).*?)\-(.*?(\d+[\.\,\d]*).*)$/',$filter,$matches)) {
							$label = $matches[1].' &mdash; '.$matches[3];
							if ($matches[2] == 0) $label = __('Under ','Ecart').$matches[3];
							if ($matches[4] == 0) $label = $matches[1].' '.__('and up','Ecart');
						} else $label = $filter;
						if (!empty($filter)) $list .= '<li><strong>'.$facet.'</strong>: '.stripslashes($label).' <a href="'.$href.'=" class="cancel">'.$options['cancel'].'</a></li>';
					}
					$output .= '<ul class="filters enabled">'.$list.'</ul>';
				}

				if ($this->pricerange == "auto" && empty($CategoryFilters['Price'])) {
					if (!$this->loaded) $this->load_products();
					$list = "";
					$this->priceranges = auto_ranges($this->pricing['average'],$this->pricing['max'],$this->pricing['min']);
					foreach ($this->priceranges as $range) {
						$href = add_query_arg('ecart_catfilters[Price]',urlencode(money($range['min']).'-'.money($range['max'])),$link);
						$label = money($range['min']).' &mdash; '.money($range['max']-0.01);
						if ($range['min'] == 0) $label = __('Under ','Ecart').money($range['max']);
						elseif ($range['max'] == 0) $label = money($range['min']).' '.__('and up','Ecart');
						$list .= '<li><a href="'.$href.'">'.$label.'</a></li>';
					}
					if (!empty($this->priceranges)) $output .= '<h4>'.__('Price Range','Ecart').'</h4>';
					$output .= '<ul>'.$list.'</ul>';
				}

				$catalogtable = DatabaseObject::tablename(Catalog::$table);
				$producttable = DatabaseObject::tablename(Product::$table);
				$spectable = DatabaseObject::tablename(Spec::$table);

				$query = "SELECT spec.name,spec.value,
					IF(spec.numeral > 0,spec.name,spec.value) AS merge,
					count(*) AS total,avg(numeral) AS avg,max(numeral) AS max,min(numeral) AS min
					FROM $catalogtable AS cat
					LEFT JOIN $producttable AS p ON cat.product=p.id
					LEFT JOIN $spectable AS spec ON p.id=spec.parent AND spec.context='product' AND spec.type='spec'
					WHERE cat.parent=$this->id AND cat.type='category' AND spec.value != '' AND spec.value != '0' GROUP BY merge ORDER BY spec.name,merge";

				$results = $db->query($query,AS_ARRAY);

				$specdata = array();
				foreach ($results as $data) {
					if (isset($specdata[$data->name])) {
						if (!is_array($specdata[$data->name]))
							$specdata[$data->name] = array($specdata[$data->name]);
						$specdata[$data->name][] = $data;
					} else $specdata[$data->name] = $data;
				}

				if (is_array($this->specs)) {
					foreach ($this->specs as $spec) {
						$list = "";
						if (!empty($CategoryFilters[$spec['name']])) continue;

						// For custom menu presets
						if ($spec['facetedmenu'] == "custom" && !empty($spec['options'])) {
							foreach ($spec['options'] as $option) {
								$href = add_query_arg('ecart_catfilters['.$spec['name'].']',urlencode($option['name']),$link);
								$list .= '<li><a href="'.$href.'">'.$option['name'].'</a></li>';
							}
							$output .= '<h4>'.$spec['name'].'</h4><ul>'.$list.'</ul>';

						// For preset ranges
						} elseif ($spec['facetedmenu'] == "ranges" && !empty($spec['options'])) {
							foreach ($spec['options'] as $i => $option) {
								$matches = array();
								$format = '%s-%s';
								$next = 0;
								if (isset($spec['options'][$i+1])) {
									if (preg_match('/(\d+[\.\,\d]*)/',$spec['options'][$i+1]['name'],$matches))
										$next = $matches[0];
								}
								$matches = array();
								$range = array("min" => 0,"max" => 0);
								if (preg_match('/^(.*?)(\d+[\.\,\d]*)(.*)$/',$option['name'],$matches)) {
									$base = $matches[2];
									$format = $matches[1].'%s'.$matches[3];
									if (!isset($spec['options'][$i+1])) $range['min'] = $base;
									else $range = array("min" => $base, "max" => ($next-1));
								}
								if ($i == 1) {
									$href = add_query_arg('ecart_catfilters['.$spec['name'].']', urlencode(sprintf($format,'0',$range['min'])),$link);
									$label = __('Under ','Ecart').sprintf($format,$range['min']);
									$list .= '<li><a href="'.$href.'">'.$label.'</a></li>';
								}

								$href = add_query_arg('ecart_catfilters['.$spec['name'].']', urlencode(sprintf($format,$range['min'],$range['max'])), $link);
								$label = sprintf($format,$range['min']).' &mdash; '.sprintf($format,$range['max']);
								if ($range['max'] == 0) $label = sprintf($format,$range['min']).' '.__('and up','Ecart');
								$list .= '<li><a href="'.$href.'">'.$label.'</a></li>';
							}
							$output .= '<h4>'.$spec['name'].'</h4><ul>'.$list.'</ul>';

						// For automatically building the menu options
						} elseif ($spec['facetedmenu'] == "auto" && isset($specdata[$spec['name']])) {

							if (is_array($specdata[$spec['name']])) { // Generate from text values
								foreach ($specdata[$spec['name']] as $option) {
									$href = add_query_arg('ecart_catfilters['.$spec['name'].']',urlencode($option->value),$link);
									$list .= '<li><a href="'.$href.'">'.$option->value.'</a></li>';
								}
								$output .= '<h4>'.$spec['name'].'</h4><ul>'.$list.'</ul>';
							} else { // Generate number ranges
								$format = '%s';
								if (preg_match('/^(.*?)(\d+[\.\,\d]*)(.*)$/',$specdata[$spec['name']]->content,$matches))
									$format = $matches[1].'%s'.$matches[3];

								$ranges = auto_ranges($specdata[$spec['name']]->avg,$specdata[$spec['name']]->max,$specdata[$spec['name']]->min);
								foreach ($ranges as $range) {
									$href = add_query_arg('ecart_catfilters['.$spec['name'].']', urlencode($range['min'].'-'.$range['max']), $link);
									$label = sprintf($format,$range['min']).' &mdash; '.sprintf($format,$range['max']);
									if ($range['min'] == 0) $label = __('Under ','Ecart').sprintf($format,$range['max']);
									elseif ($range['max'] == 0) $label = sprintf($format,$range['min']).' '.__('and up','Ecart');
									$list .= '<li><a href="'.$href.'">'.$label.'</a></li>';
								}
								if (!empty($list)) $output .= '<h4>'.$spec['name'].'</h4>';
								$output .= '<ul>'.$list.'</ul>';

							}
						}
					}
				}


				return $output;
				break;
			case "hasimages":
			case "has-images":
				if (empty($this->images)) $this->load_images();
				if (empty($this->images)) return false;
				return true;
				break;
			case "images":
				if (!isset($this->_images_loop)) {
					reset($this->images);
					$this->_images_loop = true;
				} else next($this->images);

				if (current($this->images) !== false) return true;
				else {
					unset($this->_images_loop);
					return false;
				}
				break;
			case "coverimage":
			case "thumbnail": // deprecated
				// Force select the first loaded image
				unset($options['id']);
				$options['index'] = 0;
			case "image":
				if (empty($this->images)) $this->load_images();
				if (!(count($this->images) > 0)) return "";

				// Compatibility defaults
				$_size = 96;
				$_width = $Ecart->Settings->get('gallery_thumbnail_width');
				$_height = $Ecart->Settings->get('gallery_thumbnail_height');
				if (!$_width) $_width = $_size;
				if (!$_height) $_height = $_size;

				$defaults = array(
					'img' => false,
					'id' => false,
					'index' => false,
					'class' => '',
					'width' => false,
					'height' => false,
					'width_a' => false,
					'height_a' => false,
					'size' => false,
					'fit' => false,
					'sharpen' => false,
					'quality' => false,
					'bg' => false,
					'alt' => '',
					'title' => '',
					'zoom' => '',
					'zoomfx' => 'ecart-zoom',
					'property' => false
				);
				$options = array_merge($defaults,$options);
				extract($options);

				// Select image by database id
				if ($id !== false) {
					for ($i = 0; $i < count($this->images); $i++) {
						if ($img->id == $id) {
							$img = $this->images[$i]; break;
						}
					}
					if (!$img) return "";
				}

				// Select image by index position in the list
				if ($index !== false && isset($this->images[$index]))
					$img = $this->images[$index];

				// Use the current image pointer by default
				if (!$img) $img = current($this->images);

				if ($size !== false) $width = $height = $size;
				if (!$width) $width = $_width;
				if (!$height) $height = $_height;

				$scale = $fit?array_search($fit,$img->_scaling):false;
				$sharpen = $sharpen?min($sharpen,$img->_sharpen):false;
				$quality = $quality?min($quality,$img->_quality):false;
				$fill = $bg?hexdec(ltrim($bg,'#')):false;

				list($width_a,$height_a) = array_values($img->scaled($width,$height,$scale));
				if ($size == "original") {
					$width_a = $img->width;
					$height_a = $img->height;
				}
				if ($width_a === false) $width_a = $width;
				if ($height_a === false) $height_a = $height;

				$alt = esc_attr(empty($alt)?(empty($img->alt)?$img->name:$img->alt):$alt);
				$title = empty($title)?$img->title:$title;
				$titleattr = empty($title)?'':' title="'.esc_attr($title).'"';
				$classes = empty($class)?'':' class="'.esc_attr($class).'"';

				$src = ecarturl($img->id,'images');
				if ($size != "original") {
					$src = add_query_string(
						$img->resizing($width,$height,$scale,$sharpen,$quality,$fill),
						trailingslashit(ecarturl($img->id,'images')).$img->filename
					);
				}

				switch (strtolower($property)) {
					case "id": return $img->id; break;
					case "url":
					case "src": return $src; break;
					case "title": return $title; break;
					case "alt": return $alt; break;
					case "width": return $width_a; break;
					case "height": return $height_a; break;
					case "class": return $class; break;
				}

				$imgtag = '<img src="'.$src.'"'.$titleattr.' alt="'.$alt.'" width="'.$width_a.'" height="'.$height_a.'" '.$classes.' />';

				if (value_is_true($zoom))
					return '<a href="'.ecarturl($img->id,'images').'/'.$img->filename.'" class="'.$zoomfx.'" rel="product-'.$this->id.'">'.$imgtag.'</a>';

				return $imgtag;
				break;
			case "slideshow":
				$options['load'] = array('images');
				if (!$this->loaded) $this->load_products($options);
				if (count($this->products) == 0) return false;

				$defaults = array(
					'width' => '440',
					'height' => '180',
					'fit' => 'crop',
					'fx' => 'fade',
					'duration' => 1000,
					'delay' => 7000,
					'order' => 'normal'
				);
				$options = array_merge($defaults,$options);
				extract($options, EXTR_SKIP);

				$href = ecarturl(ECART_PERMALINKS?trailingslashit('000'):'000','images');
				$imgsrc = add_query_string("$width,$height",$href);

				$string = '<ul class="slideshow '.$fx.'-fx '.$order.'-order duration-'.$duration.' delay-'.$delay.'">';
				$string .= '<li class="clear"><img src="'.$imgsrc.'" width="'.$width.'" height="'.$height.'" /></li>';
				foreach ($this->products as $Product) {
					if (empty($Product->images)) continue;
					$string .= '<li><a href="'.$Product->tag('url').'">';
					$string .= $Product->tag('image',array('width'=>$width,'height'=>$height,'fit'=>$fit));
					$string .= '</a></li>';
				}
				$string .= '</ul>';
				return $string;
				break;
			case "carousel":
				$options['load'] = array('images');
				if (!$this->loaded) $this->load_products($options);
				if (count($this->products) == 0) return false;

				$defaults = array(
					'imagewidth' => '96',
					'imageheight' => '96',
					'fit' => 'all',
					'duration' => 500
				);
				$options = array_merge($defaults,$options);
				extract($options, EXTR_SKIP);

				$string = '<div class="carousel duration-'.$duration.'">';
				$string .= '<div class="frame">';
				$string .= '<ul>';
				foreach ($this->products as $Product) {
					if (empty($Product->images)) continue;
					$string .= '<li><a href="'.$Product->tag('url').'">';
					$string .= $Product->tag('image',array('width'=>$imagewidth,'height'=>$imageheight,'fit'=>$fit));
					$string .= '</a></li>';
				}
				$string .= '</ul></div>';
				$string .= '<button type="button" name="left" class="left">&nbsp;</button>';
				$string .= '<button type="button" name="right" class="right">&nbsp;</button>';
				$string .= '</div>';
				return $string;
				break;
		}
	}
 function tag($property, $options = array())
 {
     global $Shopp;
     $db = DB::get();
     $page = $Shopp->link('catalog');
     if (SHOPP_PERMALINKS) {
         $imageuri = trailingslashit($page) . "images/";
     } else {
         $imageuri = add_query_arg('shopp_image', '=', $page);
     }
     if (SHOPP_PERMALINKS) {
         $pages = $Shopp->Settings->get('pages');
         if ($page == trailingslashit(get_bloginfo('url'))) {
             $page .= $pages['catalog']['name'] . "/";
         }
     }
     switch ($property) {
         case "link":
         case "url":
             return SHOPP_PERMALINKS ? $Shopp->shopuri . "category/" . urldecode($this->uri) : add_query_arg('shopp_category', $this->id, $Shopp->shopuri);
             break;
         case "id":
             return $this->id;
             break;
         case "name":
             return $this->name;
             break;
         case "slug":
             return urldecode($this->slug);
             break;
         case "description":
             return wpautop($this->description);
             break;
         case "total":
             return $this->total;
             break;
         case "has-products":
         case "hasproducts":
             if (empty($this->id) && empty($this->slug)) {
                 return false;
             }
             if (isset($options['load'])) {
                 $dataset = explode(",", $options['load']);
                 $options['load'] = array();
                 foreach ($dataset as $name) {
                     $options['load'][] = trim($name);
                 }
             } else {
                 $options['load'] = array('prices');
             }
             if (!$this->loaded) {
                 $this->load_products($options);
             }
             if (count($this->products) > 0) {
                 return true;
             } else {
                 return false;
             }
             break;
         case "products":
             if (!$this->productloop) {
                 reset($this->products);
                 $Shopp->Product = current($this->products);
                 $this->productsidx = 0;
                 $this->productloop = true;
             } else {
                 $Shopp->Product = next($this->products);
                 $this->productsidx++;
             }
             if (current($this->products)) {
                 $Shopp->Product = current($this->products);
                 return true;
             } else {
                 $this->productloop = false;
                 return false;
             }
             break;
         case "row":
             if (empty($options['products'])) {
                 $options['products'] = $Shopp->Settings->get('row_products');
             }
             if ($this->productsidx > 0 && $this->productsidx % $options['products'] == 0) {
                 return true;
             } else {
                 return false;
             }
             break;
         case "has-categories":
         case "hascategories":
             if (empty($this->children)) {
                 $this->load_children();
             }
             return !empty($this->children);
             break;
         case "is-subcategory":
         case "issubcategory":
             return $this->parent != 0;
             break;
         case "subcategories":
             if (!$this->childloop) {
                 reset($this->children);
                 $this->child = current($this->children);
                 $this->childidx = 0;
                 $this->childloop = true;
             } else {
                 $this->child = next($this->children);
                 $this->childidx++;
             }
             if (current($this->children)) {
                 $this->child = current($this->children);
                 return true;
             } else {
                 $this->childloop = false;
                 return false;
             }
             break;
         case "subcategory-list":
             if (isset($Shopp->Category->controls)) {
                 return false;
             }
             $defaults = array('title' => '', 'before' => '', 'after' => '', 'class' => '', 'depth' => 0, 'orderby' => 'name', 'order' => 'ASC', 'parent' => false, 'showall' => false, 'dropdown' => false, 'hierarchy' => false, 'products' => false);
             $options = array_merge($defaults, $options);
             extract($options, EXTR_SKIP);
             if (!$this->children) {
                 $this->load_children(array('orderby' => $orderby, 'order' => $order));
             }
             if (empty($this->children)) {
                 return false;
             }
             $string = "";
             $depthlimit = $depth;
             $depth = 0;
             $count = 0;
             if (value_is_true($dropdown)) {
                 $string .= $title;
                 $string .= '<select name="shopp_cats" id="shopp-' . $this->slug . '-subcategories-menu" class="shopp-categories-menu">';
                 $string .= '<option value="">' . __('Select a sub-category&hellip;', 'Shopp') . '</option>';
                 foreach ($this->children as &$category) {
                     if (!empty($show) && $count + 1 > $show) {
                         break;
                     }
                     if (value_is_true($hierarchy) && $depthlimit && $category->depth >= $depthlimit) {
                         continue;
                     }
                     if ($category->products == 0) {
                         continue;
                     }
                     // Only show categories with products
                     if (value_is_true($hierarchy) && $category->depth > $depth) {
                         $parent =& $previous;
                         if (!isset($parent->path)) {
                             $parent->path = '/' . $parent->slug;
                         }
                     }
                     $padding = str_repeat("&nbsp;", $category->depth * 3);
                     if (SHOPP_PERMALINKS) {
                         $link = $Shopp->shopuri . 'category/' . $category->uri;
                     } else {
                         $link = add_query_arg('shopp_category', $category->id, $Shopp->shopuri);
                     }
                     $total = '';
                     if (value_is_true($products)) {
                         $total = '&nbsp;&nbsp;(' . $category->products . ')';
                     }
                     $string .= '<option value="' . htmlentities($link) . '">' . $padding . $category->name . $total . '</option>';
                     $previous =& $category;
                     $depth = $category->depth;
                     $count++;
                 }
                 $string .= '</select>';
                 $string .= '<script type="text/javascript">';
                 $string .= 'var menu = document.getElementById(\'shopp-' . $this->slug . '-subcategories-menu\');';
                 $string .= 'if (menu)';
                 $string .= '	menu.onchange = function () {';
                 $string .= '		document.location.href = this.options[this.selectedIndex].value;';
                 $string .= '	}';
                 $string .= '</script>';
             } else {
                 if (!empty($class)) {
                     $classes = ' class="' . $class . '"';
                 }
                 $string .= $title . '<ul' . $classes . '>';
                 foreach ($this->children as &$category) {
                     if (!empty($show) && $count + 1 > $show) {
                         break;
                     }
                     if (value_is_true($hierarchy) && $depthlimit && $category->depth >= $depthlimit) {
                         continue;
                     }
                     if (value_is_true($hierarchy) && $category->depth > $depth) {
                         $parent =& $previous;
                         if (!isset($parent->path)) {
                             $parent->path = $parent->slug;
                         }
                         $string .= '<ul class="children">';
                     }
                     if (value_is_true($hierarchy) && $category->depth < $depth) {
                         $string .= '</ul>';
                     }
                     if (SHOPP_PERMALINKS) {
                         $link = $Shopp->shopuri . 'category/' . $category->uri;
                     } else {
                         $link = add_query_arg('shopp_category', $category->id, $Shopp->shopuri);
                     }
                     $total = '';
                     if (value_is_true($products)) {
                         $total = ' (' . $category->products . ')';
                     }
                     if (value_is_true($showall) || $category->products > 0 || $category->smart) {
                         // Only show categories with products
                         $string .= '<li><a href="' . $link . '">' . $category->name . '</a>' . $total . '</li>';
                     }
                     $previous =& $category;
                     $depth = $category->depth;
                     $count++;
                 }
                 if (value_is_true($hierarchy)) {
                     for ($i = 0; $i < $depth; $i++) {
                         $string .= "</ul>";
                     }
                 }
                 $string .= '</ul>';
             }
             return $string;
             break;
         case "section-list":
             if (empty($this->id)) {
                 return false;
             }
             if (isset($Shopp->Category->controls)) {
                 return false;
             }
             if (empty($Shopp->Catalog->categories)) {
                 $Shopp->Catalog->load_categories(array("where" => "(pd.published='on' OR pd.id IS NULL)"));
             }
             if (empty($Shopp->Catalog->categories)) {
                 return false;
             }
             if (!$this->children) {
                 $this->load_children();
             }
             $defaults = array('title' => '', 'before' => '', 'after' => '', 'class' => '', 'classes' => '', 'exclude' => '', 'total' => '', 'current' => '', 'listing' => '', 'depth' => 0, 'parent' => false, 'showall' => false, 'linkall' => false, 'dropdown' => false, 'hierarchy' => false, 'products' => false, 'wraplist' => true);
             $options = array_merge($defaults, $options);
             extract($options, EXTR_SKIP);
             $string = "";
             $depthlimit = $depth;
             $depth = 0;
             $wraplist = value_is_true($wraplist);
             $exclude = explode(",", $exclude);
             $section = array();
             // Identify root parent
             if (empty($this->id)) {
                 return false;
             }
             $parent = $this->id;
             while ($parent != 0) {
                 if ($Shopp->Catalog->categories[$parent]->parent == 0 || $Shopp->Catalog->categories[$parent]->parent == $parent) {
                     break;
                 }
                 $parent = $Shopp->Catalog->categories[$parent]->parent;
             }
             $root = $Shopp->Catalog->categories[$parent];
             if ($this->id == $parent && empty($this->children)) {
                 return false;
             }
             // Build the section
             $section[] = $root;
             $in = false;
             foreach ($Shopp->Catalog->categories as &$c) {
                 if ($in && $c->depth == $root->depth) {
                     break;
                 }
                 // Done
                 if ($in) {
                     $section[] = $c;
                 }
                 if (!$in && isset($c->id) && $c->id == $root->id) {
                     $in = true;
                 }
             }
             if (value_is_true($dropdown)) {
                 $string .= $title;
                 $string .= '<select name="shopp_cats" id="shopp-' . $this->slug . '-subcategories-menu" class="shopp-categories-menu">';
                 $string .= '<option value="">' . __('Select a sub-category&hellip;', 'Shopp') . '</option>';
                 foreach ($section as &$category) {
                     if (value_is_true($hierarchy) && $depthlimit && $category->depth >= $depthlimit) {
                         continue;
                     }
                     if (in_array($category->id, $exclude)) {
                         continue;
                     }
                     // Skip excluded categories
                     if ($category->products == 0) {
                         continue;
                     }
                     // Only show categories with products
                     if (value_is_true($hierarchy) && $category->depth > $depth) {
                         $parent =& $previous;
                         if (!isset($parent->path)) {
                             $parent->path = '/' . $parent->slug;
                         }
                     }
                     $padding = str_repeat("&nbsp;", $category->depth * 3);
                     if (SHOPP_PERMALINKS) {
                         $link = $Shopp->shopuri . 'category/' . $category->uri;
                     } else {
                         $link = add_query_arg('shopp_category', $category->id, $Shopp->shopuri);
                     }
                     $total = '';
                     if (value_is_true($products)) {
                         $total = '&nbsp;&nbsp;(' . $category->total . ')';
                     }
                     $string .= '<option value="' . htmlentities($link) . '">' . $padding . $category->name . $total . '</option>';
                     $previous =& $category;
                     $depth = $category->depth;
                 }
                 $string .= '</select>';
                 $string .= '<script type="text/javascript">';
                 $string .= 'var menu = document.getElementById(\'shopp-' . $this->slug . '-subcategories-menu\');';
                 $string .= 'if (menu)';
                 $string .= '	menu.onchange = function () {';
                 $string .= '		document.location.href = this.options[this.selectedIndex].value;';
                 $string .= '	}';
                 $string .= '</script>';
             } else {
                 if (!empty($class)) {
                     $classes = ' class="' . $class . '"';
                 }
                 $string .= $title;
                 if ($wraplist) {
                     $string .= '<ul' . $classes . '>';
                 }
                 foreach ($section as &$category) {
                     if (in_array($category->id, $exclude)) {
                         continue;
                     }
                     // Skip excluded categories
                     if (value_is_true($hierarchy) && $depthlimit && $category->depth >= $depthlimit) {
                         continue;
                     }
                     if (value_is_true($hierarchy) && $category->depth > $depth) {
                         $parent =& $previous;
                         if (!isset($parent->path) && isset($parent->slug)) {
                             $parent->path = $parent->slug;
                         }
                         $string = substr($string, 0, -5);
                         $string .= '<ul class="children">';
                     }
                     if (value_is_true($hierarchy) && $category->depth < $depth) {
                         $string .= '</ul></li>';
                     }
                     if (SHOPP_PERMALINKS) {
                         $link = $Shopp->shopuri . 'category/' . $category->uri;
                     } else {
                         $link = add_query_arg('shopp_category', $category->id, $Shopp->shopuri);
                     }
                     if (value_is_true($products)) {
                         $total = ' <span>(' . $category->total . ')</span>';
                     }
                     if ($category->total > 0 || isset($category->smart) || $linkall) {
                         $listing = '<a href="' . $link . '"' . $current . '>' . $category->name . $total . '</a>';
                     } else {
                         $listing = $category->name;
                     }
                     if (value_is_true($showall) || $category->total > 0 || $category->children) {
                         $string .= '<li>' . $listing . '</li>';
                     }
                     $previous =& $category;
                     $depth = $category->depth;
                 }
                 if (value_is_true($hierarchy) && $depth > 0) {
                     for ($i = $depth; $i > 0; $i--) {
                         $string .= '</ul></li>';
                     }
                 }
                 if ($wraplist) {
                     $string .= '</ul>';
                 }
             }
             return $string;
             break;
         case "pagination":
             if (!$this->paged) {
                 return "";
             }
             global $wp;
             // Set options
             if (!isset($options['label'])) {
                 $options['label'] = __("Pages:", "Shopp");
             }
             if (!isset($options['next'])) {
                 $options['next'] = __("next", "Shopp");
             }
             if (!isset($options['previous'])) {
                 $options['previous'] = __("previous", "Shopp");
             }
             $navlimit = 1000;
             if (!empty($options['show'])) {
                 $navlimit = $options['show'];
             }
             $before = "<div>" . $options['label'];
             // Set the label
             if (!empty($options['before'])) {
                 $before = $options['before'];
             }
             $after = "</div>";
             if (!empty($options['after'])) {
                 $after = $options['after'];
             }
             $type = "category";
             if (isset($wp->query_vars['shopp_tag'])) {
                 $type = "tag";
             }
             $string = "";
             if (isset($this->alpha) && $this->paged) {
                 $string .= '<ul class="paging">';
                 foreach ($this->alpha as $alpha) {
                     $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$alpha->letter}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$alpha->letter}";
                     if ($alpha->total > 0) {
                         $string .= '<li><a href="' . $link . '">' . $alpha->letter . '</a></li>';
                     } else {
                         $string .= '<li><span>' . $alpha->letter . '</span></li>';
                     }
                 }
                 $string .= '</ul>';
                 return $string;
             }
             if ($this->pages > 1) {
                 if ($this->pages > $navlimit) {
                     $visible_pages = $navlimit + 1;
                 } else {
                     $visible_pages = $this->pages + 1;
                 }
                 $jumps = ceil($visible_pages / 2);
                 $string .= $before;
                 $string .= '<ul class="paging">';
                 if ($this->page <= floor($navlimit / 2)) {
                     $i = 1;
                 } else {
                     $i = $this->page - floor($navlimit / 2);
                     $visible_pages = $this->page + floor($navlimit / 2) + 1;
                     if ($visible_pages > $this->pages) {
                         $visible_pages = $this->pages + 1;
                     }
                     if ($i > 1) {
                         $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$i}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$i}";
                         $string .= '<li><a href="' . $link . '">1</a></li>';
                         $pagenum = $this->page - $jumps;
                         if ($pagenum < 1) {
                             $pagenum = 1;
                         }
                         $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$pagenum}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$pagenum}";
                         $string .= '<li><a href="' . $link . '">&laquo;</a></li>';
                     }
                 }
                 // Add previous button
                 if (!value_is_true($options['previous']) && $this->page > 1) {
                     $prev = $this->page - 1;
                     $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$prev}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$prev}";
                     $string .= '<li class="previous"><a href="' . $link . '">' . $options['previous'] . '</a></li>';
                 } else {
                     $string .= '<li class="previous disabled">' . $options['previous'] . '</li>';
                 }
                 // end previous button
                 while ($i < $visible_pages) {
                     $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$i}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$i}";
                     if ($i == $this->page) {
                         $string .= '<li class="active">' . $i . '</li>';
                     } else {
                         $string .= '<li><a href="' . $link . '">' . $i . '</a></li>';
                     }
                     $i++;
                 }
                 if ($this->pages > $visible_pages) {
                     $pagenum = $this->page + $jumps;
                     if ($pagenum > $this->pages) {
                         $pagenum = $this->pages;
                     }
                     $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$pagenum}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$pagenum}";
                     $string .= '<li><a href="' . $link . '">&raquo;</a></li>';
                     $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$this->pages}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$this->pages}";
                     $string .= '<li><a href="' . $link . '">' . $this->pages . '</a></li>';
                 }
                 // Add next button
                 if (!value_is_true($options['next']) && $this->page < $this->pages) {
                     $next = $this->page + 1;
                     $link = SHOPP_PERMALINKS ? "{$page}" . "{$type}/{$this->uri}/page/{$next}/" : "{$page}&shopp_{$type}={$this->uri}&paged={$next}";
                     $string .= '<li class="next"><a href="' . $link . '">' . $options['next'] . '</a></li>';
                 } else {
                     $string .= '<li class="next disabled">' . $options['next'] . '</li>';
                 }
                 $string .= '</ul>';
                 $string .= $after;
             }
             return $string;
             break;
         case "has-faceted-menu":
             return $this->facetedmenus == "on";
             break;
         case "faceted-menu":
             if ($this->facetedmenus == "off") {
                 return;
             }
             $output = "";
             $CategoryFilters =& $Shopp->Cart->data->Category[$this->slug];
             $link = $_SERVER['REQUEST_URI'];
             if (!isset($options['cancel'])) {
                 $options['cancel'] = "X";
             }
             if (strpos($_SERVER['REQUEST_URI'], "?") !== false) {
                 list($link, $query) = explode("?", $_SERVER['REQUEST_URI']);
             }
             $query = $_GET;
             unset($query['shopp_catfilters']);
             $query = http_build_query($query);
             if (!empty($query)) {
                 $query .= '&';
             }
             $list = "";
             if (is_array($CategoryFilters)) {
                 foreach ($CategoryFilters as $facet => $filter) {
                     $href = add_query_arg('shopp_catfilters[' . urlencode($facet) . ']', '', esc_url($link));
                     if (preg_match('/^(.*?(\\d+[\\.\\,\\d]*).*?)\\-(.*?(\\d+[\\.\\,\\d]*).*)$/', $filter, $matches)) {
                         $label = $matches[1] . ' &mdash; ' . $matches[3];
                         if ($matches[2] == 0) {
                             $label = __('Under ', 'Shopp') . $matches[3];
                         }
                         if ($matches[4] == 0) {
                             $label = $matches[1] . ' ' . __('and up', 'Shopp');
                         }
                     } else {
                         $label = $filter;
                     }
                     if (!empty($filter)) {
                         $list .= '<li><strong>' . $facet . '</strong>: ' . stripslashes($label) . ' <a href="' . $href . '" class="cancel">' . $options['cancel'] . '</a></li>';
                     }
                 }
                 $output .= '<ul class="filters enabled">' . $list . '</ul>';
             }
             if ($this->pricerange == "auto" && empty($CategoryFilters['Price'])) {
                 if (!$this->loaded) {
                     $this->load_products();
                 }
                 $list = "";
                 $this->priceranges = auto_ranges($this->pricing['average'], $this->pricing['max'], $this->pricing['min']);
                 foreach ($this->priceranges as $range) {
                     $href = add_query_arg('shopp_catfilters[Price]', urlencode(money($range['min']) . '-' . money($range['max'])), esc_url($link));
                     $label = money($range['min']) . ' &mdash; ' . money($range['max'] - 0.01);
                     if ($range['min'] == 0) {
                         $label = __('Under ', 'Shopp') . money($range['max']);
                     } elseif ($range['max'] == 0) {
                         $label = money($range['min']) . ' ' . __('and up', 'Shopp');
                     }
                     $list .= '<li><a href="' . $href . '">' . $label . '</a></li>';
                 }
                 if (!empty($this->priceranges)) {
                     $output .= '<h4>' . __('Price Range', 'Shopp') . '</h4>';
                 }
                 $output .= '<ul>' . $list . '</ul>';
             }
             $catalogtable = DatabaseObject::tablename(Catalog::$table);
             $producttable = DatabaseObject::tablename(Product::$table);
             $spectable = DatabaseObject::tablename(Spec::$table);
             $results = $db->query("SELECT spec.name,spec.content,\n\t\t\t\t\tIF(spec.numeral > 0,spec.name,spec.content) AS merge,\n\t\t\t\t\tcount(*) AS total,avg(numeral) AS avg,max(numeral) AS max,min(numeral) AS min \n\t\t\t\t\tFROM {$catalogtable} AS cat \n\t\t\t\t\tLEFT JOIN {$producttable} AS p ON cat.product=p.id \n\t\t\t\t\tLEFT JOIN {$spectable} AS spec ON spec.product=p.id \n\t\t\t\t\tWHERE cat.category={$this->id} GROUP BY merge ORDER BY spec.name,merge", AS_ARRAY);
             $specdata = array();
             foreach ($results as $data) {
                 if (isset($specdata[$data->name])) {
                     if (!is_array($specdata[$data->name])) {
                         $specdata[$data->name] = array($specdata[$data->name]);
                     }
                     $specdata[$data->name][] = $data;
                 } else {
                     $specdata[$data->name] = $data;
                 }
             }
             if (is_array($this->specs)) {
                 foreach ($this->specs as $spec) {
                     $list = "";
                     if (!empty($CategoryFilters[$spec['name']])) {
                         continue;
                     }
                     // For custom menu presets
                     if ($spec['facetedmenu'] == "custom" && !empty($spec['options'])) {
                         foreach ($spec['options'] as $option) {
                             $href = add_query_arg('shopp_catfilters[' . $spec['name'] . ']', urlencode($option['name']), esc_url($_SERVER['REQUEST_URI']));
                             $list .= '<li><a href="' . $href . '">' . $option['name'] . '</a></li>';
                         }
                         $output .= '<h4>' . $spec['name'] . '</h4><ul>' . $list . '</ul>';
                         // For preset ranges
                     } elseif ($spec['facetedmenu'] == "ranges" && !empty($spec['options'])) {
                         foreach ($spec['options'] as $i => $option) {
                             $matches = array();
                             $format = '%s';
                             $next = 0;
                             if (isset($spec['options'][$i + 1])) {
                                 if (preg_match('/(\\d+[\\.\\,\\d]*)/', $spec['options'][$i + 1]['name'], $matches)) {
                                     $next = $matches[0];
                                 }
                             }
                             $matches = array();
                             $range = array("min" => 0, "max" => 0);
                             if (preg_match('/^(.*?)(\\d+[\\.\\,\\d]*)(.*)$/', $option['name'], $matches)) {
                                 $base = $matches[2];
                                 $format = $matches[1] . '%s' . $matches[3];
                                 if (!isset($spec['options'][$i + 1])) {
                                     $range['min'] = $base;
                                 } else {
                                     $range = array("min" => $base, "max" => $next - 1);
                                 }
                             }
                             if ($i == 1) {
                                 $href = esc_url($link . '?' . $query) . 'shopp_catfilters[' . $spec['name'] . ']=' . urlencode(sprintf($format, '0') . '-' . sprintf($format, $range['min']));
                                 $label = __('Under ', 'Shopp') . sprintf($format, $range['min']);
                                 $list .= '<li><a href="' . $href . '">' . $label . '</a></li>';
                             }
                             $href = esc_url($link . '?' . $query) . 'shopp_catfilters[' . $spec['name'] . ']=' . urlencode(sprintf($format, $range['min']) . '-' . sprintf($format, $range['max']));
                             $label = sprintf($format, $range['min']) . ' &mdash; ' . sprintf($format, $range['max']);
                             if ($range['max'] == 0) {
                                 $label = sprintf($format, $range['min']) . ' ' . __('and up', 'Shopp');
                             }
                             $list .= '<li><a href="' . $href . '">' . $label . '</a></li>';
                         }
                         $output .= '<h4>' . $spec['name'] . '</h4><ul>' . $list . '</ul>';
                         // For automatically building the menu options
                     } elseif ($spec['facetedmenu'] == "auto" && isset($specdata[$spec['name']])) {
                         if (is_array($specdata[$spec['name']])) {
                             // Generate from text values
                             foreach ($specdata[$spec['name']] as $option) {
                                 $href = esc_url($link . '?' . $query) . 'shopp_catfilters[' . $spec['name'] . ']=' . urlencode($option->content);
                                 $list .= '<li><a href="' . $href . '">' . $option->content . '</a></li>';
                             }
                             $output .= '<h4>' . $spec['name'] . '</h4><ul>' . $list . '</ul>';
                         } else {
                             // Generate number ranges
                             $format = '%s';
                             if (preg_match('/^(.*?)(\\d+[\\.\\,\\d]*)(.*)$/', $specdata[$spec['name']]->content, $matches)) {
                                 $format = $matches[1] . '%s' . $matches[3];
                             }
                             $ranges = auto_ranges($specdata[$spec['name']]->avg, $specdata[$spec['name']]->max, $specdata[$spec['name']]->min);
                             foreach ($ranges as $range) {
                                 $href = esc_url($link . '?' . $query . 'shopp_catfilters[' . $spec['name'] . ']=' . urlencode($range['min'] . '-' . $range['max']));
                                 $label = sprintf($format, $range['min']) . ' &mdash; ' . sprintf($format, $range['max']);
                                 if ($range['min'] == 0) {
                                     $label = __('Under ', 'Shopp') . sprintf($format, $range['max']);
                                 } elseif ($range['max'] == 0) {
                                     $label = sprintf($format, $range['min']) . ' ' . __('and up', 'Shopp');
                                 }
                                 $list .= '<li><a href="' . $href . '">' . $label . '</a></li>';
                             }
                             if (!empty($list)) {
                                 $output .= '<h4>' . $spec['name'] . '</h4>';
                             }
                             $output .= '<ul>' . $list . '</ul>';
                         }
                     }
                 }
             }
             return $output;
             break;
         case "thumbnail":
             if (empty($this->images)) {
                 $this->load_images();
             }
             if (!empty($options['class'])) {
                 $options['class'] = ' class="' . $options['class'] . '"';
             }
             if (isset($this->thumbnail)) {
                 $img = $this->thumbnail;
                 return '<img src="' . $imageuri . $img->id . '" alt="' . $this->name . ' ' . $img->datatype . '" width="' . $img->properties['width'] . '" height="' . $img->properties['height'] . '" ' . $options['class'] . ' />';
                 break;
             }
             break;
         case "has-images":
             if (empty($options['type'])) {
                 $options['type'] = "thumbnail";
             }
             if (empty($this->images)) {
                 $this->load_images();
             }
             return count($this->images[$options['type']]) > 0;
             break;
         case "images":
             if (empty($options['type'])) {
                 $options['type'] = "thumbnail";
             }
             if (!$this->imageloop) {
                 reset($this->images[$options['type']]);
                 $this->imageloop = true;
             } else {
                 next($this->images[$options['type']]);
             }
             if (current($this->images[$options['type']])) {
                 return true;
             } else {
                 $this->imageloop = false;
                 return false;
             }
             break;
         case "image":
             if (empty($options['type'])) {
                 $options['type'] = "thumbnail";
             }
             $img = current($this->images[$options['type']]);
             if (!empty($options['class'])) {
                 $options['class'] = ' class="' . $options['class'] . '"';
             }
             $string = "";
             if (!empty($options['zoom'])) {
                 $string .= '<a href="' . $imageuri . $img->src . '/' . str_replace('small_', '', $img->name) . '" class="shopp-thickbox" rel="product-gallery">';
             }
             $string .= '<img src="' . $imageuri . $img->id . '" alt="' . $this->name . ' ' . $img->datatype . '" width="' . $img->properties['width'] . '" height="' . $img->properties['height'] . '" ' . $options['class'] . ' />';
             if (!empty($options['zoom'])) {
                 $string .= "</a>";
             }
             return $string;
             break;
     }
 }