Overview

Namespaces

  • None
  • Quform
    • Traduttore_Registry

Classes

  • Quform
  • Quform_Admin_InsertForm
  • Quform_Admin_Page
  • Quform_Admin_Page_Controller
  • Quform_Admin_Page_Dashboard
  • Quform_Admin_Page_Entries
  • Quform_Admin_Page_Entries_Edit
  • Quform_Admin_Page_Entries_List
  • Quform_Admin_Page_Entries_View
  • Quform_Admin_Page_Factory
  • Quform_Admin_Page_Forms_Add
  • Quform_Admin_Page_Forms_Edit
  • Quform_Admin_Page_Forms_List
  • Quform_Admin_Page_Help
  • Quform_Admin_Page_Preview
  • Quform_Admin_Page_Settings
  • Quform_Admin_Page_Tools
  • Quform_Admin_Page_Tools_ExportEntries
  • Quform_Admin_Page_Tools_ExportForm
  • Quform_Admin_Page_Tools_Home
  • Quform_Admin_Page_Tools_ImportForm
  • Quform_Admin_Page_Tools_Migrate
  • Quform_Admin_Page_Tools_Uninstall
  • Quform_Api
  • Quform_Block
  • Quform_Builder
  • Quform_Captcha
  • Quform_ClassLoader
  • Quform_Confirmation
  • Quform_Container
  • Quform_Dashboard_Widget
  • Quform_Dispatcher
  • Quform_Element
  • Quform_Element_Captcha
  • Quform_Element_Checkbox
  • Quform_Element_Column
  • Quform_Element_Container
  • Quform_Element_Container_Iterator
  • Quform_Element_Date
  • Quform_Element_Email
  • Quform_Element_Factory
  • Quform_Element_Field
  • Quform_Element_File
  • Quform_Element_Group
  • Quform_Element_Hidden
  • Quform_Element_Honeypot
  • Quform_Element_Html
  • Quform_Element_Multi
  • Quform_Element_Multiselect
  • Quform_Element_Name
  • Quform_Element_Page
  • Quform_Element_Password
  • Quform_Element_Radio
  • Quform_Element_Recaptcha
  • Quform_Element_Row
  • Quform_Element_Select
  • Quform_Element_Submit
  • Quform_Element_Text
  • Quform_Element_Textarea
  • Quform_Element_Time
  • Quform_Entry_Controller
  • Quform_Entry_Exporter
  • Quform_Entry_List_Settings
  • Quform_Entry_List_Table
  • Quform_Entry_Processor
  • Quform_Entry_UserSearcher
  • Quform_Filter_Abstract
  • Quform_Filter_Alpha
  • Quform_Filter_AlphaNumeric
  • Quform_Filter_Digits
  • Quform_Filter_Regex
  • Quform_Filter_Static
  • Quform_Filter_StripTags
  • Quform_Filter_Trim
  • Quform_Form
  • Quform_Form_Controller
  • Quform_Form_Exporter
  • Quform_Form_Factory
  • Quform_Form_Importer
  • Quform_Form_Iterator
  • Quform_Form_List_Settings
  • Quform_Form_List_Table
  • Quform_Form_Processor
  • Quform_License
  • Quform_Migrator
  • Quform_NonceRefresher
  • Quform_Notification
  • Quform_Notification_Resender
  • Quform_Options
  • Quform_Permissions
  • Quform_Repository
  • Quform_ScriptLoader
  • Quform_Session
  • Quform_Settings
  • Quform_Shortcode
  • Quform_Themes
  • Quform_TokenReplacer
  • Quform_Toolbar
  • Quform_Translations
  • Quform_Updater
  • Quform_Upgrader
  • Quform_Uploader
  • Quform_Validator_Abstract
  • Quform_Validator_Alpha
  • Quform_Validator_AlphaNumeric
  • Quform_Validator_Array
  • Quform_Validator_Captcha
  • Quform_Validator_Date
  • Quform_Validator_Digits
  • Quform_Validator_Duplicate
  • Quform_Validator_Email
  • Quform_Validator_FileUpload
  • Quform_Validator_GreaterThan
  • Quform_Validator_Honeypot
  • Quform_Validator_Identical
  • Quform_Validator_InArray
  • Quform_Validator_Length
  • Quform_Validator_LessThan
  • Quform_Validator_Recaptcha
  • Quform_Validator_Regex
  • Quform_Validator_Required
  • Quform_Validator_Static
  • Quform_Validator_Time
  • Quform_View
  • Quform_ViewFactory
  • Quform_Widget_Form
  • Quform_Widget_Popup

Interfaces

  • Quform_Attachable
  • Quform_Element_Editable
  • Quform_Filter_Interface
  • Quform_Validator_Interface

Constants

  • Quform\Traduttore_Registry\TRANSIENT_KEY_PLUGIN
  • Quform\Traduttore_Registry\TRANSIENT_KEY_THEME

Functions

  • Quform\Traduttore_Registry\add_project
  • Quform\Traduttore_Registry\clean_translations_cache
  • Quform\Traduttore_Registry\get_available_locales
  • Quform\Traduttore_Registry\get_installed_translations
  • Quform\Traduttore_Registry\get_translations
  • Quform\Traduttore_Registry\register_clean_translations_cache
  • Quform\Traduttore_Registry\sanitize_date
  • Overview
  • Namespace
  • Class
   1: <?php
   2: 
   3: /**
   4:  * @copyright Copyright (c) 2009-2022 ThemeCatcher (https://www.themecatcher.net)
   5:  */
   6: class Quform_Form
   7: {
   8:     /**
   9:      * @var int
  10:      */
  11:     protected $id;
  12: 
  13:     /**
  14:      * The unique ID is unique to each form instance, even the same form on the same page
  15:      *
  16:      * @var string
  17:      */
  18:     protected $uniqueId;
  19: 
  20:     /**
  21:      * @var Quform_Element_Page[]
  22:      */
  23:     protected $pages = array();
  24: 
  25:     /**
  26:      * @var array
  27:      */
  28:     protected $config = array();
  29: 
  30:     /**
  31:      * Notifications to send
  32:      *
  33:      * @var Quform_Notification[]
  34:      */
  35:     protected $notifications = array();
  36: 
  37:     /**
  38:      * Confirmations
  39:      *
  40:      * @var Quform_Confirmation[]
  41:      */
  42:     protected $confirmations = array();
  43: 
  44:     /**
  45:      * The single confirmation to use for the submission
  46:      *
  47:      * @var Quform_Confirmation
  48:      */
  49:     protected $confirmation;
  50: 
  51:     /**
  52:      * @var Quform_TokenReplacer
  53:      */
  54:     protected $tokenReplacer;
  55: 
  56:     /**
  57:      * @var Quform_Options
  58:      */
  59:     protected $options;
  60: 
  61:     /**
  62:      * @var Quform_Session
  63:      */
  64:     protected $session;
  65: 
  66:     /**
  67:      * Is the form active?
  68:      *
  69:      * @var bool
  70:      */
  71:     protected $active = true;
  72: 
  73:     /**
  74:      * Has the form been successfully submitted?
  75:      *
  76:      * @var boolean
  77:      */
  78:     protected $submitted = false;
  79: 
  80:     /**
  81:      * The flag for showing the global form error message for non-Ajax forms
  82:      *
  83:      * @var boolean
  84:      */
  85:     protected $showGlobalError = false;
  86: 
  87:     /**
  88:      * Character encoding to use
  89:      *
  90:      * @var string
  91:      */
  92:     protected $charset = 'UTF-8';
  93: 
  94:     /**
  95:      * The dynamic values
  96:      *
  97:      * @var array
  98:      */
  99:     protected $dynamicValues = array();
 100: 
 101:     /**
 102:      * The current submitted entry ID
 103:      *
 104:      * @var int|null
 105:      */
 106:     protected $entryId;
 107: 
 108:     /**
 109:      * The current page being viewed/processed
 110:      *
 111:      * @var Quform_Element_Page
 112:      */
 113:     protected $currentPage;
 114: 
 115:     /**
 116:      * @param  int                   $id
 117:      * @param  string                $uniqueId
 118:      * @param  Quform_Session        $session
 119:      * @param  Quform_TokenReplacer  $tokenReplacer
 120:      * @param  Quform_Options        $options
 121:      */
 122:     public function __construct($id, $uniqueId, Quform_Session $session, Quform_TokenReplacer $tokenReplacer, Quform_Options $options)
 123:     {
 124:         $this->setId($id);
 125:         $this->setUniqueId($uniqueId);
 126:         $this->session = $session;
 127:         $this->tokenReplacer = $tokenReplacer;
 128:         $this->options = $options;
 129:     }
 130: 
 131:     /**
 132:      * Set the ID of the form
 133:      *
 134:      * @param int $id
 135:      */
 136:     public function setId($id)
 137:     {
 138:         $this->id = $id;
 139:     }
 140: 
 141:     /**
 142:      * Get the ID of the form
 143:      *
 144:      * @return int
 145:      */
 146:     public function getId()
 147:     {
 148:         return $this->id;
 149:     }
 150: 
 151:     /**
 152:      * Returns the config value for the given $key
 153:      *
 154:      * If the value is null, the default will be returned
 155:      *
 156:      * @param   string|null  $key      The config key
 157:      * @param   null|mixed   $default  The default value to return if the value key not exist
 158:      * @return  mixed                  The config value or $default if not set
 159:      */
 160:     public function config($key = null, $default = null)
 161:     {
 162:         $value = Quform::get($this->config, $key, $default);
 163: 
 164:         if ($value === null) {
 165:             $value = Quform::get(call_user_func(array(get_class($this), 'getDefaultConfig')), $key, $default);
 166:         }
 167: 
 168:         return $value;
 169:     }
 170: 
 171:     /**
 172:      * Set the config value for the given $key or multiple values using an array
 173:      *
 174:      * @param   string|array  $key    Key or array of key/values
 175:      * @param   mixed         $value  Value or null if $key is array
 176:      * @return  $this
 177:      */
 178:     public function setConfig($key, $value = null)
 179:     {
 180:         if (is_array($key)) {
 181:             foreach ($key as $k => $v) {
 182:                 $this->config[$k] = $v;
 183:             }
 184:         } else {
 185:             $this->config[$key] = $value;
 186:         }
 187: 
 188:         return $this;
 189:     }
 190: 
 191:     /**
 192:      * Render the form and return the HTML
 193:      *
 194:      * @param   array   $options         The options array
 195:      * @param   string  $outputOverride  Display this HTML instead of rendering the form
 196:      * @return  string
 197:      */
 198:     public function render(array $options = array(), $outputOverride = '')
 199:     {
 200:         $outputOverride = apply_filters('quform_form_output_override', $outputOverride, $this);
 201:         $outputOverride = apply_filters('quform_form_output_override_' . $this->getId(), $outputOverride, $this);
 202: 
 203:         if (Quform::isNonEmptyString($outputOverride)) {
 204:             return $outputOverride;
 205:         }
 206: 
 207:         $options = wp_parse_args($options, array(
 208:             'show_title' => true,
 209:             'show_description' => true
 210:         ));
 211: 
 212:         ob_start();
 213:         do_action('quform_pre_display', $this);
 214:         do_action('quform_pre_display_' . $this->getId(), $this);
 215:         $output = ob_get_clean();
 216: 
 217:         $output .= sprintf('<div id="quform-%s" class="%s">', Quform::escape($this->getUniqueId()), Quform::escape(Quform::sanitizeClass($this->getContainerClasses())));
 218: 
 219:         $formAttributes = array(
 220:             'id' => sprintf('quform-form-%s', $this->getUniqueId()),
 221:             'class' => sprintf('quform-form quform-form-%d', $this->getId()),
 222:             'action' => $this->getAction(),
 223:             'method' => 'post',
 224:             'enctype' => 'multipart/form-data',
 225:             'novalidate' => 'novalidate',
 226:             'data-options' => $this->getJsConfig()
 227:         );
 228: 
 229:         $formAttributes = apply_filters('quform_form_attributes', $formAttributes, $this);
 230:         $formAttributes = apply_filters('quform_form_attributes_' . $this->getId(), $formAttributes, $this);
 231: 
 232:         $output .= sprintf('<form%s>', Quform::parseHtmlAttributes($formAttributes));
 233: 
 234:         if (!apply_filters('quform_remove_hidden_submit_button', false)) {
 235:             $output .= '<button class="quform-default-submit" name="quform_submit" type="submit" value="submit" aria-hidden="true" tabindex="-1"></button>';
 236:         }
 237: 
 238:         $output .= sprintf('<div class="quform-form-inner quform-form-inner-%d">', Quform::escape($this->getId()));
 239: 
 240:         $output .= $this->getHiddenInputHtml();
 241: 
 242:         $output .= $this->getTitleDescriptionHtml($options['show_title'], $options['show_description']);
 243: 
 244:         $output .= $this->getSuccessMessageHtml('above');
 245: 
 246:         $output .= sprintf('<div class="%s">', Quform::escape(Quform::sanitizeClass($this->getElementsClasses())));
 247: 
 248:         $output .= $this->getPageProgressHtml();
 249: 
 250:         $output .= $this->getGlobalErrorHtml();
 251: 
 252:         foreach ($this->pages as $page) {
 253:             $output .= $page->render($this->getContext());
 254:         }
 255: 
 256:         $output .= '</div>';
 257: 
 258:         $output .= $this->getReferralLinkHtml();
 259: 
 260:         $output .= $this->getUploadProgressHtml();
 261: 
 262:         $output .= $this->getSuccessMessageHtml('below');
 263: 
 264:         $output .= $this->getLoadingHtml();
 265: 
 266:         $output .= '</div>';
 267: 
 268:         $output .= $this->getEditFormLinkHtml();
 269: 
 270:         $output .= '</form></div>';
 271: 
 272:         return $output;
 273:     }
 274: 
 275:     /**
 276:      * Render a popup form
 277:      *
 278:      * @param   array   $options         The options array
 279:      * @param   string  $outputOverride  Display this HTML instead of rendering the form
 280:      * @return  string
 281:      */
 282:     public function renderPopup(array $options = array(), $outputOverride = '')
 283:     {
 284:         $options = wp_parse_args($options, array(
 285:             'content' => '',
 286:             'options' => '',
 287:             'width' => '',
 288:             'show_title' => true,
 289:             'show_description' => true
 290:         ));
 291: 
 292:         if (Quform::isNonEmptyString($options['options'])) {
 293:             $options['options'] = json_decode($options['options'], true);
 294:         }
 295: 
 296:         if ( ! is_array($options['options'])) {
 297:             $options['options'] = array();
 298:         }
 299: 
 300:         if (Quform::isNonEmptyString($options['width'])) {
 301:             $options['options']['width'] = $options['width'];
 302:         }
 303: 
 304:         $classes = array('quform-popup-link', 'quform-popup-link-' . $this->getId());
 305: 
 306:         $attributes = array(
 307:             'class' => join(' ', $classes),
 308:             'data-unique-id' => $this->getUniqueId()
 309:         );
 310: 
 311:         if ( ! empty($options['options'])) {
 312:             $attributes['data-options'] = wp_json_encode($options['options']);
 313:         }
 314: 
 315:         ob_start();
 316:         do_action('quform_pre_display_popup', $this);
 317:         do_action('quform_pre_display_popup_' . $this->getId(), $this);
 318:         $output = ob_get_clean();
 319: 
 320:         $output .= Quform::getHtmlTag('span', $attributes, do_shortcode($options['content']));
 321: 
 322:         $output .= '<div class="quform-popup">';
 323: 
 324:         $output .= $this->render($options, $outputOverride);
 325: 
 326:         $output .= '</div>';
 327: 
 328:         return $output;
 329:     }
 330: 
 331:     /**
 332:      * Get the HTML for the title and description
 333:      *
 334:      * @param   bool    $showTitle
 335:      * @param   bool    $showDescription
 336:      * @return  string
 337:      */
 338:     protected function getTitleDescriptionHtml($showTitle = true, $showDescription = true)
 339:     {
 340:         $output = '';
 341:         $title = $this->config('title');
 342:         $description = $this->config('description');
 343:         $showTitle = Quform::isNonEmptyString($title) && $showTitle;
 344:         $showDescription = Quform::isNonEmptyString($description) && $showDescription;
 345: 
 346:         if ($showTitle || $showDescription) {
 347:             $output .= '<div class="quform-form-title-description">';
 348: 
 349:             if ($showTitle) {
 350:                 $output .= Quform::getHtmlTag($this->config('titleTag'), array('class' => 'quform-form-title'), do_shortcode($title));
 351:             }
 352: 
 353:             if ($showDescription) {
 354:                 $output .= Quform::getHtmlTag('p', array('class' => 'quform-form-description'), do_shortcode($description));
 355:             }
 356: 
 357:             $output .= '</div>';
 358:         }
 359: 
 360:         return $output;
 361:     }
 362: 
 363:     /**
 364:      * Get the classes for the outermost wrapper
 365:      *
 366:      * @return array
 367:      */
 368:     protected function getContainerClasses()
 369:     {
 370:         $classes = array('quform', sprintf('quform-%d', $this->getId()));
 371: 
 372:         if (Quform::isNonEmptyString($this->config('theme'))) {
 373:             $classes[] = sprintf('quform-theme-%s', $this->config('theme'));
 374:         }
 375: 
 376:         if (Quform::isNonEmptyString($this->config('width')) && Quform::isNonEmptyString($this->config('position'))) {
 377:             $classes[] = sprintf('quform-position-%s', $this->config('position'));
 378:         }
 379: 
 380:         if ($this->isRtl()) {
 381:             $classes[] = 'quform-rtl';
 382:         }
 383: 
 384:         if ($this->options->get('preventFouc')) {
 385:             $classes[] = 'quform-prevent-fouc';
 386:         }
 387: 
 388:         if ($this->options->get('supportPageCaching') && ! Quform::isPostRequest()) {
 389:             $classes[] = 'quform-support-page-caching';
 390:         }
 391: 
 392:         if ( ! $this->config('ajax')) {
 393:             $classes[] = 'quform-no-ajax';
 394:         }
 395: 
 396:         if ($this->hasPages()) {
 397:             if ($this->getCurrentPage()->getId() === $this->getFirstPage()->getId()) {
 398:                 $classes[] = 'quform-is-first-page';
 399:             } elseif ($this->getCurrentPage()->getId() === $this->getLastPage()->getId()) {
 400:                 $classes[] = 'quform-is-last-page';
 401:             }
 402:         }
 403: 
 404:         if (Quform::isNonEmptyString($this->config('errorsPosition'))) {
 405:             $classes[] = sprintf('quform-errors-%s', $this->config('errorsPosition'));
 406:         }
 407: 
 408:         $classes = apply_filters('quform_form_container_classes', $classes, $this);
 409:         $classes = apply_filters('quform_form_container_classes_' . $this->getId(), $classes, $this);
 410: 
 411:         return $classes;
 412:     }
 413: 
 414:     /**
 415:      * Is the form RTL?
 416:      *
 417:      * @return bool
 418:      */
 419:     public function isRtl()
 420:     {
 421:         if ($this->config('rtl') == 'enabled') {
 422:             return true;
 423:         }
 424: 
 425:         if ($this->config('rtl') == 'global') {
 426:             return $this->options->get('rtl') == 'enabled' || ($this->options->get('rtl') === '' && is_rtl());
 427:         }
 428: 
 429:         return false;
 430:     }
 431: 
 432:     /**
 433:      * Get the locale for this form
 434:      *
 435:      * @return string
 436:      */
 437:     public function getLocale()
 438:     {
 439:         $locale = $this->config('locale');
 440: 
 441:         if (Quform::isNonEmptyString($locale)) {
 442:             $locale = apply_filters('quform_form_locale', $locale, $this);
 443:             $locale = apply_filters('quform_form_locale_' . $this->getId(), $locale, $this);
 444: 
 445:             return $locale;
 446:         }
 447: 
 448:         return $this->options->getLocale();
 449:     }
 450: 
 451:     /**
 452:      * Get the date format for PHP
 453:      *
 454:      * @return string
 455:      */
 456:     public function getDateFormat()
 457:     {
 458:         return Quform::isNonEmptyString($this->config('dateFormat')) ? $this->config('dateFormat') : $this->options->get('dateFormat');
 459:     }
 460: 
 461:     /**
 462:      * Get the date format for JavaScript
 463:      *
 464:      * @return string
 465:      */
 466:     public function getDateFormatJs()
 467:     {
 468:         return Quform::isNonEmptyString($this->config('dateFormatJs')) ? $this->config('dateFormatJs') : $this->options->get('dateFormatJs');
 469:     }
 470: 
 471:     /**
 472:      * Get the time format for PHP
 473:      *
 474:      * @return string
 475:      */
 476:     public function getTimeFormat()
 477:     {
 478:         return Quform::isNonEmptyString($this->config('timeFormat')) ? $this->config('timeFormat') : $this->options->get('timeFormat');
 479:     }
 480: 
 481:     /**
 482:      * Get the time format for JavaScript
 483:      *
 484:      * @return string
 485:      */
 486:     public function getTimeFormatJs()
 487:     {
 488:         return Quform::isNonEmptyString($this->config('timeFormatJs')) ? $this->config('timeFormatJs') : $this->options->get('timeFormatJs');
 489:     }
 490: 
 491:     /**
 492:      * Get the datetime format for PHP
 493:      *
 494:      * @return string
 495:      */
 496:     public function getDateTimeFormat()
 497:     {
 498:         return Quform::isNonEmptyString($this->config('dateTimeFormat')) ? $this->config('dateTimeFormat') : $this->options->get('dateTimeFormat');
 499:     }
 500: 
 501:     /**
 502:      * Get the datetime format for JavaScript
 503:      *
 504:      * @return string
 505:      */
 506:     public function getDateTimeFormatJs()
 507:     {
 508:         return Quform::isNonEmptyString($this->config('dateTimeFormatJs')) ? $this->config('dateTimeFormatJs') : $this->options->get('dateTimeFormatJs');
 509:     }
 510: 
 511:     /**
 512:      * Get the classes for the elements wrapper div
 513:      *
 514:      * @return array
 515:      */
 516:     protected function getElementsClasses()
 517:     {
 518:         $classes = array(
 519:             'quform-elements',
 520:             sprintf('quform-elements-%d', $this->getId()),
 521:             'quform-cf'
 522:         );
 523: 
 524:         if (Quform::isNonEmptyString($this->config('responsiveElements')) && $this->config('responsiveElements') != 'custom') {
 525:             $classes[] = sprintf('quform-responsive-elements-%s', $this->config('responsiveElements'));
 526:         }
 527: 
 528:         if ($this->isSubmitted()) {
 529:             $confirmation = $this->getConfirmation();
 530: 
 531:             if ($confirmation instanceof Quform_Confirmation && $confirmation->config('hideForm')) {
 532:                 $classes[] = 'quform-hidden';
 533:             }
 534:         }
 535: 
 536:         return $classes;
 537:     }
 538: 
 539:     /**
 540:      * Get the action attribute for the &lt;form&gt;
 541:      *
 542:      * @return string
 543:      */
 544:     protected function getAction()
 545:     {
 546:         $useAnchor = apply_filters('quform_use_anchor', true);
 547:         $useAnchor = apply_filters("quform_use_anchor_{$this->getId()}", $useAnchor);
 548: 
 549:         return add_query_arg(array()) . ($useAnchor ? "#quform-{$this->getUniqueId()}" : '');
 550:     }
 551: 
 552:     /**
 553:      * Get the HTML for the hidden inputs
 554:      *
 555:      * @return string
 556:      */
 557:     protected function getHiddenInputHtml()
 558:     {
 559:         $output = '';
 560: 
 561:         $inputs = array(
 562:             'quform_form_id' => $this->getId(),
 563:             'quform_form_uid' => $this->getUniqueId(),
 564:             'quform_count' => $this->config('count'),
 565:             'form_url' => Quform::getCurrentUrl(),
 566:             'referring_url' => Quform::getHttpReferer(),
 567:             'post_id' => Quform::getPostProperty('ID'),
 568:             'post_title' => Quform::getPostProperty('post_title'),
 569:             'quform_current_page_id' => $this->getCurrentPage()->getId(),
 570:         );
 571: 
 572:         if ($this->options->get('csrfProtection')) {
 573:             $inputs['quform_csrf_token'] = $this->getCsrfToken();
 574:         }
 575: 
 576:         foreach ($inputs as $name => $value) {
 577:             $output .= sprintf('<input type="hidden" name="%s" value="%s" />', Quform::escape($name), Quform::escape($value));
 578:         }
 579: 
 580:         return $output;
 581:     }
 582: 
 583:     /**
 584:      * Get the context (inheritable settings) for passing to child elements when rendering the form and generating CSS
 585:      *
 586:      * @return array
 587:      */
 588:     public function getContext()
 589:     {
 590:         return array(
 591:             'labelPosition' => $this->config('labelPosition'),
 592:             'labelWidth' => $this->config('labelWidth'),
 593:             'tooltipType' => $this->config('tooltipType'),
 594:             'tooltipEvent' => $this->config('tooltipEvent'),
 595:             'fieldSize' => $this->config('fieldSize'),
 596:             'fieldWidth' => $this->config('fieldWidth'),
 597:             'fieldWidthCustom' => $this->config('fieldWidthCustom'),
 598:             'buttonStyle' => $this->config('buttonStyle'),
 599:             'buttonSize' => $this->config('buttonSize'),
 600:             'buttonWidth' => $this->config('buttonWidth'),
 601:             'buttonWidthCustom' => $this->config('buttonWidthCustom'),
 602:             'responsiveColumns' => $this->config('responsiveColumns'),
 603:             'responsiveColumnsCustom' => $this->config('responsiveColumnsCustom'),
 604:             'optionsStyle' => $this->config('optionsStyle'),
 605:             'optionsButtonStyle' => $this->config('optionsButtonStyle'),
 606:             'optionsButtonSize' => $this->config('optionsButtonSize'),
 607:             'optionsButtonWidth' => $this->config('optionsButtonWidth'),
 608:             'optionsButtonWidthCustom' => $this->config('optionsButtonWidthCustom'),
 609:             'optionsButtonIconPosition' => $this->config('optionsButtonIconPosition')
 610:         );
 611:     }
 612: 
 613:     /**
 614:      * Get the HTML for the Quform referral link
 615:      *
 616:      * @return string
 617:      */
 618:     protected function getReferralLinkHtml()
 619:     {
 620:         $output = '';
 621: 
 622:         if ($this->options->get('referralEnabled') && Quform::isNonEmptyString($this->options->get('referralLink'))) {
 623:             $output .= '<div class="quform-referral-link">';
 624:             $output .= sprintf('<a href="%s">%s</a>', esc_url($this->options->get('referralLink')), $this->options->get('referralText'));
 625:             $output .= '</div>';
 626:         }
 627: 
 628:         return $output;
 629:     }
 630: 
 631:     /**
 632:      * Get the HTML for the file upload progress bar
 633:      *
 634:      * @return string
 635:      */
 636:     protected function getUploadProgressHtml()
 637:     {
 638:         $output = '';
 639: 
 640:         if ($this->hasEnhancedFileUploadElement()) {
 641:             $classes = array('quform-upload-progress-wrap');
 642: 
 643:             if (Quform::isNonEmptyString($this->config('loadingType')) &&
 644:                 $this->config('loadingOverlay') &&
 645:                 ($this->config('loadingPosition') == 'over-form' || $this->config('loadingPosition') == 'over-screen')
 646:             ) {
 647:                 $classes[] = sprintf('quform-loading-position-%s', $this->config('loadingPosition'));
 648:             }
 649: 
 650:             $output .= sprintf('<div class="%s">', Quform::escape(join(' ', $classes)));
 651:             $output .= '<div class="quform-upload-progress-bar-wrap"><div class="quform-upload-progress-bar"></div></div>';
 652:             $output .= '<div class="quform-upload-info quform-cf"><div class="quform-upload-filename"></div></div>';
 653:             $output .= '</div>';
 654:         }
 655: 
 656:         return $output;
 657:     }
 658: 
 659:     /**
 660:      * Get the HTML for the edit form link
 661:      *
 662:      * @return string
 663:      */
 664:     protected function getEditFormLinkHtml()
 665:     {
 666:         $output = '';
 667: 
 668:         if ($this->options->get('showEditLink') && $this->config('environment') != 'preview' && is_user_logged_in() && current_user_can('quform_edit_forms')) {
 669:             $output .= '<div class="quform-edit-form">';
 670: 
 671:             $editUrl = sprintf(admin_url('admin.php?page=quform.forms&sp=edit&id=%d'), $this->getId());
 672: 
 673:             $output .= sprintf('<a class="quform-edit-form-link" href="%s"><i class="qicon qicon-mode_edit"></i>%s</a>', esc_url($editUrl), esc_html__('Edit this form', 'quform'));
 674: 
 675:             $output .= '</div>';
 676:         }
 677: 
 678:         return $output;
 679:     }
 680: 
 681:     /**
 682:      * Add a form notification
 683:      *
 684:      * @param   Quform_Notification  $notification
 685:      * @return  Quform_Form
 686:      */
 687:     public function addNotification(Quform_Notification $notification)
 688:     {
 689:         $this->notifications[] = $notification;
 690: 
 691:         return $this;
 692:     }
 693: 
 694:     /**
 695:      * Get the form notifications
 696:      *
 697:      * @return Quform_Notification[]
 698:      */
 699:     public function getNotifications()
 700:     {
 701:         return $this->notifications;
 702:     }
 703: 
 704:     /**
 705:      * Get a notification by identifier
 706:      *
 707:      * @param   string  $identifier  The notification identifier (aka unique ID)
 708:      * @return  Quform_Notification|null
 709:      */
 710:     public function getNotification($identifier)
 711:     {
 712:         foreach ($this->getNotifications() as $notification) {
 713:             if ($notification->getIdentifier() == $identifier) {
 714:                 return $notification;
 715:             }
 716:         }
 717: 
 718:         return null;
 719:     }
 720: 
 721:     /**
 722:      * Replace the variables in the given string
 723:      *
 724:      * @param   string  $text
 725:      * @param   string  $format
 726:      * @return  string
 727:      */
 728:     public function replaceVariables($text, $format = 'text')
 729:     {
 730:         return $this->tokenReplacer->replaceVariables($text, $format, $this);
 731:     }
 732: 
 733:     /**
 734:      * Replace the variables in the given text before the form is processed
 735:      *
 736:      * @param   string  $text
 737:      * @param   string  $format
 738:      * @return  string
 739:      */
 740:     public function replaceVariablesPreProcess($text, $format = 'text')
 741:     {
 742:         return $this->tokenReplacer->replaceVariablesPreProcess($text, $format, $this);
 743:     }
 744: 
 745:     /**
 746:      * @return bool
 747:      */
 748:     public function isActive()
 749:     {
 750:         return $this->active;
 751:     }
 752: 
 753:     /**
 754:      * Set whether the form is active
 755:      *
 756:      * @param   boolean  $flag
 757:      * @return  Quform_Form
 758:      */
 759:     public function setIsActive($flag)
 760:     {
 761:         $this->active = $flag;
 762: 
 763:         return $this;
 764:     }
 765: 
 766:     /**
 767:      * Add a confirmation
 768:      *
 769:      * @param Quform_Confirmation $confirmation
 770:      */
 771:     public function addConfirmation(Quform_Confirmation $confirmation)
 772:     {
 773:         $this->confirmations[] = $confirmation;
 774:     }
 775: 
 776:     /**
 777:      * Set the confirmation to use for this submission
 778:      *
 779:      * If there is only one confirmation, it will be used, otherwise the first matching conditional logic
 780:      */
 781:     public function setConfirmation()
 782:     {
 783:         $this->confirmation = $this->confirmations[0];
 784:         $count = count($this->confirmations);
 785: 
 786:         if ($count > 1) {
 787:             for ($i = 1; $i < $count; $i++) {
 788:                 $confirmation = $this->confirmations[$i];
 789: 
 790:                 if ($confirmation->config('enabled') && count($confirmation->config('logicRules')) && $this->checkLogicAction($confirmation->config('logicAction'), $confirmation->config('logicMatch'), $confirmation->config('logicRules'))) {
 791:                     $this->confirmation = $confirmation;
 792:                     break;
 793:                 }
 794:             }
 795:         }
 796:     }
 797: 
 798:     /**
 799:      * Get the confirmation to use for this submission
 800:      *
 801:      * @return Quform_Confirmation
 802:      */
 803:     public function getConfirmation()
 804:     {
 805:         return $this->confirmation;
 806:     }
 807: 
 808:     /**
 809:      * Get the HTML for the success message
 810:      *
 811:      * @param   string  $position
 812:      * @return  string
 813:      */
 814:     public function getSuccessMessageHtml($position = 'above')
 815:     {
 816:         $confirmation = $this->getConfirmation();
 817:         $output = '';
 818: 
 819:         if ($this->isSubmitted() &&
 820:             $confirmation->config('messagePosition') == $position &&
 821:             Quform::isNonEmptyString($message = $confirmation->getMessage())
 822:         ) {
 823:             $hasIcon = Quform::isNonEmptyString($confirmation->config('messageIcon'));
 824: 
 825:             $output = sprintf(
 826:                 '<div class="quform-success-message quform-success-message-%s%s">',
 827:                 $this->getId(),
 828:                 $hasIcon ? ' quform-success-message-has-icon' : ''
 829:             );
 830: 
 831:             if ($hasIcon) {
 832:                 $output .= sprintf('<div class="quform-success-message-icon"><i class="%s"></i></div>', $confirmation->config('messageIcon'));
 833:             }
 834: 
 835:             $output .= sprintf('<div class="quform-success-message-content">%s</div>', $message);
 836: 
 837:             $output .= '</div>';
 838:         }
 839: 
 840:         return $output;
 841:     }
 842: 
 843:     /**
 844:      * Get the HTML for the page progress
 845:      *
 846:      * @return string
 847:      */
 848:     public function getPageProgressHtml()
 849:     {
 850:         $type = $this->config('pageProgressType');
 851: 
 852:         if ( ! $this->hasPages() || ! Quform::isNonEmptyString($type)) {
 853:             return '';
 854:         }
 855: 
 856:         $currentPage = $this->getCurrentPage();
 857:         $currentPageIndex = 1;
 858: 
 859:         foreach ($this->pages as $pages) {
 860:             if ($pages == $currentPage) {
 861:                 break;
 862:             }
 863:             $currentPageIndex++;
 864:         }
 865: 
 866:         $output = sprintf('<div class="quform-page-progress quform-page-progress-type-%s">', $type);
 867: 
 868:         if ($type == 'numbers' || $type == 'percentage') {
 869:             $percent = round(($currentPageIndex / $this->getPageCount()) * 100);
 870: 
 871:             $output .= sprintf('<div class="quform-page-progress-bar" style="width: %d%%;">', esc_attr($percent));
 872:             $output .= '<span class="quform-page-progress-text">';
 873: 
 874:             if ($type == 'numbers') {
 875:                 $output .= sprintf(
 876:                     /* translators: Page x of x, %1$s: the current page number, %2$s: the total number of pages */
 877:                     esc_html($this->getTranslation('pageProgressNumbersText', __('Page %1$s of %2$s', 'quform'))),
 878:                     sprintf('<span class="quform-page-progress-number">%s</span>', $currentPageIndex),
 879:                     $this->getPageCount()
 880:                 );
 881:             } else {
 882:                 $output .= sprintf(
 883:                     /* translators: page progress percentage, %s: the page progress percentage, %%: the percentage sign */
 884:                     esc_html__('%s%%', 'quform'),
 885:                     sprintf('<span class="quform-page-progress-percentage">%s</span>', $percent)
 886:                 );
 887:             }
 888: 
 889:             $output .= '</span>';
 890:             $output .= '</div>';
 891:         } else if ($type == 'tabs') {
 892:             $output .= sprintf('<div class="quform-page-progress-tabs quform-%d-pages quform-cf">', count($this->pages));
 893: 
 894:             foreach ($this->pages as $page) {
 895:                 $output .= sprintf(
 896:                     '<div class="quform-page-progress-tab%s" data-id="%d">%s</div>',
 897:                     $page == $this->currentPage ? ' quform-current-tab' : '',
 898:                     $page->getId(),
 899:                     esc_html($page->getLabel())
 900:                 );
 901:             }
 902: 
 903:             $output .= '</div>';
 904:         }
 905: 
 906:         $output .= '</div>';
 907: 
 908:         return $output;
 909:     }
 910: 
 911:     /**
 912:      * Get the HTML for the loading indicator
 913:      *
 914:      * @return string
 915:      */
 916:     protected function getLoadingHtml()
 917:     {
 918:         if ( ! Quform::isNonEmptyString($this->config('loadingType')) ||
 919:             ($this->config('loadingPosition') != 'over-form' && $this->config('loadingPosition') != 'over-screen'))
 920:         {
 921:             return '';
 922:         }
 923: 
 924:         $classes = array(
 925:             'quform-loading',
 926:             sprintf('quform-loading-position-%s', $this->config('loadingPosition')),
 927:             sprintf('quform-loading-type-%s', $this->config('loadingType'))
 928:         );
 929: 
 930:         $output = sprintf('<div class="%s">', esc_attr(join(' ', $classes)));
 931: 
 932:         if ($this->config('loadingOverlay')) {
 933:             $output .= '<div class="quform-loading-overlay"></div>';
 934:         }
 935: 
 936:         $output .= '<div class="quform-loading-inner">';
 937: 
 938:         if ($this->config('loadingType') == 'custom') {
 939:             $output .= do_shortcode($this->config('loadingCustom'));
 940:         } else {
 941:             $output .= '<div class="quform-loading-spinner"><div class="quform-loading-spinner-inner"></div></div>';
 942:         }
 943: 
 944:         $output .= '</div></div>';
 945: 
 946:         return $output;
 947:     }
 948: 
 949:     /**
 950:      * Set the unique form ID
 951:      *
 952:      * The unique ID is unique to each form instance, even the same form on the same page
 953:      *
 954:      * @param string $uniqueId
 955:      */
 956:     public function setUniqueId($uniqueId)
 957:     {
 958:         $this->uniqueId = $uniqueId;
 959:     }
 960: 
 961:     /**
 962:      * Get the unique form ID
 963:      */
 964:     public function getUniqueId()
 965:     {
 966:         return $this->uniqueId;
 967:     }
 968: 
 969:     /**
 970:      * Get the JavaScript configuration for the frontend
 971:      *
 972:      * @return string
 973:      */
 974:     public function getJsConfig()
 975:     {
 976:         $config = array(
 977:             'id' => $this->getId(),
 978:             'uniqueId' => $this->getUniqueId(),
 979:             'theme' => $this->config('theme'),
 980:             'ajax' => $this->config('ajax'),
 981:             'logic' => $this->getLogicConfig(),
 982:             'currentPageId' => $this->getCurrentPage()->getId(),
 983:             'errorsIcon' => $this->config('errorsIcon'),
 984:             'updateFancybox' => apply_filters('quform_update_fancybox', true, $this),
 985:             'hasPages' => $this->hasPages(),
 986:             'pages' => $this->getPageIds(),
 987:             'pageProgressType' => $this->config('pageProgressType'),
 988:             'tooltipsEnabled' => $this->config('tooltipsEnabled'),
 989:             'tooltipClasses' => $this->config('tooltipClasses'),
 990:             'tooltipMy' => $this->config('tooltipMy'),
 991:             'tooltipAt' => $this->config('tooltipAt'),
 992:             'isRtl' => $this->isRtl()
 993:         );
 994: 
 995:         if (is_numeric($this->options->get('scrollOffset'))) {
 996:             $config['scrollOffset'] = ((int) $this->options->get('scrollOffset')) * -1;
 997:         }
 998: 
 999:         if (is_numeric($this->options->get('scrollSpeed'))) {
1000:             $config['scrollSpeed'] = (int) $this->options->get('scrollSpeed');
1001:         }
1002: 
1003:         $config = apply_filters('quform_form_js_config', $config, $this);
1004:         $config = apply_filters('quform_form_js_config_' . $this->getId(), $config, $this);
1005: 
1006:         return wp_json_encode($config);
1007:     }
1008: 
1009:     /**
1010:      * Get the character encoding
1011:      *
1012:      * @return string
1013:      */
1014:     public function getCharset()
1015:     {
1016:         return $this->charset;
1017:     }
1018: 
1019:     /**
1020:      * Set the character encoding
1021:      *
1022:      * @param string $charset
1023:      */
1024:     public function setCharset($charset)
1025:     {
1026:         $this->charset = $charset;
1027:     }
1028: 
1029:     /**
1030:      * @return array
1031:      */
1032:     public function getPages()
1033:     {
1034:         return $this->pages;
1035:     }
1036: 
1037:     /**
1038:      * Add a page to the form
1039:      *
1040:      * @param Quform_Element_Page $page The page to add
1041:      */
1042:     public function addPage(Quform_Element_Page $page)
1043:     {
1044:         $this->pages[$page->getName()] = $page;
1045:     }
1046: 
1047:     /**
1048:      * Set the values for all form elements
1049:      *
1050:      * @param  array  $values          The values to set
1051:      * @param  bool   $setOthersEmpty  Set the value of elements that are not in $values to empty
1052:      */
1053:     public function setValues($values, $setOthersEmpty = false)
1054:     {
1055:         foreach ($this->getRecursiveIterator() as $element) {
1056:             if ( ! ($element instanceof Quform_Element_Field) || $element instanceof Quform_Element_File) {
1057:                 continue;
1058:             }
1059: 
1060:             if ( ! $element->isVisible()) {
1061:                 // For non-visible fields, set the default or the empty value if there is no default
1062:                 $element->setValue($element->hasDefaultValue() ? $element->getDefaultValue() : $element->getEmptyValue());
1063:             } else if (isset($values[$element->getName()])) {
1064:                 $element->setValue($values[$element->getName()]);
1065:             } else if ($setOthersEmpty) {
1066:                 $element->setValue($element->getEmptyValue());
1067:             }
1068:         }
1069:     }
1070: 
1071:     /**
1072:      * Is the form valid?
1073:      *
1074:      * @return array The first value is valid boolean, second is the first page instance that has an error
1075:      */
1076:     public function isValid()
1077:     {
1078:         $valid = true;
1079:         $firstErrorPage = null;
1080: 
1081:         foreach ($this->pages as $page) {
1082:             if ( ! $page->isValid()) {
1083:                 $valid = false;
1084: 
1085:                 if (is_null($firstErrorPage)) {
1086:                     $firstErrorPage = $page;
1087:                 }
1088:             }
1089:         }
1090: 
1091:         return array($valid, $firstErrorPage);
1092:     }
1093: 
1094:     /**
1095:      * Get the validation errors
1096:      *
1097:      * @return array
1098:      */
1099:     public function getErrors()
1100:     {
1101:         $errors = array();
1102: 
1103:         foreach ($this->getRecursiveIterator() as $element) {
1104:             if ( ! $element instanceof Quform_Element_Field) {
1105:                 continue;
1106:             }
1107: 
1108:             if ($element->hasError()) {
1109:                 foreach ($element->getErrorArray() as $identifier => $message) {
1110:                     $errors[$identifier] = $message;
1111:                 }
1112:             }
1113:         }
1114: 
1115:         return $errors;
1116:     }
1117: 
1118:     /**
1119:      * Set the flag to show the global form error message
1120:      *
1121:      * @param bool $flag
1122:      */
1123:     public function setShowGlobalError($flag)
1124:     {
1125:         $this->showGlobalError = $flag;
1126:     }
1127: 
1128:     /**
1129:      * Get the flag to show the global form error message
1130:      *
1131:      * @return bool
1132:      */
1133:     public function getShowGlobalError()
1134:     {
1135:         return $this->showGlobalError;
1136:     }
1137: 
1138:     /**
1139:      * Get the global error message data
1140:      *
1141:      * @return array
1142:      */
1143:     public function getGlobalError()
1144:     {
1145:         return array(
1146:             'enabled' => $this->config('errorEnabled'),
1147:             'title' => $this->config('errorTitle'),
1148:             'content' => $this->config('errorContent')
1149:         );
1150:     }
1151: 
1152:     /**
1153:      * Get the HTML for the global form error message if enabled
1154:      *
1155:      * @return string
1156:      */
1157:     protected function getGlobalErrorHtml()
1158:     {
1159:         $output = '';
1160: 
1161:         if ($this->getShowGlobalError() && Quform::isNonEmptyString($this->config('errorContent'))) {
1162:             $output .= '<div class="quform-error-message"><div class="quform-error-message-inner">';
1163: 
1164:             if (Quform::isNonEmptyString($this->config('errorTitle'))) {
1165:                 $output .= sprintf('<div class="quform-error-message-title">%s</div>', $this->config('errorTitle'));
1166:             }
1167: 
1168:             $output .= sprintf('<div class="quform-error-message-content">%s</div>', $this->config('errorContent'));
1169: 
1170: 
1171:             $output .= '</div></div>';
1172:         }
1173: 
1174:         return $output;
1175:     }
1176: 
1177:     /**
1178:      * Get the element with the given name
1179:      *
1180:      * Returns the element or null if it does not exist
1181:      *
1182:      * @param   string               $nameOrId
1183:      * @return  Quform_Element|null
1184:      */
1185:     public function getElement($nameOrId)
1186:     {
1187:         return is_numeric($nameOrId) ? $this->getElementById($nameOrId) : $this->getElementByName($nameOrId);
1188:     }
1189: 
1190:     /**
1191:      * Set the value of the element with the given name
1192:      *
1193:      * @param   string|int   $nameOrId
1194:      * @param   mixed        $value
1195:      * @return  Quform_Form
1196:      */
1197:     public function setValue($nameOrId, $value)
1198:     {
1199:         $element = $this->getElement($nameOrId);
1200: 
1201:         if ($element instanceof Quform_Element_Field) {
1202:             $element->setValue($value);
1203:         }
1204: 
1205:         return $this;
1206:     }
1207: 
1208:     /**
1209:      * Set the value of the element with the given name
1210:      *
1211:      * @param   string|int  $nameOrId
1212:      * @param   mixed       $value
1213:      * @return  $this
1214:      */
1215:     public function setValueFromStorage($nameOrId, $value)
1216:     {
1217:         $element = $this->getElement($nameOrId);
1218: 
1219:         if ($element instanceof Quform_Element_Field) {
1220:             $element->setValueFromStorage($value);
1221:         }
1222: 
1223:         return $this;
1224:     }
1225: 
1226:     /**
1227:      * Get the values of all fields
1228:      *
1229:      * @return array The values of all fields
1230:      */
1231:     public function getValues()
1232:     {
1233:         $values = array();
1234: 
1235:         foreach ($this->getRecursiveIterator() as $element) {
1236:             if ($element instanceof Quform_Element_Field) {
1237:                 $values[$element->getName()] = $element->getValue();
1238:             }
1239:         }
1240: 
1241:         return $values;
1242:     }
1243: 
1244:     /**
1245:      * Get the values of a single field
1246:      *
1247:      * @param   string|int  $nameOrId
1248:      * @return  mixed       The value of the given field or null
1249:      */
1250:     public function getValue($nameOrId)
1251:     {
1252:         $element = $this->getElement($nameOrId);
1253: 
1254:         return $element instanceof Quform_Element_Field ? $element->getValue() : null;
1255:     }
1256: 
1257:     /**
1258:      * Get the value of the element with the given name, formatted in HTML
1259:      *
1260:      * @param   string  $nameOrId  The unique element name
1261:      * @return  string             The formatted HTML
1262:      */
1263:     public function getValueHtml($nameOrId)
1264:     {
1265:         $element = $this->getElement($nameOrId);
1266: 
1267:         return $element instanceof Quform_Element_Field ? $element->getValueHtml() : '';
1268:     }
1269: 
1270:     /**
1271:      * Get the value of the element with the given name, formatted in plain text
1272:      *
1273:      * @param   string $nameOrId   The unique element name
1274:      * @param   string $separator  The separator for array types
1275:      * @return  string             The formatted plain text
1276:      */
1277:     public function getValueText($nameOrId, $separator = ', ')
1278:     {
1279:         $element = $this->getElement($nameOrId);
1280: 
1281:         return $element instanceof Quform_Element_Field ? $element->getValueText($separator) : '';
1282:     }
1283: 
1284:     /**
1285:      * Get the element with the given ID.
1286:      *
1287:      * Returns the element or null if no element was found.
1288:      *
1289:      * @param   int                  $id
1290:      * @return  Quform_Element|null
1291:      */
1292:     public function getElementById($id)
1293:     {
1294:         foreach ($this->getRecursiveIterator(RecursiveIteratorIterator::SELF_FIRST) as $element) {
1295:             if ($element->getId() == $id) {
1296:                 return $element;
1297:             }
1298:         }
1299: 
1300:         return null;
1301:     }
1302: 
1303:     /**
1304:      * Get the element with the given name.
1305:      *
1306:      * Returns the element or null if no element was found.
1307:      *
1308:      * @param   string               $name
1309:      * @return  Quform_Element|null
1310:      */
1311:     public function getElementByName($name)
1312:     {
1313:         foreach ($this->getRecursiveIterator(RecursiveIteratorIterator::SELF_FIRST) as $element) {
1314:             if ($element->getName() == $name) {
1315:                 return $element;
1316:             }
1317:         }
1318: 
1319:         return null;
1320:     }
1321: 
1322:     /**
1323:      * Get the conditional logic configuration array
1324:      *
1325:      * @return array
1326:      */
1327:     public function getLogicConfig()
1328:     {
1329:         $logic = array();
1330:         $dependents = array();
1331:         $elementIds = array();
1332:         $dependentElementIds = array();
1333: 
1334:         foreach ($this->getRecursiveIterator(RecursiveIteratorIterator::SELF_FIRST) as $element) {
1335:             if ($element->config('logicEnabled') && count($element->config('logicRules'))) {
1336:                 $elementId = $element->getId();
1337:                 $elementIds[] = $elementId;
1338:                 $logic[$elementId] = array(
1339:                     'action' => $element->config('logicAction'),
1340:                     'match' => $element->config('logicMatch'),
1341:                     'rules' => $element->config('logicRules')
1342:                 );
1343: 
1344:                 foreach ($element->config('logicRules') as $elementLogicRule) {
1345:                     if ( ! isset($dependents[$elementLogicRule['elementId']])) {
1346:                         $dependents[$elementLogicRule['elementId']] = array();
1347:                     }
1348: 
1349:                     $dependents[$elementLogicRule['elementId']][] = $elementId;
1350:                     $dependentElementIds[] = $elementLogicRule['elementId'];
1351:                 }
1352:             }
1353:         }
1354: 
1355:         return array(
1356:             'logic' => $logic,
1357:             'dependents' => $dependents,
1358:             'elementIds' => $elementIds,
1359:             'dependentElementIds' => array_values(array_unique($dependentElementIds)),
1360:             'animate' => $this->config('logicAnimation')
1361:         );
1362:     }
1363: 
1364:     /**
1365:      * Get the CSS for this form
1366:      *
1367:      * @return  string
1368:      */
1369:     public function getCss()
1370:     {
1371:         $css = '';
1372:         $styles = array();
1373: 
1374:         // Mapping of form style options to global styles
1375:         $map = array(
1376:             array('verticalElementSpacing', 'padding-bottom: %s;', 'elementSpacer'),
1377:             array('width', 'width: %s;', 'formOuter'),
1378:             array('labelTextColor', 'color: %s;', 'elementLabelText'),
1379:             array('requiredTextColor', 'color: %s;', 'elementRequiredText'),
1380:             array('fieldBackgroundColor', 'background-color: %s;', 'elementText', 'elementTextarea', 'elementSelect'),
1381:             array('fieldBackgroundColorHover', 'background-color: %s;', 'elementTextHover', 'elementTextareaHover', 'elementSelectHover'),
1382:             array('fieldBackgroundColorFocus', 'background-color: %s;', 'elementTextFocus', 'elementTextareaFocus', 'elementSelectFocus', 'timeDropdown', 'select2Drop', 'select2DropBorders'),
1383:             array('fieldBackgroundColorFocus', 'border-color: rgba(0,0,0,0.2);', 'select2DropBorders'),
1384:             array('fieldBorderColor', 'border-color: %s;', 'elementText', 'elementTextarea', 'elementSelect'),
1385:             array('fieldBorderColorHover', 'border-color: %s;', 'elementTextHover', 'elementTextareaHover', 'elementSelectHover'),
1386:             array('fieldBorderColorFocus', 'border-color: %s;', 'elementTextFocus', 'elementTextareaFocus', 'elementSelectFocus', 'timeDropdown', 'select2Drop'),
1387:             array('fieldTextColor', 'color: %s;', 'elementText', 'elementTextarea', 'elementSelect', 'elementIcon', 'select2DropText'),
1388:             array('fieldTextColorHover', 'color: %s;', 'elementTextHover', 'elementTextareaHover', 'elementSelectHover', 'elementIconHover'),
1389:             array('fieldTextColorFocus', 'color: %s;', 'elementTextFocus', 'elementTextareaFocus', 'elementSelectFocus', 'timeDropdown', 'select2Drop', 'timeDropdownText', 'select2DropTextFocus', 'select2DropBorders'),
1390:             array('buttonBackgroundColor', 'background-color: %s;', 'submitButton', 'backButton', 'nextButton', 'uploadButton'),
1391:             array('buttonBackgroundColorHover', 'background-color: %s;', 'submitButtonHover', 'backButtonHover', 'nextButtonHover', 'uploadButtonHover'),
1392:             array('buttonBackgroundColorActive', 'background-color: %s;', 'submitButtonActive', 'backButtonActive', 'nextButtonActive', 'uploadButtonActive'),
1393:             array('buttonBorderColor', 'border-color: %s;', 'submitButton', 'backButton', 'nextButton', 'uploadButton'),
1394:             array('buttonBorderColorHover', 'border-color: %s;', 'submitButtonHover', 'backButtonHover', 'nextButtonHover', 'uploadButtonHover'),
1395:             array('buttonBorderColorActive', 'border-color: %s;', 'submitButtonActive', 'backButtonActive', 'nextButtonActive', 'uploadButtonActive'),
1396:             array('buttonTextColor', 'color: %s;', 'submitButtonText', 'backButtonText', 'nextButtonText', 'uploadButtonText'),
1397:             array('buttonTextColorHover', 'color: %s;', 'submitButtonTextHover', 'backButtonTextHover', 'nextButtonTextHover', 'uploadButtonTextHover'),
1398:             array('buttonTextColorActive', 'color: %s;', 'submitButtonTextActive', 'backButtonTextActive', 'nextButtonTextActive', 'uploadButtonTextActive'),
1399:             array('buttonIconColor', 'color: %s;', 'submitButtonIcon', 'backButtonIcon', 'nextButtonIcon', 'uploadButtonIcon'),
1400:             array('buttonIconColorHover', 'color: %s;', 'submitButtonIconHover', 'backButtonIconHover', 'nextButtonIconHover', 'uploadButtonIconHover'),
1401:             array('buttonIconColorActive', 'color: %s;', 'submitButtonIconActive', 'backButtonIconActive', 'nextButtonIconActive', 'uploadButtonIconActive'),
1402:         );
1403: 
1404:         foreach ($map as $selectors) {
1405:             $value = $this->config(array_shift($selectors));
1406: 
1407:             if (Quform::isNonEmptyString($value)) {
1408:                 $rule = sprintf(array_shift($selectors), $value);
1409: 
1410:                 foreach ($selectors as $selector) {
1411:                     $styles[] = array('type' => trim($selector), 'css' => $rule);
1412:                 }
1413:             }
1414:         }
1415: 
1416:         $styles = array_merge($styles, $this->config('styles'));
1417: 
1418:         foreach ($styles as $style) {
1419:             $selector = $this->getCssSelector($style['type']);
1420: 
1421:             if ( ! empty($selector) && ! empty($style['css'])) {
1422:                 $css .= sprintf('%s { %s }', $selector, $style['css']);
1423:             }
1424:         }
1425: 
1426:         $theme = $this->config('theme');
1427:         if (Quform::isNonEmptyString($theme)) {
1428:             $css .= Quform_Themes::getPrimaryColorCustomCss($theme, $this->getId(), $this->config('themePrimaryColor'));
1429:             $css .= Quform_Themes::getSecondaryColorCustomCss($theme, $this->getId(), $this->config('themeSecondaryColor'));
1430:             $css .= Quform_Themes::getPrimaryForegroundColorCustomCss($theme, $this->getId(), $this->config('themePrimaryForegroundColor'));
1431:             $css .= Quform_Themes::getSecondaryForegroundColorCustomCss($theme, $this->getId(), $this->config('themeSecondaryForegroundColor'));
1432:         }
1433: 
1434:         if ($this->config('responsiveElements') == 'custom' && Quform::isNonEmptyString($this->config('responsiveElementsCustom'))) {
1435:             $css .= sprintf('@media (max-width: %s) {', Quform::addCssUnit($this->config('responsiveElementsCustom')));
1436:             $css .= sprintf('
1437:                 .quform-%1$d .quform-input,
1438:                 .quform-%1$d .quform-upload-dropzone {
1439:                     width: 100%% !important;
1440:                     min-width: 10px;
1441:                 }
1442:                 .quform-%1$d .quform-error > .quform-error-inner {
1443:                     float: none;
1444:                     display: block;
1445:                 }
1446:                 .quform-%1$d .quform-element-submit button {
1447:                     margin: 0;
1448:                     width: 100%%;
1449:                 }
1450:                 .quform-%1$d .quform-element-submit.quform-button-width-full > .quform-button-submit-default,
1451:                 .quform-%1$d .quform-element-submit.quform-button-width-full > .quform-button-back-default,
1452:                 .quform-%1$d .quform-element-submit.quform-button-width-full > .quform-button-next-default {
1453:                     width: 100%%;
1454:                     float: none;
1455:                 }
1456:                 .quform-%1$d .quform-button-next-default,
1457:                 .quform-%1$d .quform-button-back-default,
1458:                 .quform-%1$d .quform-button-submit-default {
1459:                     float: none;
1460:                     margin: 5px 0;
1461:                 }
1462:                 .quform-%1$d .quform-loading-position-left {
1463:                     padding-left: 0;
1464:                 }
1465:                 .quform-%1$d .quform-loading-position-right {
1466:                     padding-right: 0;
1467:                 }
1468:                 .quform-%1$d .quform-labels-left > .quform-spacer > .quform-label {
1469:                     float: none;
1470:                     width: auto;
1471:                 }
1472:                 .quform-%1$d .quform-labels-left.quform-element > .quform-spacer > .quform-inner {
1473:                     margin-left: 0 !important;
1474:                     padding-left: 0 !important;
1475:                     margin-right: 0 !important;
1476:                     padding-right: 0 !important;
1477:                 }
1478:                 .quform-%1$d .select2-container--quform .select2-selection--multiple .select2-selection__choice {
1479:                     display: block;
1480:                     float: none;
1481:                     width: auto;
1482:                     padding-top: 10px;
1483:                     padding-bottom: 10px;
1484:                     margin-right: 25px;
1485:                 }
1486:                 ', $this->getId());
1487:             $css .= '}';
1488:         }
1489: 
1490:         if ($this->config('responsiveColumns') == 'custom' && Quform::isNonEmptyString($this->config('responsiveColumnsCustom'))) {
1491:             $css .= sprintf('@media (max-width: %s) {', Quform::addCssUnit($this->config('responsiveColumnsCustom')));
1492:             $css .= sprintf('
1493:                 .quform-%1$d .quform-element-row > .quform-element-column,
1494:                 .quform-%1$d .quform-options-columns > .quform-option {
1495:                     float: none;
1496:                     width: 100%% !important;
1497:                 }
1498:                 ', $this->getId());
1499:             $css .= '}';
1500:         }
1501: 
1502:         if (Quform::isNonEmptyString($this->config('fieldPlaceholderStyles'))) {
1503:             // The selectors must be separate for this to work
1504:             $css .= sprintf(
1505:                 '.quform-%1$d ::-webkit-input-placeholder {
1506:                     %2$s
1507:                 }
1508:                 .quform-%1$d :-moz-placeholder {
1509:                     %2$s
1510:                 }
1511:                 .quform-%1$d ::-moz-placeholder {
1512:                     %2$s
1513:                 }
1514:                 .quform-%1$d :-ms-input-placeholder {
1515:                     %2$s
1516:                 }
1517:                 .quform-%1$d ::placeholder {
1518:                     %2$s
1519:                 }',
1520:                 $this->getId(),
1521:                 $this->config('fieldPlaceholderStyles')
1522:             );
1523:         }
1524: 
1525:         if (Quform::isNonEmptyString($this->config('loadingColor'))) {
1526:             if ($this->config('loadingType') == 'spinner-1') {
1527:                 $css .= sprintf(
1528:                     '.quform-%1$d .quform-loading-type-spinner-1 .quform-loading-spinner,
1529:                     .quform-%1$d .quform-loading-type-spinner-1 .quform-loading-spinner:after { border-top-color: %2$s; }',
1530:                     $this->getId(),
1531:                     $this->config('loadingColor')
1532:                 );
1533:             } else if ($this->config('loadingType') == 'spinner-2') {
1534:                 $css .= sprintf(
1535:                     '.quform-%1$d .quform-loading-type-spinner-2 .quform-loading-spinner { background-color: %2$s; }',
1536:                     $this->getId(),
1537:                     $this->config('loadingColor')
1538:                 );
1539:             } else if ($this->config('loadingType') == 'spinner-3') {
1540:                 $css .= sprintf(
1541:                     '.quform-%1$d .quform-loading-type-spinner-3 .quform-loading-spinner,
1542:                     .quform-%1$d .quform-loading-type-spinner-3 .quform-loading-spinner:after { color: %2$s; }',
1543:                     $this->getId(),
1544:                     $this->config('loadingColor')
1545:                 );
1546:             } else if ($this->config('loadingType') == 'spinner-4') {
1547:                 $css .= sprintf(
1548:                     '.quform-%1$d .quform-loading-type-spinner-4 .quform-loading-spinner:after { background-color: %2$s; }',
1549:                     $this->getId(),
1550:                     $this->config('loadingColor')
1551:                 );
1552:             } else if ($this->config('loadingType') == 'spinner-5') {
1553:                 $css .= sprintf(
1554:                     '.quform-%1$d .quform-loading-type-spinner-5 .quform-loading-spinner { border-left-color: %2$s; }',
1555:                     $this->getId(),
1556:                     $this->config('loadingColor')
1557:                 );
1558:             } else if ($this->config('loadingType') == 'spinner-6') {
1559:                 $css .= sprintf(
1560:                     '.quform-%1$d .quform-loading-type-spinner-6 .quform-loading-spinner-inner { color: %2$s; }',
1561:                     $this->getId(),
1562:                     $this->config('loadingColor')
1563:                 );
1564:             } else if ($this->config('loadingType') == 'spinner-7') {
1565:                 $css .= sprintf(
1566:                     '.quform-%1$d .quform-loading-type-spinner-7 .quform-loading-spinner-inner,
1567:                     .quform-%1$d .quform-loading-type-spinner-7 .quform-loading-spinner-inner:before,
1568:                     .quform-%1$d .quform-loading-type-spinner-7 .quform-loading-spinner-inner:after { background-color: %2$s; color: %2$s; }',
1569:                      $this->getId(),
1570:                     $this->config('loadingColor')
1571:                 );
1572:             } else if ($this->config('loadingType') == 'custom') {
1573:                 $css .= sprintf(
1574:                     '.quform-%1$d .quform-loading-type-custom .quform-loading-inner { color: %2$s; }',
1575:                     $this->getId(),
1576:                     $this->config('loadingColor')
1577:                 );
1578:             }
1579:         }
1580: 
1581:         if ($this->config('loadingOverlay') && Quform::isNonEmptyString($this->config('loadingOverlayColor'))) {
1582:             $css .= sprintf(
1583:                 '.quform-%1$d .quform-loading-overlay, .quform-%1$d .quform-loading.quform-loading-triggered.quform-loading-position-over-button { background-color: %2$s; }',
1584:                 $this->getId(),
1585:                 $this->config('loadingOverlayColor')
1586:             );
1587:         }
1588: 
1589:         foreach ($this->pages as $page) {
1590:             $css .= $page->getCss($this->getContext());
1591:         }
1592: 
1593:         $css .= apply_filters('quform_custom_css_' . $this->getId(), '', $this);
1594: 
1595:         return $css;
1596:     }
1597: 
1598:     /**
1599:      * Get the list of CSS selectors
1600:      *
1601:      * @return array
1602:      */
1603:     protected function getCssSelectors()
1604:     {
1605:         return array(
1606:             'formOuter' => '%s',
1607:             'formInner' => '%s .quform-form-inner',
1608:             'formSuccess' => '%s .quform-success-message',
1609:             'formSuccessIcon' => '%s .quform-success-message-icon',
1610:             'formSuccessContent' => '%s .quform-success-message-content',
1611:             'formTitle' => '%s .quform-form-title',
1612:             'formDescription' => '%s .quform-form-description',
1613:             'formElements' => '%s .quform-elements',
1614:             'formError' => '%s .quform-error-message',
1615:             'formErrorInner' => '%s .quform-error-message-inner',
1616:             'formErrorTitle' => '%s .quform-error-message-title',
1617:             'formErrorContent' => '%s .quform-error-message-content',
1618:             'element' => '%s .quform-element',
1619:             'elementLabel' => '%s .quform-label',
1620:             'elementLabelText' => '%s .quform-label > label',
1621:             'elementRequiredText' => '%s .quform-label > label > .quform-required',
1622:             'elementInner' => '%s .quform-inner',
1623:             'elementInput' => '%s .quform-input',
1624:             'elementText' => '
1625:                 %1$s .select2-container--quform .select2-selection,
1626:                 %1$s .quform-field-text,
1627:                 %1$s .quform-field-email,
1628:                 %1$s .quform-field-date,
1629:                 %1$s .quform-field-time,
1630:                 %1$s .quform-field-captcha,
1631:                 %1$s .quform-field-password',
1632:             'elementTextHover' => '
1633:                 %1$s .select2-container--quform .select2-selection:hover,
1634:                 %1$s .quform-field-text:hover,
1635:                 %1$s .quform-field-email:hover,
1636:                 %1$s .quform-field-date:hover,
1637:                 %1$s .quform-field-time:hover,
1638:                 %1$s .quform-field-captcha:hover,
1639:                 %1$s .quform-field-password:hover',
1640:             'elementTextFocus' => '
1641:                 %1$s .select2-container--quform.select2-container--open .select2-selection,
1642:                 %1$s .quform-field-text:focus,
1643:                 %1$s .quform-field-email:focus,
1644:                 %1$s .quform-field-date:focus,
1645:                 %1$s .quform-field-time:focus,
1646:                 %1$s .quform-field-captcha:focus,
1647:                 %1$s .quform-field-password:focus,
1648:                 %1$s .quform-field-text:active,
1649:                 %1$s .quform-field-email:active,
1650:                 %1$s .quform-field-date:active,
1651:                 %1$s .quform-field-time:active,
1652:                 %1$s .quform-field-captcha:active,
1653:                 %1$s .quform-field-password:active',
1654:             'elementTextarea' => '%s .quform-field-textarea',
1655:             'elementTextareaHover' => '%s .quform-field-textarea:hover',
1656:             'elementTextareaFocus' => '
1657:                 %1$s .quform-field-textarea:focus,
1658:                 %1$s .quform-field-textarea:active',
1659:             'elementSelect' => '
1660:                 %1$s .quform-field-select,
1661:                 %1$s .quform-field-multiselect',
1662:             'elementSelectHover' => '
1663:                 %1$s .quform-field-select:hover,
1664:                 %1$s .quform-field-multiselect:hover',
1665:             'elementSelectFocus' => '
1666:                 %1$s .quform-field-select:focus,
1667:                 %1$s .quform-field-select:active,
1668:                 %1$s .quform-field-multiselect:focus,
1669:                 %1$s .quform-field-multiselect:active',
1670:             'elementIcon' => '
1671:                 %1$s .select2-container--quform .select2-selection--multiple .select2-selection__rendered:before,
1672:                 %1$s .select2-container--quform .select2-selection__arrow b,
1673:                 %1$s-select2.select2-container--quform .select2-search--dropdown:before,
1674:                 %1$s .quform-field-icon',
1675:             'elementIconHover' => '
1676:                 %1$s .select2-container--quform .select2-selection--multiple .select2-selection__rendered:hover:before,
1677:                 %1$s .select2-container--quform .select2-selection__arrow b:hover,
1678:                 %1$s-select2.select2-container--quform .select2-search--dropdown:hover:before,
1679:                 %1$s .quform-field-icon:hover',
1680:             'timeDropdown' => '
1681:                 %s-timepicker.quform-timepicker.k-list-container.k-popup',
1682:             'timeDropdownText' => '
1683:                 %1$s-timepicker.quform-timepicker.k-popup ul.k-list li.k-item.k-state-selected,
1684:                 %1$s-timepicker.quform-timepicker.k-popup ul.k-list li.k-item,
1685:                 %1$s-timepicker.quform-timepicker.k-popup ul.k-list li.k-item.k-state-hover',
1686:             'select2Drop' => '
1687:                 %s-select2.select2-container--quform .select2-dropdown',
1688:             'select2DropBorders' => '
1689:                 %1$s-timepicker.quform-timepicker.k-popup ul.k-list li.k-item.k-state-hover,
1690:                 %1$s-select2.select2-container--quform .select2-dropdown--below .select2-results__options,
1691:                 %1$s-select2.select2-container--quform .select2-results__option--highlighted[aria-selected],
1692:                 %1$s-select2.select2-container--quform .select2-search--dropdown .select2-search__field',
1693:             'select2DropText' => '%s .select2-container--quform .select2-search--inline .select2-search__field',
1694:             'select2DropTextFocus' => '
1695:                 %1$s-select2.select2-container--quform .select2-results__options[aria-multiselectable="true"] .select2-results__option[aria-selected="true"],
1696:                 %1$s .select2-container--quform .select2-search--inline .select2-search__field:focus,
1697:                 %1$s-select2.select2-container--quform .select2-results__option,
1698:                 %1$s-select2.quform-timepicker.k-popup ul.k-list li.k-item,
1699:                 %1$s-select2.quform-timepicker.k-popup ul.k-list li.k-item.k-state-hover',
1700:             'elementDescription' => '%s .quform-description',
1701:             'elementSpacer' => '%1$s .quform-spacer,
1702: %1$s .quform-element-group.quform-group-style-bordered > .quform-spacer,
1703: %1$s .quform-group-style-bordered > .quform-child-elements',
1704:             'elementSubLabel' => '%s .quform-sub-label',
1705:             'options' => '%s .quform-options',
1706:             'option' => '%s .quform-option',
1707:             'optionRadioButton' => '%s .quform-option .quform-field-radio',
1708:             'optionCheckbox' => '%s .quform-option .quform-field-checkbox',
1709:             'optionLabel' => '%1$s .quform-options .quform-option .quform-option-label',
1710:             'optionLabelHover' => '%1$s .quform-options .quform-option .quform-option-label:hover',
1711:             'optionLabelSelected' => '%1$s .quform-options .quform-option .quform-field:checked + .quform-option-label',
1712:             'optionIcon' => '%1$s .quform-option .quform-option-icon',
1713:             'optionIconSelected' => '%1$s .quform-option .quform-option-icon-selected',
1714:             'optionText' => '%1$s .quform-option .quform-option-text',
1715:             'optionTextSelected' => '%1$s .quform-option .quform-field:checked + .quform-option-label .quform-option-text',
1716:             'elementError' => '%s .quform-error',
1717:             'elementErrorInner' => '%s .quform-error > .quform-error-inner',
1718:             'elementErrorText' => '%s .quform-error > .quform-error-inner > .quform-error-text',
1719:             'page' => '%s .quform-element-page',
1720:             'pageTitle' => '%s .quform-page-title',
1721:             'pageDescription' => '%s .quform-page-description',
1722:             'pageElements' => '%s .quform-element-page > .quform-child-elements',
1723:             'group' => '%s .quform-element-group',
1724:             'groupTitle' => '%s .quform-spacer > .quform-group-title-description quform-group-title',
1725:             'groupDescription' => '%s .quform-spacer > .quform-group-title-description p.quform-group-description',
1726:             'groupElements' => '%s .quform-element-group > .quform-spacer > .quform-child-elements',
1727:             'pageProgress' => '%s .quform-page-progress',
1728:             'pageProgressBar' => '%s .quform-page-progress-bar',
1729:             'pageProgressBarText' => '%s .quform-page-progress-text',
1730:             'pageProgressTabs' => '%s .quform-page-progress-tabs',
1731:             'pageProgressTab' => '%s .quform-page-progress-tab',
1732:             'pageProgressTabActive' => '%s .quform-page-progress-tab.quform-current-tab',
1733:             'submit' => '%s .quform-element-submit',
1734:             'submitInner' => '%s .quform-button-submit',
1735:             'submitButton' => '%1$s .quform-button-submit button, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button',
1736:             'submitButtonHover' => '%1$s .quform-button-submit button:hover, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button:hover',
1737:             'submitButtonActive' => '%1$s .quform-button-submit button:active, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button:active',
1738:             'submitButtonText' => '%1$s .quform-button-submit button .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button .quform-button-text',
1739:             'submitButtonTextHover' => '%1$s .quform-button-submit button:hover .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button:hover .quform-button-text',
1740:             'submitButtonTextActive' => '%1$s .quform-button-submit button:active .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button:active .quform-button-text',
1741:             'submitButtonIcon' => '%1$s .quform-button-submit button .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button .quform-button-icon',
1742:             'submitButtonIconHover' => '%1$s .quform-button-submit button:hover .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button:hover .quform-button-icon',
1743:             'submitButtonIconActive' => '%1$s .quform-button-submit button:active .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-submit button:active .quform-button-icon',
1744:             'backInner' => '%s .quform-button-back',
1745:             'backButton' => '%1$s .quform-button-back button, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button',
1746:             'backButtonHover' => '%1$s .quform-button-back button:hover, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button:hover',
1747:             'backButtonActive' => '%1$s .quform-button-back button:active, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button:active',
1748:             'backButtonText' => '%1$s .quform-button-back button .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button .quform-button-text',
1749:             'backButtonTextHover' => '%1$s .quform-button-back button:hover .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button:hover .quform-button-text',
1750:             'backButtonTextActive' => '%1$s .quform-button-back button:active .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button:active .quform-button-text',
1751:             'backButtonIcon' => '%1$s .quform-button-back button .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button .quform-button-icon',
1752:             'backButtonIconHover' => '%1$s .quform-button-back button:hover .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button:hover .quform-button-icon',
1753:             'backButtonIconActive' => '%1$s .quform-button-back button:active .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-back button:active .quform-button-icon',
1754:             'nextInner' => '%s .quform-button-next',
1755:             'nextButton' => '%1$s .quform-button-next button, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button',
1756:             'nextButtonHover' => '%1$s .quform-button-next button:hover, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button:hover',
1757:             'nextButtonActive' => '%1$s .quform-button-next button:active, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button:active',
1758:             'nextButtonText' => '%1$s .quform-button-next button .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button .quform-button-text',
1759:             'nextButtonTextHover' => '%1$s .quform-button-next button:hover .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button:hover .quform-button-text',
1760:             'nextButtonTextActive' => '%1$s .quform-button-next button:active .quform-button-text, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button:active .quform-button-text',
1761:             'nextButtonIcon' => '%1$s .quform-button-next button .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button .quform-button-icon',
1762:             'nextButtonIconHover' => '%1$s .quform-button-next button:hover .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button:hover .quform-button-icon',
1763:             'nextButtonIconActive' => '%1$s .quform-button-next button:active .quform-button-icon, %1$s .quform-element-submit.quform-button-style-theme .quform-button-next button:active .quform-button-icon',
1764:             'uploadButton' => '%1$s .quform-upload-button, %1$s .quform-button-style-theme .quform-upload-button',
1765:             'uploadButtonHover' => '%1$s .quform-upload-button:hover, %1$s .quform-button-style-theme .quform-upload-button:hover',
1766:             'uploadButtonActive' => '%1$s .quform-upload-button:active, %1$s .quform-button-style-theme .quform-upload-button:active',
1767:             'uploadButtonText' => '%1$s .quform-upload-button .quform-upload-button-text, %1$s .quform-button-style-theme .quform-upload-button .quform-upload-button-text',
1768:             'uploadButtonTextHover' => '%1$s .quform-upload-button:hover .quform-upload-button-text, %1$s .quform-button-style-theme .quform-upload-button:hover .quform-upload-button-text',
1769:             'uploadButtonTextActive' => '%1$s .quform-upload-button:active .quform-upload-button-text, %1$s .quform-button-style-theme .quform-upload-button:active .quform-upload-button-text',
1770:             'uploadButtonIcon' => '%1$s .quform-upload-button .quform-upload-button-icon, %1$s .quform-button-style-theme .quform-upload-button .quform-upload-button-icon',
1771:             'uploadButtonIconHover' => '%1$s .quform-upload-button:hover .quform-upload-button-icon, %1$s .quform-button-style-theme .quform-upload-button:hover .quform-upload-button-icon',
1772:             'uploadButtonIconActive' => '%1$s .quform-upload-button:active .quform-upload-button-icon, %1$s .quform-button-style-theme .quform-upload-button:active .quform-upload-button-icon',
1773:             'uploadDropzone' => '%1$s .quform-upload-dropzone',
1774:             'uploadDropzoneHover' => '%1$s .quform-upload-dropzone:hover',
1775:             'uploadDropzoneActive' => '%1$s .quform-upload-dropzone:active',
1776:             'uploadDropzoneText' => '%1$s .quform-upload-dropzone .quform-upload-dropzone-text',
1777:             'uploadDropzoneTextHover' => '%1$s .quform-upload-dropzone:hover .quform-upload-dropzone-text',
1778:             'uploadDropzoneTextActive' => '%1$s .quform-upload-dropzone:active .quform-upload-dropzone-text',
1779:             'uploadDropzoneIcon' => '%1$s .quform-upload-dropzone .quform-upload-dropzone-icon',
1780:             'uploadDropzoneIconHover' => '%1$s .quform-upload-dropzone:hover .quform-upload-dropzone-icon',
1781:             'uploadDropzoneIconActive' => '%1$s .quform-upload-dropzone:active .quform-upload-dropzone-icon',
1782:             'datepickerHeader' => '%1$s-datepicker.quform-datepicker .k-calendar .k-header, %1$s-datepicker.quform-datepicker .k-calendar .k-header .k-state-hover',
1783:             'datepickerHeaderText' => '%s-datepicker.quform-datepicker .k-calendar .k-header .k-link',
1784:             'datepickerHeaderTextHover' => '%s-datepicker.quform-datepicker .k-calendar .k-header .k-link:hover',
1785:             'datepickerFooter' => '%s-datepicker.quform-datepicker .k-calendar .k-footer',
1786:             'datepickerFooterText' => '%s-datepicker.quform-datepicker .k-calendar .k-footer .k-link',
1787:             'datepickerFooterTextHover' => '%s-datepicker.quform-datepicker .k-calendar .k-footer .k-link:hover',
1788:             'datepickerSelectionText' => '%s-datepicker.quform-datepicker .k-calendar td.k-state-focused .k-link',
1789:             'datepickerSelectionTextHover' => '%s-datepicker.quform-datepicker .k-calendar td.k-state-focused .k-link:hover',
1790:             'datepickerSelectionActiveText' => '%s-datepicker.quform-datepicker .k-calendar td.k-state-selected.k-state-focused .k-link',
1791:             'datepickerSelectionActiveTextHover' => '%s-datepicker.quform-datepicker .k-calendar td.k-state-selected.k-state-focused .k-link:hover',
1792:             'datepickerSelection' => '%s-datepicker.quform-datepicker .k-calendar td.k-state-focused',
1793:             'datepickerSelectionActive' => '%s-datepicker.quform-datepicker .k-calendar td.k-state-selected.k-state-focused',
1794:         );
1795:     }
1796: 
1797:     /**
1798:      * Get the CSS selector for the given style type
1799:      *
1800:      * @param   string  $type
1801:      * @return  string
1802:      */
1803:     protected function getCssSelector($type)
1804:     {
1805:         $selector = '';
1806:         $selectors = $this->getCssSelectors();
1807: 
1808:         if (array_key_exists($type, $selectors)) {
1809:             $prefix = sprintf('.quform-%d', $this->getId());
1810:             $selector = sprintf($selectors[$type], $prefix);
1811:         }
1812: 
1813:         return $selector;
1814:     }
1815: 
1816:     /**
1817:      * Does the form have at least one File Upload element with enhanced upload enabled?
1818:      *
1819:      * @return boolean
1820:      */
1821:     public function hasEnhancedFileUploadElement()
1822:     {
1823:         foreach ($this->getRecursiveIterator() as $element) {
1824:             if ($element instanceof Quform_Element_File && $element->config('enhancedUploadEnabled')) {
1825:                 return true;
1826:             }
1827:         }
1828: 
1829:         return false;
1830:     }
1831: 
1832:     /**
1833:      * Sets whether the form been successfully submitted
1834:      *
1835:      * @param boolean $flag
1836:      */
1837:     public function setSubmitted($flag)
1838:     {
1839:         $this->submitted = (bool) $flag;
1840:     }
1841: 
1842:     /**
1843:      * Has the form been successfully submitted?
1844:      *
1845:      * @return boolean
1846:      */
1847:     public function isSubmitted()
1848:     {
1849:         return $this->submitted;
1850:     }
1851: 
1852:     /**
1853:      * Reset all form values to their default
1854:      */
1855:     public function reset()
1856:     {
1857:         foreach ($this->getRecursiveIterator() as $element) {
1858:             if ( ! $element instanceof Quform_Element_Field) {
1859:                 continue;
1860:             }
1861: 
1862:             switch ($this->getConfirmation()->config('resetForm')) {
1863:                 default:
1864:                 case '':
1865:                     $element->reset();
1866:                     break;
1867:                 case 'clear':
1868:                     $element->setValue($element->getEmptyValue());
1869:                     break;
1870:                 case 'keep':
1871:                     /* Do nothing */
1872:                     break;
1873:             }
1874:         }
1875: 
1876:         $this->setCurrentPageById($this->getFirstPage()->getId());
1877:     }
1878: 
1879:     /**
1880:      * Sets the dynamic values
1881:      *
1882:      * @param string|array $dynamicValues
1883:      */
1884:     public function setDynamicValues($dynamicValues)
1885:     {
1886:         if (is_string($dynamicValues)) {
1887:             parse_str($dynamicValues, $dynamicValues);
1888:         }
1889: 
1890:         $this->dynamicValues = $dynamicValues;
1891:     }
1892: 
1893:     /**
1894:      * Get the dynamic values
1895:      *
1896:      * @return array
1897:      */
1898:     public function getDynamicValues()
1899:     {
1900:         return $this->dynamicValues;
1901:     }
1902: 
1903:     /**
1904:      * Server-side conditional logic processing, determines which form elements are hidden by conditional logic
1905:      */
1906:     public function calculateElementVisibility()
1907:     {
1908:         $ancestors = array();
1909:         $currentPage = null;
1910:         $this->calculateElementVisibilityHelper($this->pages, $ancestors, $currentPage);
1911:     }
1912: 
1913:     /**
1914:      * Recursive helper function for calculateElementVisibility
1915:      *
1916:      * @param  array                     $elements
1917:      * @param  array                     $ancestors
1918:      * @param  Quform_Element_Page|null  $currentPage
1919:      */
1920:     protected function calculateElementVisibilityHelper($elements, &$ancestors, &$currentPage)
1921:     {
1922:         foreach ($elements as $element) {
1923:             if ( ! $element instanceof Quform_Element_Field && ! $element instanceof Quform_Element_Container && ! $element instanceof Quform_Element_Html) {
1924:                 // Skip non-fields and containers
1925:                 continue;
1926:             }
1927: 
1928:             $isConditionallyHidden = false;
1929:             $hasNonVisibleAncestor = false;
1930:             $elementIsEmpty = $element->isEmpty();
1931: 
1932:             if ($elementIsEmpty && $element instanceof Quform_Element_File) {
1933:                 // Check for uploaded files pending processing
1934:                 $elementName = $element->getName();
1935: 
1936:                 if (
1937:                     array_key_exists($elementName, $_FILES) &&
1938:                     is_array($_FILES[$elementName]) &&
1939:                     array_key_exists('error', $_FILES[$elementName]) &&
1940:                     is_array($_FILES[$elementName]['error'])
1941:                 ) {
1942:                     foreach ($_FILES[$elementName]['error'] as $error) {
1943:                         if ($error == UPLOAD_ERR_OK) {
1944:                             $elementIsEmpty = false;
1945:                             break;
1946:                         }
1947:                     }
1948:                 }
1949:             }
1950: 
1951:             // Check if this element is hidden by its ancestor's conditional logic rules
1952:             foreach ($ancestors as $ancestor) {
1953:                 if ($ancestor->isConditionallyHidden()) {
1954:                     $isConditionallyHidden = true;
1955:                 }
1956: 
1957:                 if ( ! $ancestor->isVisible()) {
1958:                     $hasNonVisibleAncestor = true;
1959:                 }
1960: 
1961:                 if ( ! $elementIsEmpty) {
1962:                     $ancestor->setHasNonEmptyChild(true);
1963:                 }
1964:             }
1965: 
1966:             // If this page is hidden, so are all elements inside it
1967:             if ( ! $element instanceof Quform_Element_Page && $currentPage instanceof Quform_Element_Page && $currentPage->isConditionallyHidden()) {
1968:                 $isConditionallyHidden = true;
1969:             }
1970: 
1971:             // Calculate the visibility based on the logic rules
1972:             if ( ! $isConditionallyHidden && $element->config('logicEnabled') && count($element->config('logicRules'))) {
1973:                 if ( ! $this->checkLogicAction($element->config('logicAction'), $element->config('logicMatch'), $element->config('logicRules'))) {
1974:                     $isConditionallyHidden = true;
1975:                 }
1976:             }
1977: 
1978:             // Set the flags on the parent groups if we have a visible or non-empty element
1979:             foreach ($ancestors as $ancestor) {
1980:                 if ( ! $isConditionallyHidden) {
1981:                     $ancestor->setHasVisibleChild(true);
1982:                 }
1983:             }
1984: 
1985:             // Set the flag that this element is conditionally hidden, either by its own rules or by parent group rules
1986:             $element->setConditionallyHidden($isConditionallyHidden);
1987:             $element->setHasNonVisibleAncestor($hasNonVisibleAncestor);
1988: 
1989:             if ($element instanceof Quform_Element_Page) {
1990:                 $currentPage = $element;
1991:             }
1992: 
1993:             if ($element instanceof Quform_Element_Group || $element instanceof Quform_Element_Row || $element instanceof Quform_Element_Column) {
1994:                 array_push($ancestors, $element);
1995:                 $this->calculateElementVisibilityHelper($element->getElements(), $ancestors, $currentPage);
1996:                 array_pop($ancestors);
1997:             }
1998:         }
1999:     }
2000: 
2001:     /**
2002:      * Set the current page to the one with the given ID
2003:      *
2004:      * @param int $pageId
2005:      */
2006:     public function setCurrentPageById($pageId)
2007:     {
2008:         foreach ($this->pages as $pages) {
2009:             if ($pages->getId() == $pageId) {
2010:                 $this->currentPage = $pages;
2011:             }
2012:         }
2013:     }
2014: 
2015:     /**
2016:      * Get the current page being viewed/processed
2017:      *
2018:      * If no page is found it's set to the first page
2019:      *
2020:      * @return Quform_Element_Page
2021:      */
2022:     public function getCurrentPage()
2023:     {
2024:         if ( ! $this->currentPage) {
2025:             $this->currentPage = $this->getFirstPage();
2026:         }
2027: 
2028:         return $this->currentPage;
2029:     }
2030: 
2031:     /**
2032:      * Get the ID of the next page that isn't conditionally hidden
2033:      *
2034:      * @param   bool      $reverse  If true it will search in reverse and get the previous page
2035:      * @return  int|null            The ID of the next page or null if none found
2036:      */
2037:     public function getNextPageId($reverse = false)
2038:     {
2039:         $pages = $reverse ? array_reverse($this->pages, true) : $this->pages;
2040:         $currentPage = $this->getCurrentPage();
2041:         $foundCurrentPage = false;
2042: 
2043:         foreach ($pages as $page) {
2044:             if ($foundCurrentPage && ! $page->isConditionallyHidden()) {
2045:                 return $page->getId();
2046:             }
2047: 
2048:             if ($page == $currentPage) {
2049:                 $foundCurrentPage = true;
2050:             }
2051:         }
2052: 
2053:         return null;
2054:     }
2055: 
2056:     /**
2057:      * Get the total number of pages
2058:      *
2059:      * @return integer
2060:      */
2061:     public function getPageCount()
2062:     {
2063:         return count($this->pages);
2064:     }
2065: 
2066:     /**
2067:      * Does the form have more than one page?
2068:      *
2069:      * @return boolean
2070:      */
2071:     public function hasPages()
2072:     {
2073:         return $this->getPageCount() > 1;
2074:     }
2075: 
2076:     /**
2077:      * Get the array of page IDs
2078:      *
2079:      * Send to the JavaScript to determine progress percentage
2080:      *
2081:      * @return array
2082:      */
2083:     protected function getPageIds()
2084:     {
2085:         $pageIds = array();
2086: 
2087:         foreach ($this->pages as $page) {
2088:             $pageIds[] = $page->getId();
2089:         }
2090: 
2091:         return $pageIds;
2092:     }
2093: 
2094:     /**
2095:      * Set the current submitted entry ID
2096:      *
2097:      * @param   int|null     $entryId
2098:      * @return  Quform_Form
2099:      */
2100:     public function setEntryId($entryId)
2101:     {
2102:         $this->entryId = $entryId;
2103: 
2104:         return $this;
2105:     }
2106: 
2107:     /**
2108:      * Get the current submitted entry ID
2109:      *
2110:      * @return int|null
2111:      */
2112:     public function getEntryId()
2113:     {
2114:         return $this->entryId;
2115:     }
2116: 
2117:     /**
2118:      * Check the given logic data against the form data and return the action
2119:      *
2120:      * @param   string  $action
2121:      * @param   string  $match
2122:      * @param   array   $rules
2123:      * @return  bool
2124:      */
2125:     public function checkLogicAction($action, $match, array $rules)
2126:     {
2127:         $matches = 0;
2128:         $ruleCount = count($rules);
2129: 
2130:         for ($i = 0; $i < $ruleCount; $i++) {
2131:             if ($this->isLogicRuleMatch($rules[$i])) {
2132:                 $matches++;
2133:             }
2134:         }
2135: 
2136:         if ( ! (($match == 'any' && $matches > 0) || ($match == 'all' && $matches == $ruleCount))) {
2137:             // Invert the action, the rules don't match
2138:             $action = ! $action;
2139:         }
2140: 
2141:         return $action;
2142:     }
2143: 
2144:     /**
2145:      * Does the given logic rule pass with the current form data?
2146:      *
2147:      * @param   array  $rule
2148:      * @return  bool
2149:      */
2150:     protected function isLogicRuleMatch(array $rule)
2151:     {
2152:         $element = $this->getElementById($rule['elementId']);
2153: 
2154:         if ( ! $element instanceof Quform_Element_Field) {
2155:             return false;
2156:         }
2157: 
2158:         return $element->isLogicRuleMatch($rule);
2159:     }
2160: 
2161:     /**
2162:      * Get the first page of the form
2163:      *
2164:      * @return Quform_Element_Page|null
2165:      */
2166:     public function getFirstPage()
2167:     {
2168:         return reset($this->pages);
2169:     }
2170: 
2171:     /**
2172:      * Get the last page of the form
2173:      *
2174:      * @return Quform_Element_Page|null
2175:      */
2176:     public function getLastPage()
2177:     {
2178:         return end($this->pages);
2179:     }
2180: 
2181:     /**
2182:      * Get the session storage key for this form
2183:      *
2184:      * @return string
2185:      */
2186:     public function getSessionKey()
2187:     {
2188:         return 'quform-' . $this->getUniqueId();
2189:     }
2190: 
2191:     /**
2192:      * Get the CSRF protection token
2193:      *
2194:      * @return string
2195:      */
2196:     public function getCsrfToken()
2197:     {
2198:         return $this->session->getToken();
2199:     }
2200: 
2201:     /**
2202:      * Get the recursive iterator to iterate over the form elements
2203:      *
2204:      * Modes:
2205:      * RecursiveIteratorIterator::LEAVES_ONLY
2206:      * RecursiveIteratorIterator::SELF_FIRST
2207:      * RecursiveIteratorIterator::CHILD_FIRST
2208:      * RecursiveIteratorIterator::CATCH_GET_CHILD
2209:      *
2210:      * @param  int $mode
2211:      * @return RecursiveIteratorIterator
2212:      */
2213:     public function getRecursiveIterator($mode = RecursiveIteratorIterator::LEAVES_ONLY)
2214:     {
2215:         return new RecursiveIteratorIterator(
2216:             new Quform_Form_Iterator($this),
2217:             $mode
2218:         );
2219:     }
2220: 
2221:     /**
2222:      * Is the given unique ID valid?
2223:      *
2224:      * @param   string  $id
2225:      * @return  bool
2226:      */
2227:     public static function isValidUniqueId($id)
2228:     {
2229:         return is_string($id) && preg_match('/^[a-f0-9]{6}$/', $id);
2230:     }
2231: 
2232:     /**
2233:      * Generate a unique ID (6 digit hex)
2234:      *
2235:      * @return string
2236:      */
2237:     public static function generateUniqueId()
2238:     {
2239:         return sprintf('%06x', mt_rand(0, 0xffffff));
2240:     }
2241: 
2242:     /**
2243:      * @param   string  $key
2244:      * @param   string  $default
2245:      * @return  string
2246:      */
2247:     public function getTranslation($key, $default = '')
2248:     {
2249:         $string = $this->config($key);
2250: 
2251:         if (Quform::isNonEmptyString($string)) {
2252:             return $string;
2253:         }
2254: 
2255:         return $default;
2256:     }
2257: 
2258:     /**
2259:      * Get the default form configuration
2260:      *
2261:      * @param   string|null  $key  Get the config by key, if omitted the full config is returned
2262:      * @return  array
2263:      */
2264:     public static function getDefaultConfig($key = null)
2265:     {
2266:         $config = apply_filters('quform_default_config_form', array(
2267:             // General - Form
2268:             'name' => '',
2269:             'title' => '',
2270:             'titleTag' => 'h2',
2271:             'description' => '',
2272:             'active' => true,
2273:             'inactiveMessage' => '',
2274:             'trashed' => false,
2275:             'ajax' => true,
2276:             'saveEntry' => true,
2277:             'honeypot' => true,
2278:             'logicAnimation' => true,
2279: 
2280:             // General - Limits
2281:             'oneEntryPerUser' => false,
2282:             'oneEntryPer' => 'logged-in-user',
2283:             'limitEntries' => false,
2284:             'entryLimit' => '',
2285:             'entryLimitReachedMessage' => sprintf('<p>%s</p>', esc_html__('This form is currently closed for submissions.', 'quform')),
2286: 
2287:             // General - Scheduling
2288:             'enableSchedule' => false,
2289:             'scheduleStart' => '',
2290:             'scheduleStartMessage' => sprintf('<p>%s</p>', esc_html__('This form is not yet open for submissions.', 'quform')),
2291:             'scheduleEnd' => '',
2292:             'scheduleEndMessage' => sprintf('<p>%s</p>', esc_html__('This form is currently closed for submissions.', 'quform')),
2293: 
2294:             // Style - Global
2295:             'theme' => '',
2296:             'themePrimaryColor' => '',
2297:             'themeSecondaryColor' => '',
2298:             'themePrimaryForegroundColor' => '',
2299:             'themeSecondaryForegroundColor' => '',
2300:             'responsiveElements' => 'phone-landscape',
2301:             'responsiveElementsCustom' => '',
2302:             'responsiveColumns' => 'phone-landscape',
2303:             'responsiveColumnsCustom' => '',
2304:             'verticalElementSpacing' => '',
2305:             'width' => '',
2306:             'position' => '',
2307:             'previewColor' => '',
2308:             'styles' => array(),
2309: 
2310:             // Style - Labels
2311:             'labelTextColor' => '',
2312:             'labelPosition' => '',
2313:             'labelWidth' => '150px',
2314:             'requiredText' => '*',
2315:             'requiredTextColor' => '',
2316: 
2317:             // Style - Fields
2318:             'fieldSize' => '',
2319:             'fieldWidth' => '',
2320:             'fieldWidthCustom' => '',
2321:             'fieldBackgroundColor' => '',
2322:             'fieldBackgroundColorHover' => '',
2323:             'fieldBackgroundColorFocus' => '',
2324:             'fieldBorderColor' => '',
2325:             'fieldBorderColorHover' => '',
2326:             'fieldBorderColorFocus' => '',
2327:             'fieldTextColor' => '',
2328:             'fieldTextColorHover' => '',
2329:             'fieldTextColorFocus' => '',
2330:             'fieldPlaceholderStyles' => '',
2331: 
2332:             // Style - Buttons
2333:             'buttonStyle' => 'theme',
2334:             'buttonSize' => '',
2335:             'buttonWidth' => '',
2336:             'buttonWidthCustom' => '',
2337:             'buttonAnimation' => '',
2338:             'buttonBackgroundColor' => '',
2339:             'buttonBackgroundColorHover' => '',
2340:             'buttonBackgroundColorActive' => '',
2341:             'buttonBorderColor' => '',
2342:             'buttonBorderColorHover' => '',
2343:             'buttonBorderColorActive' => '',
2344:             'buttonTextColor' => '',
2345:             'buttonTextColorHover' => '',
2346:             'buttonTextColorActive' => '',
2347:             'buttonIconColor' => '',
2348:             'buttonIconColorHover' => '',
2349:             'buttonIconColorActive' => '',
2350:             'submitType' => 'default',
2351:             'submitText' => '',
2352:             'submitIcon' => '',
2353:             'submitIconPosition' => 'right',
2354:             'submitImage' => '',
2355:             'submitHtml' => '',
2356:             'nextType' => 'default',
2357:             'nextText' => '',
2358:             'nextIcon' => '',
2359:             'nextIconPosition' => 'right',
2360:             'nextImage' => '',
2361:             'nextHtml' => '',
2362:             'backType' => 'default',
2363:             'backText' => '',
2364:             'backIcon' => '',
2365:             'backIconPosition' => 'left',
2366:             'backImage' => '',
2367:             'backHtml' => '',
2368:             'backLocation' => '',
2369:             'optionsStyle' => '',
2370:             'optionsButtonStyle' => '',
2371:             'optionsButtonSize' => '',
2372:             'optionsButtonWidth' => '',
2373:             'optionsButtonWidthCustom' => '',
2374:             'optionsButtonIconPosition' => '',
2375: 
2376:             // Style - Pages
2377:             'pageProgressType' => 'numbers',
2378: 
2379:             // Style - Loading
2380:             'loadingType' => 'spinner-1',
2381:             'loadingCustom' => '',
2382:             'loadingPosition' => 'left',
2383:             'loadingColor' => '',
2384:             'loadingOverlay' => false,
2385:             'loadingOverlayColor' => '',
2386: 
2387:             // Style - Tooltips
2388:             'tooltipsEnabled' => true,
2389:             'tooltipType' => 'icon',
2390:             'tooltipEvent' => 'hover',
2391:             'tooltipIcon' => 'qicon-question-circle',
2392:             'tooltipStyle' => 'qtip-quform-dark',
2393:             'tooltipCustom' => '',
2394:             'tooltipMy' => 'left center',
2395:             'tooltipAt' => 'right center',
2396:             'tooltipShadow' => true,
2397:             'tooltipRounded' => false,
2398:             'tooltipClasses' => 'qtip-quform-dark qtip-shadow',
2399: 
2400:             // Notifications
2401:             'notifications' => array(),
2402:             'nextNotificationId' => 1,
2403: 
2404:             // Confirmations
2405:             'confirmations' => array(),
2406:             'nextConfirmationId' => 1,
2407: 
2408:             // Errors
2409:             'errorsPosition' => '',
2410:             'errorsIcon' => '',
2411:             'errorEnabled' => false,
2412:             'errorTitle' => '',
2413:             'errorContent' => '',
2414: 
2415:             // Language
2416:             'locale' => '',
2417:             'rtl' => 'global',
2418:             'dateFormatJs' => '',
2419:             'timeFormatJs' => '',
2420:             'dateTimeFormatJs' => '',
2421:             'dateFormat' => '',
2422:             'timeFormat' => '',
2423:             'dateTimeFormat' => '',
2424:             'messageRequired' => '',
2425:             'pageProgressNumbersText' => '',
2426:             'onlyOneSubmissionAllowed' => '',
2427:             'thisFormIsCurrentlyClosed' => '',
2428:             'formIsNotYetOpenForSubmissions' => '',
2429:             'formIsNoLongerOpenForSubmissions' => '',
2430: 
2431:             // Database
2432:             'databaseEnabled' => false,
2433:             'databaseWordpress' => true,
2434:             'databaseHost' => 'localhost',
2435:             'databaseUsername' => '',
2436:             'databasePassword' => '',
2437:             'databaseDatabase' => '',
2438:             'databaseTable' => '',
2439:             'databaseColumns' => array(),
2440: 
2441:             // Feature cache & locales
2442:             'hasDatepicker' => false,
2443:             'hasTimepicker' => false,
2444:             'hasEnhancedUploader' => false,
2445:             'hasEnhancedSelect' => false,
2446:             'locales' => array(),
2447: 
2448:             // Elements
2449:             'elements' => array(),
2450:             'nextElementId' => 1,
2451: 
2452:             // Misc
2453:             'entriesTableColumns' => array(),
2454:             'environment' => 'frontend'
2455:         ));
2456: 
2457:         if (Quform::isNonEmptyString($key)) {
2458:             return Quform::get($config, $key);
2459:         }
2460: 
2461:         return $config;
2462:     }
2463: }
2464: 
API documentation generated by ApiGen