links.  This is useful in Ajax applications.
	 * It returns the SSViewer objects, so that you can call new SSViewer("X")->dontRewriteHashlinks()->process();
	 */
	public function dontRewriteHashlinks() {
		$this->rewriteHashlinks = false;
		Config::inst()->update('SSViewer', 'rewrite_hash_links', false);
		return $this;
	}
	
	public function exists() {
		return $this->chosenTemplates;
	}
	/**
	 * @param string $identifier A template name without '.ss' extension or path
	 * @param string $type The template type, either "main", "Includes" or "Layout"
	 *
	 * @return string Full system path to a template file
	 */
	public static function getTemplateFileByType($identifier, $type) {
		$loader = SS_TemplateLoader::instance();
		if(Config::inst()->get('SSViewer', 'theme_enabled')) {
			$theme = Config::inst()->get('SSViewer', 'theme');
		} else {
			$theme = null;
		}
		$found  = $loader->findTemplates("$type/$identifier", $theme);
		if (isset($found['main'])) {
			return $found['main'];
		}
		else if (!empty($found)) {
			$founds = array_values($found);
			return $founds[0];
		}
	}
	
	/**
	 * Clears all parsed template files in the cache folder.
	 *
	 * Can only be called once per request (there may be multiple SSViewer instances).
	 *
	 * @param bool $force Set this to true to force a re-flush. If left to false, flushing
	 * may only be performed once a request.
	 */
	public static function flush_template_cache($force = false) {
		if (!self::$template_cache_flushed || $force) {
			$dir = dir(TEMP_FOLDER);
			while (false !== ($file = $dir->read())) {
				if (strstr($file, '.cache')) unlink(TEMP_FOLDER . '/' . $file);
			}
			self::$template_cache_flushed = true;
		}
	}
	/**
	 * Clears all partial cache blocks.
	 *
	 * Can only be called once per request (there may be multiple SSViewer instances).
	 *
	 * @param bool $force Set this to true to force a re-flush. If left to false, flushing
	 * may only be performed once a request.
	 */
	public static function flush_cacheblock_cache($force = false) {
		if (!self::$cacheblock_cache_flushed || $force) {
			$cache = SS_Cache::factory('cacheblock');
			$backend = $cache->getBackend();
			
			if(
				$backend instanceof Zend_Cache_Backend_ExtendedInterface
				&& ($capabilities = $backend->getCapabilities())
				&& $capabilities['tags']
			) {
				$cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $cache->getTags());
			} else {
				$cache->clean(Zend_Cache::CLEANING_MODE_ALL);
			}
			
			self::$cacheblock_cache_flushed = true;
		}
	}
	/**
	 * @var Zend_Cache_Core
	 */
	protected $partialCacheStore = null;
	/**
	 * Set the cache object to use when storing / retrieving partial cache blocks.
	 *
	 * @param Zend_Cache_Core $cache
	 */
	public function setPartialCacheStore($cache) {
		$this->partialCacheStore = $cache;
	}
	/**
	 * Get the cache object to use when storing / retrieving partial cache blocks.
	 *
	 * @return Zend_Cache_Core
	 */
	public function getPartialCacheStore() {
		return $this->partialCacheStore ? $this->partialCacheStore : SS_Cache::factory('cacheblock');
	}
	/**
	 * Flag whether to include the requirements in this response.
	 *
	 * @param boolean
	 */
	public function includeRequirements($incl = true) {
		$this->includeRequirements = $incl;
	}
	/**
	 * An internal utility function to set up variables in preparation for including a compiled
	 * template, then do the include
	 *
	 * Effectively this is the common code that both SSViewer#process and SSViewer_FromString#process call
	 *
	 * @param string $cacheFile - The path to the file that contains the template compiled to PHP
	 * @param Object $item - The item to use as the root scope for the template
	 * @param array|null $overlay - Any variables to layer on top of the scope
	 * @param array|null $underlay - Any variables to layer underneath the scope
	 * @param Object $inheritedScope - the current scope of a parent template including a sub-template
	 *
	 * @return string - The result of executing the template
	 */
	protected function includeGeneratedTemplate($cacheFile, $item, $overlay, $underlay, $inheritedScope = null) {
		if(isset($_GET['showtemplate']) && $_GET['showtemplate'] && Permission::check('ADMIN')) {
			$lines = file($cacheFile);
			echo "Template: $cacheFile
";
			echo "";
			foreach($lines as $num => $line) {
				echo str_pad($num+1,5) . htmlentities($line, ENT_COMPAT, 'UTF-8');
			}
			echo "";
		}
		$cache = $this->getPartialCacheStore();
		$scope = new SSViewer_DataPresenter($item, $overlay, $underlay, $inheritedScope);
		$val = '';
		include($cacheFile);
		return $val;
	}
	/**
	 * The process() method handles the "meat" of the template processing.
	 *
	 * It takes care of caching the output (via {@link SS_Cache}), as well as 
	 * replacing the special "$Content" and "$Layout" placeholders with their 
	 * respective subtemplates.
	 *
	 * The method injects extra HTML in the header via {@link Requirements::includeInHTML()}.
	 * 
	 * Note: You can call this method indirectly by {@link ViewableData->renderWith()}.
	 * 
	 * @param ViewableData $item
	 * @param array|null $arguments - arguments to an included template
	 * @param Object $inheritedScope - the current scope of a parent template including a sub-template
	 *
	 * @return HTMLText Parsed template output.
	 */
	public function process($item, $arguments = null, $inheritedScope = null) {
		SSViewer::$topLevel[] = $item;
		if ($arguments && $arguments instanceof Zend_Cache_Core) {
			Deprecation::notice('3.0', 'Use setPartialCacheStore to override the partial cache storage backend, ' .
				'the second argument to process is now an array of variables.');
			$this->setPartialCacheStore($arguments);
			$arguments = null;
		}
		if(isset($this->chosenTemplates['main'])) {
			$template = $this->chosenTemplates['main'];
		} else {
			$keys = array_keys($this->chosenTemplates);
			$key = reset($keys);
			$template = $this->chosenTemplates[$key];
		}
		
		$cacheFile = TEMP_FOLDER . "/.cache" 
			. str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
		$lastEdited = filemtime($template);
		if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited) {
			$content = file_get_contents($template);
			$content = $this->parseTemplateContent($content, $template);
			
			$fh = fopen($cacheFile,'w');
			fwrite($fh, $content);
			fclose($fh);
		}
		$underlay = array('I18NNamespace' => basename($template));
		// Makes the rendered sub-templates available on the parent item,
		// through $Content and $Layout placeholders.
		foreach(array('Content', 'Layout') as $subtemplate) {
			if(isset($this->chosenTemplates[$subtemplate])) {
				$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate], $this->parser);
				$subtemplateViewer->includeRequirements(false);
				$subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore());
				$underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
			}
		}
		$output = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay, $inheritedScope);
		
		if($this->includeRequirements) {
			$output = Requirements::includeInHTML($template, $output);
		}
		
		array_pop(SSViewer::$topLevel);
		// If we have our crazy base tag, then fix # links referencing the current page.
		
		$rewrite = Config::inst()->get('SSViewer', 'rewrite_hash_links');
		if($this->rewriteHashlinks && $rewrite) {
			if(strpos($output, '";
				} else { 
					$thisURLRelativeToBase = Convert::raw2att($_SERVER['REQUEST_URI']);
				}
				$output = preg_replace('/(