/**
  * Process the tag and return the result.
  *
  * @see modElement::process()
  * @param array|string $properties An array of properties or a formatted
  * property string.
  * @param string $content Optional content to use for the element
  * processing.
  * @return mixed The result of processing the tag.
  */
 public function process($properties = null, $content = null)
 {
     $this->modx->getParser();
     $this->getProperties($properties);
     $this->getTag();
     $this->filterInput();
     if ($this->modx->getDebug() === true) {
         $this->modx->log(xPDO::LOG_LEVEL_DEBUG, "Processing Element: " . $this->get('name') . ($this->_tag ? "\nTag: {$this->_tag}" : "\n") . "\nProperties: " . print_r($this->_properties, true));
     }
     if ($this->isCacheable() && isset($this->modx->elementCache[$this->_tag])) {
         $this->_output = $this->modx->elementCache[$this->_tag];
         $this->_processed = true;
     } else {
         $this->getContent(is_string($content) ? array('content' => $content) : array());
     }
     return $this->_result;
 }
 /**
  * Prepare the final response after the resource has been processed.
  *
  * @param array $options Various options that can be set.
  */
 public function outputContent(array $options = array())
 {
     if (!($this->contentType = $this->modx->resource->getOne('ContentType'))) {
         if ($this->modx->getDebug() === true) {
             $this->modx->log(modX::LOG_LEVEL_DEBUG, "No valid content type for RESOURCE: " . print_r($this->modx->resource->toArray(), true));
         }
         $this->modx->log(modX::LOG_LEVEL_FATAL, "The requested resource has no valid content type specified.");
     }
     if (!$this->contentType->get('binary')) {
         $this->modx->resource->_output = $this->modx->resource->process();
         $this->modx->resource->_jscripts = $this->modx->jscripts;
         $this->modx->resource->_sjscripts = $this->modx->sjscripts;
         $this->modx->resource->_loadedjscripts = $this->modx->loadedjscripts;
         /* collect any uncached element tags in the content and process them */
         $this->modx->getParser();
         $maxIterations = intval($this->modx->getOption('parser_max_iterations', $options, 10));
         $this->modx->parser->processElementTags('', $this->modx->resource->_output, true, false, '[[', ']]', array(), $maxIterations);
         $this->modx->parser->processElementTags('', $this->modx->resource->_output, true, true, '[[', ']]', array(), $maxIterations);
         /*FIXME: only do this for HTML content ?*/
         if (strpos($this->contentType->get('mime_type'), 'text/html') !== false) {
             /* Insert Startup jscripts & CSS scripts into template - template must have a </head> tag */
             if (($js = $this->modx->getRegisteredClientStartupScripts()) && strpos($this->modx->resource->_output, '</head>') !== false) {
                 /* change to just before closing </head> */
                 $this->modx->resource->_output = preg_replace("/(<\\/head>)/i", $js . "\n\\1", $this->modx->resource->_output, 1);
             }
             /* Insert jscripts & html block into template - template must have a </body> tag */
             if (strpos($this->modx->resource->_output, '</body>') !== false && ($js = $this->modx->getRegisteredClientScripts())) {
                 $this->modx->resource->_output = preg_replace("/(<\\/body>)/i", $js . "\n\\1", $this->modx->resource->_output, 1);
             }
         }
         $this->modx->beforeRender();
         /* invoke OnWebPagePrerender event */
         if (!isset($options['noEvent']) || empty($options['noEvent'])) {
             $this->modx->invokeEvent('OnWebPagePrerender');
         }
         $totalTime = $this->modx->getMicroTime() - $this->modx->startTime;
         $queryTime = $this->modx->queryTime;
         $queryTime = sprintf("%2.4f s", $queryTime);
         $queries = isset($this->modx->executedQueries) ? $this->modx->executedQueries : 0;
         $totalTime = sprintf("%2.4f s", $totalTime);
         $phpTime = $totalTime - $queryTime;
         $phpTime = sprintf("%2.4f s", $phpTime);
         $source = $this->modx->resourceGenerated ? "database" : "cache";
         $this->modx->resource->_output = str_replace("[^q^]", $queries, $this->modx->resource->_output);
         $this->modx->resource->_output = str_replace("[^qt^]", $queryTime, $this->modx->resource->_output);
         $this->modx->resource->_output = str_replace("[^p^]", $phpTime, $this->modx->resource->_output);
         $this->modx->resource->_output = str_replace("[^t^]", $totalTime, $this->modx->resource->_output);
         $this->modx->resource->_output = str_replace("[^s^]", $source, $this->modx->resource->_output);
     } else {
         $this->modx->beforeRender();
         /* invoke OnWebPagePrerender event */
         if (!isset($options['noEvent']) || empty($options['noEvent'])) {
             $this->modx->invokeEvent("OnWebPagePrerender");
         }
     }
     /* send out content-type, content-disposition, and custom headers from the content type */
     if ($this->modx->getOption('set_header')) {
         $type = $this->contentType->get('mime_type') ? $this->contentType->get('mime_type') : 'text/html';
         $header = 'Content-Type: ' . $type;
         if (!$this->contentType->get('binary')) {
             $charset = $this->modx->getOption('modx_charset', null, 'UTF-8');
             $header .= '; charset=' . $charset;
         }
         header($header);
         if (!$this->checkPreview()) {
             $dispositionSet = false;
             if ($customHeaders = $this->contentType->get('headers')) {
                 foreach ($customHeaders as $headerKey => $headerString) {
                     header($headerString);
                     if (strpos($headerString, 'Content-Disposition:') !== false) {
                         $dispositionSet = true;
                     }
                 }
             }
             if (!$dispositionSet && $this->modx->resource->get('content_dispo')) {
                 if ($alias = array_search($this->modx->resourceIdentifier, $this->modx->aliasMap)) {
                     $name = basename($alias);
                 } elseif ($this->modx->resource->get('alias')) {
                     $name = $this->modx->resource->get('alias');
                     if ($ext = $this->contentType->getExtension()) {
                         $name .= ".{$ext}";
                     }
                 } elseif ($name = $this->modx->resource->get('pagetitle')) {
                     $name = $this->modx->resource->cleanAlias($name);
                     if ($ext = $this->contentType->getExtension()) {
                         $name .= ".{$ext}";
                     }
                 } else {
                     $name = 'download';
                     if ($ext = $this->contentType->getExtension()) {
                         $name .= ".{$ext}";
                     }
                 }
                 $header = 'Cache-Control: public';
                 header($header);
                 $header = 'Content-Disposition: attachment; filename=' . $name;
                 header($header);
                 $header = 'Vary: User-Agent';
                 header($header);
             }
         }
     }
     /* tell PHP to call _postProcess after returning the response (for caching) */
     register_shutdown_function(array(&$this->modx, "_postProcess"));
     if ($this->modx->resource instanceof modStaticResource && $this->contentType->get('binary')) {
         $this->modx->resource->process();
     } else {
         if ($this->contentType->get('binary')) {
             $this->modx->resource->_output = $this->modx->resource->process();
         }
         @session_write_close();
         echo $this->modx->resource->_output;
         while (@ob_end_flush()) {
         }
         flush();
         exit;
     }
 }