<commit_before><template>

  <div class="setup">
    <div class="wrapper" role="main">
      <img class="logo" src="./icons/logo-min.png" alt="Kolibri logo">


      <form @submit.prevent="submitSetupForm" novalidate class="container">
        <h1>{{ $tr('formHeader') }}</h1>


        <fieldset :disabled="submitted" class="setup-owner">

          <legend class="title">
            {{ $tr('deviceOwnerSectionHeader') }}
          </legend>
          <p class="description">{{ $tr('deviceOwnerDescription') }}</p>

          <k-textbox
            @focus="firstUsernameFieldVisit || visitUsername()"
            @blur="validateUsername()"
            :invalid="!!usernameError"
            :invalidText="usernameError"
            :required="true"
            :label="$tr('usernameInputLabel')"
            :maxlength="30"
            v-model="username"
          />

          <k-textbox
            @focus="firstPasswordFieldsVisit || visitPassword()"
            :invalid="!!passwordError"
            :invalidText="passwordError"
            :required="true"
            :label="$tr('passwordInputLabel')"
            type="password"
            v-model="password"
          />

          <k-textbox
            @blur="validatePassword()"
            :invalid="!!passwordError"
            :required="true"
            :label="$tr('reEnterPasswordInputLabel')"
            type="password"
            v-model="passwordConfirm"
          />

        </fieldset>
        <fieldset :disabled="submitted" class="setup-facility">

          <legend class="title">
            {{ $tr('facilitySectionHeader') }}
          </legend>
          <p class="description">{{ $tr('facilityDescription') }}</p>

          <k-textbox
            @focus="firstFacilityFieldVisit || visitFacility()"
            @blur="validateFacility"
            :invalid="!!facilityError"
            :invalidText="facilityError"
            :required="true"
            :label="$tr('facilityInputLabel')"
            :maxlength="100"
            v-model="facility"
          />
        </fieldset>


        <div class="setup-submission">
          <ui-alert
            class="setup-submission-alert"
            type="error"
            @dismiss="clearGlobalError()"
            v-if="globalError">
            {{ globalError }}
          </ui-alert>

          <ui-alert
            class="setup-submission-alert"
            type="info"
            :dismissible="false"
            :remove-icon="true"
            v-if="submitted">
            {{ $tr('setupProgressFeedback') }}
          </ui-alert>

          <k-button :disabled="submitted" :primary="true" :text="$tr('formSubmissionButton')" type="submit"/>
        </div>
      </form>

    </div>
  </div>

</template>


<script>

  import { provisionDevice } from '../state/actions';
  import store from '../state/store';
  import kTextbox from 'kolibri.coreVue.components.kTextbox';
  import kButton from 'kolibri.coreVue.components.kButton';
  import uiAlert from 'keen-ui/src/UiAlert';
  import { facilityPresetChoices } from '../state/constants';
  export default {
    name: 'setupWizard',
    $trs: {
      formHeader: 'Create device owner and facility',
      deviceOwnerSectionHeader: 'Device Owner',
      facilitySectionHeader: 'Facility',
      usernameInputLabel: 'Username',
      passwordInputLabel: 'Password',
      reEnterPasswordInputLabel: 'Re-enter password',
      facilityInputLabel: 'Facility name',
      deviceOwnerDescription:
        'To use Kolibri, you first need to create a Device Owner. This account will be used to configure high-level settings for this installation, and create other administrator accounts',
      facilityDescription:
        'You also need to create a Facility. This represents your school, training center, or other installation location',
      formSubmissionButton: 'Create and get started',
      usernameFieldEmptyErrorMessage: 'Username cannot be empty',
      usernameCharacterErrorMessage: 'Username can only contain letters, numbers, and underscores',
      passwordFieldEmptyErrorMessage: 'Password cannot be empty',
      passwordsMismatchErrorMessage: 'Passwords do not match',
      facilityFieldEmptyErrorMessage: 'Facility cannot be empty',
      cannotSubmitPageError: 'Please resolve all of the errors shown',
      genericPageError: 'Something went wrong',
      setupProgressFeedback: 'Setting up your device...',
    },
    name: 'setupWizard',
    data() {
      return {
        username: '',
        usernameError: null,
        password: '',
        passwordConfirm: '',
        passwordError: null,
        facility: '',
        facilityError: null,
        globalError: null,
        preset: facilityPresetChoices[0],
      };
    },
    components: {
      kTextbox,
      kButton,
      uiAlert,
    },
    computed: {
      firstUsernameFieldVisit() {
        return this.usernameError === null;
      },
      usernameFieldPopulated() {
        return !!this.username;
      },
      usernameValidityCheck() {
        return /^\w+$/g.test(this.username);
      },
      firstPasswordFieldsVisit() {
        return this.passwordError === null;
      },
      passwordFieldsMatch() {
        return this.password === this.passwordConfirm;
      },
      passwordFieldsPopulated() {
        return !!(this.password && this.passwordConfirm);
      },
      facilityFieldPopulated() {
        return !!this.facility;
      },
      firstFacilityFieldVisit() {
        return this.facilityError === null;
      },
      presetValidityCheck() {
        facilityPresetChoices.includes(this.preset);
      },
      allFieldsPopulated() {
        return (
          this.passwordFieldsPopulated && this.usernameFieldPopulated && this.facilityFieldPopulated
        );
      },
      canSubmit() {
        return (
          !this.submitted &&
          this.passwordFieldsMatch &&
          this.usernameValidityCheck &&
          this.allFieldsPopulated
        );
      },
    },
    methods: {
      submitSetupForm() {
        this.globalError = '';

        if (this.canSubmit) {
          const superuser = {
            password: <PASSWORD>,
            username: this.username,
          };
          const facility = { name: this.facility };
          // TODO (rtibbles - paging DXCanas): Actually set these!
          const languageCode = 'en';
          const preset = 'nonformal';
          this.provisionDevice(superuser, facility, preset, languageCode);
        } else {
          if (this.firstUsernameFieldVisit) {
            this.visitUsername();
            this.validateUsername();
          }

          if (this.firstPasswordFieldsVisit) {
            this.visitPassword();
            this.validatePassword();
          }

          if (this.firstFacilityFieldVisit) {
            this.visitFacility();
            this.validateFacility();
          }

          this.globalError = this.$tr('cannotSubmitPageError');
        }
      },
      clearGlobalError() {
        this.globalError = '';
      },
      visitUsername() {
        this.usernameError = '';
      },
      visitPassword() {
        this.passwordError = '';
      },
      visitFacility() {
        this.facilityError = '';
      },
      validateUsername() {
        if (!this.usernameFieldPopulated) {
          this.usernameError = this.$tr('usernameFieldEmptyErrorMessage');
        } else if (!this.usernameValidityCheck) {
          this.usernameError = this.$tr('usernameCharacterErrorMessage');
        }
      },
      validatePassword() {
        if (!this.passwordFieldsMatch) {
          this.passwordError = this.$tr('passwordsMismatchErrorMessage');
        } else if (!this.passwordFieldsPopulated) {
          this.passwordError = this.$tr('passwordFieldEmptyErrorMessage');
        }
      },
      validateFacility() {
        if (!this.facilityFieldPopulated) {
          this.facilityError = this.$tr('facilityFieldEmptyErrorMessage');
        }
      },
    },
    vuex: {
      actions: {
        provisionDevice,
      },
      getters: {
        submitted: state => state.pageState.submitted,
      },
    },
    store,
  };

</script>


<style lang="stylus" scoped>

  @require '~kolibri.styles.definitions'

  .setup
    position: absolute
    overflow-y: scroll
    width: 100%
    height: 100%

    &-owner, &-facility
      // fighting pureCSS
      border: none
      margin: 0
      padding: 0

    &-submission
      margin-top: 16px
      text-align: center

  .wrapper
    position: absolute
    max-height: 100%
    top: 50%
    left: 50%
    transform: translate(-50%, -50%)
  .container
    background: #fff
    width: 100%
    max-width: 430px
    min-width: 320px
    border-radius: $radius
    margin: 0 auto
    padding: 20px 30px
  h1
    font-size: 18px
  .title
    font-size: 14px
    font-weight: bold
  .description
    font-size: 12px
    color: $core-text-annotation
  .logo
    height: 40%
    width: 40%
    max-height: 160px
    min-height: 100px
    max-width: 160px
    min-width: 100px
    display: block
    margin-left: auto
    margin-right: auto

</style>
<commit_msg>Change regex that validates username<commit_after><template>

  <div class="setup">
    <div class="wrapper" role="main">
      <img class="logo" src="./icons/logo-min.png" alt="Kolibri logo">


      <form @submit.prevent="submitSetupForm" novalidate class="container">
        <h1>{{ $tr('formHeader') }}</h1>


        <fieldset :disabled="submitted" class="setup-owner">

          <legend class="title">
            {{ $tr('deviceOwnerSectionHeader') }}
          </legend>
          <p class="description">{{ $tr('deviceOwnerDescription') }}</p>

          <k-textbox
            @focus="firstUsernameFieldVisit || visitUsername()"
            @blur="validateUsername()"
            :invalid="!!usernameError"
            :invalidText="usernameError"
            :required="true"
            :label="$tr('usernameInputLabel')"
            :maxlength="30"
            v-model="username"
          />

          <k-textbox
            @focus="firstPasswordFieldsVisit || visitPassword()"
            :invalid="!!passwordError"
            :invalidText="passwordError"
            :required="true"
            :label="$tr('passwordInputLabel')"
            type="password"
            v-model="password"
          />

          <k-textbox
            @blur="validatePassword()"
            :invalid="!!passwordError"
            :required="true"
            :label="$tr('reEnterPasswordInputLabel')"
            type="password"
            v-model="passwordConfirm"
          />

        </fieldset>
        <fieldset :disabled="submitted" class="setup-facility">

          <legend class="title">
            {{ $tr('facilitySectionHeader') }}
          </legend>
          <p class="description">{{ $tr('facilityDescription') }}</p>

          <k-textbox
            @focus="firstFacilityFieldVisit || visitFacility()"
            @blur="validateFacility"
            :invalid="!!facilityError"
            :invalidText="facilityError"
            :required="true"
            :label="$tr('facilityInputLabel')"
            :maxlength="100"
            v-model="facility"
          />
        </fieldset>


        <div class="setup-submission">
          <ui-alert
            class="setup-submission-alert"
            type="error"
            @dismiss="clearGlobalError()"
            v-if="globalError">
            {{ globalError }}
          </ui-alert>

          <ui-alert
            class="setup-submission-alert"
            type="info"
            :dismissible="false"
            :remove-icon="true"
            v-if="submitted">
            {{ $tr('setupProgressFeedback') }}
          </ui-alert>

          <k-button :disabled="submitted" :primary="true" :text="$tr('formSubmissionButton')" type="submit"/>
        </div>
      </form>

    </div>
  </div>

</template>


<script>

  import { provisionDevice } from '../state/actions';
  import store from '../state/store';
  import kTextbox from 'kolibri.coreVue.components.kTextbox';
  import kButton from 'kolibri.coreVue.components.kButton';
  import uiAlert from 'keen-ui/src/UiAlert';
  import { facilityPresetChoices } from '../state/constants';
  export default {
    name: 'setupWizard',
    $trs: {
      formHeader: 'Create device owner and facility',
      deviceOwnerSectionHeader: 'Device Owner',
      facilitySectionHeader: 'Facility',
      usernameInputLabel: 'Username',
      passwordInputLabel: 'Password',
      reEnterPasswordInputLabel: 'Re-enter password',
      facilityInputLabel: 'Facility name',
      deviceOwnerDescription:
        'To use Kolibri, you first need to create a Device Owner. This account will be used to configure high-level settings for this installation, and create other administrator accounts',
      facilityDescription:
        'You also need to create a Facility. This represents your school, training center, or other installation location',
      formSubmissionButton: 'Create and get started',
      usernameFieldEmptyErrorMessage: 'Username cannot be empty',
      usernameCharacterErrorMessage: 'Username can only contain letters, numbers, and underscores',
      passwordFieldEmptyErrorMessage: 'Password cannot be empty',
      passwordsMismatchErrorMessage: 'Passwords do not match',
      facilityFieldEmptyErrorMessage: 'Facility cannot be empty',
      cannotSubmitPageError: 'Please resolve all of the errors shown',
      genericPageError: 'Something went wrong',
      setupProgressFeedback: 'Setting up your device...',
    },
    data() {
      return {
        username: '',
        usernameError: null,
        password: '',
        passwordConfirm: '',
        passwordError: null,
        facility: '',
        facilityError: null,
        globalError: null,
        preset: facilityPresetChoices[0],
      };
    },
    components: {
      kTextbox,
      kButton,
      uiAlert,
    },
    computed: {
      firstUsernameFieldVisit() {
        return this.usernameError === null;
      },
      usernameFieldPopulated() {
        return !!this.username;
      },
      usernameValidityCheck() {
        const hasPuncRe = /[\s`~!@#$%^&*()-+={}\[\]\|\\\/:;"'<>,\.\?]/; // eslint-disable-line
        return !hasPuncRe.test(this.username);
      },
      firstPasswordFieldsVisit() {
        return this.passwordError === null;
      },
      passwordFieldsMatch() {
        return this.password === this.<PASSWORD>;
      },
      passwordFieldsPopulated() {
        return !!(this.password && this.passwordConfirm);
      },
      facilityFieldPopulated() {
        return !!this.facility;
      },
      firstFacilityFieldVisit() {
        return this.facilityError === null;
      },
      presetValidityCheck() {
        facilityPresetChoices.includes(this.preset);
      },
      allFieldsPopulated() {
        return (
          this.passwordFieldsPopulated && this.usernameFieldPopulated && this.facilityFieldPopulated
        );
      },
      canSubmit() {
        return (
          !this.submitted &&
          this.passwordFieldsMatch &&
          this.usernameValidityCheck &&
          this.allFieldsPopulated
        );
      },
    },
    methods: {
      submitSetupForm() {
        this.globalError = '';

        if (this.canSubmit) {
          const superuser = {
            password: <PASSWORD>,
            username: this.username,
          };
          const facility = { name: this.facility };
          // TODO (rtibbles - paging DXCanas): Actually set these!
          const languageCode = 'en';
          const preset = 'nonformal';
          this.provisionDevice(superuser, facility, preset, languageCode);
        } else {
          if (this.firstUsernameFieldVisit) {
            this.visitUsername();
            this.validateUsername();
          }

          if (this.firstPasswordFieldsVisit) {
            this.visitPassword();
            this.validatePassword();
          }

          if (this.firstFacilityFieldVisit) {
            this.visitFacility();
            this.validateFacility();
          }

          this.globalError = this.$tr('cannotSubmitPageError');
        }
      },
      clearGlobalError() {
        this.globalError = '';
      },
      visitUsername() {
        this.usernameError = '';
      },
      visitPassword() {
        this.passwordError = '';
      },
      visitFacility() {
        this.facilityError = '';
      },
      validateUsername() {
        if (!this.usernameFieldPopulated) {
          this.usernameError = this.$tr('usernameFieldEmptyErrorMessage');
        } else if (!this.usernameValidityCheck) {
          this.usernameError = this.$tr('usernameCharacterErrorMessage');
        }
      },
      validatePassword() {
        if (!this.passwordFieldsMatch) {
          this.passwordError = this.$tr('passwordsMismatchErrorMessage');
        } else if (!this.passwordFieldsPopulated) {
          this.passwordError = this.$tr('passwordFieldEmptyErrorMessage');
        }
      },
      validateFacility() {
        if (!this.facilityFieldPopulated) {
          this.facilityError = this.$tr('facilityFieldEmptyErrorMessage');
        }
      },
    },
    vuex: {
      actions: {
        provisionDevice,
      },
      getters: {
        submitted: state => state.pageState.submitted,
      },
    },
    store,
  };

</script>


<style lang="stylus" scoped>

  @require '~kolibri.styles.definitions'

  .setup
    position: absolute
    overflow-y: scroll
    width: 100%
    height: 100%

    &-owner, &-facility
      // fighting pureCSS
      border: none
      margin: 0
      padding: 0

    &-submission
      margin-top: 16px
      text-align: center

  .wrapper
    position: absolute
    max-height: 100%
    top: 50%
    left: 50%
    transform: translate(-50%, -50%)
  .container
    background: #fff
    width: 100%
    max-width: 430px
    min-width: 320px
    border-radius: $radius
    margin: 0 auto
    padding: 20px 30px
  h1
    font-size: 18px
  .title
    font-size: 14px
    font-weight: bold
  .description
    font-size: 12px
    color: $core-text-annotation
  .logo
    height: 40%
    width: 40%
    max-height: 160px
    min-height: 100px
    max-width: 160px
    min-width: 100px
    display: block
    margin-left: auto
    margin-right: auto

</style>

{'fraction_non_alphanumeric': 0.1249182472204055, 'fraction_numerical': 0.007194244604316547, 'mean_word_length': 2.0963550455619306, 'pattern_counts': {'":': 0, '<': 83, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2271661', 'n_tokens_mistral': 5190, 'n_tokens_neox': 4940, 'n_words': 1010}