/** * @param Context $context * * @return SafeString */ public function render_($context) { return $this->nodelist->render($context); }
/** * @param Context $context * @return SafeString|string * @throws Exception|TypeError|VariableDoesNotExist * @throws RuntimeError */ public function render($context) { if (isset($context['forloop'])) { $parentloop = $context['forloop']; } else { $parentloop = array(); } $context->push(); try { $values = $this->sequence->resolve($context, True); } catch (VariableDoesNotExist $e) { $values = array(); } if ($values === null) { $values = array(); } elseif (is_string($values)) { // We convert a string to array to make it iterable. $values = str_split($values); // TODO Check unicode handling. } if (!is_array($values) && !$values instanceof Iterator) { throw new RuntimeError('Noniterable "' . $values . '" is passed to for loop.'); } $len_values = count($values); if ($len_values < 1) { $context->pop(); return $this->nodelist_empty->render($context); } $nodelist = new NodeList(); if ($this->is_reversed) { $values_ = $values; krsort($values_); $values = $values_; unset($values_); } $unpack = count($this->loopvars) > 1; // Create a forloop value in the context. We'll update counters on each iteration just below. $loop_dict = $context['forloop'] = array('parentloop' => $parentloop); foreach ($values as $i => $item) { // Shortcuts for current loop iteration number. $loop_dict['counter0'] = $i; $loop_dict['counter'] = $i + 1; // Reverse counter iteration numbers. $loop_dict['revcounter'] = $len_values - $i; $loop_dict['revcounter0'] = $len_values - $i - 1; // Boolean values designating first and last times through loop. $loop_dict['first'] = $i == 0; $loop_dict['last'] = $i == $len_values - 1; $context['forloop'] = array_merge($context['forloop'], $loop_dict); $pop_context = False; if ($unpack) { // If there are multiple loop variables, unpack the item into them. $success_ = True; try { $unpacked_vars = py_zip($this->loopvars, $item); } catch (TypeError $e) { $success_ = False; } if ($success_) { $pop_context = True; $context->update($unpacked_vars); } } else { $context[$this->loopvars[0]] = $item; } // In TEMPLATE_DEBUG mode provide source of the node which actually raised the exception if (Dja::getSetting('TEMPLATE_DEBUG')) { foreach ($this->nodelist_loop as $node) { /** @var $node Node */ try { $nodelist[] = $node->render($context); } catch (Exception $e) { if (!py_hasattr($e, 'django_template_source')) { $e->django_template_source = $node->source; } throw $e; } } } else { foreach ($this->nodelist_loop as $node) { $nodelist[] = $node->render($context); } } if ($pop_context) { /* * The loop variables were pushed on to the context so pop them * off again. This is necessary because the tag lets the length * of loopvars differ to the length of each set of items and we * don't want to leave any vars from the previous loop on the * context. */ $context->pop(); } } $context->pop(); return $nodelist->render($context); }