tablerender(), default 'featurebox'
	 * - notablestyle (null): if isset - disable tablerender()
	 * - force (boolean): force category model load , default false
	 * - ids (string): comma separated id list - load specific featurebox items, default empty 
	 * 
	 * @param string $parm parameters
	 * @param string $mod category template
	 * @example {FEATUREBOX=cols=2|tabs}
	 */
	function sc_featurebox($parm=null, $mod = '')
	{
		if($parm == null && $mod == '') // ie {FEATUREBOX}
		{
			$menCat = e107::getPlugPref('featurebox','menu_category');
			$type 	= vartrue($menCat,'bootstrap_carousel');
			$text = e107::getParser()->parseTemplate("{FEATUREBOX|".$type."}");
			
			return $text;
		}
		
		// TODO cache
		if(!e107::isInstalled('featurebox')) //just in case
		{
			return '';
		}
		
		if(!$mod)
		{
			$ctemplate = 'default';
	
		}
		else
		{
			$ctemplate = $mod;
		}
		if(is_string($parm))
		{
			parse_str($parm, $parm);
		}
		
		$category = $this->getCategoryModel($ctemplate, (vartrue($parm['force']) ? true : false));
		$defopt = array(
			'force' => 0,
			'no_fill_empty' => 0,
			'tablestyle' => 'featurebox',
			'cols' => 1,	
			'ids' => '',
			'notablestyle' => null
		);
		// reset to default, update current
		$category->setParams($defopt)
			->updateParams($parm);
		if(!$category->hasData())
		{
			
			return '';
		}
		
		$tmpl = $this->getFboxTemplate($ctemplate);
		
		
		$type = vartrue($tmpl['js_type'],''); // Legacy support (prototype.js)
		
		if(vartrue($tmpl['js']))
		{		
			$tmp = explode(',', $tmpl['js']);
			
			foreach($tmp as $file)
			{
				e107::js('footer',$file,$type);	
			}
		}
		
		$tp = e107::getParser();
				
		if(vartrue($tmpl['js_inline']))
		{
			$data = $tp->toText($category->getData('fb_category_parms'));
			$jsInline = str_replace("{FEATUREBOX_PARMS}","{".trim($data)."}",$tmpl['js_inline']);
			e107::js('footer-inline', $jsInline, $type, 3);
		}
				
	
		
		// Fix - don't use tablerender if no result (category could contain hidden items)
		$ret = $this->render($category, $ctemplate, $parm);
		if(empty($ret))
		{
			e107::getMessage()->addDebug('Featurebox returned nothing.')->addDebug('Category: '.print_a($category,true))->addDebug('Template: '.$ctemplate)->addDebug('Param: '.print_a($parm,true));
			return '';
		}
		
		$ret = $tp->parseTemplate($tmpl['list_start'], true, $category).$ret.$tp->parseTemplate($tmpl['list_end'], true, $category);
		if(isset($parm['notablestyle']))
		{
			return $ret;
		}
		
		return e107::getRender()->tablerender(LAN_PLUGIN_FEATUREBOX_NAME, $ret, vartrue($parm['tablestyle'], 'featurebox'), true);
	}
	
	/**
	 * Render featurebox navigation
	 * Available parameters (GET string format)
	 * - loop (boolean): loop using 'nav_loop' template, default 0
	 * - base (string): template key prefix, default is 'nav'. Example: 'mynav' base key will search templates 'mynav_start', 'mynav_loop', 'mynav_end'.
	 * - nolimit (boolean): ignore 'limit' field , use 'total' items number for navigation looping
	 * - uselimit (boolean): ignore 'limit' field , use 'total' items number for navigation looping
	 * 
	 * @param string $parm parameters
	 * @param string $mod category template
	 */
	function sc_featurebox_navigation($parm=null, $mod = '')
	{
		// TODO cache	
		//TODO default $parm values. eg. assume 'tabs' when included in the 'tabs' template. 	
		
		if(!e107::isInstalled('featurebox')) //just in case
		{
			return '';
		}
		
		if(!$mod)
		{
			$ctemplate = 'default';
		}
		else
		{
			$ctemplate = $mod;
		}
		parse_str($parm, $parm);
		
		$category = $this->getCategoryModel($ctemplate); 
		
		if(!$category->hasData())
		{
			return '';
		}
		$tree = $category->getItemTree();
		if($tree->isEmpty())
		{
			return '';
		}
		$tmpl = $this->getFboxTemplate($ctemplate);
		
		if($category->get('fb_category_random'))
		{
			unset($parm['loop']);
		}
		
		$base = vartrue($parm['base'], 'nav').'_';
		$tree_ids = array_keys($tree->getTree()); //all available item ids
		
		
		
		$ret = $category->toHTML(varset($tmpl[$base.'start']), true); 
		$cols = $category->getParam('cols', 1);
		
		if(isset($parm['loop']) && $tree->getTotal() > 0 && vartrue($tmpl[$base.'item']))
		{
			// loop for all
			if(isset($parm['nolimit'])) 
			{
				$total = $tree->getTotal();
			}
		
			// loop for limit number
			elseif(isset($parm['uselimit'])) 
			{
				$total = $category->sc_featurebox_category_limit() ? intval($category->sc_featurebox_category_limit()) : $tree->getTotal();
				if($total > $tree->getTotal())
				{
					$total = $tree->getTotal();
				}
			}
			// default - number based on all / limit (usefull for ajax navigation)
			else 
			{ 
				$total = ceil($tree->getTotal() / ($category->sc_featurebox_category_limit() ? intval($category->sc_featurebox_category_limit()) : $tree->getTotal()) );
			}
			if($cols > 1)
			{
				$total = ceil($total / $cols);
			}
				
			$model = clone $category;
			$item = new plugin_featurebox_item();
			$tmp = array();
			
			for ($index = 1; $index <= $total; $index++)
			{
				
				$nodeid = varset($tree_ids[($index - 1) * $cols], 0); 
				if($nodeid && $tree->hasNode($nodeid))
				{
					
					$model->setParam('counter', $index)
						->setParam('total', $total)
						->setParam('active', $index == varset($parm['from'], 1));
						
					$node = $tree->getNode($nodeid);
					
					e107::getScParser()->resetScClass('plugin_featurebox_category', $model);
					$node->setCategory($model)
						->setParam('counter', $index)
						->setParam('total', $total)
						->setParam('limit', $category->get('fb_category_limit'))
						;
	
					$tmp[] = $node->toHTML($tmpl[$base.'item'], true); 
					continue;
				}
				
				e107::getScParser()->resetScClass('plugin_featurebox_item', $item);
				$tmp[] = $model->setParam('counter', $index)
					->setParam('active', $index == varset($parm['from'], 1))
					->toHTML($tmpl[$base.'item'], true);
			} 
			$ret .= implode(varset($tmpl[$base.'separator']), $tmp);
			unset($model, $tmp);
		}
		$ret .= $category->toHTML(varset($tmpl[$base.'end']), true);
		
		// Moved to 'sc_featurebox' - as it wouldn't load js if navigation was not used. 
		/*
		$type = vartrue($tmpl['js_type'],''); // Legacy support (prototype.js)
		
		if(vartrue($tmpl['js']))
		{		
			$tmp = explode(',', $tmpl['js']);
			
			foreach($tmp as $file)
			{
				e107::js('footer',$file,$type);	
			}
		}
				
		if(vartrue($tmpl['js_inline']))
		{
			e107::js('inline',$tmpl['js_inline'],$type,3);
			// e107::getJs()->footerInline($tmpl['js_inline'], 3);
		}
		
		if(vartrue($tmpl['css']))
		{		
			$tmp = explode(',', $tmpl['css']);
			foreach($tmp as $file)
			{
				e107::css('url',$file,$type);	
			}
		}
		*/
		return $ret;
	}
	
	/**
	 * Get & Render featurebox items (custom)
	 * Available parameters (GET string format)
	 * - cols (integer): number of items per column, default 1
	 * - no_fill_empty (boolean): don't fill last column with empty items (if required), default 0
	 * - from (integer): start load at
	 * - limit (integer): load to
	 * 
	 * @param string $parm parameters
	 * @param string $mod category template
	 */
	function sc_featurebox_items($parm=null, $mod = '')
	{
		// TODO cache
		if(!e107::isInstalled('featurebox')) //just in case
		{
			return '';
		}
		
		if(!$mod)
		{
			$ctemplate = 'default';
		}
		else
		{
			$ctemplate = $mod;
		}
		if(!empty($parm))
		{
			parse_str($parm, $parm);
		}
		
		$category = clone $this->getCategoryModel($ctemplate);
		if(!$category->hasData())
		{
			return '';
		}
		return $this->render($category, $ctemplate, $parm);
	}
	
	/**
	 * Render featurebox list
	 * @param plugin_featurebox_category $category
	 * @param string $ctemplate category template
	 * @param array $parm
	 */
	public function render($category, $ctemplate, $parm)
	{
		$tmpl = $this->getFboxTemplate($ctemplate);
		$cols = intval(vartrue($parm['cols'], 1));
		$limit = intval(varset($parm['limit'], $category->sc_featurebox_category_limit()));
		$from = (intval(vartrue($parm['from'], 1)) - 1) * $limit;
		$category->setParam('cols', $cols)
			->setParam('no_fill_empty', isset($parm['no_fill_empty']) ? 1 : 0)
			->setParam('limit', $limit)
			->setParam('from', $from);
			
		$tree = $category->getItemTree(true); 
		if($tree->isEmpty())
		{
			return '';
		}
		
		$total = $tree->getTotal();
		$category->setParam('total', $total);
		$counter = 1;
		$col_counter = 1;
		$cols_counter = 1; // column counter
		$ret = '';
		
		foreach ($tree->getTree() as $id => $node) 
		{
			$tmpl_item = e107::getTemplate('featurebox', 'featurebox', $node->get('fb_template'));
			if(!$tmpl_item) 
			{
				$tmpl_item = e107::getTemplate('featurebox', 'featurebox', 'default', true, true);
			}
			
			// reset column counter
			if($col_counter > $cols)
			{
				$col_counter = 1;
			}
			
			// item container (category template)
			$tmpl_item = $tmpl['item_start'].$tmpl_item.$tmpl['item_end'];
			// add column start
			if(1 == $col_counter && vartrue($tmpl['col_start']))
			{
				$tmpl_item = $tmpl['col_start'].$tmpl_item;
			}
			
			// there is more
			if(($total - $counter) > 0)
			{
				// add column end if column end reached
				if($cols == $col_counter && vartrue($tmpl['col_end']))
				{
					$tmpl_item .= $tmpl['col_end'];
				}
				// else add item separator
				elseif($cols != $col_counter && 1 != $col_counter)
				{
					$tmpl_item .= $tmpl['item_separator'];
				}
			}
			// no more items - clean & close
			else
			{
				$empty_cnt = $cols - $col_counter;
				if($empty_cnt > 0 && !$category->getParam('no_fill_empty'))
				{
					// empty items fill
					$tmp = new plugin_featurebox_item();
					$tmp->setCategory($category);
					$tmp_tmpl_item = $tmpl['item_separator'].$tmpl['item_start'].varset($tmpl['item_empty'], '