Fix linter errors

Fix linter errors

<!--
Displays a field as a color picker (can be used with v-model)
-->
<template>
  <base-picker
    icon="mdi-palette"
    :icon-color="value"
    :value="value"
    v-bind="$attrs"
    :parse-picker="parsePicker"
    :vee-rules="{ regex: /#([a-f0-9]{3}){1,2}\b/i }"
    @input="$emit('input', $event)">
    <template slot-scope="picker">
      <v-card>
        <v-color-picker v-if="picker.showPicker"
                        :value="picker.value"
                        flat
                        @input="picker.on.input" />
        <v-spacer />
        <v-btn text color="primary"
               data-testid="action-cancel"
               @click="picker.on.close">
          {{ $tc('global.button.cancel') }}
        </v-btn>
        <v-btn text color="primary"
               data-testid="action-ok"
               @click="picker.on.save">
          {{ $tc('global.button.ok') }}
        </v-btn>
      </v-card>
    </template>

    <!-- passing the append slot through -->
    <template v-slot:append>
      <slot name="append" />
    </template>
  </base-picker>
</template>

<script>
import BasePicker from './BasePicker'

export default {
  name: 'EColorPicker',
  components: { BasePicker },
  props: {
    value: { type: String, required: true }
  },
  methods: {
    parsePicker (val) {
      if (typeof val === 'object') return Promise.resolve(this.removeAlpha(val.hex))
      return Promise.resolve(this.removeAlpha(val))
    },
    removeAlpha (hex) {
      return hex.length === 9 ? hex.substring(0, 7) : hex
    }
  }
}
</script>

<style scoped>
</style>

<!--
Displays a field as a color picker (can be used with v-model)
-->
<template>
  <base-picker
    icon="mdi-palette"
    :icon-color="value"
    :value="value"
    v-bind="$attrs"
    :parse-picker="parsePicker"
    :vee-rules="{ regex: /#([a-f0-9]{3}){1,2}\b/i }"
    @input="$emit('input', $event)">
    <template slot-scope="picker">
      <v-card>
        <v-color-picker v-if="picker.showPicker"
                        :value="picker.value"
                        flat
                        @input="picker.on.input" />
        <v-spacer />
        <v-btn text color="primary" @click="picker.on.close" data-testid="action-cancel">{{ $tc('global.button.cancel') }}</v-btn>
        <v-btn text color="primary" @click="picker.on.save" data-testid="action-ok">{{ $tc('global.button.ok') }}</v-btn>
      </v-card>
    </template>

    <!-- passing the append slot through -->
    <template v-slot:append>
      <slot name="append" />
    </template>
  </base-picker>
</template>

<script>
import BasePicker from './BasePicker'

export default {
  name: 'EColorPicker',
  components: { BasePicker },
  props: {
    value: { type: String, required: true }
  },
  methods: {
    parsePicker (val) {
      if (typeof val === 'object') return Promise.resolve(this.removeAlpha(val.hex))
      return Promise.resolve(this.removeAlpha(val))
    },
    removeAlpha (hex) {
      return hex.length === 9 ? hex.substring(0, 7) : hex
    }
  }
}
</script>

<style scoped>
</style>


testing

testing

<template>

  <div v-el:videowrapperwrapper class="videowrapperwrapper">
    <loading-spinner v-show="loading"></loading-spinner>
    <div v-el:videowrapper v-show="!loading" class="videowrapper">
      <video v-el:video class="video-js vjs-default-skin" @seeking="handleSeek" @timeupdate="updateTime">
        <template v-for="video in videoSources">
          <source :src="video.storage_url" :type="'video/' + video.extension">
        </template>
        <template v-for="track in trackSources">
          <track kind="captions" :src="track.storage_url" :srclang="track.lang" :label="getLangName(track.lang)">
        </template>
      </video>
    </div>
  </div>

</template>

<script>

  const videojs = require('video.js');
  const langcodes = require('./langcodes.json');
  require('./videojs-centerbtns');
  const debounce = require('vue').util.debounce;

  module.exports = {

    props: ['files'],

    data: () => ({
      videoWidth: 0,
      videoHeight: 0,
      dummyTime: 0,
      progressStartingPoint: 0,
      lastUpdateTime: 0,
      loading: true,
    }),

    computed: {
      posterSource() {
        const posterFileExtensions = ['png', 'jpg'];
        const posterArray = this.files.filter(
          (file) => posterFileExtensions.some((ext) => ext === file.extension)
        );
        if (posterArray.length === 0) {
          return '';
        }
        return posterArray[0].storage_url;
      },

      videoSources() {
        const videoFileExtensions = ['mp4', 'webm', 'ogg'];
        return this.files.filter(
          (file) => videoFileExtensions.some((ext) => ext === file.extension)
        );
      },

      trackSources() {
        const trackFileExtensions = ['vtt'];
        return this.files.filter(
          (file) => trackFileExtensions.some((ext) => ext === file.extension)
        );
      },

      aspectRatio() {
        return this.videoWidth / this.videoHeight;
      },

    },

    methods: {
      getLangName(langCode) {
        return langcodes.filter(
            (lang) => lang.code === langCode
        )[0].lang;
      },

      setPlayState(state) {
        this.recordProgress();
        if (state === true) {
          this.videoPlayer.$('.videotoggle').classList.add('videopaused');
          this.videoPlayer.$('.videoreplay').classList.add('display');
          this.videoPlayer.$('.videoforward').classList.add('display');
          this.$emit('startTracking');
        } else {
          this.videoPlayer.$('.videotoggle').classList.remove('videopaused');
          this.videoPlayer.$('.videoreplay').classList.remove('display');
          this.videoPlayer.$('.videoforward').classList.remove('display');
          this.$emit('stopTracking');
        }
      },

      loadedMetaData() {
        this.videoWidth = this.videoPlayer.videoWidth();
        this.videoHeight = this.videoPlayer.videoHeight();
        this.resizeVideo();
        this.videoPlayerIsReady();
        this.loading = false;
      },

      resizeVideo() {
        const wrapperWrapperWidth = this.$els.videowrapperwrapper.clientWidth;
        const wrapperWrapperHeight = this.$els.videowrapperwrapper.clientHeight;

        const neededHeightGivenWidth = wrapperWrapperWidth * (1 / this.aspectRatio);
        const neededWidthGivenHeight = wrapperWrapperHeight * this.aspectRatio;

        let newWidth = 0;
        let newHeight = 0;

        if (neededHeightGivenWidth <= wrapperWrapperHeight) {
          newWidth = wrapperWrapperWidth;
          newHeight = neededHeightGivenWidth;
        } else {
          newWidth = neededWidthGivenHeight;
          newHeight = wrapperWrapperHeight;
        }

        this.$els.videowrapper.setAttribute('style', `width:${newWidth}px;height:${newHeight}px`);
      },

      get debouncedResizeVideo() {
        return debounce(this.resizeVideo, 300);
      },

      videoPlayerIsReady() {
        // this.videoPlayer.addChild('ReplayButton');
        // this.videoPlayer.addChild('ForwardButton');
        // this.videoPlayer.addChild('TogglePlayButton');

        // const centerButtons = this.$els.videowrapper.childNodes[1];
        // const toggleButton = centerButtons
        //   .getElementsByClassName('videotoggle')[0];
        // const replayButton = centerButtons
        //   .getElementsByClassName('videoreplay')[0];
        // const forwardButton = centerButtons
        //   .getElementsByClassName('videoforward')[0];

        // videojs(this.$els.video).on('useractive', () => {
        //   toggleButton.classList.remove('userInactive');
        //   replayButton.classList.remove('userInactive');
        //   forwardButton.classList.remove('userInactive');
        // });

        // videojs(this.$els.video).on('userinactive', () => {
        //   toggleButton.classList.add('userInactive');
        //   replayButton.classList.add('userInactive');
        //   forwardButton.classList.add('userInactive');
        // });

        videojs(this.$els.video).on('play', () => {
          this.setPlayState(true);
        });

        videojs(this.$els.video).on('pause', () => {
          this.setPlayState(false);
        });

        videojs(this.$els.video).on('ended', () => {
          this.setPlayState(false);
        });
      },

      updateTime() {
        this.dummyTime = this.videoPlayer.currentTime();
        if (this.dummyTime - this.lastUpdateTime >= 5) {
          this.recordProgress();
          this.lastUpdateTime = this.dummyTime;
        }
      },
      /* Catches when a user jumps around/skips while playing the video */
      handleSeek() {
        /* Record any progress up to this point */
        this.recordProgress();
        /* Set last check to be where player is at now */
        this.dummyTime = this.videoPlayer.currentTime();
        this.lastUpdateTime = this.dummyTime;
      },

      recordProgress() {
        this.$emit('progressUpdate', Math.max(0,
          (this.dummyTime - this.progressStartingPoint) /
          Math.floor(this.videoPlayer.duration())));
        this.progressStartingPoint = this.videoPlayer.currentTime();
      },
    },

    ready() {
      this.videoPlayer = videojs(this.$els.video, {
        fluid: true,
        autoplay: false,
        controls: true,
        textTrackDisplay: true,
        bigPlayButton: true,
        inactivityTimeout: 1000,
        preload: 'metadata',
        // poster: this.posterSource,
        playbackRates: [0.5, 1.0, 1.25, 1.5, 2.0],
        controlBar: {
          children: [
            { name: 'playToggle' },
            { name: 'currentTimeDisplay' },
            { name: 'timeDivider' },
            { name: 'progressControl' },
            { name: 'durationDisplay' },
            { name: 'remainingTimeDisplay' },
            { name: 'muteToggle' },
            { name: 'VolumeBar' },
            { name: 'playbackRateMenuButton' },
            { name: 'captionsButton' },
            { name: 'fullscreenToggle' },
          ],
        },
      });

      this.videoPlayer.on('loadedmetadata', this.loadedMetaData);
      global.addEventListener('resize', this.debouncedResizeVideo);
    },
    beforeDestroy() {
      this.recordProgress();
      this.$emit('stopTracking');
      global.removeEventListener('resize', this.debouncedResizeVideo);
      this.videoPlayer.dispose();
    },
  };

</script>

<style lang="stylus">

  // Default videojs stylesheet
  // Unable to reference the videojs using require since videojs doesn't have good webpack support
  @import '../../../../../../node_modules/video.js/dist/video-js.css'

  // Videojs skin customization
  .video-js
    font-size: 1em
    color: #fff
    margin: 0 auto
    .vjs-slider
      background-color: #545454
      background-color: rgba(84, 84, 84, 0.5)
    .vjs-load-progress
      background: lighten(#545454, 25%)
      background: rgba(84, 84, 84, 0.5)
      div
        background: lighten(#545454, 50%)
        background: rgba(84, 84, 84, 0.75)

  .video-js .vjs-control-bar,
  .video-js .vjs-big-play-button,
  .video-js .vjs-menu-button .vjs-menu-content
    background-color: #000
    background-color: rgba(0, 0, 0, 0.7)

   // Custom style
  .videowrapperwrapper
    width: 100%
    height: 100%
    background-color: rgba(0, 0, 0, 0)
    position: relative

  .videowrapper
    top: 50%
    left: 50%
    transform: translate(-50%, -50%)
    position: relative
    height: 100%
    background-color: #000

  .video-js .vjs-menu
    font-family: 'NotoSans', 'sans-serif'

  .video-js .vjs-current-time
    display: block

  .video-js .vjs-play-progress
    background-color: #996189

  .video-js .userInactive
    visibility: visible
    opacity: 0
    transition: visibility 1s, opacity 1s

  .video-js .videoreplay,
  .video-js .videoforward,
  .video-js .videotoggle
    background-repeat: no-repeat
    background-size: contain
    cursor: pointer
    position: absolute
    top: 50%
    transform: translate(-50%, -50%)

  .video-js .videoreplay,
  .video-js .videoforward
    display: none
    height: 75px
    width: 75px

  .video-js .videoreplay
    background: url('../icons/ic_replay_10_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)
    left: calc(50% - 125px)

  .video-js .videoforward
    background: url('../icons/ic_forward_10_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)
    left: calc(50% + 125px)

  .video-js .videotoggle
    background: url('../icons/ic_play_circle_outline_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)
    left: 50%
    height: 125px
    width: 125px

  .video-js .videopaused
    background: url('../icons/ic_pause_circle_outline_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)

  .video-js .display
    display: block

</style>

<template>

  <div v-el:videowrapperwrapper class="videowrapperwrapper">
    <loading-spinner v-show="loading"></loading-spinner>
    <div v-el:videowrapper v-show="!loading" class="videowrapper">
      <video v-el:video class="video-js vjs-default-skin" @seeking="handleSeek" @timeupdate="updateTime">
        <template v-for="video in videoSources">
          <source :src="video.storage_url" :type="'video/' + video.extension">
        </template>
        <template v-for="track in trackSources">
          <track kind="captions" :src="track.storage_url" :srclang="track.lang" :label="getLangName(track.lang)">
        </template>
      </video>
    </div>
  </div>

</template>

<script>

  const videojs = require('video.js');
  const langcodes = require('./langcodes.json');
  require('./videojs-centerbtns');
  const debounce = require('vue').util.debounce;

  module.exports = {

    props: ['files'],

    data: () => ({
      videoWidth: 0,
      videoHeight: 0,
      dummyTime: 0,
      progressStartingPoint: 0,
      lastUpdateTime: 0,
      loading: true,
    }),

    computed: {
      posterSource() {
        const posterFileExtensions = ['png', 'jpg'];
        const posterArray = this.files.filter(
          (file) => posterFileExtensions.some((ext) => ext === file.extension)
        );
        if (posterArray.length === 0) {
          return '';
        }
        return posterArray[0].storage_url;
      },

      videoSources() {
        const videoFileExtensions = ['mp4', 'webm', 'ogg'];
        return this.files.filter(
          (file) => videoFileExtensions.some((ext) => ext === file.extension)
        );
      },

      trackSources() {
        const trackFileExtensions = ['vtt'];
        return this.files.filter(
          (file) => trackFileExtensions.some((ext) => ext === file.extension)
        );
      },

      aspectRatio() {
        return this.videoWidth / this.videoHeight;
      },

    },

    methods: {
      getLangName(langCode) {
        return langcodes.filter(
            (lang) => lang.code === langCode
        )[0].lang;
      },

      setPlayState(state) {
        this.recordProgress();
        if (state === true) {
          this.videoPlayer.$('.videotoggle').classList.add('videopaused');
          this.videoPlayer.$('.videoreplay').classList.add('display');
          this.videoPlayer.$('.videoforward').classList.add('display');
          this.$emit('startTracking');
        } else {
          this.videoPlayer.$('.videotoggle').classList.remove('videopaused');
          this.videoPlayer.$('.videoreplay').classList.remove('display');
          this.videoPlayer.$('.videoforward').classList.remove('display');
          this.$emit('stopTracking');
        }
      },

      loadedMetaData() {
        this.videoWidth = this.videoPlayer.videoWidth();
        this.videoHeight = this.videoPlayer.videoHeight();
        this.resizeVideo();
        this.videoPlayerIsReady();
        this.loading = false;
      },

      resizeVideo() {
        const wrapperWrapperWidth = this.$els.videowrapperwrapper.clientWidth;
        const wrapperWrapperHeight = this.$els.videowrapperwrapper.clientHeight;

        const neededHeightGivenWidth = wrapperWrapperWidth * (1 / this.aspectRatio);
        const neededWidthGivenHeight = wrapperWrapperHeight * this.aspectRatio;

        let newWidth = 0;
        let newHeight = 0;

        if (neededHeightGivenWidth <= wrapperWrapperHeight) {
          newWidth = wrapperWrapperWidth;
          newHeight = neededHeightGivenWidth;
        } else {
          newWidth = neededWidthGivenHeight;
          newHeight = wrapperWrapperHeight;
        }

        this.$els.videowrapper.setAttribute('style', `width:${newWidth}px;height:${newHeight}px`);
      },

      get debouncedResizeVideo() {
        return debounce(this.resizeVideo, 300);
      },

      videoPlayerIsReady() {
        this.videoPlayer.addChild('ReplayButton');
        this.videoPlayer.addChild('ForwardButton');
        this.videoPlayer.addChild('TogglePlayButton');

        const centerButtons = this.$els.videowrapper.childNodes[1];
        const toggleButton = centerButtons
          .getElementsByClassName('videotoggle')[0];
        const replayButton = centerButtons
          .getElementsByClassName('videoreplay')[0];
        const forwardButton = centerButtons
          .getElementsByClassName('videoforward')[0];

        videojs(this.$els.video).on('useractive', () => {
          toggleButton.classList.remove('userInactive');
          replayButton.classList.remove('userInactive');
          forwardButton.classList.remove('userInactive');
        });

        videojs(this.$els.video).on('userinactive', () => {
          toggleButton.classList.add('userInactive');
          replayButton.classList.add('userInactive');
          forwardButton.classList.add('userInactive');
        });

        videojs(this.$els.video).on('play', () => {
          this.setPlayState(true);
        });

        videojs(this.$els.video).on('pause', () => {
          this.setPlayState(false);
        });

        videojs(this.$els.video).on('ended', () => {
          this.setPlayState(false);
        });
      },

      updateTime() {
        this.dummyTime = this.videoPlayer.currentTime();
        if (this.dummyTime - this.lastUpdateTime >= 5) {
          this.recordProgress();
          this.lastUpdateTime = this.dummyTime;
        }
      },
      /* Catches when a user jumps around/skips while playing the video */
      handleSeek() {
        /* Record any progress up to this point */
        this.recordProgress();
        /* Set last check to be where player is at now */
        this.dummyTime = this.videoPlayer.currentTime();
        this.lastUpdateTime = this.dummyTime;
      },

      recordProgress() {
        this.$emit('progressUpdate', Math.max(0,
          (this.dummyTime - this.progressStartingPoint) /
          Math.floor(this.videoPlayer.duration())));
        this.progressStartingPoint = this.videoPlayer.currentTime();
      },
    },

    ready() {
      this.videoPlayer = videojs(this.$els.video, {
        fluid: true,
        autoplay: false,
        controls: true,
        textTrackDisplay: true,
        bigPlayButton: false,
        inactivityTimeout: 1000,
        preload: 'metadata',
        // poster: this.posterSource,
        playbackRates: [0.5, 1.0, 1.25, 1.5, 2.0],
        controlBar: {
          children: [
            { name: 'currentTimeDisplay' },
            { name: 'timeDivider' },
            { name: 'progressControl' },
            { name: 'durationDisplay' },
            { name: 'remainingTimeDisplay' },
            { name: 'muteToggle' },
            { name: 'VolumeBar' },
            { name: 'playbackRateMenuButton' },
            { name: 'captionsButton' },
            { name: 'fullscreenToggle' },
          ],
        },
      });

      this.videoPlayer.on('loadedmetadata', this.loadedMetaData);
      global.addEventListener('resize', this.debouncedResizeVideo);
    },
    beforeDestroy() {
      this.recordProgress();
      this.$emit('stopTracking');
      global.removeEventListener('resize', this.debouncedResizeVideo);
      this.videoPlayer.dispose();
    },
  };

</script>

<style lang="stylus">

  // Default videojs stylesheet
  // Unable to reference the videojs using require since videojs doesn't have good webpack support
  @import '../../../../../../node_modules/video.js/dist/video-js.css'

  // Videojs skin customization
  .video-js
    font-size: 1em
    color: #fff
    margin: 0 auto
    .vjs-slider
      background-color: #545454
      background-color: rgba(84, 84, 84, 0.5)
    .vjs-load-progress
      background: lighten(#545454, 25%)
      background: rgba(84, 84, 84, 0.5)
      div
        background: lighten(#545454, 50%)
        background: rgba(84, 84, 84, 0.75)

  .video-js .vjs-control-bar,
  .video-js .vjs-big-play-button,
  .video-js .vjs-menu-button .vjs-menu-content
    background-color: #000
    background-color: rgba(0, 0, 0, 0.7)

   // Custom style
  .videowrapperwrapper
    width: 100%
    height: 100%
    background-color: rgba(0, 0, 0, 0)
    position: relative

  .videowrapper
    top: 50%
    left: 50%
    transform: translate(-50%, -50%)
    position: relative
    height: 100%
    background-color: #000

  .video-js .vjs-menu
    font-family: 'NotoSans', 'sans-serif'

  .video-js .vjs-current-time
    display: block

  .video-js .vjs-play-progress
    background-color: #996189

  .video-js .userInactive
    visibility: visible
    opacity: 0
    transition: visibility 1s, opacity 1s

  .video-js .videoreplay,
  .video-js .videoforward,
  .video-js .videotoggle
    background-repeat: no-repeat
    background-size: contain
    cursor: pointer
    position: absolute
    top: 50%
    transform: translate(-50%, -50%)

  .video-js .videoreplay,
  .video-js .videoforward
    display: none
    height: 75px
    width: 75px

  .video-js .videoreplay
    background: url('../icons/ic_replay_10_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)
    left: calc(50% - 125px)

  .video-js .videoforward
    background: url('../icons/ic_forward_10_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)
    left: calc(50% + 125px)

  .video-js .videotoggle
    background: url('../icons/ic_play_circle_outline_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)
    left: 50%
    height: 125px
    width: 125px

  .video-js .videopaused
    background: url('../icons/ic_pause_circle_outline_white.svg')
    background-repeat: no-repeat
    background-size: contain
    background-color: rgba(0, 0, 0, 0.3)

  .video-js .display
    display: block

</style>


Gets exam preview within exams page working

<template>

  <core-modal :title="$tr('preview')" @cancel="close" maxWidth="100%">
    <ui-progress-linear v-show="loading"/>

        {{ $tr('numQuestions', { num: exam.questionCount })}}

{{$tr('exercise', { num: exerciseIndex + 1 })}}

 q.contentId === exercise.exercise_id)">
                <ui-button
                  @click="goToQuestion(question.itemId)"
                  :type="isSelected(question.itemId) ? 'primary' : 'secondary'">
                    {{ $tr('question', { num: questionIndex + 1 }) }}
                </ui-button>

          <content-renderer
            v-if="content && itemId"
            class="content-renderer"
            ref="contentRenderer"
            :id="content.pk"
            :kind="content.kind"
            :files="content.files"
            :contentId="content.content_id"
            :channelId="exam.channelId"
            :available="content.available"
            :extraFields="content.extra_fields"
            :itemId="itemId"
            :assessment="true"
            :allowHints="false"/>

  </core-modal>

</template>

<template>

  <core-modal :title="$tr('preview')" @cancel="close" maxWidth="100%">

          {{ $tr('numQuestions', { num: exam.questionCount })}}

        <ui-collapsible v-for="(source, exIndex) in questionSources" :title="$tr('exercise', { num: exIndex + 1 })" :open="exIndex===0">

            <template v-for="(question, index) in questions.filter(q => q.contentId === source.exercise_id)">

                  {{ $tr('question', { num: index + 1 }) }}

            </template>

        </ui-collapsible>

        <content-renderer
          v-if="content && itemId"
          class="content-renderer"
          ref="contentRenderer"
          :id="content.pk"
          :kind="content.kind"
          :files="content.files"
          :contentId="content.content_id"
          :channelId="exam.channelId"
          :available="content.available"
          :extraFields="content.extra_fields"
          :itemId="itemId"
          :assessment="true"
          :allowHints="false"/>

    <icon-button :text="$tr('close')" @click="close"/>
  </core-modal>

</template>


bolded username

bolded username

<template>

    <modal v-ref:modal btntext="Edit">

Edit Account Info

          Full Name:
          <input type="text" class="edit-form edit-fullname" aria-label="fullname" id="name" v-model="fullName_new">

          Username:
          <input type="text" class="edit-form edit-username" aria-label="username" id="username" v-model="username_new">

          User Role
          <select v-model="role_new" id="user-role">
            <option :selected="role_new == admin ? true : false" v-if="role_new" value="learner"> Learner </option>
            <option :selected="role_new == admin ? true : false" value="admin"> Admin </option>
          </select>

          <button @click="pw_reset=!pw_reset"> Reset Password </button>
          <button @click="usr_delete=!usr_delete"> Delete User</button>

Username: {{username_new}}

          Enter new password:
          <input type="password" class="edit-form" id="password" required v-model="password_new">

          Confirm new password:
          <input type="password" class="edit-form" id="password-confirm" required v-model="password_new_confirm">

 Are you sure you want to delete
          {{username_new}}?

 {{error_message}}

        <button class="cancel-btn" type="button" @click="cancel">

          <template v-if="pw_reset"> Back </template>

          <template v-if="usr_delete"> No </template>

          <template v-if="!pw_reset && !usr_delete"> Cancel </template>
        </button>

        <button class="confirm-btn" type="button" @click="editUser">
          <template v-if="pw_reset"> Save </template>
          <template v-if="usr_delete"> Yes </template>
          <template v-if="!pw_reset && !usr_delete"> Confirm </template>
        </button>

      <button class="no-border" slot="openbtn">
        Edit Account Info
        <svg class="manage-edit" src="../icons/pencil.svg"></svg>
      </button>

    </modal>

</template>


Key condensed messages by first message in array

<template>

			<button
				ref="loadMoreButton"
				:disabled="channel.historyLoading || !$root.isConnected"
				class="btn"
				@click="onShowMoreClick"
			>
				Loading…
				Show older messages
			</button>

			<template v-for="(message, id) in condensedMessages">
				<DateMarker
					v-if="shouldDisplayDateMarker(message, id)"
					:key="message.id + '-date'"
					:message="message"
				/>

				<MessageCondensed
					v-if="message.type === 'condensed'"
					:key="message.messages[0].id"
					:network="network"
					:keep-scroll-position="keepScrollPosition"
					:messages="message.messages"
				/>
				<Message
					v-else
					:key="message.id"
					:network="network"
					:message="message"
					:keep-scroll-position="keepScrollPosition"
					@linkPreviewToggle="onLinkPreviewToggle"
				/>
			</template>

</template>

<template>

			<button
				ref="loadMoreButton"
				:disabled="channel.historyLoading || !$root.isConnected"
				class="btn"
				@click="onShowMoreClick"
			>
				Loading…
				Show older messages
			</button>

			<template v-for="(message, id) in condensedMessages">
				<DateMarker
					v-if="shouldDisplayDateMarker(message, id)"
					:key="message.id + '-date'"
					:message="message"
				/>

				<MessageCondensed
					v-if="message.type === 'condensed'"
					:key="message.id"
					:network="network"
					:keep-scroll-position="keepScrollPosition"
					:messages="message.messages"
				/>
				<Message
					v-else
					:key="message.id"
					:network="network"
					:message="message"
					:keep-scroll-position="keepScrollPosition"
					@linkPreviewToggle="onLinkPreviewToggle"
				/>
			</template>

</template>


chore(quasar): cleanup touch-pan demo page

<template>

        Click then pan in a direction with your mouse
        Touch and pan in a direction
        on the area below to see it in action.

        Page scrolling is prevented, but you can opt out if you wish.

        Click status: {{ clickStatus }}

 onEvt('click', e)"
        @mousedown="e => onEvt('mousedown', e)"
        @mousemove="e => onEvt('mousemove', e)"
        @mouseup="e => onEvt('mouseup', e)"
        class="custom-area row flex-center"
        ref="area"
      >

{{ info }}

          <q-icon name="arrow_upward" />

            <q-icon name="arrow_back" />

Pan in any direction

            <q-icon name="arrow_forward" />

          <q-icon name="arrow_downward" />

          <q-icon name="touch_app" />

        Panning works both with a mouse or a native touch action.

        You can also capture pan to certain directions (any) only as you'll see below.

        Example on capturing only horizontal panning.

        Notice that on touch capable devices the scrolling is automatically not blocked, since
        we are only capturing horizontally.

        Click status: {{ clickStatus }}

 onEvt('click', e)"
        @mousedown="e => onEvt('mousedown', e)"
        @mousemove="e => onEvt('mousemove', e)"
        @mouseup="e => onEvt('mouseup', e)"
        class="custom-area row flex-center"
      >

{{ infoHorizontal }}

          <q-icon name="arrow_back" />

Pan to left or right only

          <q-icon name="arrow_forward" />

          <q-icon name="touch_app" />

        Example on capturing only vertically panning.
        Page scrolling is prevented, but you can opt out if you wish.

        Click status: {{ clickStatus }}

 onEvt('click', e)"
        @mousedown="e => onEvt('mousedown', e)"
        @mousemove="e => onEvt('mousemove', e)"
        @mouseup="e => onEvt('mouseup', e)"
        class="custom-area row flex-center"
      >

{{ infoVertical }}

          <q-icon name="arrow_upward" />

            Pan to up or down only

          <q-icon name="arrow_downward" />

          <q-icon name="touch_app" />

        For desktops, you can configure to avoid capturing mouse pans if you wish.

</template>

<template>

        Click then pan in a direction with your mouse
        Touch and pan in a direction
        on the area below to see it in action.

        Page scrolling is prevented, but you can opt out if you wish.

        Click status: {{ clickStatus }}

 onEvt('click', e)"
        @mousedown="e => onEvt('mousedown', e)"
        @mousemove="e => onEvt('mousemove', e)"
        @mouseup="e => onEvt('mouseup', e)"
        class="custom-area row flex-center"
        ref="area"
      >

{{ info }}

          <q-icon name="arrow_upward" />

            <q-icon name="arrow_back" />

Pan in any direction (mouse prevent)

            <q-icon name="arrow_forward" />

          <q-icon name="arrow_downward" />

          <q-icon name="touch_app" />

        Panning works both with a mouse or a native touch action.

        You can also capture pan to certain directions (any) only as you'll see below.

        Example on capturing only horizontal panning.

        Notice that on touch capable devices the scrolling is automatically not blocked, since
        we are only capturing horizontally.

        Click status: {{ clickStatus }}

 onEvt('click', e)"
        @mousedown="e => onEvt('mousedown', e)"
        @mousemove="e => onEvt('mousemove', e)"
        @mouseup="e => onEvt('mouseup', e)"
        class="custom-area row flex-center"
      >

{{ infoHorizontal }}

          <q-icon name="arrow_back" />

Pan to left or right only (mouse stop + prevent)

          <q-icon name="arrow_forward" />

          <q-icon name="touch_app" />

        Example on capturing only vertically panning.
        Page scrolling is prevented, but you can opt out if you wish.

        Click status: {{ clickStatus }}

 onEvt('click', e)"
        @mousedown="e => onEvt('mousedown', e)"
        @mousemove="e => onEvt('mousemove', e)"
        @mouseup="e => onEvt('mouseup', e)"
        class="custom-area row flex-center"
      >

{{ infoVertical }}

          <q-icon name="arrow_upward" />

            Pan to up or down only (mouse stop)

          <q-icon name="arrow_downward" />

          <q-icon name="touch_app" />

        For desktops, you can configure to avoid capturing mouse pans if you wish.

</template>


Complete HeaderBar component

Complete HeaderBar component

Related to #1

<template>

        <router-link to="/">mediaGUI</router-link>

              <router-link to="/movies" class="mv0 mh2">MOVIES</router-link>

              <select v-model="selected" @change="changeFilter">
                <option v-for="option in filters" :value="option.value">{{option.label}}</option>
              </select>

              <input type="search" placeholder="Enter search string" :value="query" @input="updateQuery">

              <select v-model="sortBy" @change="changeSort">
                <option v-for="sort in sorts" :value="sort.value">{{sort.label}}</option>
              </select>

							|

              <router-link to="/import" class="mv0">IMPORT</router-link>

              <router-link to="/settings">SETTINGS</router-link>
							|
              <router-link to="/duplicates">DUPLICATES</router-link>
              <router-link to="/prune" class="mv0 mh2">PRUNE</router-link>

</template>

<template>

        <router-link to="/">mediaGUI</router-link>

              <router-link to="/movies" class="mv0 mh3">MOVIES</router-link>

              <select v-model="selected" @change="changeFilter">
                <option v-for="option in filters" :value="option.value">{{option.label}}</option>
              </select>

              <input type="search" placeholder="Enter search string" :value="query" @input="updateQuery">

              <select v-model="sortBy" @change="changeSort">
                <option v-for="sort in sorts" :value="sort.value">{{sort.label}}</option>
              </select>

							|

              <router-link to="/import" class="mv0">IMPORT</router-link>

</template>


lil bit darker

lil bit darker

<template>
  <transition name="fade">

        <drag-icon class="apos-slat__control apos-slat__control--drag" :size="13" />
        <AposContextMenu
          v-if="item._fields"
          :button="more.button"
          :menu="more.menu"
          @item-clicked="$emit('item-clicked', item)"
        />

          <eye-icon :size="14" />

          <span class="apos-slat__extension" :class="[`apos-slat__extension--${item.ext}`]">
            {{ item.ext }}

          {{ item.title }}

{{ item.size }}

        <AposButton
          @click="remove"
          icon="close-icon"
          :icon-only="true"
          :modifiers="['inline']"
          label="Remove Item"
        />

  </transition>
</template>


Performs refactoring

Performs refactoring

<template>

        <form>

                    <fg-input label="Description" placeholder="Vehicle Description..." v-model="description"></fg-input>

                    <fg-input label="Chasis No." placeholder="AESFAIAS7676Q..." :min="17" :max="17" v-model="chassis"></fg-input>

                    <fg-input label="Engine No." placeholder="ADFGHGREADSDFSDR..." :min="11" :max="11" v-model="engine"></fg-input>

                    <fg-input label="Plate No." placeholder="ADFGHGREADSDFSDR..." :min="8" :max="8" v-model="plate"></fg-input>

                    <fg-select label="Vehicle type" placeholder="Select a vehicle type from the list below..." v-model="typeId" :options="types" @change="updateType"></fg-select>

                    <fg-select label="Vehicle brand" placeholder="Select a brand from the list below..." v-model="brandId" :options="brands" @change="updateBrand"></fg-select>

                    <fg-select label="Vehicle model" placeholder="Select a model from the list below..." v-model="modelId" :options="models" @change="updateModel"></fg-select>

                    <fg-select label="Fuel" placeholder="Select a fuel type from the list below..." v-model="fuelId" :options="fuels" @change="updateFuel"></fg-select>

                    <button class="btn btn-info btn-fill btn-wd" :class="{'disabled': isSavingVehicle}" @click.prevent="validBeforeSave">
                                                                    Save
                                                                </button>

        </form>

        <notifications></notifications>

</template>


remove submitOutcome from  outcome modal (#6146)

* remove submitOutcome from  outcome modal

* be sure to flash success

<script lang="coffee">
import Records from '@/shared/services/records'
import { fieldFromTemplate } from '@/shared/helpers/poll'
import PollModalMixin from '@/mixins/poll_modal'
import AnnouncementModalMixin from '@/mixins/announcement_modal'
import Flash from '@/shared/services/flash'

import Vue     from 'vue'
import { exact } from '@/shared/helpers/format_time'
import { parseISO } from 'date-fns'
import { map, sortBy, head, find } from 'lodash'

export default
  mixins: [PollModalMixin, AnnouncementModalMixin]

  props:
    outcome: Object
    close: Function

  data: ->
    options: []
    bestOption: null
    clone: null
    isDisabled: false

  created: ->
    @clone = @outcome.clone()

    if @datesAsOptions()
      @options = map @clone.poll().pollOptions(), (option) ->
        id:        option.id
        value:     exact(parseISO(option.name))
        attendees: option.stances().length

      @bestOption = head sortBy @options, (option) ->
        -1 * option.attendees # sort descending, so the best option is first

      Vue.set(@clone, 'calendarInvite', true)

      @clone.pollOptionId = @outcome.pollOptionId or @bestOption.id
      @clone.customFields.event_summary = @clone.customFields.event_summary or @clone.poll().title

  methods:
    submit: ->
      @clone.customFields.should_send_calendar_invite = @clone.calendarInvite
      @clone.customFields.event_description = @clone.statement if @datesAsOptions()

      if @clone.isNew()
        actionName = "created"
      else
        actionName = "updated"

      @clone.save().then (data) =>
        eventData = find(data.events, (event) => event.kind == 'outcome_created') || {}
        event = Records.events.find(eventData.id)
        Flash.success("poll_common_outcome_form.outcome_#{actionName}")
        @closeModal()
        @openAnnouncementModal(Records.announcements.buildFromModel(event))

    datesAsOptions: ->
      fieldFromTemplate @clone.poll().pollType, 'dates_as_options'

</script>

<template lang="pug">
v-card.poll-common-outcome-modal(@keyup.ctrl.enter="submit()" @keydown.meta.enter.stop.capture="submit()")
  submit-overlay(:value='clone.processing')
  v-card-title
    h1.headline
      span(v-if='clone.isNew()' v-t="'poll_common_outcome_form.new_title'")
      span(v-if='!clone.isNew()' v-t="'poll_common_outcome_form.update_title'")
    v-spacer
    dismiss-modal-button(:close="close")
  .poll-common-outcome-form.px-6.py-4

    .poll-common-calendar-invite(v-if='datesAsOptions()')
      .poll-common-calendar-invite__checkbox.poll-common-checkbox-option
        v-checkbox(v-model='clone.calendarInvite' :label="$t('poll_common_calendar_invite.calendar_invite')")
      .poll-common-calendar-invite__form.animated(v-if='clone.calendarInvite')
        .poll-common-calendar-invite--pad-top
          v-select.lmo-flex__grow(v-model='clone.pollOptionId' :items="options" item-value="id" item-text="value" :label="$t('poll_common_calendar_invite.poll_option_id')")
        .poll-common-calendar-invite--pad-top
          v-text-field.poll-common-calendar-invite__summary(type='text' :placeholder="$t('poll_common_calendar_invite.event_summary_placeholder')" v-model='clone.customFields.event_summary' :label="$t('poll_common_calendar_invite.event_summary')")
          validation-errors(:subject='clone', field='event_summary')
          v-text-field.poll-common-calendar-invite__location(type='text' :placeholder="$t('poll_common_calendar_invite.location_placeholder')" v-model='clone.customFields.event_location' :label="$t('poll_common_calendar_invite.location')")
          //- v-textarea.md-input.poll-common-calendar-invite__description(type='text' :placeholder="$t('poll_common_calendar_invite.event_description_placeholder')" v-model='clone.customFields.event_description' :label="$t('poll_common_calendar_invite.event_description')")

    lmo-textarea.poll-common-outcome-form__statement.lmo-primary-form-input(:model='clone' field='statement' :label="$t('poll_common.statement')" :placeholder="$t('poll_common_outcome_form.statement_placeholder')")
      template(v-slot:actions)
        v-btn.poll-common-outcome-form__submit(color="primary" @click='submit()' v-t="'common.action.save'")
    validation-errors(:subject="clone" field="statement")
</template>

<script lang="coffee">
import Records from '@/shared/services/records'
import { applySequence } from '@/shared/helpers/apply'
import { submitOutcome, eventKind } from '@/shared/helpers/form'
import { fieldFromTemplate } from '@/shared/helpers/poll'
import PollModalMixin from '@/mixins/poll_modal'
import AnnouncementModalMixin from '@/mixins/announcement_modal'

import Vue     from 'vue'
import { exact } from '@/shared/helpers/format_time'
import { parseISO } from 'date-fns'
import { map, sortBy, head, find } from 'lodash'

export default
  mixins: [PollModalMixin, AnnouncementModalMixin]

  props:
    outcome: Object
    close: Function

  data: ->
    options: []
    bestOption: null
    clone: null
    isDisabled: false

  created: ->
    @clone = @outcome.clone()

    if @datesAsOptions()
      @options = map @clone.poll().pollOptions(), (option) ->
        id:        option.id
        value:     exact(parseISO(option.name))
        attendees: option.stances().length

      @bestOption = head sortBy @options, (option) ->
        -1 * option.attendees # sort descending, so the best option is first

      Vue.set(@clone, 'calendarInvite', true)

      @clone.pollOptionId = @outcome.pollOptionId or @bestOption.id
      @clone.customFields.event_summary = @clone.customFields.event_summary or @clone.poll().title

    @submit = submitOutcome @, @clone,
      prepareFn: (prepareArgs) =>
        @clone.customFields.should_send_calendar_invite = @clone.calendarInvite
        @clone.customFields.event_description = @clone.statement if @datesAsOptions()
      successCallback: (data) =>
        eventData = find(data.events, (event) => event.kind == 'outcome_created') || {}
        event = Records.events.find(eventData.id)
        @closeModal()
        @openAnnouncementModal(Records.announcements.buildFromModel(event))

  methods:
    datesAsOptions: ->
      fieldFromTemplate @clone.poll().pollType, 'dates_as_options'

</script>

<template lang="pug">
v-card.poll-common-outcome-modal(@keyup.ctrl.enter="submit()" @keydown.meta.enter.stop.capture="submit()")
  submit-overlay(:value='clone.processing')
  v-card-title
    h1.headline
      span(v-if='clone.isNew()' v-t="'poll_common_outcome_form.new_title'")
      span(v-if='!clone.isNew()' v-t="'poll_common_outcome_form.update_title'")
    v-spacer
    dismiss-modal-button(:close="close")
  .poll-common-outcome-form.px-6.py-4

    .poll-common-calendar-invite(v-if='datesAsOptions()')
      .poll-common-calendar-invite__checkbox.poll-common-checkbox-option
        v-checkbox(v-model='clone.calendarInvite' :label="$t('poll_common_calendar_invite.calendar_invite')")
      .poll-common-calendar-invite__form.animated(v-if='clone.calendarInvite')
        .poll-common-calendar-invite--pad-top
          v-select.lmo-flex__grow(v-model='clone.pollOptionId' :items="options" item-value="id" item-text="value" :label="$t('poll_common_calendar_invite.poll_option_id')")
        .poll-common-calendar-invite--pad-top
          v-text-field.poll-common-calendar-invite__summary(type='text' :placeholder="$t('poll_common_calendar_invite.event_summary_placeholder')" v-model='clone.customFields.event_summary' :label="$t('poll_common_calendar_invite.event_summary')")
          validation-errors(:subject='clone', field='event_summary')
          v-text-field.poll-common-calendar-invite__location(type='text' :placeholder="$t('poll_common_calendar_invite.location_placeholder')" v-model='clone.customFields.event_location' :label="$t('poll_common_calendar_invite.location')")
          //- v-textarea.md-input.poll-common-calendar-invite__description(type='text' :placeholder="$t('poll_common_calendar_invite.event_description_placeholder')" v-model='clone.customFields.event_description' :label="$t('poll_common_calendar_invite.event_description')")

    lmo-textarea.poll-common-outcome-form__statement.lmo-primary-form-input(:model='clone' field='statement' :label="$t('poll_common.statement')" :placeholder="$t('poll_common_outcome_form.statement_placeholder')")
      template(v-slot:actions)
        v-btn.poll-common-outcome-form__submit(color="primary" @click='submit()' v-t="'common.action.save'")
    validation-errors(:subject="clone" field="statement")
</template>


Hide individual learners selector

Hide individual learners selector

<template>

  <div>
    <KRadioButton
      :value="true"
      :label="coachString('entireClassLabel')"
      :currentValue="entireClassIsSelected"
      :disabled="disabled"
      @change="selectEntireClass()"
    />
    <KCheckbox
      v-for="group in groups"
      :key="group.id"
      :label="group.name"
      :checked="groupIsChecked(group.id)"
      :disabled="disabled"
      @change="toggleGroup($event, group.id)"
    />
    <IndividualLearnerSelector
      v-show="false"
      :selectedGroupIds="selectedGroupIds"
      :entireClassIsSelected="entireClassIsSelected"
      :initialAdHocLearners="initialAdHocLearners"
      :targetClassId="classId"
      :disabled="disabled"
      @toggleCheck="toggleGroup"
      @updateLearners="learners => $emit('updateLearners', learners)"
    />
  </div>

</template>

<script>

  import isEqual from 'lodash/isEqual';
  import { coachStringsMixin } from '../../common/commonCoachStrings';
  import IndividualLearnerSelector from './IndividualLearnerSelector';

  export default {
    name: 'RecipientSelector',
    components: { IndividualLearnerSelector },
    mixins: [coachStringsMixin],
    props: {
      // Needs to equal [classId] if entire class is selected
      // Otherwise, [groupId_1, groupId_2] for individual Learner Groups
      value: {
        type: Array,
        required: true,
      },
      // Array of objects, each with 'group' and 'name'
      groups: {
        type: Array,
        required: true,
        validator(value) {
          for (let i = 0; i < value.length; i++) {
            if (!value[i].name || !value[i].id) {
              return false;
            }
          }
          return true;
        },
      },
      // For the 'Entire Class' option
      classId: {
        type: String,
        required: true,
      },
      disabled: {
        type: Boolean,
        default: false,
      },
      initialAdHocLearners: {
        type: Array,
        required: false,
        default: new Array(),
      },
    },
    computed: {
      entireClassIsSelected() {
        return isEqual(this.value, [this.classId]);
      },
      selectedGroupIds() {
        return this.groups.filter(group => this.groupIsChecked(group.id)).map(group => group.id);
      },
    },
    methods: {
      groupIsChecked(groupId) {
        return this.value.includes(groupId);
      },
      selectEntireClass() {
        this.$emit('input', [this.classId]);
      },
      toggleGroup(isChecked, id) {
        let newValue;
        if (isChecked) {
          // If a group is selected, remove classId if it is there
          newValue = [...this.value].filter(id => id !== this.classId);
          this.$emit('input', [...newValue, id]);
        } else {
          newValue = [...this.value].filter(groupId => id !== groupId);
          // If un-selecting the last group, auto-select 'Entire Class'
          if (newValue.length === 0) {
            newValue = [this.classId];
          }
          this.$emit('input', newValue);
        }
      },
    },
    $trs: {},
  };

</script>

<style lang="scss" scoped></style>

<template>

  <div>
    <KRadioButton
      :value="true"
      :label="coachString('entireClassLabel')"
      :currentValue="entireClassIsSelected"
      :disabled="disabled"
      @change="selectEntireClass()"
    />
    <KCheckbox
      v-for="group in groups"
      :key="group.id"
      :label="group.name"
      :checked="groupIsChecked(group.id)"
      :disabled="disabled"
      @change="toggleGroup($event, group.id)"
    />
    <IndividualLearnerSelector
      :selectedGroupIds="selectedGroupIds"
      :entireClassIsSelected="entireClassIsSelected"
      :initialAdHocLearners="initialAdHocLearners"
      :targetClassId="classId"
      :disabled="disabled"
      @toggleCheck="toggleGroup"
      @updateLearners="learners => $emit('updateLearners', learners)"
    />
  </div>

</template>

<script>

  import isEqual from 'lodash/isEqual';
  import { coachStringsMixin } from '../../common/commonCoachStrings';
  import IndividualLearnerSelector from './IndividualLearnerSelector';

  export default {
    name: 'RecipientSelector',
    components: { IndividualLearnerSelector },
    mixins: [coachStringsMixin],
    props: {
      // Needs to equal [classId] if entire class is selected
      // Otherwise, [groupId_1, groupId_2] for individual Learner Groups
      value: {
        type: Array,
        required: true,
      },
      // Array of objects, each with 'group' and 'name'
      groups: {
        type: Array,
        required: true,
        validator(value) {
          for (let i = 0; i < value.length; i++) {
            if (!value[i].name || !value[i].id) {
              return false;
            }
          }
          return true;
        },
      },
      // For the 'Entire Class' option
      classId: {
        type: String,
        required: true,
      },
      disabled: {
        type: Boolean,
        default: false,
      },
      initialAdHocLearners: {
        type: Array,
        required: false,
        default: new Array(),
      },
    },
    computed: {
      entireClassIsSelected() {
        return isEqual(this.value, [this.classId]);
      },
      selectedGroupIds() {
        return this.groups.filter(group => this.groupIsChecked(group.id)).map(group => group.id);
      },
    },
    methods: {
      groupIsChecked(groupId) {
        return this.value.includes(groupId);
      },
      selectEntireClass() {
        this.$emit('input', [this.classId]);
      },
      toggleGroup(isChecked, id) {
        let newValue;
        if (isChecked) {
          // If a group is selected, remove classId if it is there
          newValue = [...this.value].filter(id => id !== this.classId);
          this.$emit('input', [...newValue, id]);
        } else {
          newValue = [...this.value].filter(groupId => id !== groupId);
          // If un-selecting the last group, auto-select 'Entire Class'
          if (newValue.length === 0) {
            newValue = [this.classId];
          }
          this.$emit('input', newValue);
        }
      },
    },
    $trs: {},
  };

</script>

<style lang="scss" scoped></style>


Fix modal text centering and name-invalid error message in Safari

<template>
<form class="sticker-creation-modal lite-modal" @submit.prevent="emitAddSticker">

	clear

Add a sticker

Drag image or click to upload

		<input name="sticker" type="file" placeholder="Image" accept="image/png, image/jpeg" @change="showStickerPreview($event)" required>

	<input v-model="newStickerName" name="name" placeholder="Sticker Name" pattern="^:?-?[a-z0-9]+:?$" maxlength="20" autocomplete="off" spellcheck="false" oninvalid="setCustomValidity('Lowercase letters and numbers only')" required>

 0" class="sticker-upload-error">{{stickerUploadError}}

	<button class="btn">Add</button>

</form>
</template>

<template>
<form class="sticker-creation-modal lite-modal" @submit.prevent="emitAddSticker">

	clear

Add a sticker

Drag image or click to upload

		<input name="sticker" type="file" placeholder="Image" accept="image/png, image/jpeg" @change="showStickerPreview($event)" required>

	<input v-model="newStickerName" name="name" placeholder="Sticker Name" pattern="^:?-?[a-z0-9]+:?$" maxlength="20" autocomplete="off" spellcheck="false" title="Lowercase letters and numbers only" required>

 0" class="sticker-upload-error">{{stickerUploadError}}

	<button class="btn">Add</button>

</form>
</template>


user widget - adjust gravatar size

user widget - adjust gravatar size

<template>

    <form class="pk-panel-teaser uk-form uk-form-stacked" v-if="editing">

            {{ 'User Type' | trans }}

                    <input type="radio" value="login" v-model="widget.show"> {{ 'Logged in' | trans }}

                    <input type="radio" value="registered" v-model="widget.show"> {{ 'Last registered' | trans }}

            {{ 'Display' | trans }}

                    <input type="radio" value="thumbnail" v-model="widget.display"> {{ 'Thumbnail' | trans }}

                    <input type="radio" value="list" v-model="widget.display"> {{ 'List' | trans }}

            {{ 'Total Users' | trans }}

                    <input type="radio" value="1" v-model="widget.total"> {{ 'Show' | trans }}

                    <input type="radio" value="" v-model="widget.total"> {{ 'Hide' | trans }}

            {{ 'Number of Users' | trans }}

                <select id="form-user-number" class="uk-width-1-1" v-model="widget.count" number>
                    <option value="6">6</option>
                    <option value="12">12</option>
                    <option value="18">18</option>
                    <option value="24">24</option>
                </select>

    </form>

{{ userscount }}

{{ '{0} Registered Users|{1} Registered User|]1,Inf[ Registered Users' | transChoice userscount}}

{{ '{0} Logged in Users|{1} Logged in User|]1,Inf[ Logged in Users' | transChoice userscount}}

{{ 'Latest registered Users' | trans }}

{{ 'Latest logged in Users' | trans }}

                {{ user.username }}

{{ user.email }}

</template>


Fix homepage typo (#214)

Fix homepage typo (#214)

<template>

Explore decentralized applications (projects built
on Ethereum)

Discover the possibilities of the Ethereum blockchain with the definitive registry of ÐApp projects. <nuxt-link class="description-link" :to="{ name: 'what'}" @click.native="trackHomeHeroCta('learn')">Learn more about ÐApps</nuxt-link>

<nuxt-link class="cta-link" :to="{ name: 'dapps' }" @click.native="trackHomeHeroCta('browse')">Browse the ÐApps</nuxt-link>

<nuxt-link class="cta-link -submit" :to="{ name: 'dapps-new' }" @click.native="trackHomeHeroCta('submit')">Submit a ÐApp</nuxt-link>

</template>

<template>

Explore decentralized applications (projects built
on Ethereum)

Discover the possiblities of the Ethereum blockchain with the definitive registry of ÐApp projects. <nuxt-link class="description-link" :to="{ name: 'what'}" @click.native="trackHomeHeroCta('learn')">Learn more about ÐApps</nuxt-link>

<nuxt-link class="cta-link" :to="{ name: 'dapps' }" @click.native="trackHomeHeroCta('browse')">Browse the ÐApps</nuxt-link>

<nuxt-link class="cta-link -submit" :to="{ name: 'dapps-new' }" @click.native="trackHomeHeroCta('submit')">Submit a ÐApp</nuxt-link>

</template>


False default import.

False default import.

<template>
    <div class="mdc-simple-menu" :class="classes" :id="id" tabindex="-1">
        <slot />
    </div>
</template>

<script>
import { MDCSimpleMenu } from '@material/menu'

export default {
    props: {
        id: {
            type: String,
            required: false
        },
        open: {
            type: Boolean,
            required: true
        },
        fromTopToLeft: {
            type: Boolean,
            required: false
        },
        fromTopToRight: {
            type: Boolean,
            required: false
        },
        fromBottomLeft: {
            type: Boolean,
            required: false
        },
        fromBottomRight: {
            type: Boolean,
            required: false
        }
    },
    data() {
        return {
            mdcSimpleMenu: null
        }
    },
    mounted() {
        this.mdcSimpleMenu = new MDCSimpleMenu(this.$el)

        if (this.$slots.default)
            this.$slots.default.map((n) => {
                n.elm.classList.add('mdc-simple-menu__items')
            })
    },
    destroyed() {
        this.mdcSimpleMenu.destroy()
    },
    computed: {
        classes() {
            return {
                'mdc-simple-menu--open': this.open,
                'mdc-simple-menu--open-from-top-left': this.fromTopLeft,
                'mdc-simple-menu--open-from-top-right': this.fromTopRight,
                'mdc-simple-menu--open-from-bottom-left': this.fromBottomLeft,
                'mdc-simple-menu--open-from-bottom-right': this.fromBottomRight
            }
        }
    }

}
</script>

<style lang="scss">
@import "@material/menu/mdc-menu";
</style>

<template>
    <div class="mdc-simple-menu" :class="classes" :id="id" tabindex="-1">
        <slot />
    </div>
</template>

<script>
import MDCSimpleMenu from '@material/menu'

export default {
    props: {
        id: {
            type: String,
            required: false
        },
        open: {
            type: Boolean,
            required: true
        },
        fromTopToLeft: {
            type: Boolean,
            required: false
        },
        fromTopToRight: {
            type: Boolean,
            required: false
        },
        fromBottomLeft: {
            type: Boolean,
            required: false
        },
        fromBottomRight: {
            type: Boolean,
            required: false
        }
    },
    data() {
        return {
            mdcSimpleMenu: null
        }
    },
    mounted() {
        this.mdcSimpleMenu = new MDCSimpleMenu(this.$el)

        if (this.$slots.default)
            this.$slots.default.map((n) => {
                n.elm.classList.add('mdc-simple-menu__items')
            })
    },
    destroyed() {
        this.mdcSimpleMenu.destroy()
    },
    computed: {
        classes() {
            return {
                'mdc-simple-menu--open': this.open,
                'mdc-simple-menu--open-from-top-left': this.fromTopLeft,
                'mdc-simple-menu--open-from-top-right': this.fromTopRight,
                'mdc-simple-menu--open-from-bottom-left': this.fromBottomLeft,
                'mdc-simple-menu--open-from-bottom-right': this.fromBottomRight
            }
        }
    }

}
</script>

<style lang="scss">
@import "@material/menu/mdc-menu";
</style>


fixed file download

fixed file download

<template lang="html">

    <app-toolbar :title="storename"></app-toolbar>

    <app-folder
        :name="displayedFolder.name"
        :files="displayedFolder.files"
        :folders="displayedFolder.folders"
        :isroot="displaysRoot"
        @goUp="goUp"
        @openfolder="openFolder"
        @openfile="openFile"
        v-if="store">
    </app-folder>

    <md-button class="md-fab md-mini md-clean fab-settings" v-if="store && !uploadFormVisible && store.owner.id == $store.state.account.user.id" @click.native="openSettings()">
      <md-icon>settings</md-icon>
    </md-button>

    <md-sidenav class="md-right settings-panel" ref="settingsPanel">
      <app-store-settings :store="store" @close="closeSettings"/>
    </md-sidenav>

    <md-speed-dial class="fab" md-direction="bottom" v-if="store && !uploadFormVisible">
      <md-button class="md-fab" id="store-fab" md-fab-trigger>
        <md-icon md-icon-morph>close</md-icon>
        <md-icon>add</md-icon>
      </md-button>
      <md-button class="md-fab md-primary md-mini md-clean" @click.native="openDialog('folderCreationDialog')">
        <md-icon>create_new_folder</md-icon>
        <md-tooltip md-direction="left">Create folder</md-tooltip>
      </md-button>
      <md-button class="md-fab md-primary md-mini md-clean" @click.native="uploadFormVisible = true">
        <md-icon>cloud_upload</md-icon>
        <md-tooltip md-direction="left">Upload files</md-tooltip>
      </md-button>
    </md-speed-dial>

    <app-form title="Unlock Store" submitBtnText="unlock" v-if="!store && !loading" @submit="fetchStore">
      <md-input-container>
        Store Password
        <md-input type="password" v-model="password" />
      </md-input-container>
    </app-form>

Loading Store

Upload new Files

      <dropzone id="fileUploadZone" :url="fileUploadUrl" :headers="fileUploadHeaders" :maxFileSizeInMB="150" @vdropzone-success="uploadDone"></dropzone>
      <md-button class="md-raised" @click.native="uploadFormVisible = false">Done</md-button>

    <md-dialog md-open-from="#store-fab" md-close-to="#store-fab" ref="folderCreationDialog">
      <md-dialog-title>Add a new Folder</md-dialog-title>
      <md-dialog-content>
        <md-input-container>
          Folder name
          <md-input type="text" v-model="newFolderName" />
        </md-input-container>
      </md-dialog-content>
      <md-dialog-actions>
        <md-button class="md-primary" @click.native="closeDialog('folderCreationDialog')">Cancel</md-button>
        <md-button class="md-primary" @click.native="createFolder">Ok</md-button>
      </md-dialog-actions>
    </md-dialog>

    <md-snackbar md-position="bottom center" ref="snackbar" md-duration="3000">
      an Error occurred
    </md-snackbar>

</template>


make the import templates button more visible

<template>
  <v-container>

    <v-row>
      <v-col>
        <v-list
          subheader
          elevation="1"
          class="pt-2"
        >

            <v-subheader
              v-if="Object.keys(templates).length !== 1"
              :class="isDark() ? headerClasses.dark : headerClasses.light"
              v-text="type"
            />

              <v-list-item :to="(hasScope('templates.edit') || isAdmin()) ? {name: 'Template', params: {id: template.name}} : undefined">
                <v-list-item-content>
                  <v-list-item-title v-text="template.display" />
                </v-list-item-content>
              </v-list-item>

            <v-divider v-if="i !== Object.keys(templates).length - 1" />

        </v-list>

          <v-tooltip left>
            <template v-slot:activator="{ on }">
              <v-btn
                v-show="hasScope('templates.edit') || isAdmin()"
                color="primary"
                class="mb-4"
                fab
                dark
                small
                v-on="on"
                @click="loadTemplateImporter"
              >
                <v-icon>mdi-import</v-icon>
              </v-btn>
            </template>

          </v-tooltip>
          <v-btn
            v-show="hasScope('templates.edit') || isAdmin()"
            color="primary"
            fab
            dark
            large
            :to="{name: 'AddTemplate'}"
          >
            <v-icon>mdi-plus</v-icon>
          </v-btn>

      </v-col>
    </v-row>
    <ui-overlay
      v-model="showTemplateImporter"
      card
      closable
      :title="$t('templates.import.Import')"
    >
      <v-alert
        border="bottom"
        text
        type="warning"
        dense
      >
        {{ $t('templates.import.OverrideWarning') }}
      </v-alert>
      <v-autocomplete
        v-model="templatesToImport"
        :items="importableTemplates"
        chips
        :label="$t('templates.import.Select')"
        multiple
        clearable
        deletable-chips
        hide-selected
        outlined
        open-on-clear
      />
      <v-row>
        <v-col>
          <v-btn
            block
            color="error"
            @click="showTemplateImporter = false"
            v-text="$t('common.Cancel')"
          />
        </v-col>
        <v-col>
          <v-btn
            block
            color="success"
            @click="doImports()"
            v-text="$t('templates.import.Import')"
          />
        </v-col>
      </v-row>
    </ui-overlay>
    <ui-overlay
      v-model="offerImport"
      card
      closable
      :title="$t('templates.import.NoTemplatesTitle')"
    >
      <v-row>
        <v-col>

        </v-col>
      </v-row>
      <v-row>
        <v-col>
          <v-btn
            block
            color="error"
            @click="importDeclined()"
            v-text="$t('common.Cancel')"
          />
        </v-col>
        <v-col>
          <v-btn
            block
            color="success"
            @click="offerImport = false; loadTemplateImporter()"
            v-text="$t('templates.import.Import')"
          />
        </v-col>
      </v-row>
    </ui-overlay>
  </v-container>
</template>

<template>
  <v-container>

      <v-tooltip left>
        <template v-slot:activator="{ on }">
          <v-btn
            icon
            v-on="on"
            @click="loadTemplateImporter"
          >
            <v-icon>mdi-import</v-icon>
          </v-btn>
        </template>

      </v-tooltip>

    <v-row>
      <v-col>
        <v-list
          subheader
          elevation="1"
          class="pt-2"
        >

            <v-subheader
              v-if="Object.keys(templates).length !== 1"
              :class="isDark() ? headerClasses.dark : headerClasses.light"
              v-text="type"
            />

              <v-list-item :to="(hasScope('templates.edit') || isAdmin()) ? {name: 'Template', params: {id: template.name}} : undefined">
                <v-list-item-content>
                  <v-list-item-title v-text="template.display" />
                </v-list-item-content>
              </v-list-item>

            <v-divider v-if="i !== Object.keys(templates).length - 1" />

        </v-list>
        <v-btn
          v-show="hasScope('templates.edit') || isAdmin()"
          color="primary"
          bottom
          right
          fixed
          fab
          dark
          large
          :to="{name: 'AddTemplate'}"
        >
          <v-icon>mdi-plus</v-icon>
        </v-btn>
      </v-col>
    </v-row>
    <ui-overlay
      v-model="showTemplateImporter"
      card
      closable
      :title="$t('templates.import.Import')"
    >
      <v-alert
        border="bottom"
        text
        type="warning"
        dense
      >
        {{ $t('templates.import.OverrideWarning') }}
      </v-alert>
      <v-autocomplete
        v-model="templatesToImport"
        :items="importableTemplates"
        chips
        :label="$t('templates.import.Select')"
        multiple
        clearable
        deletable-chips
        hide-selected
        outlined
        open-on-clear
      />
      <v-row>
        <v-col>
          <v-btn
            block
            color="error"
            @click="showTemplateImporter = false"
            v-text="$t('common.Cancel')"
          />
        </v-col>
        <v-col>
          <v-btn
            block
            color="success"
            @click="doImports()"
            v-text="$t('templates.import.Import')"
          />
        </v-col>
      </v-row>
    </ui-overlay>
    <ui-overlay
      v-model="offerImport"
      card
      closable
      :title="$t('templates.import.NoTemplatesTitle')"
    >
      <v-row>
        <v-col>

        </v-col>
      </v-row>
      <v-row>
        <v-col>
          <v-btn
            block
            color="error"
            @click="importDeclined()"
            v-text="$t('common.Cancel')"
          />
        </v-col>
        <v-col>
          <v-btn
            block
            color="success"
            @click="offerImport = false; loadTemplateImporter()"
            v-text="$t('templates.import.Import')"
          />
        </v-col>
      </v-row>
    </ui-overlay>
  </v-container>
</template>


Fixed node icon styling

Fixed node icon styling

<template>

    <v-chip
      v-if="showColor"
      label
      :color="kind"
      :textColor="fontColor"
      small
    >
      <v-icon small :color="fontColor">{{ icon }}</v-icon>
      {{ text }}
    </v-chip>

      <v-icon :color="fontColor">{{ icon }}</v-icon>
      {{ text }}

</template>


검색결과 목록 아이템 meta label 크기 조정

검색결과 목록 아이템 meta label 크기 조정

<template>

 {{$t('popular.search.words')}}

        <router-link v-for="(word, index) in popularSearchWords" :key="word.key" :class="indexedColor(index, false)" :to="{name: 'search', query: {q: word.key, w: 'PO;CO;GA', from: 0, size: 3}}" class="ui label search-keyword">{{word.key}}</router-link>

        {{$t('search.post.results', {n: searchResult.postResult.totalCount})}}
        <router-link v-if="searchResult.postResult.totalCount" :to="{name: 'search', query: {q: $route.query.q, w: 'PO'}}" class="pull-right">
          {{$t('common.button.more')}}
        </router-link>

           {{$t('search.no.result')}}

          <router-link :to="{name: 'board.view', params: {name:'free', seq: result.seq}}" v-for="result in searchResult.postResult.posts" :key="result.id" class="item">

                    {{$t(categoryLabel(result.category))}}

{{result.seq}}

                    {{result.writer.username}}

{{result.id | IdToRegDate('LL')}}

          </router-link>

        {{$t('search.comment.results', {n: searchResult.commentResult.totalCount})}}
        <router-link v-if="searchResult.commentResult.totalCount" :to="{name: 'search', query: {q: $route.query.q, w: 'CO'}}" class="pull-right">
          {{$t('common.button.more')}}
        </router-link>

           {{$t('search.no.result')}}

          <router-link :to="{name: 'board.view', params: {name:'free', seq: result.parentBoard.seq}}" v-for="result in searchResult.commentResult.comments" :key="result.id" class="item">

                  {{result.writer.username}}

{{result.id | IdToRegDate('LL')}}

          </router-link>

        {{$t('search.gallery.results', {n: searchResult.galleryResult.totalCount})}}
        <router-link v-if="searchResult.galleryResult.totalCount" :to="{name: 'search', query: {q: $route.query.q, w: 'GA'}}" class="pull-right">
          {{$t('common.button.more')}}
        </router-link>

           {{$t('search.no.result')}}

          <router-link :to="{name: 'gallery.view', params: {id: result.id}}" v-for="result in searchResult.galleryResult.galleries" :key="result.id" class="card">

                {{result.writer.username}}

          </router-link>

</template>


调整侧滑菜单页面布局

调整侧滑菜单页面布局

<template>
	<view>
		<view class="header">
			<view class="input-view">
				<uni-icon type="search" size="22" color="#666666"></uni-icon>
				<input confirm-type="search" @confirm="confirm" class="input" type="text" placeholder="输入搜索关键词" />
			</view>
			<!-- #ifdef MP-WEIXIN -->
			<view class="icon" @tap="showRightDrawer">
				<uni-icon type="bars" color="#666666" size="22"></uni-icon>
			</view>
			<!-- #endif -->
		</view>
		<view class="uni-padding-wrap" style="margin-top:30upx;">
			这是抽屉式导航组件使用示例，可以指定菜单左侧或者右侧弹出（仅初始化生效），组件内部可以放置任何内容。点击页面右上角的按钮即可显示导航菜单。
		</view>
		<uni-drawer :visible="rightDrawerVisible" mode="right" @close="closeRightDrawer">
			<view style="padding:30upx;">
				<view class="uni-title">抽屉式导航</view>
				<view class="uni-helllo-text">
					这是抽屉式导航组件使用示例，你可以在这里放置任何内容。关闭抽屉式导航有多种方式：</text>
					<text>\n1.点击本导航之外的任意位置；</text>
					<text>\n2.点击如下红色按钮；</text>
					<!-- #ifdef APP-PLUS -->
					<text>\n3.点击页面右上角的按钮；</text>
					<!-- #endif -->
				</view>
				<view class="uni-common-mt">
					<button class="button" type="warn" size="mini" @tap="closeRightDrawer">关闭抽屉式导航</button>
				</view>
				<view class="uni-list uni-common-mt">
					<view class="uni-list-cell" hover-class="uni-list-cell-hover">
						<view class="uni-list-cell-navigate uni-navigate-right" @tap="item1">
							Item1
						</view>
					</view>
					<view class="uni-list-cell uni-list-cell-last" hover-class="uni-list-cell-hover">
						<view class="uni-list-cell-navigate uni-navigate-right" @tap="item2">
							Item2
						</view>
					</view>
				</view>
			</view>
		</uni-drawer>
	</view>
</template>
<script>
	import uniDrawer from '../../../components/uni-drawer.vue';
	import uniIcon from '../../../components/uni-icon.vue';
	export default {
		components: {
			uniDrawer,
			uniIcon
		},
		data() {
			return {
				rightDrawerVisible: false
			}
		},
		methods: {
			closeRightDrawer() {
				this.rightDrawerVisible = false;
			},
			showRightDrawer() {
				this.rightDrawerVisible = true;
			},
			item1() {
				this.rightDrawerVisible = false;
				uni.showToast({
					title: 'item1'
				});
			},
			item2() {
				this.rightDrawerVisible = false;
				uni.showToast({
					title: 'item2'
				});
			},
			confirm() {
				uni.showToast({
					title: '搜索'
				})
			}
		},
		onNavigationBarButtonTap(e) {
			this.rightDrawerVisible = !this.rightDrawerVisible
		}
	}
</script>

<style>
	.header {
		display: flex;
		flex-direction: row;
		padding: 10px 15px;
		align-items: center;
	}

	.input-view {
		display: flex;
		align-items: center;
		flex-direction: row;
		background-color: #e7e7e7;
		height: 30px;
		border-radius: 15px;
		padding: 0 10px;
		flex: 1;
	}

	.input {
		flex: 1;
		padding: 0 5px;
		height: 24px;
		line-height: 24px;
		font-size: 16px;
	}

	.icon {
		display: flex;
		flex-direction: column;
		justify-content: center;
		margin-left: 10px;
	}
</style>

<template>
	<view>
		<view class="uni-padding-wrap">
			<view class="uni-helllo-text" style="margin-top:80upx;">
				这是抽屉式导航组件使用示例，可以指定菜单左侧或者右侧弹出（仅初始化生效），组件内部可以放置任何内容。
			</view>
			<!-- #ifdef APP-PLUS -->
			<view class="uni-center uni-link uni-common-mt" @tap="showRightDrawer">
				点击这里或页面右上角的按钮显示导航菜单。
			</view>
			<!-- #endif -->
			<!-- #ifdef MP-WEIXIN -->
			<view class="uni-common-mt uni-btn-v">
				<button type="primary" @tap="showRightDrawer">弹出导航</button>
			</view>
			<!-- #endif -->
		</view>
		<uni-drawer :visible="rightDrawerVisible" mode="right" @close="closeRightDrawer">
			<view style="padding:30upx;">
				<view class="uni-title">抽屉式导航</view>
				<view class="uni-helllo-text">
					这是抽屉式导航组件使用示例，你可以在这里放置任何内容。
					<text>\n关闭抽屉式导航有多种方式：</text>
					<text>\n1.点击本导航之外的任意位置；</text>
					<text>\n2.点击如下红色按钮；</text>
					<!-- #ifdef APP-PLUS -->
					<text>\n3.点击页面右上角的按钮；</text>
					<!-- #endif -->
				</view>
				<view class="uni-common-mt">
					<button class="button" type="warn" @tap="closeRightDrawer">关闭抽屉式导航</button>
				</view>
				<view class="uni-list uni-common-mt">
					<view class="uni-list-cell" hover-class="uni-list-cell-hover">
						<view class="uni-list-cell-navigate uni-navigate-right" @tap="item1">
							Item1
						</view>
					</view>
					<view class="uni-list-cell uni-list-cell-last" hover-class="uni-list-cell-hover">
						<view class="uni-list-cell-navigate uni-navigate-right" @tap="item2">
							Item2
						</view>
					</view>
				</view>
			</view>
		</uni-drawer>
	</view>
</template>
<script>
	import uniDrawer from '../../../components/uni-drawer.vue';
	import uniIcon from '../../../components/uni-icon.vue';
	export default {
		components: {
			uniDrawer,
			uniIcon
		},
		data() {
			return {
				rightDrawerVisible: false
			}
		},
		methods: {
			closeRightDrawer() {
				this.rightDrawerVisible = false;
			},
			showRightDrawer() {
				this.rightDrawerVisible = true;
			},
			item1() {
				this.rightDrawerVisible = false;
				uni.showToast({
					title: 'item1'
				});
			},
			item2() {
				this.rightDrawerVisible = false;
				uni.showToast({
					title: 'item2'
				});
			},
			confirm() {
				uni.showToast({
					title: '搜索'
				})
			}
		},
		onNavigationBarButtonTap(e) {
			this.rightDrawerVisible = !this.rightDrawerVisible
		}
	}
</script>

<style>

</style>


Clear code

Clear code

<template>

{{ currentApp.appData.image | appName }}

Description

            {{ currentApp.appData.image.description }}

Policy

                Workspace accessible

                Workspace not accessible

                Volume mounted: {{ appPolicy.volume_source }} → {{ appPolicy.volume_target }} ({{ appPolicy.volume_mode }})

                No volumes mounted

Configuration

            <form class="configuration">
              <fieldset v-if="currentApp.configurables.length === 0">No configurable options for this image</fieldset>
              <fieldset v-else :disabled="currentApp.isStarting()">
                <component v-for="configurable in currentApp.configurables"
                :key="configurable.tag"
                :is="configurable.tag + '-component'"
                :configDict.sync="configurable.configDict"></component>
              </fieldset>
            </form>

            <button class="btn btn-primary pull-right start-button"
            @click="startApplication()"
            :disabled="currentApp.isStarting()">
              Start
            </button>

</template>


chore: update email

chore: update email

<template>

        ASP.NET Core & Vue Starter Template

        Made with <icon icon="heart" class="fuscia" /> by
        Trilon.io

          Enterprise Consulting | Development | Training | Workshops

        Get your Team & Application up to speed by working with some of the leading industry
        experts in JavaScript & ASP.NET!

Official NestJS Consulting

Architectural Review

JavaScript Application Consulting

Vue | React | Angular

Best-practices

Team Augmentation & Support

Much more!

      Let's get in touch and see how Trilon can help you!

<icon icon="envelope"/> hello@trilon.io

Find us Online

      Website: https://trilon.io

      Twitter: @Trilon_IO

      Github: @Trilon_IO

</template>


Add focus core outline to UI select

Add focus core outline to UI select

<template>

    <input
      v-if="name"
      class="ui-select-hidden-input"

      type="hidden"
      :name="name"

      :value="submittedValue"
    >

      <slot name="icon">
        <UiIcon :icon="icon" :style="activeColorStyle" />
      </slot>

          <slot>{{ label }}</slot>

            {{ hasDisplayText ? displayText : (
              hasFloatingLabel && isLabelInline) ? null : placeholder }}

          <UiIcon v-if="!clearableState" class="ui-select-dropdown-button">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M6.984 9.984h10.03L12 15z" /></svg>
          </UiIcon>
          <KIconButton
            v-else
            class="overlay-close-button"
            icon="close"
            :ariaLabel="coreString('clearAction')"
            :color="$themeTokens.text"
            :tooltip="coreString('clearAction')"
            @click.stop="setValue()"
          />

        <transition name="ui-select-transition-fade">
          <div
            v-show="showDropdown"
            ref="dropdown"
            class="ui-select-dropdown"
            :style="{ color: $themeTokens.primary, backgroundColor: $themeTokens.surface }"
            tabindex="-1"
            @keydown.enter.prevent.stop="selectHighlighted"
            @keydown.space.prevent.stop="selectHighlighted"
            @keydown.esc.prevent="closeDropdown()"
            @keydown.tab="onBlur"
            @keydown.up.prevent.stop="highlightPreviousOption"
            @keydown.down.prevent.stop="highlightNextOption"
            @keydown.self="highlightQuickMatch"
          >

              <input
                ref="searchInput"
                v-model="query"
                autocomplete="off"
                class="ui-select-search-input"

                type="text"

                :placeholder="searchPlaceholder"
              >

              <UiIcon class="ui-select-search-icon">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                  <path
                    d="M9.516 14.016c2.484 0 4.5-2.016 4.5-4.5s-2.016-4.5-4.5-4.5-4.5 2.016-4.5
                    4.5 2.016 4.5 4.5 4.5zm6 0l4.97
                    4.97-1.5 1.5-4.97-4.97v-.797l-.28-.282c-1.126.984-2.626 1.547-4.22 1.547-3.61
                    0-6.516-2.86-6.516-6.47S5.906 3 9.516 3s6.47 2.906 6.47 6.516c0 1.594-.564
                    3.094-1.548 4.22l.28.28h.798z"
                  />
                </svg>
              </UiIcon>

              <KCircularLoader
                v-show="loading"
                class="ui-select-search-progress"
                :size="20"
                :stroke="4"
              />

              <KeenUiSelectOption
                v-for="(option, index) in filteredOptions"
                ref="options"
                :key="index"
                :highlighted="isOptionHighlighted(option)"
                :keys="keys"
                :multiple="multiple"
                :option="option"
                :selected="isOptionSelected(option)"

                :type="type"
                @click.native.stop="selectOption(option)"

                @mouseover.native.stop="onMouseover(option)"
              >
                <slot
                  name="option"

                  :highlighted="isOptionHighlighted(option)"
                  :index="index"
                  :option="option"
                  :selected="isOptionSelected(option)"
                ></slot>
              </KeenUiSelectOption>

                <slot name="no-results">
                  No results found
                </slot>

        </transition>

          <slot name="error">
            {{ error }}
          </slot>

          <slot name="help">
            {{ help }}
          </slot>

</template>


Remove extra request in Library page labels - not needed to filter

<template>

    <main
      class="main-grid"
      :style="{ marginLeft: `${(sidePanelWidth + 24)}px` }"
    >

        <KButton
          icon="channel"
          :text="coreString('searchLabel')"
          :primary="false"
          @click="toggleSidePanelVisibility"
        />

{{ coreString('channelsLabel') }}

        <ChannelCardGroupGrid
          v-if="channels.length"
          class="grid"
          :contents="channels"
        />
        <div v-if="!(windowBreakpoint < 1 )" class="toggle-view-buttons">
          <KIconButton
            icon="menu"
            :ariaLabel="$tr('viewAsList')"
            :color="$themeTokens.text"
            :tooltip="$tr('viewAsList')"
            @click="toggleCardView('list')"
          />
          <KIconButton
            icon="channel"
            :ariaLabel="$tr('viewAsGrid')"
            :color="$themeTokens.text"
            :tooltip="$tr('viewAsGrid')"
            @click="toggleCardView('card')"
          />

{{ $tr('recent') }}

        <HybridLearningCardGrid
          v-if="popular.length"
          :cardViewStyle="currentViewStyle"
          :numCols="numCols"
          :genContentLink="genContentLink"
          :contents="trimmedPopular"
          :currentPage="currentPage"
          @toggleInfoPanel="toggleInfoPanel"
        />

          {{ $tr('results', { results: results.length }) }}

        <KButton
          v-if="more"
          :text="coreString('viewMoreAction')"
          appearance="basic-link"
          :disabled="moreLoading"
          class="filter-action-button"
          @click="searchMore"
        />
        <div v-if="!(windowBreakpoint < 1)" class="toggle-view-buttons">
          <KIconButton
            icon="menu"
            :ariaLabel="$tr('viewAsList')"
            :color="$themeTokens.text"
            :tooltip="$tr('viewAsList')"
            @click="toggleCardView('list')"
          />
          <KIconButton
            icon="channel"
            :ariaLabel="$tr('viewAsGrid')"
            :color="$themeTokens.text"
            :tooltip="$tr('viewAsGrid')"
            @click="toggleCardView('card')"
          />

{{ item }}

              <KIconButton
                icon="close"
                size="mini"
                class="filter-chip-button"
                @click="removeFilterTag(item, key)"
              />

          <KButton
            :text="$tr('clearAll')"
            appearance="basic-link"
            class="filter-action-button"
            @click="clearSearch"
          />

        <HybridLearningCardGrid
          v-if="results.length"
          :numCols="numCols"
          :cardViewStyle="currentViewStyle"
          :genContentLink="genContentLink"
          :contents="results"
          @toggleInfoPanel="toggleInfoPanel"
        />

        <KButton
          v-if="more"
          :text="coreString('viewMoreAction')"
          appearance="basic-link"
          :disabled="moreLoading"
          class="filter-action-button"
          @click="searchMore"
        />

        <KCircularLoader
          v-if="searchLoading"
          class="loader"
          type="indeterminate"
          :delay="false"
        />

    <EmbeddedSidePanel
      v-if="!!windowIsLarge"
      v-model="searchTerms"
      :width="`${sidePanelWidth}px`"
      :availableLabels="labels"
      position="embedded"
      @currentCategory="handleShowSearchModal"
    />

    <FullScreenSidePanel
      v-if="!windowIsLarge && sidePanelIsOpen"
      alignment="left"
      class="full-screen-side-panel"
      closeButtonHidden="true"
      :sidePanelOverrideWidth="`${sidePanelOverlayWidth + 64}px`"
      @closePanel="toggleSidePanelVisibility"
    >
      <KIconButton
        v-if="windowIsSmall && !currentCategory"
        class="overlay-close-button"
        icon="close"
        :ariaLabel="coreString('closeAction')"
        :color="$themeTokens.text"
        :tooltip="coreString('closeAction')"
        @click="toggleSidePanelVisibility"
      />
      <KIconButton
        v-if="windowIsSmall && currentCategory"
        icon="back"
        :ariaLabel="coreString('back')"
        :color="$themeTokens.text"
        :tooltip="coreString('back')"
        @click="closeCategoryModal"
      />
      <EmbeddedSidePanel
        v-if="!currentCategory"
        v-model="searchTerms"
        :width="`${sidePanelOverlayWidth}px`"
        :availableLabels="labels"
        position="overlay"
        @currentCategory="handleShowSearchModal"
      />
      <CategorySearchModal
        v-if="currentCategory && windowIsSmall"
        :selectedCategory="currentCategory"
        :numCols="numCols"
        :availableLabels="labels"
        position="fullscreen"
        @cancel="currentCategory = null"
        @input="handleCategory"
      />
    </FullScreenSidePanel>

    <CategorySearchModal
      v-if="(windowIsMedium || windowIsLarge) && currentCategory"
      :selectedCategory="currentCategory"
      :numCols="numCols"
      :availableLabels="labels"
      position="modal"
      @cancel="currentCategory = null"
      @input="handleCategory"
    />

    <FullScreenSidePanel
      v-if="sidePanelContent"
      @closePanel="sidePanelContent = null"
    >
      <BrowseResourceMetadata :content="sidePanelContent" :canDownloadContent="true" />
    </FullScreenSidePanel>

</template>


Added focus to <select> channel switch dropdown menu

<template>

  <div v-bind:class="['toolbar-show', displayToolBar ? 'toolbar-hide' : '' ]" >
    <breadcrumbs class="breadcrumbs"></breadcrumbs>
    <div :class="{ 'toggle-menu-on' : more }">
      <label for="chan-select" :class="[ more ? 'lable-on' : 'visuallyhidden' ]" >Switch Channels</label>
      <select
        class="chan-select"
        :class="[ more ? 'chan-select-mobile-location' : 'chan-select-location' ]"
        id="chan-select"
        name="chan-select"
        v-model="getCurrentChannel"
        @change="switchChannel($event)"
      >
        <option v-for="channel in getChannels" :value="channel.id">{{ channel.name }}</option>
      </select>
    </div>
    <search-button @scrolling="handleScroll" class='search-btn'></search-button>
    <button class="more" @click="toggleMore" ><svg src="../icons/more-ver.svg"></svg></button>
  </div>

</template>

<script>

  const constants = require('../../state/constants');
  const getters = require('../../state/getters');

  module.exports = {

    data: () => ({
      currScrollTop: 0,
      delta: 5,
      lastScrollTop: 0,
      displayToolBar: false,
      more: false,
    }),
    components: {
      'search-widget': require('../search-widget'),
      'search-button': require('../search-widget/search-button'),
      'breadcrumbs': require('../breadcrumbs'),
    },
    computed: {
      /*
      * Get a list of channels.
      */
      getChannels() {
        return this.channelList;
      },
      /*
      * Get the current channel ID.
      */
      getCurrentChannel() {
        return this.currentChannel;
      },
    },
    methods: {
      handleScroll(position) {
        this.position = position;
        this.currScrollTop = position.scrollTop;

        if (Math.abs(this.lastScrollTop - this.currScrollTop) <= this.delta) {
          return;
        }

        this.more = false;

        if (this.currScrollTop > this.lastScrollTop) {
          this.displayToolBar = true;
        } else {
          this.displayToolBar = false;
        }
        this.lastScrollTop = this.currScrollTop;
      },
      /*
      * Route to selected channel.
      */
      switchChannel(event) {
        let rootPage;
        this.more = false;
        if (this.exploreMode) {
          rootPage = constants.PageNames.EXPLORE_CHANNEL;
        } else {
          rootPage = constants.PageNames.LEARN_CHANNEL;
        }
        this.$router.go(
          {
            name: rootPage,
            params: {
              channel_id: event.target.value,
            },
          }
        );
      },
      toggleMore() {
        this.more = !this.more;
      },
    },
    vuex: {
      getters: {
        rootTopicId: state => state.rootTopicId,
        topic: state => state.pageState.topic,
        isRoot: (state) => state.pageState.topic.id === state.rootTopicId,
        pageMode: getters.pageMode,
        pageName: state => state.pageName,
        currentChannel: state => state.currentChannel,
        channelList: state => state.channelList,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  @require '~core-theme.styl'
  @require '../learn.styl'

  .breadcrumbs
    position: relative
    left: 120px
    @media screen and (max-width: $portrait-breakpoint)
      left: 3rem

  .toolbar-show
    position: fixed
    left: -20px
    top: 0
    width: 100%
    height: 42px
    background: $core-bg-canvas
    z-index: 100
    transition: top 0.2s ease-in-out

  .toolbar-hide
    position: fixed
    left: -20px
    top: -40px

  .breadcrumbs
    left: 120px
    bottom: 22px
    position: relative
    @media screen and (max-width: $portrait-breakpoint)
      left: 24px

  .toggle-menu-on
    position: fixed
    display: table
    top: 3em
    right: 20px
    width: 200px
    height: 100px
    background: $core-bg-light
    border-radius: 4px
    text-align: center
    font-size: 0.9em
    color: $core-text-annotation
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25)
    z-index: 2

  .toggle-menu-on::before
    content: ''
    position: absolute
    border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent
    top: -11px
    border-style: solid
    border-width: 0 10px 10px 10px
    right: 9px
    margin-left: -8px

  .toggle-menu-on::after
    content: ''
    position: absolute
    border-style: solid
    border-width: 0 10px 10px 10px
    right: 9px
    margin-left: -8px
    border-color: transparent transparent #fff transparent
    top: -10px

  select:focus
    outline: $core-action-light 2px solid

  .chan-select
    z-index: 1
    width: 11em
    padding: 0.2em 0.8em
    color: $core-text-annotation
    font-size: 0.9rem
    border: 1px solid $core-text-annotation
    border-radius: 50px
    background: url(../icons/arrowdown.svg) no-repeat right
    background-color: $core-bg-canvas
    -webkit-appearance: none
    -moz-appearance: none
    outline: none
    @media screen and (max-width: $portrait-breakpoint)
      display: none

  .chan-select-location
    position: absolute
    top: 0.5rem
    right: 6em

  .chan-select-mobile-location
    position: relative
    display: table-cell
    margin: 0 auto
    top: 2.4em
    right: auto
    background-color: $core-bg-light

  .lable-on
    position: relative
    top: 1.2rem

  .search-btn
    position: absolute
    top: 0.1rem
    right: 1.2rem
    z-index: 1
    @media screen and (max-width: $portrait-breakpoint)
      right: 3rem

  .more
    display: none
    @media screen and (max-width: $portrait-breakpoint)
      position: absolute
      display: block
      top: 0.3rem
      right: 0
      border: none
      z-index: 1

</style>

<template>

  <div v-bind:class="['toolbar-show', displayToolBar ? 'toolbar-hide' : '' ]" >
    <breadcrumbs class="breadcrumbs"></breadcrumbs>
    <div :class="{ 'toggle-menu-on' : more }">
      <label for="chan-select" :class="[ more ? 'lable-on' : 'visuallyhidden' ]" >Switch Channels</label>
      <select
        class="chan-select"
        :class="[ more ? 'chan-select-mobile-location' : 'chan-select-location' ]"
        id="chan-select"
        name="chan-select"
        v-model="getCurrentChannel"
        @change="switchChannel($event)"
      >
        <option v-for="channel in getChannels" :value="channel.id">{{ channel.name }}</option>
      </select>
    </div>
    <search-button @scrolling="handleScroll" class='search-btn'></search-button>
    <button class="more" @click="toggleMore" ><svg src="../icons/more-ver.svg"></svg></button>
  </div>

</template>

<script>

  const constants = require('../../state/constants');
  const getters = require('../../state/getters');

  module.exports = {

    data: () => ({
      currScrollTop: 0,
      delta: 5,
      lastScrollTop: 0,
      displayToolBar: false,
      more: false,
    }),
    components: {
      'search-widget': require('../search-widget'),
      'search-button': require('../search-widget/search-button'),
      'breadcrumbs': require('../breadcrumbs'),
    },
    computed: {
      /*
      * Get a list of channels.
      */
      getChannels() {
        return this.channelList;
      },
      /*
      * Get the current channel ID.
      */
      getCurrentChannel() {
        return this.currentChannel;
      },
    },
    methods: {
      handleScroll(position) {
        this.position = position;
        this.currScrollTop = position.scrollTop;

        if (Math.abs(this.lastScrollTop - this.currScrollTop) <= this.delta) {
          return;
        }

        this.more = false;

        if (this.currScrollTop > this.lastScrollTop) {
          this.displayToolBar = true;
        } else {
          this.displayToolBar = false;
        }
        this.lastScrollTop = this.currScrollTop;
      },
      /*
      * Route to selected channel.
      */
      switchChannel(event) {
        let rootPage;
        this.more = false;
        if (this.exploreMode) {
          rootPage = constants.PageNames.EXPLORE_CHANNEL;
        } else {
          rootPage = constants.PageNames.LEARN_CHANNEL;
        }
        this.$router.go(
          {
            name: rootPage,
            params: {
              channel_id: event.target.value,
            },
          }
        );
      },
      toggleMore() {
        this.more = !this.more;
      },
    },
    vuex: {
      getters: {
        rootTopicId: state => state.rootTopicId,
        topic: state => state.pageState.topic,
        isRoot: (state) => state.pageState.topic.id === state.rootTopicId,
        pageMode: getters.pageMode,
        pageName: state => state.pageName,
        currentChannel: state => state.currentChannel,
        channelList: state => state.channelList,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  @require '~core-theme.styl'
  @require '../learn.styl'

  .breadcrumbs
    position: relative
    left: 120px
    @media screen and (max-width: $portrait-breakpoint)
      left: 3rem

  .toolbar-show
    position: fixed
    left: -20px
    top: 0
    width: 100%
    height: 42px
    background: $core-bg-canvas
    z-index: 100
    transition: top 0.2s ease-in-out

  .toolbar-hide
    position: fixed
    left: -20px
    top: -40px

  .breadcrumbs
    left: 120px
    bottom: 22px
    position: relative
    @media screen and (max-width: $portrait-breakpoint)
      left: 24px

  .toggle-menu-on
    position: fixed
    display: table
    top: 3em
    right: 20px
    width: 200px
    height: 100px
    background: $core-bg-light
    border-radius: 4px
    text-align: center
    font-size: 0.9em
    color: $core-text-annotation
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25)
    z-index: 2

  .toggle-menu-on::before
    content: ''
    position: absolute
    border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent
    top: -11px
    border-style: solid
    border-width: 0 10px 10px 10px
    right: 9px
    margin-left: -8px

  .toggle-menu-on::after
    content: ''
    position: absolute
    border-style: solid
    border-width: 0 10px 10px 10px
    right: 9px
    margin-left: -8px
    border-color: transparent transparent #fff transparent
    top: -10px

  .chan-select
    z-index: 1
    width: 11em
    padding: 0.2em 0.8em
    color: $core-text-annotation
    font-size: 0.9rem
    border: 1px solid $core-text-annotation
    border-radius: 50px
    background: url(../icons/arrowdown.svg) no-repeat right
    background-color: $core-bg-canvas
    -webkit-appearance: none
    -moz-appearance: none
    outline: none
    @media screen and (max-width: $portrait-breakpoint)
      display: none

  .chan-select-location
    position: absolute
    top: 0.5rem
    right: 6em

  .chan-select-mobile-location
    position: relative
    display: table-cell
    margin: 0 auto
    top: 2.4em
    right: auto
    background-color: $core-bg-light

  .lable-on
    position: relative
    top: 1.2rem

  .search-btn
    position: absolute
    top: 0.1rem
    right: 1.2rem
    z-index: 1
    @media screen and (max-width: $portrait-breakpoint)
      right: 3rem

  .more
    display: none
    @media screen and (max-width: $portrait-breakpoint)
      position: absolute
      display: block
      top: 0.3rem
      right: 0
      border: none
      z-index: 1

</style>