Simplify transition logic in regards to error handling

<template>

  <NotificationsRoot>
    <ImmersivePage
      :appBarTitle="coreString('changeLearningFacility')"
      :route="$router.getRoute('PROFILE')"
    >
      <KPageContainer style="maxWidth: 1000px; margin: 32px auto 0;">
        <router-view />
      </KPageContainer>
    </ImmersivePage>
  </NotificationsRoot>

</template>

<script>

  import NotificationsRoot from 'kolibri.coreVue.components.NotificationsRoot';
  import ImmersivePage from 'kolibri.coreVue.components.ImmersivePage';
  import responsiveWindowMixin from 'kolibri.coreVue.mixins.responsiveWindowMixin';
  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import { computed } from 'kolibri.lib.vueCompositionApi';
  import { interpret } from 'xstate';
  import { mapGetters } from 'vuex';
  import { changeFacilityMachine } from '../../machines/changeFacilityMachine';
  import plugin_data from 'plugin_data';

  export default {
    name: 'ChangeFacility',
    metaInfo() {
      return {
        title: this.$tr('documentTitle'),
      };
    },
    components: { NotificationsRoot, ImmersivePage },
    mixins: [responsiveWindowMixin, commonCoreStrings],
    setup() {
      const { isSubsetOfUsersDevice } = plugin_data;
      return {
        isSubsetOfUsersDevice,
      };
    },

    data() {
      return {
        service: interpret(changeFacilityMachine),
        previousMachineStateName: '',
        state: changeFacilityMachine.initialState,
        currentRoute: this.$router.currentRoute.name,
        appBarHeight: 0,
        internalNavigation: false,
      };
    },
    provide() {
      return {
        changeFacilityService: this.service,
        state: computed(() => this.state.context),
      };
    },

    computed: {
      ...mapGetters(['session', 'getUserKind']),
    },

    beforeRouteUpdate(to, from, next) {
      if (!this.internalNavigation) this.service.send('BACK');
      next();
    },
    created() {
      this.service.start();
      this.service.onTransition(state => {
        if (state.value === 'error') {
          this.onMachineError(state);
          return;
        }
        const stateID = Object.keys(state.meta)[0];
        if (state.meta[stateID] !== undefined) {
          let newRoute = state.meta[stateID].route;
          if (newRoute != this.$router.currentRoute.name) {
            if ('path' in state.meta[stateID]) {
              this.internalNavigation = true;
              this.$router.push(
                { name: newRoute, path: state.meta[stateID].path },
                function() {
                  this.internalNavigation = false;
                }.bind(this)
              );
            } else this.$router.push(newRoute);
          }
          this.currentRoute = newRoute;
        }
        this.state = state;
      });
    },
    destroyed() {
      this.service.stop();
    },
    mounted() {
      this.$nextTick(() => {
        if (this.$refs.appBar) {
          this.appBarHeight = this.$refs.appBar.$el.clientHeight;
        }
      });

      const ctx = this.service.state.context;
      if (ctx.username === '') {
        // machine initialized with its default context
        this.service.send({
          type: 'SETCONTEXT',
          value: {
            facility: this.session.facility_id,
            username: this.session.username,
            userId: this.session.user_id,
            role: this.getUserKind,
          },
        });
      }
    },
    methods: {
      onMachineError(machineState) {
        this.$store.commit(
          'CORE_SET_ERROR',
          `An error occured in the '${this.previousMachineStateName}' state of the change facility machine`
        );
        this.service.send('RESET');
        this.previousMachineStateName = machineState.value;
      },
    },
    $trs: {
      documentTitle: {
        message: 'Change Facility',
        context: 'Title of the change facility page.',
      },
    },
  };

</script>

<style lang="scss" scoped>

  @import '../../../../../../core/assets/src/styles/definitions';

  .container {
    max-width: $page-container-max-width;
    margin-right: auto;
    margin-left: auto;
  }

</style>

<template>

  <NotificationsRoot>
    <ImmersivePage
      :appBarTitle="coreString('changeLearningFacility')"
      :route="$router.getRoute('PROFILE')"
    >
      <KPageContainer style="maxWidth: 1000px; margin: 32px auto 0;">
        <router-view />
      </KPageContainer>
    </ImmersivePage>
  </NotificationsRoot>

</template>

<script>

  import NotificationsRoot from 'kolibri.coreVue.components.NotificationsRoot';
  import ImmersivePage from 'kolibri.coreVue.components.ImmersivePage';
  import responsiveWindowMixin from 'kolibri.coreVue.mixins.responsiveWindowMixin';
  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import { computed } from 'kolibri.lib.vueCompositionApi';
  import { interpret } from 'xstate';
  import { mapGetters } from 'vuex';
  import { changeFacilityMachine } from '../../machines/changeFacilityMachine';
  import plugin_data from 'plugin_data';

  export default {
    name: 'ChangeFacility',
    metaInfo() {
      return {
        title: this.$tr('documentTitle'),
      };
    },
    components: { NotificationsRoot, ImmersivePage },
    mixins: [responsiveWindowMixin, commonCoreStrings],
    setup() {
      const { isSubsetOfUsersDevice } = plugin_data;
      return {
        isSubsetOfUsersDevice,
      };
    },

    data() {
      return {
        service: interpret(changeFacilityMachine),
        previousMachineStateName: '',
        state: changeFacilityMachine.initialState,
        currentRoute: this.$router.currentRoute.name,
        appBarHeight: 0,
        internalNavigation: false,
      };
    },
    provide() {
      return {
        changeFacilityService: this.service,
        state: computed(() => this.state.context),
      };
    },

    computed: {
      ...mapGetters(['session', 'getUserKind']),
    },

    beforeRouteUpdate(to, from, next) {
      if (!this.internalNavigation) this.service.send('BACK');
      next();
    },
    created() {
      this.service.start();
      this.service.onTransition(state => {
        if (state.value === 'error') {
          this.$store.commit(
            'CORE_SET_ERROR',
            `An error occured in the '${this.previousMachineStateName}' state of the change facility machine`
          );
          this.service.send('RESET');
        } else {
          const stateID = Object.keys(state.meta)[0];
          if (state.meta[stateID] !== undefined) {
            let newRoute = state.meta[stateID].route;
            if (newRoute != this.$router.currentRoute.name) {
              if ('path' in state.meta[stateID]) {
                this.internalNavigation = true;
                this.$router.push(
                  { name: newRoute, path: state.meta[stateID].path },
                  function() {
                    this.internalNavigation = false;
                  }.bind(this)
                );
              } else this.$router.push(newRoute);
            }
            this.currentRoute = newRoute;
          }
        }
        this.previousMachineStateName = state.value;
        this.state = state;
      });
    },
    destroyed() {
      this.service.stop();
    },
    mounted() {
      this.$nextTick(() => {
        if (this.$refs.appBar) {
          this.appBarHeight = this.$refs.appBar.$el.clientHeight;
        }
      });

      const ctx = this.service.state.context;
      if (ctx.username === '') {
        // machine initialized with its default context
        this.service.send({
          type: 'SETCONTEXT',
          value: {
            facility: this.session.facility_id,
            username: this.session.username,
            userId: this.session.user_id,
            role: this.getUserKind,
          },
        });
      }
    },
    $trs: {
      documentTitle: {
        message: 'Change Facility',
        context: 'Title of the change facility page.',
      },
    },
  };

</script>

<style lang="scss" scoped>

  @import '../../../../../../core/assets/src/styles/definitions';

  .container {
    max-width: $page-container-max-width;
    margin-right: auto;
    margin-left: auto;
  }

</style>


Filter duplicated object from pickMultipleObjects

<template>

    <canvas id="deck-canvas"></canvas>

      <ColorBinsLegend
        v-if="variableMax"
        :title="variableName"
        :min="variableMin"
        :max="variableMax"
        :avg="variableAvg"
        :bins="variableBins"
      />
      <ColorCategoriesLegend
        v-else-if="variableCategories"
        :title="variableName"
        :categories="variableCategories"
      />

{{ variableDescription }}

Recenter

</template>


Make page title reflect breadcrumbs (as original)

<template>

                <router-link v-if="item.url" :to="item.url">{{item.title}}</router-link>
                {{item.title}}

</template>


log out no longer part of user object

log out no longer part of user object

<template>

            <router-link
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              exact
              :to="{ name: 'home' }"
              >Account Overview</router-link
            >

            <router-link
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              :to="{ name: 'membership' }"
              >Membership</router-link
            >

            <router-link
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              :to="{ name: 'blast' }"
              >The Blast</router-link
            >

            <button
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              @click="logOut"
            >
              Log Out
            </button>

            <button
              class="c-navbar__item-content c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
              aria-label="Show menu"
              @click="toggleDropdown"
            >
              <icon name="bars" :display="{ size: 's' }" />
               Menu
            </button>

            <button
              class="c-navbar__item-content c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
              aria-label="Hide menu"
              @click="toggleDropdown"
            >
              <icon name="close" :display="{ size: 's' }" />
               Close
            </button>

          <router-link
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            :to="{ name: 'home' }"
            active-class="is-active"
            exact
            >Account Overview</router-link
          >

          <router-link
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            :to="{ name: 'membership' }"
            active-class="is-active"
            >Membership</router-link
          >

          <router-link
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            :to="{ name: 'blast' }"
            active-class="is-active"
            >The Blast</router-link
          >

          <button
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            @click="logOut"
          >
            Log Out
          </button>

</template>

<template>

            <router-link
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              exact
              :to="{ name: 'home' }"
              >Account Overview</router-link
            >

            <router-link
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              :to="{ name: 'membership' }"
              >Membership</router-link
            >

            <router-link
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              :to="{ name: 'blast' }"
              >The Blast</router-link
            >

            <button
              class="c-navbar__item-content c-navbar__clickable c-navbar__clickable--animated t-size-xxs t-uppercase t-uppercase--extra-wide"
              active-class="is-active"
              @click="logOut"
            >
              Log Out
            </button>

            <button
              class="c-navbar__item-content c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
              aria-label="Show menu"
              @click="toggleDropdown"
            >
              <icon name="bars" :display="{ size: 's' }" />
               Menu
            </button>

            <button
              class="c-navbar__item-content c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
              aria-label="Hide menu"
              @click="toggleDropdown"
            >
              <icon name="close" :display="{ size: 's' }" />
               Close
            </button>

          <router-link
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            :to="{ name: 'home' }"
            active-class="is-active"
            exact
            >Account Overview</router-link
          >

          <router-link
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            :to="{ name: 'membership' }"
            active-class="is-active"
            >Membership</router-link
          >

          <router-link
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            :to="{ name: 'blast' }"
            active-class="is-active"
            >The Blast</router-link
          >

          <button
            class="c-navbar__clickable t-size-xxs t-uppercase t-uppercase--extra-wide"
            @click="user.logOut"
          >
            Log Out
          </button>

</template>


fix: fix icon promo popup icons height

fix: fix icon promo popup icons height

<template>
  <app-modal
    width="395"
    :show="showPromo"
    :title="title"
    has-body-content
    :has-buttons="false"
    :has-close-button="false"
    :close="setHidePopUp"
  >
    <template #dialogBody>

          MEW has joined with Moonpay
          to offer more crypto buying and selling options. Help us celebrate
          with zero fees when you buy and sell crypto this week.

          Enjoy 0.9% fee when you select ‘Bank account’ as payment method.

      <mew-button
        class="mt-8"
        title="Buy Crypto"
        has-full-width
        btn-size="xlarge"
        @click.native="goToPromo()"
      />

    </template>
  </app-modal>
</template>


Otimizando a validação

Otimizando a validação

Otimizando a validação substituindo os alerts padrões do javascript para outros alertas manuais

<template>

Preencha o campo abaixo com seus dados pessoais.

                        <form class="form" role="form" autocomplete="off" id="formCad" action="">

                                <input type="email" class="form-control" name="email" ref="email" placeholder="E-mail">
                                Você deve digitar um E-mail válido!

                                <input type="text" class="form-control" name="nome" ref="nome"  placeholder="Nome">

                                <input type="text" class="form-control" name="sobrenome" ref="sobrenome" placeholder="Sobrenome">

                                <input type="number" class="form-control" name="idade" ref="idade"  placeholder="Idade">
                                Nenês não podem usar esse tipo de aplicativo!

Preferência em

                                  Homens
                                  Mulheres

                                Você deve escolher ao menos uma das Preferências!

Sexo

                                          <input type="radio" class="option-input radio" name="radio" id="radio1" ref="radio1"/>
                                           Agênero

                                          <input type="radio" class="option-input radio" name="radio" id="radio2" ref="radio2"/>
                                           Bigênero

                                          <input type="radio" class="option-input radio" name="radio" id="radio3" ref="radio3"/>
                                           Transgênero

                                          <input type="radio" class="option-input radio" name="radio" id="radio4" ref="radio4"/>
                                            Masculino

                                          <input type="radio" class="option-input radio" name="radio" id="radio5" ref="radio5"/>
                                           Feminino

                                Você deve escolher ao menos uma das opções!

                                <input type="text" class="form-control" name="usuario" ref="usuario"  placeholder="Usuário">
                                Seu usuário tem que ter no mínimo 4 caracteres!
                                Seu usuário tem que ter no máximo 10 caracteres!

                                <input type="password" class="form-control" name="senha" ref="senha"  placeholder="Senha">
                                Sua Senha tem que ter no mínimo 7 caracteres!
                                Sua Senha tem que ter no máximo 20 caracteres!

                                <input type="password" class="form-control" name="senha2" ref="senha2"  placeholder="Confirmar Senha">
                                As senhas informadas precisam ser iguais!

                            <router-link class="btn btn-outline-secondary btn-lg" role="button" aria-pressed="true" to="/usuario-nao-autenticado">Cancelar</router-link>
                            Confirmar

Opa!

Todos os campos devem ser preenchidos!

                                Voltar

                        </form>

</template>

<template>

Preencha o campo abaixo com seus dados pessoais.

                        <form class="form" role="form" autocomplete="off" id="formCad" action="">

                                <input type="email" class="form-control" name="email" ref="email" placeholder="E-mail">

                                <input type="text" class="form-control" name="nome" ref="nome"  placeholder="Nome">

                                <input type="text" class="form-control" name="sobrenome" ref="sobrenome"  required placeholder="Sobrenome" id="sobrenome">

                            <input type="number" class="form-control" name="idade" ref="idade"  placeholder="Idade">

Preferência em

                                  Homens
                                  Mulheres

Sexo

                                      <input type="radio" class="option-input radio" name="radio" id="radio1" ref="radio1"/>
                                       Agênero

                                      <input type="radio" class="option-input radio" name="radio" id="radio2" ref="radio2"/>
                                       Bigênero

                                      <input type="radio" class="option-input radio" name="radio" id="radio3" ref="radio3"/>
                                       Transgênero

                                      <input type="radio" class="option-input radio" name="radio" id="radio4" ref="radio4"/>
                                        Masculino

                                      <input type="radio" class="option-input radio" name="radio" id="radio5" ref="radio5"/>
                                       Feminino

                                <input type="text" class="form-control" name="usuario" ref="usuario" required placeholder="Usuário">

                                <input type="password" class="form-control" name="senha" ref="senha" required placeholder="Senha">

                                <input type="password" class="form-control" name="senha2" ref="senha2" required placeholder="Confirmar Senha">

                            <router-link class="btn btn-outline-secondary btn-lg" role="button" aria-pressed="true" to="/usuario-nao-autenticado">Cancelar</router-link>
                            Confirmar

                            Todos os campos devem ser preenchidos!
                        </form>

</template>


update progress bar placement on mobile

update progress bar placement on mobile

<template>

  <router-link :to="link" :class="$computedClass({ ':focus': $coreOutline })">

      <KFixedGrid numCols="4">
        <KFixedGridItem :span="isMobile ? 4 : 1" class="thumb-area">
          <CardThumbnail
            :kind="content.kind"
            v-bind="{ thumbnail }"
            :activityLength="content.duration"
            :contentNode="content"
          />

            {{ bookmarkCreated }}

          <ProgressBar v-if="!isMobile" :contentNode="content" />
        </KFixedGridItem>

        <KFixedGridItem :span="isMobile ? 4 : 3" class="text-area">

              <LearningActivityLabel :contentNode="content" labelAfter condensed />

              <TextTruncatorCss :text="content.title" :maxLines="1" />

              <TextTruncatorCss :text="content.description" :maxLines="4" />

{{ categoryAndLevelString }}

              <KButton
                v-if="isLibraryPage && content.copies"
                appearance="basic-link"
                class="copies"
                :style="{ color: $themeTokens.text }"
                :text="coreString('copies', { num: content.copies.length })"
                @click.prevent="$emit('openCopiesModal', content.copies)"
              />

        </KFixedGridItem>
      </KFixedGrid>

        <ProgressBar v-if="!!isMobile" :contentNode="content" class="footer-progress" />

          <KIconButton
            v-for="(value, key) in footerIcons"
            :key="key"
            :icon="key"
            size="mini"
            :color="$themePalette.grey.v_400"
            :ariaLabel="coreString(value)"
            :tooltip="coreString(value)"
            class="icon-fix"
            @click.prevent="$emit(value)"
          />

  </router-link>

</template>

<template>

  <router-link :to="link" :class="$computedClass({ ':focus': $coreOutline })">

      <KFixedGrid numCols="4">
        <KFixedGridItem :span="isMobile ? 4 : 1" class="thumb-area">
          <CardThumbnail
            :kind="content.kind"
            v-bind="{ thumbnail }"
            :activityLength="content.duration"
            :contentNode="content"
          />

            {{ bookmarkCreated }}

          <ProgressBar :contentNode="content" />
        </KFixedGridItem>

        <KFixedGridItem :span="isMobile ? 4 : 3" class="text-area">

              <LearningActivityLabel :contentNode="content" labelAfter condensed />

              <TextTruncatorCss :text="content.title" :maxLines="1" />

              <TextTruncatorCss :text="content.description" :maxLines="4" />

{{ categoryAndLevelString }}

              <KButton
                v-if="isLibraryPage && content.copies"
                appearance="basic-link"
                class="copies"
                :style="{ color: $themeTokens.text }"
                :text="coreString('copies', { num: content.copies.length })"
                @click.prevent="$emit('openCopiesModal', content.copies)"
              />

        </KFixedGridItem>
      </KFixedGrid>

        <KIconButton
          v-for="(value, key) in footerIcons"
          :key="key"
          :icon="key"
          size="mini"
          :color="$themePalette.grey.v_400"
          :ariaLabel="coreString(value)"
          :tooltip="coreString(value)"
          class="icon-fix"
          @click.prevent="$emit(value)"
        />

  </router-link>

</template>


Fixed https://github.com/kalisio/kCore/issues/122

<template>
  <k-modal ref="modal" :toolbar="toolbar" :buttons="[]" :options="{ 'background-color': '#000000', padding: '0px', maximized: (hasMedia ? true : false) }">

      <q-carousel ref="carousel" class="carousel text-white" actions arrows dots infinite v-show="hasMedia && !zoomedMedia" @slide="onViewMedia">

            <q-spinner-cube size="4em"/>
            {{ $t('KMediaBrowser.LOADING') }}

          <q-icon v-if="!currentMediaIsFile" @click="doZoomIn" color="white" name="zoom_in" />
          <q-icon @click="doDownload" color="white" name="cloud_download" />
          <q-icon @click="doClose" color="white" name="close" />

{{currentName}}

      </q-carousel>

        <q-icon @click="doZoomOut" color="white" name="zoom_out" />

<big>{{ $t('KMediaBrowser.NO_MEDIA') }}</big>

  </k-modal>
</template>


add buttons

add buttons

<template>

    <popover :isOpen="isOpenPopover" :trigger="popoverRef" :onClose="closeModalWindow">
      <color-picker />
    </popover>

    <resize-box v-if="isVisibleControlsBox" tag="section" resize="column" :onEndOfResize="onSetCtrlBoxWidth" :size="widthCtrlBox" class="controls-section">

          <custom-btn accesskey="s" iconClass="icon-settings" class="xs" />
          <custom-btn title="Fragment" iconClass="icon-fragment" :active="activeTabName === tabNames.FRAGMENT_SHADER" :data="tabNames.FRAGMENT_SHADER" :onClick="onSetActiveTabName" />
          <custom-btn title="Vertex" iconClass="icon-vertex" :active="activeTabName === tabNames.VERTEX_SHADER" :data="tabNames.VERTEX_SHADER" :onClick="onSetActiveTabName" />
          <custom-btn iconClass="icon-undo" class="xs" />
          <custom-btn iconClass="icon-save" class="xs" />
          <custom-btn iconClass="icon-redo" class="xs" />
          <custom-btn iconClass="icon-new-file" class="xs" />
          <custom-btn iconClass="icon-open-folder" class="xs" />
          <custom-btn iconClass="icon-gallery" class="xs" />
          <custom-btn :link="`/${urls.MATERIAL_EDITOR}`" accesskey="w" iconClass="icon-material-editor" class="xs" />

      <shader-editor-footer />
    </resize-box>

          <custom-btn iconClass="icon-list" class="xs" accesskey="q" :onClick="onToggleObjectsList" />
          <custom-btn iconClass="icon-sphere" />
          <custom-btn iconClass="icon-cube" />
          <custom-btn iconClass="icon-cylinder" />
          <custom-btn iconClass="icon-torus" />
          <custom-btn iconClass="icon-plane" />
          <input-file name="file1" />

        <transition name="slide-obj-list">
          <item-obj-list v-if="isVisibleObjectsList" />
        </transition>

          <vec2-picker :vector="[0.3, -0.5]" :onChange="onChangeVec2Picker" />
          <vec3-picker :onChange="onChangeVec3Picker" />

          <color-picker />

          <number-picker :value="0.3" :step="0.1" :min="-5" :max="5" :onChange="onChangeNumberPicker" />

          <custom-btn title="Open Modal Window" :onClick="toggleModalWindow" />
          <modal-window :isOpen="isOpenModalWindow" сloseByOverlayClick :onClose="toggleModalWindow" />
          <custom-btn title="Open popover" ref="popoverRef" :onClick="togglePopover" />

      <presentation-footer :isInFullScreenMode="isVisibleControlsBox" :onToggleFullScreenMode="onToggleFullScreenMode" />

</template>

<template>

    <popover :isOpen="isOpenPopover" :trigger="popoverRef" :onClose="closeModalWindow">
      <color-picker />
    </popover>

    <resize-box v-if="isVisibleControlsBox" tag="section" resize="column" :onEndOfResize="onSetCtrlBoxWidth" :size="widthCtrlBox" class="controls-section">

          <custom-btn accesskey="s" iconClass="icon-settings" class="xs" />
          <custom-btn title="Fragment" iconClass="icon-fragment" :active="activeTabName === tabNames.FRAGMENT_SHADER" :data="tabNames.FRAGMENT_SHADER" :onClick="onSetActiveTabName" />
          <custom-btn title="Vertex" iconClass="icon-vertex" :active="activeTabName === tabNames.VERTEX_SHADER" :data="tabNames.VERTEX_SHADER" :onClick="onSetActiveTabName" />
          <custom-btn iconClass="icon-new-file" class="xs" />
          <custom-btn iconClass="icon-save" class="xs" />
          <custom-btn iconClass="icon-open-folder" class="xs" />
          <custom-btn iconClass="icon-gallery" class="xs" />
          <custom-btn :link="`/${urls.MATERIAL_EDITOR}`" accesskey="w" iconClass="icon-material-editor" class="xs" />

      <shader-editor-footer />
    </resize-box>

          <custom-btn iconClass="icon-list" class="xs" accesskey="q" :onClick="onToggleObjectsList" />
          <custom-btn iconClass="icon-sphere" />
          <custom-btn iconClass="icon-cube" />
          <custom-btn iconClass="icon-cylinder" />
          <custom-btn iconClass="icon-torus" />
          <custom-btn iconClass="icon-plane" />
          <input-file name="file1" />

        <transition name="slide-obj-list">
          <item-obj-list v-if="isVisibleObjectsList" />
        </transition>

          <vec2-picker :vector="[0.3, -0.5]" :onChange="onChangeVec2Picker" />
          <vec3-picker :onChange="onChangeVec3Picker" />

          <color-picker />

          <number-picker :value="0.3" :step="0.1" :min="-5" :max="5" :onChange="onChangeNumberPicker" />

          <custom-btn title="Open Modal Window" :onClick="toggleModalWindow" />
          <modal-window :isOpen="isOpenModalWindow" сloseByOverlayClick :onClose="toggleModalWindow" />
          <custom-btn title="Open popover" ref="popoverRef" :onClick="togglePopover" />

      <presentation-footer :isInFullScreenMode="isVisibleControlsBox" :onToggleFullScreenMode="onToggleFullScreenMode" />

</template>


Show / mark Raspberries as offline in frontend

<template>
    <v-container fluid class="pa-0">

        <v-layout row v-for="raspberry in raspberries" :key="raspberry.hostname" class="mb-5">

            <v-flex xs2>

            </v-flex>

            <v-flex xs10 class="raspberry-meta-online" v-if="raspberry.is_online === true">

{{ raspberry.hostname }}

                    <v-icon dark class="icon mr-1">timeline</v-icon>
                    {{ raspberry.cpu.cores }}x {{ raspberry.cpu.speed }} MHz
                    – Load: {{ raspberry.load }}

                    <v-icon dark class="icon mr-1">memory</v-icon>
                    {{ raspberry.memory.percent }}% RAM
                    – {{ raspberry.uptime }} Tage Uptime
                    – {{ raspberry.temperature }} °C

                        <v-icon dark class="icon mr-1">storage</v-icon>
                        {{ raspberry.disk.label }}
                        – {{ raspberry.disk.free }} GB / {{ raspberry.disk.size }} GB verfügbar

            </v-flex>

            <v-flex xs10 class="raspberry-meta-offline" v-if="raspberry.is_online === false">

{{ raspberry.hostname }}

                    <v-icon dark class="icon mr-1">warning</v-icon>
                    Das Gerät ist zur Zeit nicht erreichbar

            </v-flex>

        </v-layout>
    </v-container>
</template>

<template>
    <v-container fluid class="pa-0">
        <v-layout row v-for="raspberry in raspberries" :key="raspberry.hostname" class="mb-5">

            <v-flex xs2>

            </v-flex>

            <v-flex xs10 class="raspberry-meta">

{{ raspberry.hostname }}

                    <v-icon dark class="icon mr-1">timeline</v-icon>
                    {{ raspberry.cpu.cores }}x {{ raspberry.cpu.speed }} MHz
                    – Load: {{ raspberry.load }}

                    <v-icon dark class="icon mr-1">memory</v-icon>
                    {{ raspberry.memory.percent }}% RAM
                    – {{ raspberry.uptime }} Tage Uptime
                    – {{ raspberry.temperature }} °C

                        <v-icon dark class="icon mr-1">storage</v-icon>
                        {{ raspberry.disk.label }}
                        – {{ raspberry.disk.free }} GB / {{ raspberry.disk.size }} GB verfügbar

            </v-flex>

        </v-layout>
    </v-container>
</template>


fix user search modal not loading in admin (#748)

<template lang="pug">
#modal-base-flat
  <iframe class="video-frame" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
  #video-close-btn.btn.well.well-sm.well-parchment.close-btn(data-dismiss="modal")
    span.glyphicon.glyphicon-remove
  #course-videos-modal
    #videos-content.modal-content.style-flat
      .modal-header
        #modal-close-btn.btn.well.well-sm.well-parchment.close-btn(data-dismiss="modal")
          span.glyphicon.glyphicon-remove
        h3.text-center
          | {{ $t('courses.concept_videos') }}
      .modal-body.container
        .row.videos-row(v-if="videoLevels.length > 0")
          .col-sm-4.course-video.m-l-3(v-for="level in videoLevels")
            .locked(v-if="level.videoStatus === 'locked'")
              .video-link
                img.video-image(
                  :src='level.thumbnail_locked'
                )
                .video-status
                  img.img-locked(src = '/images/pages/play/modal/locked_banner.png')
              .video-text.m-t-2
                span.rtl-allowed.video-title.gray-text
                  | {{ $t(`courses.${level.i18name}`) }}
                p.video-desc.gray-text
                  | {{ $t('courses.locked_videos_desc', { concept_name: $t(`courses.${level.i18name}`) }) }}
            .unlocked(v-else-if="level.videoStatus === 'unlocked'")
              .video-link
                img.video-image(
                  :src='level.thumbnail_unlocked',
                  @click="onImageClick"
                )
                .video-status
                  img.img-unlocked(src = '/images/pages/play/modal/unlocked_banner.png')
              .video-text.m-t-2
                span.rtl-allowed.video-title
                  | {{ $t(`courses.${level.i18name}`) }}
                p.video-desc
                  | {{ $t('courses.unlocked_videos_desc', { concept_name: $t(`courses.${level.i18name}`) }) }}
</template>

<script>
import api from 'core/api';
import utils from 'core/utils'
import VideoPlayer from '@vimeo/player'
import co from 'co'

export default Vue.extend({
  props: {
    courseInstanceID: {
      type: String,
      required: true
    },
    courseID: {
      type: String,
      required: true
    }
  },
  data: () => ({
    videoLevels: []
  }),
  created: co.wrap(function*() {
    try {
      // fetch the levels that contain video URLs from the classroom ID for the given course instance ID.
      const courseInstanceData = yield api.courseInstances.get({courseInstanceID: this.courseInstanceID })
      const classroomData = yield api.classrooms.get({classroomID: courseInstanceData.classroomID})
      const levels = classroomData.courses.find((c) => c._id == this.courseID).levels
      for (let level in levels) {
        const video = utils.videoLevels[levels[level].original]
        if (video) {
          this.videoLevels.push(video)
        }
      }
      // fetch the sessions for the given user and course instance, and check if video levels are locked or unlocked.
      const sessions = yield api.courseInstances.getSessions({courseInstanceID: this.courseInstanceID })
      let sessionsMap = {}
      sessions.map((s) => sessionsMap[s.level.original] = s)
      for (let l of this.videoLevels){
        if (sessionsMap[l.original]) {
          Vue.set(l, 'videoStatus', 'unlocked')
        }
        else {
          Vue.set(l, 'videoStatus', 'locked')
        }
      }
    }
    catch(err) {
      console.error("Error in fetching data:", err)
    }
  }),
  methods: {
    // open video in the iframe when video thumbnail is clicked
    onImageClick: function(e) {
      let src = e.target.src
      src = src.slice(src.search('/images'))
      let video = (this.videoLevels.find(l => l.thumbnail_unlocked == src) || {})
      let frame = $('.video-frame')[0]
      frame.src = me.showChinaVideo() ? video.cn_url : video.url
      frame.style['z-index'] = 3
      $('#videos-content')[0].style.display = "none"
      $('#video-close-btn')[0].style.display = "block"
      if(!me.showChinaVideo()){
        const p = new VideoPlayer(frame);
        p.play().catch((err) => console.log("Error while playing the video:", err))
      }
    }
  },
});
</script>

<style lang="sass" scoped>
@import "app/styles/style-flat-variables"

#modal-base-flat
  position: absolute
  left: -180px
  max-width: 1000px

  .video-frame
    position: relative
    z-index: 0
    height: 500px
    width: 1000px

  #video-close-btn
    display: none
    right: 45px
    z-index: 3

  .close-btn
    position: absolute
    right: 0px
    color: white
    top: 0px
    background: #ba1d00
    padding: 6px 6px 3px
    border: 2px solid #14110e
    border-radius: 0px

  #course-videos-modal

    #videos-content
      background: transparent url('/images/level/popover_background.png') no-repeat
      background-position: 0px 0px
      background-size: 100% 100%
      border: 0
      position: absolute
      top: 100px

      .modal-body
        max-width: 980px

    .course-video
      width: 280px

    .video-image
      height: 140px
      width: 250px

    .video-link
      position: relative

      .video-status
        position: absolute
        left: -20px
        top: -20px

        .img-locked, .img-unlocked
          width: 70px
          height: 50px

    .gray-text
      color: #565656

    .video-title
      font-size: 20px
      font-weight: bold

    .video-desc
      font-size: 14px

</style>

<template lang="pug">
#modal-base-flat
  <iframe class="video-frame" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
  #video-close-btn.btn.well.well-sm.well-parchment.close-btn(data-dismiss="modal")
    span.glyphicon.glyphicon-remove
  #course-videos-modal
    #videos-content.modal-content.style-flat
      .modal-header
        #modal-close-btn.btn.well.well-sm.well-parchment.close-btn(data-dismiss="modal")
          span.glyphicon.glyphicon-remove
        h3.text-center
          | {{ $t('courses.concept_videos') }}
      .modal-body.container
        .row.videos-row(v-if="videoLevels.length > 0")
          .col-sm-4.course-video.m-l-3(v-for="level in videoLevels")
            .locked(v-if="level.videoStatus === 'locked'")
              .video-link
                img.video-image(
                  :src='level.thumbnail_locked'
                )
                .video-status
                  img.img-locked(src = '/images/pages/play/modal/locked_banner.png')
              .video-text.m-t-2
                span.rtl-allowed.video-title.gray-text
                  | {{ $t(`courses.${level.i18name}`) }}
                p.video-desc.gray-text
                  | {{ $t('courses.locked_videos_desc', { concept_name: $t(`courses.${level.i18name}`) }) }}
            .unlocked(v-else-if="level.videoStatus === 'unlocked'")
              .video-link
                img.video-image(
                  :src='level.thumbnail_unlocked',
                  @click="onImageClick"
                )
                .video-status
                  img.img-unlocked(src = '/images/pages/play/modal/unlocked_banner.png')
              .video-text.m-t-2
                span.rtl-allowed.video-title
                  | {{ $t(`courses.${level.i18name}`) }}
                p.video-desc
                  | {{ $t('courses.unlocked_videos_desc', { concept_name: $t(`courses.${level.i18name}`) }) }}
</template>

<script>
import api from 'core/api';
import utils from 'core/utils'
import VideoPlayer from '@vimeo/player'
import co from 'co'

export default Vue.extend({
  props: {
    courseInstanceID: {
      type: String,
      required: true
    },
    courseID: {
      type: String,
      required: true
    }
  },
  data: () => ({
    videoLevels: []
  }),
  created: co.wrap(function*() {
    try {
      // fetch the levels that contain video URLs from the classroom ID for the given course instance ID.
      const courseInstanceData = yield api.courseInstances.get({courseInstanceID: this.courseInstanceID })
      const classroomData = yield api.classrooms.get({classroomID: courseInstanceData.classroomID})
      const levels = classroomData.courses.find((c) => c._id == this.courseID).levels
      for (let level in levels) {
        const video = utils.videoLevels[levels[level].original]
        if (video) {
          this.videoLevels.push(video)
        }
      }
      // fetch the sessions for the given user and course instance, and check if video levels are locked or unlocked.
      const sessions = yield api.courseInstances.getSessions({courseInstanceID: this.courseInstanceID })
      let sessionsMap = {}
      sessions.map((s) => sessionsMap[s.level.original] = s)
      for (let l of this.videoLevels){
        if (sessionsMap[l.original]) {
          Vue.set(l, 'videoStatus', 'unlocked')
        }
        else {
          Vue.set(l, 'videoStatus', 'locked')
        }
      }
    }
    catch(err) {
      console.error("Error in fetching data:", err)
    }
  }),
  methods: {
    // open video in the iframe when video thumbnail is clicked
    onImageClick: function(e) {
      let src = e.target.src
      src = src.slice(src.search('/images'))
      let video = (this.videoLevels.find(l => l.thumbnail_unlocked == src) || {})
      let frame = $('.video-frame')[0]
      frame.src = me.showChinaVideo() ? video.cn_url : video.url
      frame.style['z-index'] = 3
      $('#videos-content')[0].style.display = "none"
      $('#video-close-btn')[0].style.display = "block"
      if(!me.showChinaVideo()){
        const p = new VideoPlayer(frame);
        p.play().catch((err) => console.log("Error while playing the video:", err))
      }
    }
  },
});
</script>

<style lang="sass">
@import "app/styles/style-flat-variables"

#modal-base-flat
  position: absolute
  left: -180px
  max-width: 1000px

  .video-frame
    position: relative
    z-index: 0
    height: 500px
    width: 1000px

  #video-close-btn
    display: none
    right: 45px
    z-index: 3

  .close-btn
    position: absolute
    right: 0px
    color: white
    top: 0px
    background: #ba1d00
    padding: 6px 6px 3px
    border: 2px solid #14110e
    border-radius: 0px

  #course-videos-modal

    #videos-content
      background: transparent url('/images/level/popover_background.png') no-repeat
      background-position: 0px 0px
      background-size: 100% 100%
      border: 0
      position: absolute
      top: 100px

      .modal-body
        max-width: 980px

    .course-video
      width: 280px

    .video-image
      height: 140px
      width: 250px

    .video-link
      position: relative

      .video-status
        position: absolute
        left: -20px
        top: -20px

        .img-locked, .img-unlocked
          width: 70px
          height: 50px

    .gray-text
      color: #565656

    .video-title
      font-size: 20px
      font-weight: bold

    .video-desc
      font-size: 14px

</style>


Bold key text on /funding for better scannability

<script>
import ModalGetLicenses from '../../../ozaria/site/components/teacher-dashboard/modals/ModalGetLicenses'

export default {
  name: 'Funding',
  components: {
    ModalGetLicenses
  },

  data: () => ({
    showModalGetLicenses: false,
  }),

  beforeDestroy () {
    $('#solutions.carousel').carousel().off()
  },

  mounted () {
    $('#solutions.carousel').carousel().off().on('slide.bs.carousel', this.onCarouselSlide)
  },

  methods: {
    onClickSalesCTA (e) {
      window.tracker?.trackEvent('Funding Contact Clicked', { category: 'Teachers', label: `${this.$route.path}` })
      this.showModalGetLicenses = true
    },

    onCarouselDirectMove (slideNum, skipMove) {
      const $carousel = $("#solutions.carousel")
      if (!skipMove) {
        $carousel.carousel(slideNum)
      }
      const $carouselContainer = $(`#${$carousel.attr('id')}-carousel`)
      $carouselContainer.find(`.carousel-tabs li:not(:nth-child(${slideNum + 1}))`).removeClass('active')
      $carouselContainer.find(`.carousel-tabs li:nth-child(${slideNum + 1})`).addClass('active')
      $carouselContainer.find(`.carousel-dot:not(:nth-child(${slideNum + 1}))`).removeClass('active')
      $carouselContainer.find(`.carousel-dot:nth-child(${slideNum + 1})`).addClass('active')
    },

    onCarouselSlide (e) {
      const slideNum = parseInt($(e.relatedTarget).data('slide'), 10)
      this.onCarouselDirectMove(slideNum, true)
    },
  },
}
</script>

<template lang="pug">
  main.container-fluid#funding-view
    section.row#jumbotron-container-fluid
      .item-list
        a(href="/")
          img(src="/images/ozaria/home/ozaria_logo_sun.png" alt="Ozaria branding logo")
        h1(style="margin-bottom:15px;") Need to find funding for your CS program? We can help!
        h2.subtitle-mid Our team is here to help you navigate the COVID-19 Relief Funding Bill and get the most for you and your students.
        h2.subtitle-mid Our CS solutions align with the high-priority initiatives supported by COVID-19 CARES Act–#[strong ESSER] & #[strong GEER] relief funding programs–to provide flexible, high-quality academic and social-emotional learning curricula to address learning loss, help students meet grade-level proficiency, and accelerate learning.
        div
          a.btn.btn-primary.btn-large.btn-moon.sales-btn(@click="onClickSalesCTA") Contact Our Team

    section.section-spacer#funding-summary
      h1.heading-corner(style="max-width: 727px; margin-bottom: 60px;") What You Need to Know

      .row.flex-row
        .col-sm-7
          h2(style="margin-bottom: 20px;") What are ESSER and GEER?
          p
            span Under the CARES Act, the two main funding sources for education are the
            strong  Elementary and Secondary School Emergency Relief Fund (ESSER I and II Funds)
            span  and the
            strong  Governor's Emergency Education Relief Fund (GEER I Fund)
            span . The ESSER fund accounts for approximately $13.2 billion of funding for all states, and the GEER I Fund accounts for approximately $3 billion of funding for all states.
        .col-sm-5
          img(src="/images/pages/funding/funding-esser.png" class="img-responsive" alt="ESSER funds logo" style="border: 10px solid #77b7ac;")
      .row.flex-row
        .col-sm-12
          p ESSER Fund awards are in the same proportion for each state as for Part A of Title I of the Elementary and Secondary Education Act.
          p The GEER Fund gives governors a flexible source of federal funding to meet local educational needs resulting from the pandemic. Governors have wide discretion when it comes to allocating funds and can give GEER grants to districts that the state "deems to have been most significantly impacted by COVID-19", so these districts are able to continue providing educational services to both public and non-public schools.
      .row.flex-row(style="padding: 60px 0;")
        .col-sm-6
          img(src="/images/pages/funding/funding-teacher-image-0.jpg" class="img-responsive" loading="lazy" style="border: 10px solid #F7D047;" alt="")
        .col-sm-6
          h2(style="margin-bottom: 20px;") How can these funds be used?
          p ESSER and GEER funds can be used...
          ul
            li for "any activity authorized under Perkins V"
            li for "purchasing educational technology (including hardware and software) that aids in regular and substantive interaction between students and their classroom instructors..."
            li for providing after-school, tutoring, and summer programs or using other strategies to increase learning time
      .row.flex-row(style="margin-bottom: 50px;")
        .col-xs-12
          h2
            span See lists of funds awarded and their appropriations:
            span
            a(href="https://covid-relief-data.ed.gov/" rel="nofollow" target="_blank") ESSER
            span= ', '
            a(href="https://hunt-institute.org/covid-19-resources/geer-fund-utilization/" rel="nofollow" target="_blank") GEER

    section.full-width.section-spacer#back_cta_1
      .row#essa
        .col-xs-8.col-xs-offset-2
          a.btn.btn-primary.btn-large.btn-moon.sales-btn(@click="onClickSalesCTA") Request a Quote

    section#our-solutions
      h1.heading-corner How Our Solutions Align and Qualify
      p See below for more information on how our solutions align and qualify. Don't let these valuable resources go to waste–connect with us to get the funding you need.

    #solutions-carousel
      .container.carousel-tabs-container
        ul.carousel-tabs.nav.nav-tabs(role="tablist")
          li.carousel-tab.active(data-selector="#solutions", data-slide-num="0", @click="() => onCarouselDirectMove(0)")
            h2(data-i18n="new_home.game_based_learning") Ozaria & CodeCombat
          li.carousel-tab(data-selector="#solutions", data-slide-num="1", @click="() => onCarouselDirectMove(1)")
            h2(data-i18n="new_home.text_based_coding") Professional Development
          li.carousel-tab(data-selector="#solutions", data-slide-num="2", @click="() => onCarouselDirectMove(2)")
            h2(data-i18n="new_home.student_impact") Live Online Tutoring
      .container.carousel-container
        #solutions.carousel.slide(data-interval=8000)
          .carousel-inner
            .item.active(data-slide=0)
              .row
                .col-md-6.col-sm-12
                  h4 Ozaria & CodeCombat
                  p ESSER and GEER funds can be used for any expense geared toward learning loss mitigation, and for educational technology (hardware, software, and connectivity) that aids in the regular and substantive educational interaction between students and their classroom instructors. Additionally, all expenses authorized by the Elementary and Secondary Education Act (ESEA) and the Perkins V Act are eligible.
                  ul
                    li With our built-in #[strong scaffolded support] and #[strong self-paced learning] activities, our programs do the #[strong differentiating] for you.
                    li Our curriculum also provides extensive opportunities for #[strong remediation] and #[strong extension].
                    li Our dashboard provides teachers with actionable insights into learner pace, progress, and mastery level, allowing teachers to monitor and address #[strong learning needs at point of use].
                    li Our turn-key lesson slides address #[strong knowledge gaps] and ensure all learners are #[strong prepared for grade-level content].
                .col-md-6.col-sm-12
                  img.img-responsive(src="/images/pages/funding/funding-carousel-0.jpg" loading="lazy" alt="Students coding screenshot")
                  p With many relevant and engaging unplugged activities and collaborative projects included in our curriculum, you can prioritize students' #[strong social and emotional needs] and provide opportunities to fully #[strong re-engage them in the learning progress].
            .item(data-slide=1)
              .row
                .col-md-6.col-sm-12
                  img.img-responsive(src="/images/pages/funding/funding-carousel-1.jpg" loading="lazy" alt="Teacher with computer")
                  div(style="text-align: center; width: 100%; margin-top: 30px; margin-bottom: 30px;")
                    a.btn.btn-primary.btn-large.btn-moon.sales-btn(href="/professional-development") Try Sample PD Lesson
                .col-md-6.col-sm-12
                  h4 Professional Development
                  p ESSER and GEER funds may be used to provide professional development to educators on research-based strategies for meeting students' academic, social, emotional, mental health, and college, career, and future readiness needs.
                  p Our PD was developed using #[strong evidence-based best practices] for teaching and learning.
                  p We provide the tools, resources, and training for educators to improve their teaching quality and effectiveness and better #[strong meet the needs of diverse learners and students suffering learning loss] as a result of COVID-19.
                  p Our online course includes modules on implementing activities to support students' #[strong social and emotional learning], #[strong growth mindset], #[strong scaffolding] instruction for diverse learners, #[strong digital citizenship], and #[strong diversity, equity, and inclusion awareness], among many other topics.
            .item(data-slide=2)
              .row
                .col-md-6.col-sm-12
                  img.img-responsive(src="/images/pages/funding/funding-carousel-2.png" loading="lazy" alt="Online classes screenshot" style="margin-bottom: 30px;")
                .col-md-6.col-sm-12
                  h4 Live Online Tutoring
                  p All federal funding and grants allow for "supporting learning programs for summer school, after school, extended day, and extended year" to accommodate learning loss due to the pandemic.
                  p Our live online classes for individual students or small groups provide individualized instruction and feedback to ensure students get the #[strong personalized support] they need and are #[strong on-track for grade-level instruction].
        .col-lg-12.text-center
          //- Reference https://getbootstrap.com/docs/3.4/javascript/
          .carousel-dot.active(data-selector="#solutions", data-slide-num="0", @click="() => onCarouselDirectMove(0)")
          .carousel-dot(data-selector="#solutions", data-slide-num="1", @click="() => onCarouselDirectMove(1)")
          .carousel-dot(data-selector="#solutions", data-slide-num="2", @click="() => onCarouselDirectMove(2)")

    section.section-spacer(style="margin: 70px;")
      .row.flex-row.text-center(style="width: 100%;")
        .col-xs-8.col-xs-offset-2
          a.btn.btn-primary.btn-large.btn-moon.sales-btn(@click="onClickSalesCTA") Contact Our Team

    modal-get-licenses(v-if="showModalGetLicenses" @close="showModalGetLicenses = false" subtitle="To get help navigating the COVID-19 Relief Funding Bill, send us a message and our classroom success team will be in touch!" email-message="Hi Ozaria! I'm interested in learning more about how to use ESSER and GEER funds for my computer science program.")
</template>

<style lang="scss" scoped>
@import "app/styles/bootstrap/variables";
@import "app/styles/mixins";
@import "app/styles/style-flat-variables";
@import "ozaria/site/styles/common/common.scss";
@import "app/styles/ozaria/_ozaria-style-params.scss";

#funding-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  overflow-x: hidden;
  $teal-light-1: #1FBAB4;
  $teal-light-2: #6AE8E3;
  $teal-dark: #0E4C60;

  h1, h2, h3, p {
    font-family: Work Sans, "Open Sans", sans-serif;
  }

  h1 {
    font-weight: 600;
    font-style: normal;
    font-size: 46px;
    line-height: 56px;
    color: black;
    letter-spacing: -0.7px;
  }

  h1.smaller-38 {
    font-size: 38px;
    line-height: 45px;
  }

  h1.heading-corner {
    background: url(/images/ozaria/home/heading_corner.svg) no-repeat left 0 top 0;
    background-size: 48px;
    padding: 12px 0 12px 20px;
  }

  h2 {
    font-style: normal;
    font-weight: 600;
    font-size: 28px;
    line-height: 38px;
    letter-spacing: 0.56px;
  }

  .subtitle-mid {
    max-width: 932px;
  }

  p, ul {
    font-style: normal;
    font-size: 24px;
    line-height: 30px;
    letter-spacing: 0.444px;
    color: black;
  }

  blockquote {
    font-family: "Space Mono";
    font-weight: 400;
    font-style: normal;
    font-size: 24px;
    line-height: 30px;
  }

  blockquote footer {
    font-weight: 700;
    font-size: 16px;
    line-height: 24px;
  }

  .row.flex-row {
    display: flex;
    flex-direction: row;
    align-items: center;
  }

  .teacher-image-1 {
    transform: translateY(-40%);
  }

  @media screen and (max-width: 768px) {
    .row.flex-row {
      display: table;
    }
  }

  background: linear-gradient(277.08deg, #FFF5D1 2.71%, #FFFFFF 41.36%);

  .btn-primary.btn-moon {
    background-color: $moon;
    border-radius: 1px;
    color: $gray;
    text-shadow: unset;
    font-weight: bold;
    @include font-h-5-button-text-black;
    min-width: 260px;
    padding: 15px 0;

    &:hover {
      @include font-h-5-button-text-white;
      background-color: $goldenlight;
      transition: background-color .35s;
    }
  }

  // Most sections have a max width and are centered.
  & > section {
    max-width: 1366px;
    width:100%;
    padding: 0 70px;
    position: relative;
    z-index: 1;
  }

  // This lets us have full width sections easily.
  section.full-width {
    max-width: unset;
    padding: 0;
    margin: 0;
  }

  #jumbotron-container-fluid {
    position: relative;
    margin-top: 55px;
    margin-bottom: 80px;
    padding-bottom: 36px;
    h2 {
      margin-bottom: 40px;
      font-weight: 400;
    }
    p, a {
      margin-bottom: 20px;
    }
    @media screen and (max-width: 768px) {
      // Adds space between image and text on mobile
      & {
        padding-bottom: 50%;
      }
      .item-list{
        text-align: center;
      }
    }
  }

  #back_cta_1 {
    height: 56.4vw;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    &::before {
      content: "";
      background: url(/images/ozaria/home/background_cta_2.png) no-repeat center;
      background-size: cover;
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
  }

  #essa {
    text-align: center;
    h2, p {
      color: white;
    }
  }

  #our-solutions {
    margin-top: 50px;
    margin-bottom: 50px;

    p {
      font-size: 20px;
    }

    h2 {
      max-width: 340px;
    }
  }

  .carousel-dot {
    display: inline-block;
    width: 13px;
    height: 13px;
    border-radius: 6.5px;
    margin: 5px;
    background-color: #C4C4C4;

    &:not(.active) {
      cursor: pointer;
    }
    &.active {
      background-color: $teal-light-1;
      cursor: default;
    }
  }
  #solutions-carousel > .carousel-container.container {
    padding: 38px;
    border: 10px solid $teal-light-2;
    border-image-slice: 1;
    border-image-source: linear-gradient(to bottom right, rgb(50, 119, 215), rgb(133, 237, 200));
    border-width: 10px;

    .carousel-inner {
      @media screen and (min-width: 1200px) {
        height: 574px;
      }

      .item {
        margin-top: 10px;
        height: 100%;
      }

      h4 {
        font-weight: bold;
      }

      .row {
        @media screen and (min-width: 992px) {
          display: flex;
          align-items: center;
          height: 100%;
        }

        p, ul {
          font-family: $body-font;
          font-size: 18px;
          line-height: 24px;
          margin-top: 28px;
        }
      }
    }
  }
  #solutions-carousel > .carousel-tabs-container.container {
    margin-top: 15px;
    padding: 0 30px;

    ul {
      border: 0;

      li {
        margin: 0 10px;
        width: 350px;
        padding: 16px 0 0 0;
        height: 60px;
        border-radius: 8px 8px 0 0;
        background-color: $teal-dark;
        text-align: center;

        &:nth-child(1) {
          background-color: rgb(68, 145, 218);
          &:hover {
            background-color: lighten(rgb(68, 145, 218), 5%);
          }
          &.active {
            background-color: lighten(rgb(68, 145, 218), 10%);
          }
        }

        &:nth-child(2) {
          background-color: rgb(87, 177, 210);
          &:hover {
            background-color: lighten(rgb(87, 177, 210), 5%);
          }
          &.active {
            background-color: lighten(rgb(87, 177, 210), 10%);
          }
        }

        &:nth-child(3) {
          background-color: rgb(107, 208, 205);
          &:hover {
            background-color: lighten(rgb(107, 208, 205), 5%);
          }
          &.active {
            background-color: lighten(rgb(107, 208, 205), 10%);
          }
        }

        h2 {
          font-size: 22px;
          line-height: 30px;
          letter-spacing: 0.005em;
          text-overflow: ellipsis;
          white-space: nowrap;
          overflow: hidden;
          color: white;
        }
        &:not(.active) {
          cursor: pointer;
        }
        &.active {
          padding-bottom: 54px;
          margin-bottom: -10px;

          h2 {
            color: $teal-dark;
          }
        }
      }
    }
    @media screen and (max-width: 1200px) {
      display: none;
    }
  }
}

/* Global style to override style-flat footer gap */
.style-flat div#footer {
  margin-top: 0;
}
</style>

<script>
import ModalGetLicenses from '../../../ozaria/site/components/teacher-dashboard/modals/ModalGetLicenses'

export default {
  name: 'Funding',
  components: {
    ModalGetLicenses
  },

  data: () => ({
    showModalGetLicenses: false,
  }),

  beforeDestroy () {
    $('#solutions.carousel').carousel().off()
  },

  mounted () {
    $('#solutions.carousel').carousel().off().on('slide.bs.carousel', this.onCarouselSlide)
  },

  methods: {
    onClickSalesCTA (e) {
      window.tracker?.trackEvent('Funding Contact Clicked', { category: 'Teachers', label: `${this.$route.path}` })
      this.showModalGetLicenses = true
    },

    onCarouselDirectMove (slideNum, skipMove) {
      const $carousel = $("#solutions.carousel")
      if (!skipMove) {
        $carousel.carousel(slideNum)
      }
      const $carouselContainer = $(`#${$carousel.attr('id')}-carousel`)
      $carouselContainer.find(`.carousel-tabs li:not(:nth-child(${slideNum + 1}))`).removeClass('active')
      $carouselContainer.find(`.carousel-tabs li:nth-child(${slideNum + 1})`).addClass('active')
      $carouselContainer.find(`.carousel-dot:not(:nth-child(${slideNum + 1}))`).removeClass('active')
      $carouselContainer.find(`.carousel-dot:nth-child(${slideNum + 1})`).addClass('active')
    },

    onCarouselSlide (e) {
      const slideNum = parseInt($(e.relatedTarget).data('slide'), 10)
      this.onCarouselDirectMove(slideNum, true)
    },
  },
}
</script>

<template lang="pug">
  main.container-fluid#funding-view
    section.row#jumbotron-container-fluid
      .item-list
        a(href="/")
          img(src="/images/ozaria/home/ozaria_logo_sun.png" alt="Ozaria branding logo")
        h1(style="margin-bottom:15px;") Need to find funding for your CS program? We can help!
        h2.subtitle-mid Our team is here to help you navigate the COVID-19 Relief Funding Bill and get the most for you and your students.
        h2.subtitle-mid Our CS solutions align with the high-priority initiatives supported by COVID-19 CARES Act–ESSER & GEER relief funding programs–to provide flexible, high-quality academic and social-emotional learning curricula to address learning loss, help students meet grade-level proficiency, and accelerate learning.
        div
          a.btn.btn-primary.btn-large.btn-moon.sales-btn(@click="onClickSalesCTA") Contact Our Team

    section.section-spacer#funding-summary
      h1.heading-corner(style="max-width: 727px; margin-bottom: 60px;") What You Need to Know

      .row.flex-row
        .col-sm-7
          h2(style="margin-bottom: 20px;") What are ESSER and GEER?
          p
            span Under the CARES Act, the two main funding sources for education are the
            strong  Elementary and Secondary School Emergency Relief Fund (ESSER I and II Funds)
            span  and the
            strong  Governor's Emergency Education Relief Fund (GEER I Fund)
            span . The ESSER fund accounts for approximately $13.2 billion of funding for all states, and the GEER I Fund accounts for approximately $3 billion of funding for all states.
        .col-sm-5
          img(src="/images/pages/funding/funding-esser.png" class="img-responsive" alt="ESSER funds logo" style="border: 10px solid #77b7ac;")
      .row.flex-row
        .col-sm-12
          p ESSER Fund awards are in the same proportion for each state as for Part A of Title I of the Elementary and Secondary Education Act.
          p The GEER Fund gives governors a flexible source of federal funding to meet local educational needs resulting from the pandemic. Governors have wide discretion when it comes to allocating funds and can give GEER grants to districts that the state "deems to have been most significantly impacted by COVID-19", so these districts are able to continue providing educational services to both public and non-public schools.
      .row.flex-row(style="padding: 60px 0;")
        .col-sm-6
          img(src="/images/pages/funding/funding-teacher-image-0.jpg" class="img-responsive" loading="lazy" style="border: 10px solid #F7D047;" alt="")
        .col-sm-6
          h2(style="margin-bottom: 20px;") How can these funds be used?
          p ESSER and GEER funds can be used...
          ul
            li for "any activity authorized under Perkins V"
            li for "purchasing educational technology (including hardware and software) that aids in regular and substantive interaction between students and their classroom instructors..."
            li for providing after-school, tutoring, and summer programs or using other strategies to increase learning time
      .row.flex-row(style="margin-bottom: 50px;")
        .col-xs-12
          h2
            span See lists of funds awarded and their appropriations:
            span
            a(href="https://covid-relief-data.ed.gov/" rel="nofollow" target="_blank") ESSER
            span= ', '
            a(href="https://hunt-institute.org/covid-19-resources/geer-fund-utilization/" rel="nofollow" target="_blank") GEER

    section.full-width.section-spacer#back_cta_1
      .row#essa
        .col-xs-8.col-xs-offset-2
          a.btn.btn-primary.btn-large.btn-moon.sales-btn(@click="onClickSalesCTA") Request a Quote

    section#our-solutions
      h1.heading-corner How Our Solutions Align and Qualify
      p See below for more information on how our solutions align and qualify. Don't let these valuable resources go to waste–connect with us to get the funding you need.

    #solutions-carousel
      .container.carousel-tabs-container
        ul.carousel-tabs.nav.nav-tabs(role="tablist")
          li.carousel-tab.active(data-selector="#solutions", data-slide-num="0", @click="() => onCarouselDirectMove(0)")
            h2(data-i18n="new_home.game_based_learning") Ozaria & CodeCombat
          li.carousel-tab(data-selector="#solutions", data-slide-num="1", @click="() => onCarouselDirectMove(1)")
            h2(data-i18n="new_home.text_based_coding") Professional Development
          li.carousel-tab(data-selector="#solutions", data-slide-num="2", @click="() => onCarouselDirectMove(2)")
            h2(data-i18n="new_home.student_impact") Live Online Tutoring
      .container.carousel-container
        #solutions.carousel.slide(data-interval=8000)
          .carousel-inner
            .item.active(data-slide=0)
              .row
                .col-md-6.col-sm-12
                  h4 Ozaria & CodeCombat
                  p ESSER and GEER funds can be used for any expense geared toward learning loss mitigation, and for educational technology (hardware, software, and connectivity) that aids in the regular and substantive educational interaction between students and their classroom instructors. Additionally, all expenses authorized by the Elementary and Secondary Education Act (ESEA) and the Perkins V Act are eligible.
                  ul
                    li With our built-in scaffolded support and self-paced learning activities, our programs do the differentiating for you.
                    li Our curriculum also provides extensive opportunities for remediation and extension.
                    li Our dashboard provides teachers with actionable insights into learner pace, progress, and mastery level, allowing teachers to monitor and address learning needs at point of use.
                    li Our turn-key lesson slides address knowledge gaps and ensure all learners are prepared for grade-level content.
                .col-md-6.col-sm-12
                  img.img-responsive(src="/images/pages/funding/funding-carousel-0.jpg" loading="lazy" alt="Students coding screenshot")
                  p With many relevant and engaging unplugged activities and collaborative projects included in our curriculum, you can prioritize students' social and emotional needs and provide opportunities to fully re-engage them in the learning progress.
            .item(data-slide=1)
              .row
                .col-md-6.col-sm-12
                  img.img-responsive(src="/images/pages/funding/funding-carousel-1.jpg" loading="lazy" alt="Teacher with computer")
                  div(style="text-align: center; width: 100%; margin-top: 30px; margin-bottom: 30px;")
                    a.btn.btn-primary.btn-large.btn-moon.sales-btn(href="/professional-development") Try Sample PD Lesson
                .col-md-6.col-sm-12
                  h4 Professional Development
                  p ESSER and GEER funds may be used to provide professional development to educators on research-based strategies for meeting students' academic, social, emotional, mental health, and college, career, and future readiness needs.
                  p Our PD was developed using evidence-based best practices for teaching and learning.
                  p We provide the tools, resources, and training for educators to improve their teaching quality and effectiveness and better meet the needs of diverse learners and students suffering learning loss as a result of COVID-19.
                  p Our online course includes modules on implementing activities to support students' social and emotional learning, growth mindset, scaffolding instruction for diverse learners, digital citizenship, and diversity, equity, and inclusion awareness, among many other topics.
            .item(data-slide=2)
              .row
                .col-md-6.col-sm-12
                  img.img-responsive(src="/images/pages/funding/funding-carousel-2.png" loading="lazy" alt="Online classes screenshot" style="margin-bottom: 30px;")
                .col-md-6.col-sm-12
                  h4 Live Online Tutoring
                  p All federal funding and grants allow for "supporting learning programs for summer school, after school, extended day, and extended year" to accommodate learning loss due to the pandemic.
                  p Our live online classes for individual students or small groups provide individualized instruction and feedback to ensure students get the personalized support they need and are on-track for grade-level instruction.
        .col-lg-12.text-center
          //- Reference https://getbootstrap.com/docs/3.4/javascript/
          .carousel-dot.active(data-selector="#solutions", data-slide-num="0", @click="() => onCarouselDirectMove(0)")
          .carousel-dot(data-selector="#solutions", data-slide-num="1", @click="() => onCarouselDirectMove(1)")
          .carousel-dot(data-selector="#solutions", data-slide-num="2", @click="() => onCarouselDirectMove(2)")

    section.section-spacer(style="margin: 70px;")
      .row.flex-row.text-center(style="width: 100%;")
        .col-xs-8.col-xs-offset-2
          a.btn.btn-primary.btn-large.btn-moon.sales-btn(@click="onClickSalesCTA") Contact Our Team

    modal-get-licenses(v-if="showModalGetLicenses" @close="showModalGetLicenses = false" subtitle="To get help navigating the COVID-19 Relief Funding Bill, send us a message and our classroom success team will be in touch!" email-message="Hi Ozaria! I'm interested in learning more about how to use ESSER and GEER funds for my computer science program.")
</template>

<style lang="scss" scoped>
@import "app/styles/bootstrap/variables";
@import "app/styles/mixins";
@import "app/styles/style-flat-variables";
@import "ozaria/site/styles/common/common.scss";
@import "app/styles/ozaria/_ozaria-style-params.scss";

#funding-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  overflow-x: hidden;
  $teal-light-1: #1FBAB4;
  $teal-light-2: #6AE8E3;
  $teal-dark: #0E4C60;

  h1, h2, h3, p {
    font-family: Work Sans, "Open Sans", sans-serif;
  }

  h1 {
    font-weight: 600;
    font-style: normal;
    font-size: 46px;
    line-height: 56px;
    color: black;
    letter-spacing: -0.7px;
  }

  h1.smaller-38 {
    font-size: 38px;
    line-height: 45px;
  }

  h1.heading-corner {
    background: url(/images/ozaria/home/heading_corner.svg) no-repeat left 0 top 0;
    background-size: 48px;
    padding: 12px 0 12px 20px;
  }

  h2 {
    font-style: normal;
    font-weight: 600;
    font-size: 28px;
    line-height: 38px;
    letter-spacing: 0.56px;
  }

  .subtitle-mid {
    max-width: 932px;
  }

  p, ul {
    font-style: normal;
    font-size: 24px;
    line-height: 30px;
    letter-spacing: 0.444px;
    color: black;
  }

  blockquote {
    font-family: "Space Mono";
    font-weight: 400;
    font-style: normal;
    font-size: 24px;
    line-height: 30px;
  }

  blockquote footer {
    font-weight: 700;
    font-size: 16px;
    line-height: 24px;
  }

  .row.flex-row {
    display: flex;
    flex-direction: row;
    align-items: center;
  }

  .teacher-image-1 {
    transform: translateY(-40%);
  }

  @media screen and (max-width: 768px) {
    .row.flex-row {
      display: table;
    }
  }

  background: linear-gradient(277.08deg, #FFF5D1 2.71%, #FFFFFF 41.36%);

  .btn-primary.btn-moon {
    background-color: $moon;
    border-radius: 1px;
    color: $gray;
    text-shadow: unset;
    font-weight: bold;
    @include font-h-5-button-text-black;
    min-width: 260px;
    padding: 15px 0;

    &:hover {
      @include font-h-5-button-text-white;
      background-color: $goldenlight;
      transition: background-color .35s;
    }
  }

  // Most sections have a max width and are centered.
  & > section {
    max-width: 1366px;
    width:100%;
    padding: 0 70px;
    position: relative;
    z-index: 1;
  }

  // This lets us have full width sections easily.
  section.full-width {
    max-width: unset;
    padding: 0;
    margin: 0;
  }

  #jumbotron-container-fluid {
    position: relative;
    margin-top: 55px;
    margin-bottom: 80px;
    padding-bottom: 36px;
    h2 {
      margin-bottom: 40px;
      font-weight: 400;
    }
    p, a {
      margin-bottom: 20px;
    }
    @media screen and (max-width: 768px) {
      // Adds space between image and text on mobile
      & {
        padding-bottom: 50%;
      }
      .item-list{
        text-align: center;
      }
    }
  }

  #back_cta_1 {
    height: 56.4vw;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    &::before {
      content: "";
      background: url(/images/ozaria/home/background_cta_2.png) no-repeat center;
      background-size: cover;
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
  }

  #essa {
    text-align: center;
    h2, p {
      color: white;
    }
  }

  #our-solutions {
    margin-top: 50px;
    margin-bottom: 50px;

    p {
      font-size: 20px;
    }

    h2 {
      max-width: 340px;
    }
  }

  .carousel-dot {
    display: inline-block;
    width: 13px;
    height: 13px;
    border-radius: 6.5px;
    margin: 5px;
    background-color: #C4C4C4;

    &:not(.active) {
      cursor: pointer;
    }
    &.active {
      background-color: $teal-light-1;
      cursor: default;
    }
  }
  #solutions-carousel > .carousel-container.container {
    padding: 38px;
    border: 10px solid $teal-light-2;
    border-image-slice: 1;
    border-image-source: linear-gradient(to bottom right, rgb(50, 119, 215), rgb(133, 237, 200));
    border-width: 10px;

    .carousel-inner {
      @media screen and (min-width: 1200px) {
        height: 574px;
      }

      .item {
        margin-top: 10px;
        height: 100%;
      }

      h4 {
        font-weight: bold;
      }

      .row {
        @media screen and (min-width: 992px) {
          display: flex;
          align-items: center;
          height: 100%;
        }

        p, ul {
          font-family: $body-font;
          font-size: 18px;
          line-height: 24px;
          margin-top: 28px;
        }
      }
    }
  }
  #solutions-carousel > .carousel-tabs-container.container {
    margin-top: 15px;
    padding: 0 30px;

    ul {
      border: 0;

      li {
        margin: 0 10px;
        width: 350px;
        padding: 16px 0 0 0;
        height: 60px;
        border-radius: 8px 8px 0 0;
        background-color: $teal-dark;
        text-align: center;

        &:nth-child(1) {
          background-color: rgb(68, 145, 218);
          &:hover {
            background-color: lighten(rgb(68, 145, 218), 5%);
          }
          &.active {
            background-color: lighten(rgb(68, 145, 218), 10%);
          }
        }

        &:nth-child(2) {
          background-color: rgb(87, 177, 210);
          &:hover {
            background-color: lighten(rgb(87, 177, 210), 5%);
          }
          &.active {
            background-color: lighten(rgb(87, 177, 210), 10%);
          }
        }

        &:nth-child(3) {
          background-color: rgb(107, 208, 205);
          &:hover {
            background-color: lighten(rgb(107, 208, 205), 5%);
          }
          &.active {
            background-color: lighten(rgb(107, 208, 205), 10%);
          }
        }

        h2 {
          font-size: 22px;
          line-height: 30px;
          letter-spacing: 0.005em;
          text-overflow: ellipsis;
          white-space: nowrap;
          overflow: hidden;
          color: white;
        }
        &:not(.active) {
          cursor: pointer;
        }
        &.active {
          padding-bottom: 54px;
          margin-bottom: -10px;

          h2 {
            color: $teal-dark;
          }
        }
      }
    }
    @media screen and (max-width: 1200px) {
      display: none;
    }
  }
}

/* Global style to override style-flat footer gap */
.style-flat div#footer {
  margin-top: 0;
}
</style>


Reduced the size of the apply button on mobile

<template>
  <component
    :is="link ? 'router-link' : 'button'"
    :to="localizedRoute(link)"
    class="button-outline no-outline py15 bg-cl-transparent h4 no-underline sans-serif fs-medium"
    :class="{
      light : color === 'light', 'brdr-white' : color === 'light', 'cl-white' : color === 'light',
      dark : color === 'dark', 'brdr-darkgray' : color === 'dark', 'cl-secondary' : color === 'dark',
      px0 : link ? true : false,
      px40 : link ? false : true
    }"
  >
    <slot>Button</slot>
  </component>
</template>

<script>
import focusClean from 'theme/components/theme/directives/focusClean'

export default {
  name: 'ButtonOutline',
  directives: { focusClean },
  props: {
    color: {
      type: String,
      required: true
    },
    link: {
      type: String,
      default: null,
      required: false
    }
  }
}
</script>

<style lang="scss" scoped>
@import "~theme/css/variables/colors";
@import "~theme/css/helpers/functions/color";
$dark-border: color(secondary);
$white: color(white);
$black: color(black);

.button-outline {
  border: 2px solid;
  width: 100px;
  height: 62px;
}

@media screen and (min-width: 900px) {
  .button-outline {
    min-width: 250px;
  }
}
.dark {
  border: 1px solid $dark-border;
  &:hover,
  &:focus {
    color: $white;
    background: $black;
    border-color: $black;
  }
}
.light {
  &:hover,
  &:focus {
    color: $black;
    background: $white;
    border-color: $white;
  }
}
</style>

<template>
  <component
    :is="link ? 'router-link' : 'button'"
    :to="localizedRoute(link)"
    class="button-outline no-outline py15 bg-cl-transparent h4 no-underline sans-serif fs-medium"
    :class="{
      light : color === 'light', 'brdr-white' : color === 'light', 'cl-white' : color === 'light',
      dark : color === 'dark', 'brdr-darkgray' : color === 'dark', 'cl-secondary' : color === 'dark',
      px0 : link ? true : false,
      px40 : link ? false : true
    }"
  >
    <slot>
      Button
    </slot>
  </component>
</template>

<script>
import focusClean from 'theme/components/theme/directives/focusClean'

export default {
  name: 'ButtonOutline',
  directives: { focusClean },
  props: {
    color: {
      type: String,
      required: true
    },
    link: {
      type: String,
      default: null,
      required: false
    }
  }
}
</script>

<style lang="scss" scoped>
  @import '~theme/css/variables/colors';
  @import '~theme/css/helpers/functions/color';
  $dark-border: color(secondary);
  $white: color(white);
  $black: color(black);

  .button-outline {
    border: 2px solid;
    min-width: 250px;
  }
  .dark {
    border: 1px solid $dark-border;
    &:hover,
    &:focus {
      color: $white;
      background: $black;
      border-color: $black;
    }
  }
  .light {
    &:hover,
    &:focus {
      color: $black;
      background: $white;
      border-color: $white;
    }
  }
</style>


fix(event): use actorName() instead of actor().name

<script lang="coffee">
import Session        from '@/shared/services/session'
import AbilityService from '@/shared/services/ability_service'
import openModal from '@/shared/helpers/open_modal'

import { listenForTranslations } from '@/shared/helpers/listen'

export default
  components:
    ThreadItem: -> import('@/components/thread/item.vue')

  props:
    event: Object
    eventWindow: Object
  computed:
    eventable: -> @event.model()
    choiceInHeadline: ->
      @eventable.poll().hasOptionIcons() &&
      @eventable.stanceChoices().length == 1
    canEdit: ->
  created: ->
    @actions =
      edit_stance:
        icon: 'mdi-pencil'
        canPerform: =>
          @eventable.latest && @eventable.participant() == Session.user()
        perform: =>
          openModal
            component: 'PollCommonEditVoteModal',
            props:
              stance: @eventable.clone()
      translate_stance:
        icon: 'mdi-translate'
        canPerform: =>
          @eventable.reason && AbilityService.canTranslate(@eventable)
        perform: =>
          @eventable.translate(Session.user().locale)

      show_history:
        icon: 'mdi-history'
        canPerform: => @eventable.edited()
        perform: =>
          openModal
            component: 'RevisionHistoryModal'
            props:
              model: @eventable
  # mounted: ->
  #   listenForTranslations($scope)
</script>

<template lang="pug">
thread-item.stance-created(:event="event" :event-window="eventWindow")
  template(v-slot:actions)
    action-dock(:model="eventable" :actions="actions")
  template(v-if="choiceInHeadline" v-slot:headline)
    v-layout(align-center)
      router-link(:to="urlFor(event.actor())") {{event.actorName()}}
      space
      poll-common-stance-choice(:stance-choice="eventable.stanceChoices()[0]")
  poll-common-stance(:stance="eventable" :reason-only="choiceInHeadline")
</template>

<script lang="coffee">
import Session        from '@/shared/services/session'
import AbilityService from '@/shared/services/ability_service'
import openModal from '@/shared/helpers/open_modal'

import { listenForTranslations } from '@/shared/helpers/listen'

export default
  components:
    ThreadItem: -> import('@/components/thread/item.vue')

  props:
    event: Object
    eventWindow: Object
  computed:
    eventable: -> @event.model()
    choiceInHeadline: ->
      @eventable.poll().hasOptionIcons() &&
      @eventable.stanceChoices().length == 1
    canEdit: ->
  created: ->
    @actions =
      edit_stance:
        icon: 'mdi-pencil'
        canPerform: =>
          @eventable.latest && @eventable.participant() == Session.user()
        perform: =>
          openModal
            component: 'PollCommonEditVoteModal',
            props:
              stance: @eventable.clone()
      translate_stance:
        icon: 'mdi-translate'
        canPerform: =>
          @eventable.reason && AbilityService.canTranslate(@eventable)
        perform: =>
          @eventable.translate(Session.user().locale)

      show_history:
        icon: 'mdi-history'
        canPerform: => @eventable.edited()
        perform: =>
          openModal
            component: 'RevisionHistoryModal'
            props:
              model: @eventable
  # mounted: ->
  #   listenForTranslations($scope)
</script>

<template lang="pug">
thread-item.stance-created(:event="event" :event-window="eventWindow")
  template(v-slot:actions)
    action-dock(:model="eventable" :actions="actions")
  template(v-if="choiceInHeadline" v-slot:headline)
    v-layout(align-center)
      router-link(:to="urlFor(event.actor())") {{event.actor().name}}
      space
      poll-common-stance-choice(:stance-choice="eventable.stanceChoices()[0]")
  poll-common-stance(:stance="eventable" :reason-only="choiceInHeadline")
</template>


[gui] Make jump to PC more intuitive

[gui] Make jump to PC more intuitive

<template>
  <v-app id="simulator" light>

    <v-navigation-drawer
      fixed
      mini-variant
      permanent
      app
    >
      <v-list two-line>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="openFile()">
            <v-list-tile-action>
              <v-icon large>folder_open</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Open File
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('run')">
            <v-list-tile-action>
              <v-icon v-if="!sim.running" large color="green accent-2">play_arrow</v-icon>
              <v-icon v-else large color="red accent-2">pause</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Run
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>refresh</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Reload
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('over')">
            <v-list-tile-action>
              <v-icon large>redo</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Over
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('in')">
            <v-list-tile-action>
              <v-icon large>subdirectory_arrow_right</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step In
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('out')">
            <v-list-tile-action>
              <v-icon large>subdirectory_arrow_left</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Out
        </v-tooltip>
      </v-list>
    </v-navigation-drawer>

    <v-content>
      <v-container fluid fill-height>
        <v-layout row wrap>
          <v-flex xs12 shrink class="simulator-wrapper">

                Registers
                <v-data-table hide-headers hide-actions :items="sim.regs">
                  <template slot="items" slot-scope="props">

{{ props.item.name.toUpperCase() }}

                        <v-edit-dialog lazy>
                          {{ toHex(props.item.value) }}
                          <v-text-field
                            slot="input" label="Hex Value"
                            v-bind:value="toHex(props.item.value)"
                            @change="setDataValue($event, props.item, 'reg')"
                            :rules="[rules.hex, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field
                            slot="input" label="Decimal Value"
                            v-bind:value="props.item.value"
                            @change="setDataValue($event, props.item, 'reg')"
                            :rules="[rules.dec, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        CC: {{ PSRToCC(props.item.value) }}

                  </template>
                </v-data-table>

Console

                Memory
                <v-data-table hide-headers hide-actions :items="mem_view.data">
                  <template slot="items" slot-scope="props">

                        <v-icon v-if="breakpointAt(props.item.addr)" color="red">report</v-icon>
                        <v-icon v-else small color="grey">report</v-icon>

                        <v-icon v-if="PCAt(props.item.addr)" color="blue">play_arrow</v-icon>

{{ toHex(props.item.addr) }}

                        <v-edit-dialog lazy>
                          {{ toHex(props.item.value) }}
                          <v-text-field
                            slot="input" label="Hex Value"
                            v-bind:value="toHex(props.item.value)"
                            @change="setDataValue($event, props.item, 'mem')"
                            :rules="[rules.hex, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field
                            slot="input" label="Decimal Value"
                            v-bind:value="props.item.value"
                            @change="setDataValue($event, props.item, 'mem')"
                            :rules="[rules.dec, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        {{ props.item.line }}

                  </template>
                </v-data-table>

                    <v-text-field single-line label="Jump To Location" @change="jumpToMemViewStr"></v-text-field>

                    <v-tooltip top>
                      <v-btn flat @click="jumpToPC(true)" slot="activator">PC</v-btn>
                      Jump to PC
                    </v-tooltip>
                    <v-tooltip top>
                      <v-btn icon @click="jumpToPrevMemView" slot="activator"><v-icon>arrow_back</v-icon></v-btn>
                      {{ toHex((mem_view.start - mem_view.data.length) & 0xffff) }}
                    </v-tooltip>
                    <v-tooltip top>
                      <v-btn icon @click="jumpToNextMemView" slot="activator"><v-icon>arrow_forward</v-icon></v-btn>
                      {{ toHex((mem_view.start + mem_view.data.length) & 0xffff) }}
                    </v-tooltip>

          </v-flex>
        </v-layout>
      </v-container>
    </v-content>

  </v-app>
</template>

<template>
  <v-app id="simulator" light>

    <v-navigation-drawer
      fixed
      mini-variant
      permanent
      app
    >
      <v-list two-line>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="openFile()">
            <v-list-tile-action>
              <v-icon large>folder_open</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Open File
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('run')">
            <v-list-tile-action>
              <v-icon v-if="!sim.running" large color="green accent-2">play_arrow</v-icon>
              <v-icon v-else large color="red accent-2">pause</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Run
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>refresh</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Reload
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('over')">
            <v-list-tile-action>
              <v-icon large>redo</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Over
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('in')">
            <v-list-tile-action>
              <v-icon large>subdirectory_arrow_right</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step In
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="toggleSimulator('out')">
            <v-list-tile-action>
              <v-icon large>subdirectory_arrow_left</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Out
        </v-tooltip>
      </v-list>
    </v-navigation-drawer>

    <v-content>
      <v-container fluid fill-height>
        <v-layout row wrap>
          <v-flex xs12 shrink class="simulator-wrapper">

                Registers
                <v-data-table hide-headers hide-actions :items="sim.regs">
                  <template slot="items" slot-scope="props">

{{ props.item.name.toUpperCase() }}

                        <v-edit-dialog lazy>
                          {{ toHex(props.item.value) }}
                          <v-text-field
                            slot="input" label="Hex Value"
                            v-bind:value="toHex(props.item.value)"
                            @change="setDataValue($event, props.item, 'reg')"
                            :rules="[rules.hex, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field
                            slot="input" label="Decimal Value"
                            v-bind:value="props.item.value"
                            @change="setDataValue($event, props.item, 'reg')"
                            :rules="[rules.dec, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        CC: {{ PSRToCC(props.item.value) }}

                  </template>
                </v-data-table>

Console

                Memory
                <v-data-table hide-headers hide-actions :items="mem_view.data">
                  <template slot="items" slot-scope="props">

                        <v-icon v-if="breakpointAt(props.item.addr)" color="red">report</v-icon>
                        <v-icon v-else small color="grey">report</v-icon>

                        <v-icon v-if="PCAt(props.item.addr)" color="blue">play_arrow</v-icon>

{{ toHex(props.item.addr) }}

                        <v-edit-dialog lazy>
                          {{ toHex(props.item.value) }}
                          <v-text-field
                            slot="input" label="Hex Value"
                            v-bind:value="toHex(props.item.value)"
                            @change="setDataValue($event, props.item, 'mem')"
                            :rules="[rules.hex, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field
                            slot="input" label="Decimal Value"
                            v-bind:value="props.item.value"
                            @change="setDataValue($event, props.item, 'mem')"
                            :rules="[rules.dec, rules.size16bit]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        {{ props.item.line }}

                  </template>
                </v-data-table>

                    <v-text-field single-line label="Jump To Location" @change="jumpToMemViewStr"></v-text-field>

                    <v-tooltip top>
                      <v-btn flat @click="jumpToPC" slot="activator">PC</v-btn>
                      Jump to PC
                    </v-tooltip>
                    <v-tooltip top>
                      <v-btn icon @click="jumpToPrevMemView" slot="activator"><v-icon>arrow_back</v-icon></v-btn>
                      {{ toHex((mem_view.start - mem_view.data.length) & 0xffff) }}
                    </v-tooltip>
                    <v-tooltip top>
                      <v-btn icon @click="jumpToNextMemView" slot="activator"><v-icon>arrow_forward</v-icon></v-btn>
                      {{ toHex((mem_view.start + mem_view.data.length) & 0xffff) }}
                    </v-tooltip>

          </v-flex>
        </v-layout>
      </v-container>
    </v-content>

  </v-app>
</template>