/** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $value = isset($items[$delta]->value) ? $items[$delta]->value : NULL; $field_settings = $this->getFieldSettings(); $element += array('#type' => 'number', '#default_value' => $value, '#placeholder' => $this->getSetting('placeholder')); // Set the step for floating point and decimal numbers. switch ($this->fieldDefinition->getType()) { case 'decimal': $element['#step'] = pow(0.1, $field_settings['scale']); break; case 'float': $element['#step'] = 'any'; break; } // Set minimum and maximum. if (is_numeric($field_settings['min'])) { $element['#min'] = $field_settings['min']; } if (is_numeric($field_settings['max'])) { $element['#max'] = $field_settings['max']; } // Add prefix and suffix. if ($field_settings['prefix']) { $prefixes = explode('|', $field_settings['prefix']); $element['#field_prefix'] = FieldFilteredMarkup::create(array_pop($prefixes)); } if ($field_settings['suffix']) { $suffixes = explode('|', $field_settings['suffix']); $element['#field_suffix'] = FieldFilteredMarkup::create(array_pop($suffixes)); } return array('value' => $element); }
/** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); $field_storage = $this->entity->getFieldStorageDefinition(); $bundles = $this->entityManager->getBundleInfo($this->entity->getTargetEntityTypeId()); $form_title = $this->t('%field settings for %bundle', array('%field' => $this->entity->getLabel(), '%bundle' => $bundles[$this->entity->getTargetBundle()]['label'])); $form['#title'] = $form_title; if ($field_storage->isLocked()) { $form['locked'] = array('#markup' => $this->t('The field %field is locked and cannot be edited.', array('%field' => $this->entity->getLabel()))); return $form; } // Build the configurable field values. $form['label'] = array('#type' => 'textfield', '#title' => $this->t('Label'), '#default_value' => $this->entity->getLabel() ?: $field_storage->getName(), '#required' => TRUE, '#weight' => -20); $form['description'] = array('#type' => 'textarea', '#title' => $this->t('Help text'), '#default_value' => $this->entity->getDescription(), '#rows' => 5, '#description' => $this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => FieldFilteredMarkup::displayAllowedTags())) . '<br />' . $this->t('This field supports tokens.'), '#weight' => -10); $form['required'] = array('#type' => 'checkbox', '#title' => $this->t('Required field'), '#default_value' => $this->entity->isRequired(), '#weight' => -5); // Create an arbitrary entity object (used by the 'default value' widget). $ids = (object) array('entity_type' => $this->entity->getTargetEntityTypeId(), 'bundle' => $this->entity->getTargetBundle(), 'entity_id' => NULL); $form['#entity'] = _field_create_entity_from_ids($ids); $items = $form['#entity']->get($this->entity->getName()); $item = $items->first() ?: $items->appendItem(); // Add field settings for the field type and a container for third party // settings that modules can add to via hook_form_FORM_ID_alter(). $form['settings'] = array('#tree' => TRUE, '#weight' => 10); $form['settings'] += $item->fieldSettingsForm($form, $form_state); $form['third_party_settings'] = array('#tree' => TRUE, '#weight' => 11); // Add handling for default value. if ($element = $items->defaultValuesForm($form, $form_state)) { $element = array_merge($element, array('#type' => 'details', '#title' => $this->t('Default value'), '#open' => TRUE, '#tree' => TRUE, '#description' => $this->t('The default value for this field, used when creating new content.'))); $form['default_value'] = $element; } return $form; }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); foreach ($items as $delta => $item) { $elements[$delta] = array('#markup' => $item->value, '#allowed_tags' => FieldFilteredMarkup::allowedTags()); } return $elements; }
/** * {@inheritdoc} */ public function summaryName($data) { $value = $data->{$this->name_alias}; // If the list element has a human readable name show it. if (isset($this->allowedValues[$value]) && !empty($this->options['summary']['human'])) { $value = $this->allowedValues[$value]; } return FieldFilteredMarkup::create($this->caseTransform($value, $this->options['case'])); }
public function summaryName($data) { $value = $data->{$this->name_alias}; // If the list element has a human readable name show it, if (isset($this->allowed_values[$value]) && !empty($this->options['summary']['human'])) { return FieldFilteredMarkup::create($this->allowed_values[$value]); } else { return $value; } }
/** * Helper method. Adds prefix/suffix to a range field widget subelements. * * @param array $element * Range field widget definition array. * @param string $element_name * Form element machine name. */ protected function formElementSubElementPrefixSuffix(array &$element, $element_name) { $setting = $this->getFieldSetting($element_name); if (!empty($setting['prefix'])) { $element[$element_name]['#field_prefix'] = FieldFilteredMarkup::create($setting['prefix']); } if (!empty($setting['suffix'])) { $element[$element_name]['#field_suffix'] = FieldFilteredMarkup::create($setting['suffix']); } }
/** * {@inheritdoc} */ public function preprocessVariables(Variables $variables, $hook, array $info) { if (!empty($variables['description'])) { $variables['description'] = FieldFilteredMarkup::create($variables['description']); } $descriptions = []; $cardinality = $variables['cardinality']; if (isset($cardinality)) { if ($cardinality == -1) { $descriptions[] = t('Unlimited number of files can be uploaded to this field.'); } else { $descriptions[] = \Drupal::translation()->formatPlural($cardinality, 'One file only.', 'Maximum @count files.'); } } $upload_validators = $variables['upload_validators']; if (isset($upload_validators['file_validate_size'])) { $descriptions[] = t('@size limit.', ['@size' => format_size($upload_validators['file_validate_size'][0])]); } if (isset($upload_validators['file_validate_extensions'])) { $extensions = new FormattableMarkup('<code>@extensions</code>', ['@extensions' => implode(', ', explode(' ', $upload_validators['file_validate_extensions'][0]))]); $descriptions[] = t('Allowed types: @extensions.', ['@extensions' => $extensions]); } if (isset($upload_validators['file_validate_image_resolution'])) { $max = $upload_validators['file_validate_image_resolution'][0]; $min = $upload_validators['file_validate_image_resolution'][1]; if ($min && $max && $min == $max) { $descriptions[] = t('Images must be exactly <strong>@size</strong> pixels.', ['@size' => $max]); } elseif ($min && $max) { $descriptions[] = t('Images must be larger than <strong>@min</strong> pixels. Images larger than <strong>@max</strong> pixels will be resized.', ['@min' => $min, '@max' => $max]); } elseif ($min) { $descriptions[] = t('Images must be larger than <strong>@min</strong> pixels.', ['@min' => $min]); } elseif ($max) { $descriptions[] = t('Images larger than <strong>@max</strong> pixels will be resized.', ['@max' => $max]); } } $variables['descriptions'] = $descriptions; if ($descriptions) { $build = array(); $id = Html::getUniqueId('upload-instructions'); $build['toggle'] = ['#type' => 'link', '#title' => t('Upload requirements'), '#url' => Url::fromUserInput("#{$id}"), '#icon' => Bootstrap::glyphicon('question-sign'), '#attributes' => ['class' => ['icon-before'], 'data-toggle' => 'popover', 'data-html' => 'true', 'data-placement' => 'bottom', 'data-title' => t('Upload requirements')]]; $build['requirements'] = ['#type' => 'container', '#theme_wrappers' => ['container__file_upload_help'], '#attributes' => ['id' => $id, 'class' => ['hidden', 'help-block'], 'aria-hidden' => 'true']]; $build['requirements']['descriptions'] = ['#theme' => 'item_list__file_upload_help', '#items' => $descriptions]; $variables['popover'] = $build; } }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); // Only collect allowed options if there are actually items to display. if ($items->count()) { $provider = $items->getFieldDefinition()->getFieldStorageDefinition()->getOptionsProvider('value', $items->getEntity()); // Flatten the possible options, to support opt groups. $options = OptGroup::flattenOptions($provider->getPossibleOptions()); foreach ($items as $delta => $item) { $value = $item->value; // If the stored value is in the current set of allowed values, display // the associated label, otherwise just display the raw value. $output = isset($options[$value]) ? $options[$value] : $value; $elements[$delta] = array('#markup' => $output, '#allowed_tags' => FieldFilteredMarkup::allowedTags()); } } return $elements; }
/** * Overrides \Drupal\Core\Field\WidgetBase::formMultipleElements(). * * Special handling for draggable multiple widgets and 'add more' button. */ protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { $field_name = $this->fieldDefinition->getName(); $parents = $form['#parents']; // Load the items for form rebuilds from the field state as they might not // be in $form_state->getValues() because of validation limitations. Also, // they are only passed in as $items when editing existing entities. $field_state = static::getWidgetState($parents, $field_name, $form_state); if (isset($field_state['items'])) { $items->setValue($field_state['items']); } // Determine the number of widgets to display. $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); switch ($cardinality) { case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED: $max = count($items); $is_multiple = TRUE; break; default: $max = $cardinality - 1; $is_multiple = $cardinality > 1; break; } $title = $this->fieldDefinition->getLabel(); $description = FieldFilteredMarkup::create($this->fieldDefinition->getDescription()); $elements = array(); $delta = 0; // Add an element for every existing item. foreach ($items as $item) { $element = array('#title' => $title, '#description' => $description); $element = $this->formSingleElement($items, $delta, $element, $form, $form_state); if ($element) { // Input field for the delta (drag-n-drop reordering). if ($is_multiple) { // We name the element '_weight' to avoid clashing with elements // defined by widget. $element['_weight'] = array('#type' => 'weight', '#title' => t('Weight for row @number', array('@number' => $delta + 1)), '#title_display' => 'invisible', '#delta' => $max, '#default_value' => $item->_weight ?: $delta, '#weight' => 100); } $elements[$delta] = $element; $delta++; } } $empty_single_allowed = $cardinality == 1 && $delta == 0; $empty_multiple_allowed = ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || $delta < $cardinality) && !$form_state->isProgrammed(); // Add one more empty row for new uploads except when this is a programmed // multiple form as it is not necessary. if ($empty_single_allowed || $empty_multiple_allowed) { // Create a new empty item. $items->appendItem(); $element = array('#title' => $title, '#description' => $description); $element = $this->formSingleElement($items, $delta, $element, $form, $form_state); if ($element) { $element['#required'] = $element['#required'] && $delta == 0; $elements[$delta] = $element; } } if ($is_multiple) { // The group of elements all-together need some extra functionality after // building up the full list (like draggable table rows). $elements['#file_upload_delta'] = $delta; $elements['#type'] = 'details'; $elements['#open'] = TRUE; $elements['#theme'] = 'file_widget_multiple'; $elements['#theme_wrappers'] = array('details'); $elements['#process'] = array(array(get_class($this), 'processMultiple')); $elements['#title'] = $title; $elements['#description'] = $description; $elements['#field_name'] = $field_name; $elements['#language'] = $items->getLangcode(); $elements['#display_field'] = (bool) $this->getFieldSetting('display_field'); // The field settings include defaults for the field type. However, this // widget is a base class for other widgets (e.g., ImageWidget) that may // act on field types without these expected settings. $field_settings = $this->getFieldSettings() + array('display_field' => NULL); $elements['#display_field'] = (bool) $field_settings['display_field']; // Add some properties that will eventually be added to the file upload // field. These are added here so that they may be referenced easily // through a hook_form_alter(). $elements['#file_upload_title'] = t('Add a new file'); $elements['#file_upload_description'] = array('#theme' => 'file_upload_help', '#description' => '', '#upload_validators' => $elements[0]['#upload_validators'], '#cardinality' => $cardinality); } return $elements; }
/** * Sanitizes a string label to display as an option. * * @param string $label * The label to sanitize. */ protected function sanitizeLabel(&$label) { // Allow a limited set of HTML tags. $label = FieldFilteredMarkup::create($label); }
/** * Special handling to create form elements for multiple values. * * Handles generic features for multiple fields: * - number of widgets * - AHAH-'add more' button * - table display and drag-n-drop value reordering */ protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { $field_name = $this->fieldDefinition->getName(); $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); $parents = $form['#parents']; // Determine the number of widgets to display. switch ($cardinality) { case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED: $field_state = static::getWidgetState($parents, $field_name, $form_state); $max = $field_state['items_count']; $is_multiple = TRUE; break; default: $max = $cardinality - 1; $is_multiple = $cardinality > 1; break; } $title = $this->fieldDefinition->getLabel(); $description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())); $elements = array(); for ($delta = 0; $delta <= $max; $delta++) { // Add a new empty item if it doesn't exist yet at this delta. if (!isset($items[$delta])) { $items->appendItem(); } // For multiple fields, title and description are handled by the wrapping // table. if ($is_multiple) { $element = ['#title' => $this->t('@title (value @number)', ['@title' => $title, '@number' => $delta + 1]), '#title_display' => 'invisible', '#description' => '']; } else { $element = ['#title' => $title, '#title_display' => 'before', '#description' => $description]; } $element = $this->formSingleElement($items, $delta, $element, $form, $form_state); if ($element) { // Input field for the delta (drag-n-drop reordering). if ($is_multiple) { // We name the element '_weight' to avoid clashing with elements // defined by widget. $element['_weight'] = array('#type' => 'weight', '#title' => $this->t('Weight for row @number', array('@number' => $delta + 1)), '#title_display' => 'invisible', '#delta' => $max, '#default_value' => $items[$delta]->_weight ?: $delta, '#weight' => 100); } $elements[$delta] = $element; } } if ($elements) { $elements += array('#theme' => 'field_multiple_value_form', '#field_name' => $field_name, '#cardinality' => $cardinality, '#cardinality_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(), '#required' => $this->fieldDefinition->isRequired(), '#title' => $title, '#description' => $description, '#max_delta' => $max); // Add 'add more' button, if not working with a programmed form. if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && !$form_state->isProgrammed()) { $id_prefix = implode('-', array_merge($parents, array($field_name))); $wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper'); $elements['#prefix'] = '<div id="' . $wrapper_id . '">'; $elements['#suffix'] = '</div>'; $elements['add_more'] = array('#type' => 'submit', '#name' => strtr($id_prefix, '-', '_') . '_add_more', '#value' => t('Add another item'), '#attributes' => array('class' => array('field-add-more-submit')), '#limit_validation_errors' => array(array_merge($parents, array($field_name))), '#submit' => array(array(get_class($this), 'addMoreSubmit')), '#ajax' => array('callback' => array(get_class($this), 'addMoreAjax'), 'wrapper' => $wrapper_id, 'effect' => 'fade')); } } return $elements; }
public function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { $field_name = $this->fieldDefinition->getName(); $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); $parents = $form['#parents']; $field_state = static::getWidgetState($parents, $field_name, $form_state); $max = $field_state['items_count']; $real_item_count = $max; $is_multiple = $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(); $title = $this->fieldDefinition->getLabel(); $description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())); $elements = array(); $id_prefix = implode('-', array_merge($parents, array($field_name))); $wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper'); $elements['#prefix'] = '<div id="' . $wrapper_id . '">'; $elements['#suffix'] = '</div>'; $field_state['ajax_wrapper_id'] = $wrapper_id; static::setWidgetState($parents, $field_name, $form_state, $field_state); if ($max > 0) { for ($delta = 0; $delta < $max; $delta++) { // Add a new empty item if it doesn't exist yet at this delta. if (!isset($items[$delta])) { $items->appendItem(); } // For multiple fields, title and description are handled by the wrapping // table. $element = array('#title' => $is_multiple ? '' : $title, '#description' => $is_multiple ? '' : $description); $element = $this->formSingleElement($items, $delta, $element, $form, $form_state); if ($element) { // Input field for the delta (drag-n-drop reordering). if ($is_multiple) { // We name the element '_weight' to avoid clashing with elements // defined by widget. $element['_weight'] = array('#type' => 'weight', '#title' => t('Weight for row @number', array('@number' => $delta + 1)), '#title_display' => 'invisible', '#delta' => $max, '#default_value' => $items[$delta]->_weight ?: $delta, '#weight' => 100); } if (isset($element['#access']) && !$element['#access']) { $real_item_count--; } else { $elements[$delta] = $element; } } } } $field_state = static::getWidgetState($parents, $field_name, $form_state); $field_state['real_item_count'] = $real_item_count; static::setWidgetState($parents, $field_name, $form_state, $field_state); $entity_manager = \Drupal::entityManager(); $target_type = $this->getFieldSetting('target_type'); $bundles = $this->getAllowedTypes(); $access_control_handler = $entity_manager->getAccessControlHandler($target_type); $options = array(); $access_options = array(); $dragdrop_settings = $this->getSelectionHandlerSetting('target_bundles_drag_drop'); foreach ($bundles as $machine_name => $bundle) { if ($dragdrop_settings || (!count($this->getSelectionHandlerSetting('target_bundles')) || in_array($machine_name, $this->getSelectionHandlerSetting('target_bundles')))) { $options[$machine_name] = $bundle['label']; if ($access_control_handler->createAccess($machine_name)) { $access_options[$machine_name] = $bundle['label']; } } } if ($real_item_count > 0) { $elements += array('#theme' => 'field_multiple_value_form', '#field_name' => $field_name, '#cardinality' => $cardinality, '#cardinality_multiple' => $is_multiple, '#required' => $this->fieldDefinition->isRequired(), '#title' => $title, '#description' => $description, '#max_delta' => $max - 1); } else { // @todo: properize this. $add_text = 'No @title_multiple have been added yet. Select a @title type and press the button below to add one.'; $element_text = '<label>' . $title . "</label>"; $element_text .= '<p><em>' . t($add_text, array('@title_multiple' => $this->getSetting('title_plural'), '@title' => $this->getSetting('title'))) . '</em></p>'; $element_text .= $description ? '<div class="description">' . $description . '</div>' : ''; $elements += array('#type' => 'container', '#theme_wrappers' => array('container'), '#field_name' => $field_name, '#cardinality' => $cardinality, '#cardinality_multiple' => TRUE, '#max_delta' => $max - 1, 'text' => array('#markup' => $element_text)); } // Add 'add more' button, if not working with a programmed form. if (($real_item_count < $cardinality || $cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) && !$form_state->isProgrammed()) { // Hide the button when translating. $add_more_access = $this->getCurrentLangcode($form_state, $items) == $items->getEntity()->getUntranslated()->language()->getId(); $elements['add_more'] = array('#type' => 'container', '#theme_wrappers' => array('paragraphs_dropbutton_wrapper'), '#access' => $add_more_access); if (count($access_options)) { if ($this->getSetting('add_mode') == 'button' || $this->getSetting('add_mode') == 'dropdown' && count($access_options) === 1) { foreach ($access_options as $machine_name => $label) { $elements['add_more']['add_more_button_' . $machine_name] = array('#type' => 'submit', '#name' => strtr($id_prefix, '-', '_') . '_' . $machine_name . '_add_more', '#value' => t('Add @type', array('@type' => $label)), '#attributes' => array('class' => array('field-add-more-submit')), '#limit_validation_errors' => array(array_merge($parents, array($field_name, 'add_more'))), '#submit' => array(array(get_class($this), 'addMoreSubmit')), '#ajax' => array('callback' => array(get_class($this), 'addMoreAjax'), 'wrapper' => $wrapper_id, 'effect' => 'fade'), '#bundle_machine_name' => $machine_name); } } elseif ($this->getSetting('add_mode') == 'dropdown') { foreach ($access_options as $machine_name => $label) { $elements['add_more']['add_more_button_' . $machine_name] = array('#type' => 'submit', '#name' => strtr($id_prefix, '-', '_') . '_' . $machine_name . '_add_more', '#value' => t('Add @type', array('@type' => $label)), '#attributes' => array('class' => array('field-add-more-submit')), '#limit_validation_errors' => array(array_merge($parents, array($field_name, 'add_more'))), '#submit' => array(array(get_class($this), 'addMoreSubmit')), '#ajax' => array('callback' => array(get_class($this), 'addMoreAjax'), 'wrapper' => $wrapper_id, 'effect' => 'fade'), '#bundle_machine_name' => $machine_name, '#prefix' => '<li>', '#suffix' => '</li>'); } $elements['add_more']['#theme_wrappers'] = array('dropbutton_wrapper', 'paragraphs_dropbutton_wrapper'); $elements['add_more']['prefix'] = array('#markup' => '<ul class="dropbutton">', '#weight' => -999); $elements['add_more']['suffix'] = array('#markup' => '</ul>', '#weight' => 999); } else { $elements['add_more']['add_more_select'] = array('#type' => 'select', '#options' => $options, '#title' => t('@title type', array('@title' => $this->getSetting('title'))), '#label_display' => 'hidden'); $text = t('Add @title', array('@title' => $this->getSetting('title'))); if ($real_item_count > 0) { $text = t('Add another @title', array('@title' => $this->getSetting('title'))); } $elements['add_more']['add_more_button'] = array('#type' => 'submit', '#name' => strtr($id_prefix, '-', '_') . '_add_more', '#value' => $text, '#attributes' => array('class' => array('field-add-more-submit')), '#limit_validation_errors' => array(array_merge($parents, array($field_name, 'add_more'))), '#submit' => array(array(get_class($this), 'addMoreSubmit')), '#ajax' => array('callback' => array(get_class($this), 'addMoreAjax'), 'wrapper' => $wrapper_id, 'effect' => 'fade')); } } else { if (count($options)) { $elements['add_more']['info'] = array('#type' => 'markup', '#markup' => '<em>' . t('You are not allowed to add any of the @title types.', array('@title' => $this->getSetting('title'))) . '</em>'); } else { $elements['add_more']['info'] = array('#type' => 'markup', '#markup' => '<em>' . t('You did not add any @title types yet.', array('@title' => $this->getSetting('title'))) . '</em>'); } } } $elements['#attached']['library'][] = 'paragraphs/drupal.paragraphs.admin'; return $elements; }
/** * @covers: ::displayAllowedTags */ public function testdisplayAllowedTags() { $expected = '<a> <b> <big> <code> <del> <em> <i> <ins> <pre> <q> <small> <span> <strong> <sub> <sup> <tt> <ol> <ul> <li> <p> <br> <img>'; $this->assertSame($expected, FieldFilteredMarkup::displayAllowedTags()); }
/** * Helper method. Adds prefix and suffix to the given range field value. * * @param string $value * FROM/TO range field value. * @param boolean $display_prefix_suffix * Whether to add suffix/prefix or not. * @param array $settings * Field instance FROM/TO value settings. * * @return string * Range field value with added prefix/suffix. */ protected function viewElementPrefixSuffix($value, $display_prefix_suffix, array $settings) { if ($display_prefix_suffix) { $prefix = !empty($settings['prefix']) ? FieldFilteredMarkup::create($settings['prefix']) : ''; $suffix = !empty($settings['suffix']) ? FieldFilteredMarkup::create($settings['suffix']) : ''; return $prefix . $value . $suffix; } return $value; }
/** * Returns the filtered field description. * * @return \Drupal\Core\Field\FieldFilteredMarkup * The filtered field description, with tokens replaced. */ protected function getFilteredDescription() { return FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())); }