﻿/*****************

  (c) 2006 Q42 B.V.

  The contents of this file, partially or in whole, may not be reproduced
  without prior written permission by Q42 B.V.

*****************/

// 2.1.250806

// Some generic string methods
String.prototype.capitalize = function(){return this.charAt(0).toUpperCase()+this.substr(1).toLowerCase();}
String.prototype.trim = function(){return this.replace(/^\s+|\s+$/g,"").replace(/\s[\s]+/g, ' ');}

function FormValidator(form, options)
{
  this.__setup(form, options);
}

FormValidator.prototype = {

  form: null,
  options: {
    messages: {
      header: "\nPlease correct the following before continuing:\n\n",
      url: " must be a valid http, https, ftp or ip address",
      email: " must be a valid email address",
      compareEmailAddresses: " must match the entered email address",
      number: " must be a number",
      dateDMY: " must be a valid date (dd-MM-yyyy)",
      birthdate: " must be a valid birthdate (dd-MM-yyyy)",
      birthdate18: " must be a over 18 years old",
      already_exists: " already exists",
      required: " is required"
    },
    validateTypes: {
      required: {
        matchAgainstClassName: /required/,
        matchAgainstValue: /.+/
      },
      unrequired: {
        matchAgainstClassName: /unrequired/,
        matchAgainstValue: function(value) {
          return true;
        }
      },
      dateDMY: {
        matchAgainstClassName: /dateDMY/,
        matchAgainstValue: function(value) {
          //Only characters 0-9 allowed
          var m = value.match(/[^0-9 -]/g);
          if (m != null) return false;

          m = value.match(/\d+/g);
          var valid = true;
          if (m != null) {
            var date = new Date(m[2], m[1] - 1, m[0]);
            if (date.getFullYear() != m[2] || date.getMonth() != m[1] - 1 || date.getDate() != m[0])
              valid = false;
          }
          else
            valid = false;
          return valid;
        }
      },
      birthdate: {
        matchAgainstClassName: /birthdate/,
        matchAgainstValue: function(value) {
          //Allow empty input
          if (value == '') return true;

          //Only characters 0-9 allowed
          var m = value.match(/[^0-9 -]/g);
          if (m != null) return false;

          m = value.match(/\d+/g);
          var valid = true;
          if (m != null) {
            var date = new Date(m[2], m[1] - 1, m[0]);
            if (date.getFullYear() != m[2] || date.getMonth() != m[1] - 1 || date.getDate() != m[0])
              valid = false;

            var today = new Date();
            var minDate = new Date(today.getFullYear() - 100, today.getMonth(), today.getDate());

            if (date < minDate || date > today) valid = false;
          }
          else
            valid = false;
          return valid;
        }
      },
      birthdate18: {
        matchAgainstClassName: /birthdate18/,
        matchAgainstValue: function(value) {
          //Only return 'invalid' if date is OK and date less then 18 years fro now
          //Date itself must me validated using 'birthdate'
          //Allow empty input
          if (value == '') return true;

          //Only characters 0-9 allowed
          var m = value.match(/[^0-9 -]/g);
          if (m != null) return true;

          m = value.match(/\d+/g);
          if (m != null) {
            var valid = true;
            var date = new Date(m[2], m[1] - 1, m[0]);
            if (date.getFullYear() != m[2] || date.getMonth() != m[1] - 1 || date.getDate() != m[0])
              valid = false;

            var today = new Date();
            var minDate = new Date(today.getFullYear() - 100, today.getMonth(), today.getDate());

            if (date < minDate || date > today) valid = false;
            if (valid) {
              var toDayMinus18Year = new Date();
              var substract = 0;

              if (toDayMinus18Year.getMonth() == 1 && toDayMinus18Year.getDate() == 29) substract = 1;
              toDayMinus18Year.setFullYear(toDayMinus18Year.getFullYear() - 18, toDayMinus18Year.getMonth(), toDayMinus18Year.getDate() - substract);
              if (date > toDayMinus18Year) return false;
            }
          }
          return true;
        }
      },
      dateDay: {
        matchAgainstClassName: /dateDay/,
        matchAgainstValue: function(value) {
          return !(isNaN(value) || value < 1 || value > 31);
        }
      },
      dateMonth: {
        matchAgainstClassName: /dateMonth/,
        matchAgainstValue: function(value) {
          return !(isNaN(value) || value < 1 || value > 12);
        }
      },
      dateYear: {
        matchAgainstClassName: /dateYear/,
        matchAgainstValue: function(value) {
          return !(isNaN(value) || value > new Date().getFullYear() || value.length != 4);
        }
      },
      email: {
        matchAgainstClassName: /email/,
        // Old Pattern ^[A-z0-9]([_\w\+\-]*)(\.[_\w\+\-]+)*@([\w\-]+)+(\.[\w\-]+)*(\.[A-z0-9]{2,6})$
        // CRM Pattern ^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
        matchAgainstValue: /^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
      },
      compareEmailAddresses: {
        matchAgainstClassName: /compareEmailAddress/,
        matchAgainstValue: function(value) {
          return value == document.getElementById('booker-emailaddress').value;
        }
      },
      url: {
        matchAgainstClassName: /url/,
        matchAgainstValue: /^(http|https|ftp)\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)?((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.[a-zA-Z]{2,4})(\:[0-9]+)?(\/[^\/][a-zA-Z0-9\.\,\?\'\\\/\+&%\$#\=~_\-@]*)*\/?$/
      },
      number: { // deprecated
        matchAgainstClassName: /number/,
        matchAgainstValue: /([0-9]*\.)?[0-9]|^$/
      },
      numberLoose: {
        matchAgainstClassName: /numberLoose/,
        matchAgainstValue: /([0-9]*\.)?[0-9]|^$/
      },
      numberStrict: {
        matchAgainstClassName: /numberStrict/,
        matchAgainstValue: function(value) {
          // if the string contains anything that isn't a number, valid = false
          var valid = true;
          if (isNaN(value))
            valid = false;
          return valid;
        }
      },
      numberPhone: {
        matchAgainstClassName: /numberPhone/,
        matchAgainstValue: function(value) {
          // if the string contains anything that isn't a number, valid = false
          var valid = true;
          if (isNaN(value))
            valid = false;
          return valid;
        }
      },
      numberPhoneRegEx: {
        matchAgainstClassName: /numberPhoneRegEx/,
        matchAgainstValue: function(value) {
          //Allow empty input
          if (value == '') return true;

          //Use same validation as in CRM
          var m = value.match(/^(\+\d+(\s|\-)+)?(\s?(\(\d+\))?(\d+))?(\s|\-){0,}((\d((\s|\-)+)?)+)?$/);
          //var m = value.match(/^((\+\d{1,3}(-| \s)?\(?\d\)?(-|\s)?\d{1,5})|(\(?\d{2,6}\)?))(-|\s)?(\d{3,4})(-|\s)?(\d{4})(( x| ext)\d{1,5}){0,1}$/);
          if (m != null) return true;
          return false;
        }
      },
      postcode: {
        matchAgainstClassName: /postcode/,
        matchAgainstValue: function(value) {
          var valid = true;
          if (value == '') return valid;

          var country = '';
          var countryCtrl = document.getElementsByName('land');
          if (countryCtrl.length == 0) {
            countryCtrl = document.getElementsByName('booker-country');
          }

          if (countryCtrl.length == 1) {
            country = countryCtrl[0].value;
          }

          if (country.toUpperCase() == "NL") {
            var m = value.replace(/\s/g, '').toUpperCase().match(/^[1-9][0-9][0-9][0-9][A-Z][A-Z]$/);
            if (m == null) valid = false;
          }

          return valid;
        }
      },
      inbetween: {
        matchAgainstClassName: /inbetween/,
        matchAgainstValue: function(value) {
          if (value == '') return true;

          var i = 0;
          var valid = false;
          var inbetweens = ['a', 'aan de', 'aan den', 'aan het', 'aan \'t', 'aan t', 'aan', 'af', 'al', 'am de', 'am', 'auf dem', 'auf den', 'auf der', 'auf ter',
'auf', 'aus dem', 'aus den', 'aus der', 'aus \'m', 'aus m', 'aus', 'ben', 'bij de', 'bij den', 'bij het', 'bij \'t', 'bij t', 'bij', 'bin', 'boven d', 'boven d\'',
'd', 'd\'', 'da', 'dal', 'dal\'', 'dalla', 'das', 'de die le', 'de die', 'de l', 'de l\'', 'de la', 'de las', 'de le', 'de van de', 'de', 'deca', 'degli', 'dei',
'del', 'della', 'den', 'der', 'des', 'di', 'die le', 'do', 'don', 'dos', 'du', 'el', 'het', 'i', 'im', 'in de', 'in den', 'in der', 'in het', 'in \'t', 'in t',
'in', 'l', 'l\'', 'la', 'las', 'le', 'les', 'lo', 'los', 'of', 'onder de', 'onder den', 'onder het', 'onder \'t', 'onder t', 'onder', 'op de', 'op den', 'op der',
'op gen', 'op het', 'op \'t', 'op t', 'op ten', 'op', 'over de', 'over den', 'over het', 'over \'t', 'over t', 'over', '\'s', 's', 's\'', '\'t', 't', 'te', 'ten',
'ter', 'tho', 'thoe', 'thor', 'to', 'toe', 'tot', 'uijt de', 'uijt den', 'uijt \'t', 'uijt te d', 'uijt ten', 'uijt', 'uit de', 'uit den', 'uit het', 'uit \'t',
'uit t', 'uit te de', 'uit ten', 'uit', 'unter', 'van de l', 'van de l\'', 'van de', 'van den', 'van der', 'van gen', 'van het', 'van la', 'van \'t', 'van t',
'van ter', 'van van d', 'van', 'ver', 'vom', 'von dem', 'von den', 'von der', 'von \'t', 'von t', 'von', 'voor de', 'voor den', 'voor in \'', 'voor in t',
'voor \'t', 'voor', 'vor der', 'vor', 'zu', 'zum', 'zur', 'zvan der'];

          for (i = 0; i < inbetweens.length; i++) {
            if (value == inbetweens[i]) {
              valid = true;
              break;
            }
          }

          return valid;
        }
      },
      already_exists: {
        matchAgainstClassName: /exists/,
        matchAgainstValue: null
      },
      homepage_name: {
        matchAgainstClassName: /name/,
        matchAgainstValue: function(value) {
          return value != resources.messages.homepage_newsletter_lastname && value != resources.messages.homepage_newsletter_initials;
        }
      }
    },
    prefix: "q42fv-",
    invalidClassName: "invalid",
    validClassName: "valid",
    fieldTypes: "input|textarea|select",
    overrideAddErrorText: false,
    alertHandler: null
  },

  __errors: [],

  __setup: function(form, options) {
    this.form = form;
    if (options)
      for (option in options)
      if (typeof this.options[option] != "undefined")
      this.options[option] = options[option];
  },

  handleSubmit: function(evt) {
    if (!this.__validate()) {
      if (evt.preventDefault)
        evt.preventDefault();
      return false;
    }
    return true;
  },

  __addErrorToField: function(field, errorType, errorMessageTemp, mustAddErrorText) {
    var errorMessage = (!errorMessageTemp) ? this.options.messages[errorType] : errorMessageTemp;
    var addErrorText = (typeof (mustAddErrorText) == "undefined") ? true : mustAddErrorText;

    // limit to one error message per field
    for (var i = 0; i < this.__errors.length; i++) {
      if (this.__errors[i].field == field || this.__errors[i].field.name == field.name) {
        if (this.__errors[i].message == errorMessage)
          return;
        else
          this.__errors.splice(i, 1);
      }
    }

    this.__errors.push({
      message: errorMessage,
      field: field
    })

    // dependant fields are fields that need to be coloured too depending on this fields validation result!
    if (field.dependantFields) {
      for (i = 0; i < field.dependantFields.length; i++) {
        this.__setInvalidClassName(field.dependantFields[i]);
      }
    }
    this.__setInvalidClassName(field);

    if (document.getElementById("booking-box") && booking && booking.addError) {
      var message = (!errorMessageTemp) ? field.title + " " + errorMessage : errorMessage
      booking.addError(field, message, addErrorText);
    }
  },

  __removeErrorFromField: function(field, isAllowedEmpty) {
    if (field.dependantFields) {
      for (i = 0; i < field.dependantFields.length; i++) {
        this.__removeInvalidClassName(field.dependantFields[i], isAllowedEmpty);
      }
    }
    this.__removeInvalidClassName(field, isAllowedEmpty);
    for (var i = 0; i < this.__errors.length; i++) {
      if (this.__errors[i].field == field)
        this.__errors.splice(i, 1);
    }

    if (document.getElementById("booking-box"))
      if (booking && booking.addError)
      booking.removeError(field.name);
  },

  __getFieldsByFieldTypes: function(fieldTypes) {
    var fields = [];
    var regex = new RegExp("^(" + fieldTypes + ")$");

    for (var i = 0; i < this.form.elements.length; i++)
      if (regex.test(this.form.elements[i].nodeName.toLowerCase()))
      fields.push(this.form.elements[i]);

    return fields;
  },

  __getValidateType: function(type) {
    if (typeof this.options.validateTypes[type] != "undefined")
      return this.options.validateTypes[type];
    else
      return null;
  },

  __getClassNames: function(field) {
    if (field.className.trim().length > 0)
      return field.className.trim().split(" ");
    else
      return [];
  },

  __hasClassName: function(field, classname) {
    return field.className == undefined ? false : new RegExp("\\b" + classname + "\\b").test(field.className);
  },

  __addClassName: function(field, name) {
    field = this.__getStylableNode(field);
    var hasClassName = this.__hasClassName(field, name);
    if (!hasClassName)
      field.className += (' ' + name);
  },

  __removeClassName: function(field, name) {
    field = this.__getStylableNode(field);
    var hasClassName = this.__hasClassName(field, name);
    if (hasClassName) {
      var classname = new RegExp("\\s?" + name + "\\b", 'g');
      field.className = field.className.replace(classname, '');
    }
  },

  __setInvalidClassName: function(field) {
    this.__removeClassName(field, this.options.prefix + this.options.validClassName);
    this.__addClassName(field, this.options.prefix + this.options.invalidClassName);

    var formrow2 = field.parentNode;
    if (this.__hasClassName(formrow2, "form-row")) {
      this.__removeClassName(formrow2, "form-row-" + this.options.prefix + this.options.validClassName);
      this.__addClassName(formrow2, "form-row-" + this.options.prefix + this.options.invalidClassName);
    }

    var formrow3 = field.parentNode.parentNode;
    if (this.__hasClassName(formrow3, "form-row")) {
      this.__removeClassName(formrow3, "form-row-" + this.options.prefix + this.options.validClassName);
      this.__addClassName(formrow3, "form-row-" + this.options.prefix + this.options.invalidClassName);
    }
  },

  __removeInvalidClassName: function(field, isAllowedEmpty) {
    var isEmpty = field.value == "";
    this.__removeClassName(field, this.options.prefix + this.options.invalidClassName);

    if (isEmpty && isAllowedEmpty)
      this.__removeClassName(field, this.options.prefix + this.options.validClassName);
    else
      this.__addClassName(field, this.options.prefix + this.options.validClassName);

    var formrow2 = field.parentNode;
    if (this.__hasClassName(formrow2, "form-row")) {
      this.__removeClassName(formrow2, "form-row-" + this.options.prefix + this.options.invalidClassName);
      if (isEmpty && isAllowedEmpty)
        this.__removeClassName(formrow2, "form-row-" + this.options.prefix + this.options.validClassName);
      else
        this.__addClassName(formrow2, "form-row-" + this.options.prefix + this.options.validClassName);
    }

    var formrow3 = field.parentNode.parentNode;
    if (this.__hasClassName(formrow3, "form-row")) {
      this.__removeClassName(formrow3, "form-row-" + this.options.prefix + this.options.invalidClassName);
      if (isEmpty && isAllowedEmpty)
        this.__removeClassName(formrow3, "form-row-" + this.options.prefix + this.options.validClassName);
      else
        this.__addClassName(formrow3, "form-row-" + this.options.prefix + this.options.validClassName);
    }
  },

  /*
  * Checks if the specified form field is stylable. 
  * If not: will walk up the node tree untill it finds an element with class "stylable" and thus can be styled.
  * Otherwise: will just return the original field.
  * If, during the walk up the tree, we find an element with class "stylableWidth" will use the offsetWidth of that
  *   element for the width of the stylable element.
  */
  __getStylableNode: function(field) {
    if (field.nodeName == "SELECT" || (field.nodeName == "INPUT" && field.type == "radio")) {
      var stylableElement = field;
      while (stylableElement.parentNode != undefined) {
        stylableElement = stylableElement.parentNode;
        if (stylableElement.tagName != undefined) {
          if (this.__hasClassName(stylableElement, "stylableWidth")) {
            var stylableWidthElement = stylableElement;
          }
          if (this.__hasClassName(stylableElement, "stylable")) {
            if (stylableWidthElement != undefined) {
              stylableElement.style.width = (((stylableWidthElement.offsetWidth != undefined) ? stylableWidthElement.offsetWidth : stylableWidthElement.width)) + "px";
            }
            return stylableElement;
          }
        }
      }
    }
    return field;
  },

  __determineFieldType: function(field) {
    var fieldType = null;
    var name = field.nodeName.toLowerCase();

    if (field.type && name == "input") {
      if (field.type == "hidden")
        fieldType = "text";
      else
        fieldType = field.type;
    }
    else if (name == "select") {
      if (field.type == "select-one")
        fieldType = "selectOne";
      if (field.type == "select-multiple")
        fieldType = "selectMultiple";
    }
    else {
      fieldType = name;
    }

    return fieldType;
  },

  __getFieldErrorName: function(field) {
    return field.title || field.alt || field.name || "";
  },

  __getErrorMsg: function(errors) {
    var errorMsg = this.options.messages.header + "\n";
    var doCapitalize = resources.messages.capitalize != 'false';

    for (i = 0; i < errors.length; i++) {
      errorMsg += (i + 1) + ". ";
      if (errors[i].field) {
        if (doCapitalize)
          errorMsg += this.__getFieldErrorName(errors[i].field).capitalize().replace("_", " ");
        else
          errorMsg += this.__getFieldErrorName(errors[i].field).replace("_", " ");
      }
      errorMsg += errors[i].message + "\n";
    }
    return resources.SpecialDecode(errorMsg);
  },

  __showErrors: function() {
    if (typeof this.options.alertHandler == "function")
      (this.options.alertHandler)(this.__errors);
    else
      this.__defaultShowErrors(this.__errors);
  },

  __defaultShowErrors: function(errors) {
    errorMsg = this.__getErrorMsg(errors);
    alert(errorMsg);
  },

  __validate: function() {
    var fields = this.__getFieldsByFieldTypes(this.options.fieldTypes);

    //Reset all errors
    this.__errors = [];

    for (f = 0, fields = this.__getFieldsByFieldTypes(this.options.fieldTypes); f < fields.length; f++)
      this.__validateOneField(fields[f]);

    if (this.__errors.length > 0) {
      this.__showErrors();
      this.__errors = [];
      return false;
    }

    return true;
  },

  __validateOneField: function(field, mayAllowEmpty, mustAddErrorText) {
    var isValid = true;
    var allowEmpty = (typeof (mayAllowEmpty) == "undefined") ? false : mayAllowEmpty;
    var addErrorText = (typeof (mustAddErrorText) == "undefined") ? true : mustAddErrorText;
    var mustBeValidated = field.value != "";
    var isAllowedEmpty = false;

    types = this.__getClassNames(field);
    if (types != null) {
      for (i = 0; i < types.length; i++) {
        if (types[i].indexOf(this.options.invalidClassName) > -1 || types[i].indexOf(this.options.validClassName) > -1)
          mustBeValidated = true;
        
        //Skip validation?
        if (types[i].indexOf('skipcheck') > -1) mustBeValidated = false;

        var vt = this.__getValidateType(types[i]);

        if (vt) {
          mustBeValidated = true;
          if (allowEmpty && vt.matchAgainstClassName.toString() == '/unrequired/') isAllowedEmpty = true;

          if (mustBeValidated && !this.__validateField(field, vt)) {
            if (allowEmpty && vt.matchAgainstClassName.toString() == '/required/') {
              isAllowedEmpty = true;
            }
            else {
              isValid = false;
              if (types[i] == 'birthdate18') addErrorText = true;
              this.__addErrorToField(field, types[i], null, this.options.overrideAddErrorText ? this.options.overrideAddErrorText : addErrorText);
            }
            break;
          }
        }
      }
    }

    if (mustBeValidated && isValid)
      this.__removeErrorFromField(field, isAllowedEmpty);

    return isValid;
  },

  __validateField: function(field, type) {
    var valueRegex = type.matchAgainstValue;
    var classRegex = type.matchAgainstClassName;
    var fieldType = this.__determineFieldType(field);
    return this["__validate" + fieldType.capitalize()](field, classRegex, valueRegex);
  },

  __validateSelectone: function(field, matchClass, matchValue) {
    // Select is either required or doesn't get tested. We're not going to expect class=email on a select tag!
    return !(field.selectedIndex < 0 || field.options[field.selectedIndex].value.length == 0);
  },

  __validateSelectmultiple: function(field, matchClass, matchValue) {

    // Todo: class that requires a certain amount or scope of options to be selected
    // Will need to overhaul validationTypes to allow for numbers in the classname
    // such as limit-1 or limit-1-5
    // Right now, all that would work is hardcoding that into the VT names

    var selectedOptions = [];

    for (var i = 0; i < field.length; i++) {
      if (field.options[i].selected) {
        selectedOptions.push(field.options[i].value);
      }
    }

    var regexLength = /-[0-9]+\b/;
    if (regexLength.test(matchClass)) {
      var requiredLength = matchClass.match(regexLength)[1];
      if (selectedOptions.length != requiredLength)
        return false;
    }

    var regexScope = /-([0-9]+)-([0-9]+)\b/;
    if (regexScope.test(matchClass)) {
      var requiredLengthScope = matchClass.match(regexScope);
      if (selectedOptions.length < requiredLengthScope[1] || selectedOptions.length > requiredLengthScope[2])
        return false;
    }

    if (selectedOptions.length == 0)
      return false;

    return true;

  },

  __validateRadio: function(field, matchClass, matchValue) {

    // Either required or ignored

    var foundCheckedRadio = false;
    var radios = this.__getFieldsByFieldTypes("input");

    // Let's see if any radio buttons with the same name as the current element (eg. the same group) are checked
    for (var r = 0; r < radios.length; r++) {
      if (radios[r].type == "radio" && radios[r].name == field.name && radios[r].checked) {
        foundCheckedRadio = true;
        break;
      };
    }

    // None of the radio buttons for this name are checked. Add an error.
    if (foundCheckedRadio == false)
      return false;
    else
      return true;

  },

  __validateText: function(field, matchClass, matchValue) {

    if (typeof matchValue == "function" && matchValue.constructor != RegExp) {
      if (matchClass.test(field.className) && !matchValue(field.value))
        return false;
    }
    else {
      if (matchClass.test(field.className) && !matchValue.test(field.value))
        return false;
    }

    return true;
  },

  __validateTextarea: function(field, matchClass, matchValue) {
    return this.__validateText(field, matchClass, matchValue);
  },

  __validateCheckbox: function(field, matchClass, matchValue) { },
  __validatePassword: function(field, matchClass, matchValue) { },
  __validateImage: function(field, matchClass, matchValue) { }
}