/**
  * Parses the specified template library
  *
  * @param string $tagName
  * @param DOMNode $node
  */
 private function parseLibrary($tagName, DOMNode &$node)
 {
     // Set the My context based on the node's attributes
     $myContext = $this->nodeAttributesToScope($node);
     // Template call has child nodes, those are params that can be used in a os:Render call, store them
     $oldNodeContext = isset($this->dataContext['_os_render_nodes']) ? $this->dataContext['_os_render_nodes'] : array();
     $this->dataContext['_os_render_nodes'] = array();
     if ($node->childNodes->length) {
         foreach ($node->childNodes as $childNode) {
             if (isset($childNode->tagName) && !empty($childNode->tagName)) {
                 $nodeParam = ($pos = strpos($childNode->tagName, ':')) ? trim(substr($childNode->tagName, $pos + 1)) : trim($childNode->tagName);
                 $this->dataContext['_os_render_nodes'][$nodeParam] = $childNode;
                 $myContext[$nodeParam] = $this->nodeAttributesToScope($childNode);
             }
         }
     }
     // Parse the template library (store the My scope since this could be a nested call)
     $previousMy = $this->dataContext['My'];
     $this->dataContext['My'] = $myContext;
     $ret = $this->templateLibrary->parseTemplate($tagName, $this);
     $this->dataContext['My'] = $previousMy;
     $this->dataContext['_os_render_nodes'] = $oldNodeContext;
     if ($ret) {
         // And replace the node with the parsed output
         $ownerDocument = $node->ownerDocument;
         foreach ($ret->childNodes as $childNode) {
             $importedNode = $ownerDocument->importNode($childNode, true);
             $importedNode = $node->parentNode->insertBefore($importedNode, $node);
         }
     }
 }
 /**
  * Does the parsing of the template/data script content, then it hands
  * the os-data parsing to the DataPipeling class, and os-template tags to
  * the TemplateParser, and then returns the expanded template content (or '' on data)
  *
  * @param string $template
  * @param TemplateLibrary $templateLibrary
  * @return string
  */
 private function renderTemplate($template, TemplateLibrary $templateLibrary)
 {
     libxml_use_internal_errors(true);
     $this->doc = new DOMDocument(null, 'utf-8');
     $this->doc->preserveWhiteSpace = true;
     $this->doc->formatOutput = false;
     $this->doc->strictErrorChecking = false;
     $this->doc->recover = false;
     $this->doc->resolveExternals = false;
     if (!$this->doc->loadXML($template)) {
         return "Error parsing os-template:\n" . XmlError::getErrors($template);
     }
     if ($this->doc->childNodes->length < 1 || $this->doc->childNodes->length >> 1) {
         return 'Invalid script block';
     }
     $childNode = $this->doc->childNodes->item(0);
     if ($childNode->tagName == 'script' && $childNode->getAttribute('name') == null && $childNode->getAttribute('autoUpdate') != 'true') {
         // If the require tag is set, check to see if we have all required data parts, and if not leave it to the client to render
         if (($require = $childNode->getAttribute('require')) != null) {
             $requires = explode(',', $require);
             foreach ($requires as $val) {
                 $val = trim($val);
                 if (!isset($this->dataContext[$val])) {
                     return false;
                 }
             }
         }
         // if $childNode->tag exists, add to global $templateLibraries array, else parse normally
         $childNodeTag = $childNode->getAttribute('tag');
         if (!empty($childNodeTag)) {
             if (isset($this->templateLibraries[$childNode->getAttribute('tag')])) {
                 throw new ExpressionException("Template " . htmlentities($childNode->getAttribute('tag')) . " was already defined");
             }
             $templateLibrary->addTemplateByNode($childNode);
         } else {
             // Everything checked out, proceeding to render the template
             $parser = new TemplateParser();
             $parser->process($childNode, $this->dataContext, $templateLibrary);
             // unwrap the output, ie we only want the script block's content and not the main <script></script> node
             $output = new DOMDocument(null, 'utf-8');
             foreach ($childNode->childNodes as $node) {
                 $outNode = $output->importNode($node, true);
                 $outNode = $output->appendChild($outNode);
             }
             // Restore single tags to their html variant, and remove the xml header
             $ret = str_replace(array('<?xml version="" encoding="utf-8"?>', '<br/>', '<script type="text/javascript"><![CDATA[', ']]></script>'), array('', '<br>', '<script type="text/javascript">', '</script>'), $output->saveXML());
             return $ret;
         }
     }
     return false;
 }
 /**
  * This function parses the os-template and os-data script tags.
  * It's of vital importance to call this function *before* the rewriteContent function
  * since the html/dom parser of the later breaks, mangles and otherwise destroys the
  * os template/data script tags. So we need to expand the templates to pure html
  * before we can proceeed to dom parse the resulting document
  *
  * @param string $content html to parse
  */
 public function parseTemplates($content)
 {
     $osTemplates = array();
     $osDataRequests = array();
     // First extract all the os-data tags, and execute those in a single combined request, saves latency
     // and is consistent with other server implementations
     preg_match_all('/(<script[^>]*type="text\\/(os-data)"[^>]*>)(.*)(<\\/script>)/imsxU', $content, $osDataRequests);
     $osDataRequestsCombined = '';
     foreach ($osDataRequests[0] as $match) {
         $osDataRequestsCombined .= $match . "\n";
         // Remove the reference from the html document
         $content = str_replace($match, '', $content);
     }
     if (!empty($osDataRequestsCombined)) {
         $this->performDataRequests($osDataRequestsCombined);
     }
     preg_match_all('/(<script[^>]*type="text\\/(os-template)"[^>]*>)(.*)(<\\/script>)/imxsU', $content, $osTemplates);
     $templateLibrary = false;
     if (count($osTemplates[0])) {
         // only load the template parser if there's any templates in the gadget content
         require_once 'src/gadgets/templates/TemplateParser.php';
         require_once 'src/gadgets/templates/TemplateLibrary.php';
         $templateLibrary = new TemplateLibrary($this->gadget->gadgetContext);
         if ($this->gadget->gadgetSpec->templatesRequireLibraries) {
             foreach ($this->gadget->gadgetSpec->templatesRequireLibraries as $library) {
                 $templateLibrary->addTemplateLibrary($library);
             }
         }
         foreach ($osTemplates[0] as $match) {
             if (!$this->gadget->gadgetSpec->templatesDisableAutoProcessing && ($renderedTemplate = $this->renderTemplate($match, $templateLibrary)) !== false) {
                 // Template was rendered, insert the rendered html into the document
                 $content = str_replace($match, $renderedTemplate, $content);
             } else {
                 /*
                  * The template could not be rendered, this could happen because:
                  * - @require is present, and at least one of the required pieces of data is unavailable
                  * - @name is present
                  * - @autoUpdate == true
                  * - $this->gadget->gadgetSpec->templatesDisableAutoProcessing is set to true
                  * So set a magic marker (<template_$index>) that after the dom document parsing will be replaced with the original script content
                  */
                 $index = count($this->unparsedTemplates);
                 $this->unparsedTemplates[$index] = $match;
                 $content = str_replace($match, "<template_{$index}></template_{$index}>", $content);
             }
         }
     }
     return $content;
 }