protected function render()
 {
     $prop = $this->props;
     $isMultiple = $prop->multiple;
     $assets = $this->context->getAssetsService();
     $assets->addInlineScript("selenia.ext.select.props['{$prop->id}']=" . JavascriptCodeGen::makeOptions(['autoOpenLinked' => $prop->autoOpenLinked, 'dataUrl' => $prop->dataUrl, 'emptyLabel' => $prop->emptyLabel, 'emptySelection' => $prop->emptySelection, 'id' => $prop->id, 'labelField' => $prop->labelField, 'linkedSelector' => $prop->linkedSelector, 'linkedUrl' => $prop->linkedUrl, 'multiple' => $prop->multiple, 'noResultsText' => $prop->noResultsText, 'valueField' => $prop->valueField, 'value' => $prop->value]));
     // If required, add auto-add tag behavior to this Chosen.
     if ($prop->autoTag && $prop->multiple) {
         $assets->addInlineScript("\n\$(function () {\n  \$ ('#{$prop->id}+.chosen-container .chosen-choices input').on ('keyup', function (ev) { console.log(ev);\n    var v = \$ (this).val ();\n    if (ev.keyCode == 13 && v) {\n      var tags  = \$ ('#{$prop->id} option').map (function (i, e) { return \$ (e).val () });\n      var found = false, l = v.length;\n      tags.each (function (i, x) {\n        if (x.substr (0, l) == v) {\n          found = true;\n          return false\n        }\n      });\n      if (found) return;\n      \$ ('#{$prop->id}').append (\"<option>\" + v + \"</option>\");\n      \$ ('#{$prop->id}').trigger ('chosen:updated');\n      ev.preventDefault ();\n      var e     = jQuery.Event (\"keyup\");\n      e.which   = 13;\n      \$ ('#{$prop->id}+.chosen-container .chosen-choices input').val (v).trigger ('keyup').trigger (e);\n    }\n  })\n});\n");
     }
     $this->attr('name', $prop->multiple ? "{$prop->name}[]" : $prop->name);
     $this->attrIf($isMultiple, 'multiple', '');
     $this->attrIf($prop->onChange, 'onchange', $prop->onChange);
     $this->beginContent();
     if ($prop->emptySelection && !$prop->multiple) {
         $sel = exists($prop->value) ? '' : ' selected';
         echo '<option value=""' . $sel . '>' . $prop->emptyLabel . '</option>';
     }
     $viewModel = $prop->get('data');
     if (isset($viewModel)) {
         /** @var \Iterator $dataIter */
         $dataIter = iteratorOf($viewModel);
         $dataIter->rewind();
         if ($dataIter->valid()) {
             $values = $selValue = null;
             // SETUP MULTI-SELECT
             if ($isMultiple) {
                 if (isset($prop->value) && !is_iterable($prop->value)) {
                     throw new ComponentException($this, sprintf("Value of multiple selection component must be iterable or null; %s was given.", typeOf($prop->value)));
                 }
                 $it = Flow::from($prop->value);
                 $it->rewind();
                 $values = $it->valid() && is_scalar($it->current()) ? $it->all() : $it->map(pluck($prop->valueField))->all();
             } else {
                 $selValue = strval($prop->get('value'));
             }
             // NOW RENDER IT
             $template = $this->getChildren('listItemTemplate');
             $first = true;
             do {
                 $v = $dataIter->current();
                 $value = getField($v, $prop->valueField);
                 $label = getField($v, $prop->labelField);
                 if (!strlen($label)) {
                     $label = $prop->emptyLabel;
                 }
                 if ($isMultiple) {
                     $sel = array_search($value, $values) !== false ? ' selected' : '';
                 } else {
                     if ($first && !$prop->emptySelection && !$prop->multiple && !exists($selValue)) {
                         $prop->value = $selValue = $value;
                     }
                     $eq = $prop->strict ? $value === $selValue : $value == $selValue;
                     if ($eq) {
                         $this->selectedLabel = $label;
                     }
                     $sel = $eq ? ' selected' : '';
                 }
                 if ($template) {
                     // Render templated list
                     $viewModel['value'] = $value;
                     $viewModel['label'] = $label;
                     Component::renderSet($template);
                 } else {
                     // Render standard list
                     echo "<option value=\"{$value}\"{$sel}>{$label}</option>";
                 }
                 $dataIter->next();
                 $first = false;
             } while ($dataIter->valid());
         }
     }
 }
    protected function render()
    {
        $prop = $this->props;
        $options = JavascriptCodeGen::makeOptions(['url' => $prop->url, 'acceptedFiles' => $prop->acceptedFiles, 'maxFiles' => $prop->maxFiles, 'clickable' => true, 'addRemoveLinks' => true, 'parallelUploads' => $prop->parallelUploads, 'method' => $prop->method, 'autoProcessQueue' => $prop->autoProcessQueue, 'dictDefaultMessage' => "Arraste ficheiros para aqui ou clique para escolher os ficheiros a enviar.", 'dictInvalidFileType' => 'Ficheiro inválido', 'dictFileTooBig' => 'Ficheiro demasiado grande', 'dictResponseError' => 'Erro ao enviar', 'dictCancelUpload' => 'Cancelar', 'dictCancelUploadConfirmation' => 'Tem a certeza?', 'dictRemoveFile' => 'Apagar', 'dictMaxFilesExceeded' => 'Não pode inserir mais ficheiros'], '  ');
        $this->context->getAssetsService()->addInlineScript(<<<JS
(function(element){
  var dropzone = new Dropzone (element[0], {$options});
}) (\$('#{$prop->id}'));
JS
);
    }
    protected function render()
    {
        $prop = $this->props;
        $type = $prop->get('type', 'text');
        $name = $prop->name;
        $action = when($prop->action, "checkKeybAction(event,'" . $prop->action . "')");
        $assets = $this->context->getAssetsService();
        $assets->addInlineScript(<<<JS
selenia.validateInput = function (input) {
  var v = input.validity;
  input.setCustomValidity(
    v.badInput        && "\$VALIDATION_BAD_INPUT" ||
    v.patternMismatch && "\$VALIDATION_PATTERN_MISMATCH" ||
    v.rangeOverflow   && "\$VALIDATION_RANGE_OVERFLOW" ||
    v.rangeUnderflow  && "\$VALIDATION_RANGE_UNDERFLOW" ||
    v.stepMismatch    && "\$VALIDATION_STEP_MISMATCH" ||
    v.tooLong         && "\$VALIDATION_TOO_LONG" ||
    v.tooShort        && "\$VALIDATION_TOO_SHORT" ||
    v.typeMismatch    && "\$VALIDATION_TYPE_MISMATCH" ||
    v.valueMissing    && "\$VALIDATION_VALUE_MISSING" ||
    ''
  );
};
JS
, 'validateInput');
        $value = either($prop->value, $prop->defaultValue);
        switch ($type) {
            case 'multiline':
                $assets->addInlineScript(<<<'JS'
          $('textarea.Input').textareaAutoSize();
          selenia.on('languageChanged',function(lang){
            $('textarea.Input[lang='+lang+']').trigger('input');
          });
          selenia.on('tabChanged',function(tab){
            tab.find('textarea.Input').trigger('input');
          });
JS
, 'input-autosize');
                $this->addAttrs(['name' => $name, 'cols' => 0, 'readonly' => $prop->readOnly ? 'readonly' : null, 'disabled' => $prop->disabled ? 'disabled' : null, 'tabindex' => $prop->tabIndex, 'autofocus' => $prop->autofocus, 'onfocus' => $prop->autoselect ? 'this.select()' : 'this.value=this.value', 'onchange' => $prop->onChange, 'spellcheck' => 'false', 'maxlength' => $prop->maxLength, 'minlength' => $prop->minLength, 'required' => $prop->required]);
                $this->setContent($value);
                break;
            case 'date':
            case 'time':
            case 'datetime':
                $this->addAttrs(['type' => 'text', 'name' => $name, 'value' => $value, 'readonly' => $prop->readOnly ? 'readonly' : null, 'disabled' => $prop->disabled ? 'disabled' : null, 'tabindex' => $prop->tabIndex, 'autofocus' => $prop->autofocus, 'onfocus' => $prop->autoselect ? 'this.select()' : 'this.value=this.value', 'onchange' => $prop->onChange, 'onkeypress' => $action, 'max' => $prop->max, 'min' => $prop->min, 'maxlength' => $prop->maxLength, 'minlength' => $prop->minLength, 'pattern' => $prop->pattern, 'required' => $prop->required]);
                switch ($type) {
                    case 'date':
                        $format = $prop->dateFormat;
                        break;
                    case 'time':
                        $format = $prop->timeFormat;
                        break;
                    default:
                        $format = $prop->datetimeFormat;
                }
                $assets->addInlineScript(<<<JS
\$('#{$prop->id}').datetimepicker({
  locale:      '{$prop->lang}',
  defaultDate: '{$value}' || new moment(),
  format:      '{$format}',
  sideBySide:  true,
  showTodayButton: true,
  showClear: true,
  showClose: true
});
JS
);
                break;
            case 'color':
                $this->beginContent();
                $this->tag('input', ['type' => 'text', 'class' => 'form-control', 'name' => $name, 'value' => $value, 'readonly' => $prop->readOnly ? 'readonly' : null, 'disabled' => $prop->disabled ? 'disabled' : null, 'tabindex' => $prop->tabIndex, 'autofocus' => $prop->autofocus, 'onfocus' => $prop->autoselect ? 'this.select()' : 'this.value=this.value', 'onchange' => $prop->onChange, 'onkeypress' => $action, 'max' => $prop->max, 'min' => $prop->min, 'maxlength' => $prop->maxLength, 'minlength' => $prop->minLength, 'pattern' => $prop->pattern, 'required' => $prop->required]);
                echo '<span class="input-group-addon"><i></i></span>';
                $assets->addInlineScript("\$('#{$name}0').colorpicker();");
                break;
            case 'number':
                $this->addAttrs(['type' => 'text', 'name' => $name, 'value' => $value, 'placeholder' => $prop->placeholder, 'readonly' => $prop->readOnly ? 'readonly' : null, 'autocomplete' => $prop->autocomplete ? null : 'off', 'disabled' => $prop->disabled ? 'disabled' : null, 'tabindex' => $prop->tabIndex, 'autofocus' => $prop->autofocus, 'onfocus' => $prop->autoselect ? 'this.select()' : 'this.value=this.value', 'onchange' => $prop->onChange, 'onkeypress' => $action, 'maxlength' => $prop->maxLength, 'minlength' => $prop->minLength, 'pattern' => $prop->pattern, 'required' => $prop->required]);
                $options = JavascriptCodeGen::makeOptions(['max' => $prop->max, 'min' => $prop->min, 'step' => $prop->step, 'decimals' => $prop->decimals, 'forcestepdivisibility' => 'none'], '  ');
                $assets->addInlineScript("\$('#{$prop->id}').TouchSpin({$options});");
                break;
            default:
                $this->addAttrs(['type' => $type, 'name' => $name, 'value' => $value, 'placeholder' => $prop->placeholder, 'readonly' => $prop->readOnly ? 'readonly' : null, 'autocomplete' => $prop->autocomplete ? null : 'off', 'disabled' => $prop->disabled ? 'disabled' : null, 'tabindex' => $prop->tabIndex, 'autofocus' => $prop->autofocus, 'onfocus' => $prop->autoselect ? 'this.select()' : 'this.value=this.value', 'onchange' => $prop->onChange, 'onkeypress' => $action, 'max' => $prop->max, 'min' => $prop->min, 'maxlength' => $prop->maxLength, 'minlength' => $prop->minLength, 'pattern' => $prop->pattern, 'required' => $prop->required, 'step' => $prop->step]);
        }
    }