1<?php
2
3/* Copyright (c) 2018 Thomas Famula <famula@leifos.de> Extended GPL, see docs/LICENSE */
4
5namespace ILIAS\UI\Implementation\Component\Input\Container\Filter;
6
7use ILIAS\UI\Component;
8use ILIAS\UI\Component\Input\Container\Filter;
9use ILIAS\UI\Implementation\Component\SignalGenerator;
10use ILIAS\UI\Implementation\Render\AbstractComponentRenderer;
11use ILIAS\UI\Implementation\Render\Template;
12use ILIAS\UI\Renderer as RendererInterface;
13
14class Renderer extends AbstractComponentRenderer
15{
16
17    /**
18     * @inheritdoc
19     */
20    public function render(Component\Component $component, RendererInterface $default_renderer)
21    {
22        $this->checkComponent($component);
23
24        if ($component instanceof Filter\Standard) {
25            return $this->renderStandard($component, $default_renderer);
26        }
27
28        throw new \LogicException("Cannot render: " . get_class($component));
29    }
30
31    /**
32     * Render standard filter
33     *
34     * @param Filter\Standard $component
35     * @param RendererInterface $default_renderer
36     * @return Template string
37     */
38    protected function renderStandard(Filter\Standard $component, RendererInterface $default_renderer)
39    {
40        $tpl = $this->getTemplate("tpl.standard_filter.html", true, true);
41
42        // JavaScript
43        $component = $this->registerSignals($component);
44        $id = $this->bindJavaScript($component);
45        $tpl->setVariable('ID_FILTER', $id);
46
47        // render expand and collapse
48        $this->renderExpandAndCollapse($tpl, $component, $default_renderer);
49
50        // render apply and reset buttons
51        $this->renderApplyAndReset($tpl, $component, $default_renderer);
52
53        // render toggle button
54        $this->renderToggleButton($tpl, $component, $default_renderer);
55
56        // render inputs
57        $this->renderInputs($tpl, $component, $default_renderer);
58
59        return $tpl->get();
60    }
61
62    /**
63     * @param Filter\Filter $filter
64     * @return Component\JavaScriptBindable
65     */
66    protected function registerSignals(Filter\Filter $filter)
67    {
68        $update = $filter->getUpdateSignal();
69        return $filter->withAdditionalOnLoadCode(function ($id) use ($update) {
70            $code =
71                "$(document).on('{$update}', function(event, signalData) { il.UI.filter.onInputUpdate(event, signalData, '{$id}'); return false; });";
72            return $code;
73        });
74    }
75
76    /**
77     * Render expand/collapse section
78     *
79     * @param Template $tpl
80     * @param Filter\Standard $component
81     * @param RendererInterface $default_renderer
82     */
83    protected function renderExpandAndCollapse(Template $tpl, Filter\Standard $component, RendererInterface $default_renderer)
84    {
85        $f = $this->getUIFactory();
86
87        $tpl->setCurrentBlock("action");
88        $tpl->setVariable("ACTION_NAME", "expand");
89        $tpl->setVariable("ACTION", $component->getExpandAction());
90        $tpl->parseCurrentBlock();
91
92        $opener_expand = $f->button()->bulky($f->symbol()->glyph()->expand(), $this->txt("filter"), "")
93            ->withAdditionalOnLoadCode(function ($id) {
94                $code = "$('#$id').on('click', function(event) {
95					il.UI.filter.onAjaxCmd(event, '$id', 'expand');
96					event.preventDefault();
97			});";
98                return $code;
99            });
100
101        $tpl->setCurrentBlock("action");
102        $tpl->setVariable("ACTION_NAME", "collapse");
103        $tpl->setVariable("ACTION", $component->getCollapseAction());
104        $tpl->parseCurrentBlock();
105
106        $opener_collapse = $f->button()->bulky($f->symbol()->glyph()->collapse(), $this->txt("filter"), "")
107            ->withAdditionalOnLoadCode(function ($id) {
108                $code = "$('#$id').on('click', function(event) {
109					il.UI.filter.onAjaxCmd(event, '$id', 'collapse');
110					event.preventDefault();
111			});";
112                return $code;
113            });
114
115        if ($component->isExpanded() == false) {
116            $opener = [$opener_collapse, $opener_expand];
117            $tpl->setVariable("OPENER", $default_renderer->render($opener));
118            $tpl->setVariable("ARIA_EXPANDED", "'false'");
119            $tpl->setVariable("INPUTS_ACTIVE_EXPANDED", "in");
120        } else {
121            $opener = [$opener_expand, $opener_collapse];
122            $tpl->setVariable("OPENER", $default_renderer->render($opener));
123            $tpl->setVariable("ARIA_EXPANDED", "'true'");
124            $tpl->setVariable("INPUTS_EXPANDED", "in");
125        }
126    }
127
128    /**
129     * Render apply and reset
130     *
131     * @param Template $tpl
132     * @param Filter\Standard $component
133     * @param RendererInterface $default_renderer
134     */
135    protected function renderApplyAndReset(Template $tpl, Filter\Standard $component, RendererInterface $default_renderer)
136    {
137        $f = $this->getUIFactory();
138
139        $tpl->setCurrentBlock("action");
140        $tpl->setVariable("ACTION_NAME", "apply");
141        $tpl->setVariable("ACTION", $component->getApplyAction());
142        $tpl->parseCurrentBlock();
143
144        // render apply and reset buttons
145        $apply = $f->button()->bulky($f->symbol()->glyph()->apply(), $this->txt("apply"), "");
146
147        if (!$component->isActivated()) {
148            $apply = $apply->withUnavailableAction();
149            $reset = $f->button()->bulky($f->symbol()->glyph()->reset(), $this->txt("reset"), "")
150            ->withUnavailableAction();
151        } else {
152            $apply = $apply->withOnLoadCode(function ($id) {
153                $code = "$('#$id').on('click', function(event) {
154							il.UI.filter.onCmd(event, '$id', 'apply');
155							return false; // stop event propagation
156					});
157					$('#$id').closest('.il-filter').find(':text').on('keypress', function(ev) {
158						if (typeof ev != 'undefined' && typeof ev.keyCode != 'undefined' && ev.keyCode == 13) {
159							il.UI.filter.onCmd(event, '$id', 'apply');
160							return false; // stop event propagation
161						}
162					});
163					";
164                return $code;
165            });
166
167            $reset = $f->button()->bulky($f->symbol()->glyph()->reset(), $this->txt("reset"), $component->getResetAction());
168        }
169        $tpl->setVariable("APPLY", $default_renderer->render($apply));
170        $tpl->setVariable("RESET", $default_renderer->render($reset));
171    }
172
173    /**
174     * Render toggle button
175     *
176     * @param Template $tpl
177     * @param Filter\Standard $component
178     * @param RendererInterface $default_renderer
179     */
180    protected function renderToggleButton(Template $tpl, Filter\Standard $component, RendererInterface $default_renderer)
181    {
182        $f = $this->getUIFactory();
183
184        $tpl->setCurrentBlock("action");
185        $tpl->setVariable("ACTION_NAME", "toggleOn");
186        $tpl->setVariable("ACTION", $component->getToggleOnAction());
187        $tpl->parseCurrentBlock();
188
189        $component->getToggleOnAction();
190        $signal_generator = new SignalGenerator();
191        $toggle_on_signal = $signal_generator->create();
192        $toggle_on_action = $component->getToggleOnAction();
193        $toggle = $f->button()->toggle("", $toggle_on_signal, $component->getToggleOffAction(), $component->isActivated())
194            ->withAdditionalOnLoadCode(function ($id) use ($toggle_on_signal, $toggle_on_action) {
195                $code = "$(document).on('{$toggle_on_signal}',function(event) {
196							il.UI.filter.onCmd(event, '$id', 'toggleOn');
197							return false; // stop event propagation
198				});";
199                return $code;
200            });
201
202        $tpl->setVariable("TOGGLE", $default_renderer->render($toggle));
203    }
204
205    /**
206     * Render inputs
207     *
208     * @param Template $tpl
209     * @param Filter\Standard $component
210     * @param RendererInterface $default_renderer
211     */
212    protected function renderInputs(Template $tpl, Filter\Standard $component, RendererInterface $default_renderer)
213    {
214        // pass information on what inputs should be initially rendered
215        $is_input_rendered = $component->isInputRendered();
216        foreach ($component->getInputs() as $k => $input) {
217            $is_rendered = current($is_input_rendered);
218            $tpl->setCurrentBlock("status");
219            $tpl->setVariable("FIELD", $k);
220            $tpl->setVariable("VALUE", (int) $is_rendered);
221            $tpl->parseCurrentBlock();
222            next($is_input_rendered);
223        }
224
225        // render inputs
226        $input_group = $component->getInputGroup();
227        if ($component->isActivated()) {
228            for ($i = 1; $i <= count($component->getInputs()); $i++) {
229                $tpl->setCurrentBlock("active_inputs");
230                $tpl->setVariable("ID", $i);
231                $tpl->parseCurrentBlock();
232            }
233            if (count($component->getInputs()) > 0) {
234                $tpl->setCurrentBlock("active_inputs_section");
235                $tpl->parseCurrentBlock();
236            }
237            $tpl->touchBlock("enabled");
238        } else {
239            $tpl->touchBlock("disabled");
240            $input_group = $input_group->withDisabled(true);
241        }
242
243        $input_group = $input_group->withOnUpdate($component->getUpdateSignal());
244
245        $renderer = $default_renderer->withAdditionalContext($component);
246        $tpl->setVariable("INPUTS", $renderer->render($input_group));
247    }
248
249
250    /**
251     * @inheritdoc
252     */
253    protected function getComponentInterfaceName()
254    {
255        return array(
256            Filter\Standard::class,
257        );
258    }
259}
260