fix finalize

fix finalize

<template lang="html">

    <finalize-modal
      ref="finalizeModal"
      :domain-name="hostName"
      :finalize="finalize"
    />

{{ fullDomainName }} {{ $t('dapps.alreadyOwned') }}.

{{ $t('dapps.labelHash') }}({{ fullDomainName }}):

{{ labelHash }}

{{ $t('dapps.nameHash') }}({{ hostName }}):

{{ nameHash }}

{{ $t('dapps.owner') }}:

{{ owner }}

{{ $t('dapps.deedOwner') }}:

{{ deedOwner }}

{{ $t('dapps.resolverAddr') }}:

{{ resolverAddress }}

      <button
        v-if="
          deedOwner.toLowerCase() === account.address.toLowerCase() &&
            owner === '0x0000000000000000000000000000000000000000'
        "
        class="finalize-button"
        @click="openFinalizeModal"
      >
        {{ $t('dapps.finalize') }}
      </button>
      <button
        v-if="owner.toLowerCase() === account.address.toLowerCase()"
        class="manage-button"
        @click="manageEns"
      >
        {{ $t('dapps.manage') }}
      </button>

    <interface-bottom-text
      :link-text="$t('interface.helpCenter')"
      :question="$t('interface.haveIssues')"
      link="https://kb.myetherwallet.com"
    />

</template>

<template lang="html">

    <finalize-modal
      ref="finalizeModal"
      :domain-name="hostName"
      :finalize="finalize"
    />

{{ fullDomainName }} {{ $t('dapps.alreadyOwned') }}.

{{ $t('dapps.labelHash') }}({{ fullDomainName }}):

{{ labelHash }}

{{ $t('dapps.nameHash') }}({{ hostName }}):

{{ nameHash }}

{{ $t('dapps.owner') }}:

{{ owner }}

{{ $t('dapps.deedOwner') }}:

{{ deedOwner }}

{{ $t('dapps.resolverAddr') }}:

{{ resolverAddress }}

      <button
        v-if="
          deedOwner === account.address &&
            owner === '0x0000000000000000000000000000000000000000'
        "
        class="finalize-button"
        @click="openFinalizeModal"
      >
        {{ $t('dapps.finalize') }}
      </button>
      <button
        v-if="owner.toLowerCase() === account.address.toLowerCase()"
        class="manage-button"
        @click="manageEns"
      >
        {{ $t('dapps.manage') }}
      </button>

    <interface-bottom-text
      :link-text="$t('interface.helpCenter')"
      :question="$t('interface.haveIssues')"
      link="https://kb.myetherwallet.com"
    />

</template>


Allow IDE pipeline panel to be resizable

Closes #47538

<template>

    <resizable-panel
      v-if="rightPane"
      class="multi-file-commit-panel-inner"
      :collapsible="false"
      :initial-width="350"
      :min-size="350"
      side="right"
    >
      <component :is="rightPane" />
    </resizable-panel>

          <button
            v-tooltip
            data-container="body"
            data-placement="left"
            :title="__('Pipelines')"
            class="ide-sidebar-link is-right"
            :class="{
              active: pipelinesActive
            }"
            type="button"
            @click="clickTab($event, $options.rightSidebarViews.pipelines)"
          >
            <icon
              :size="16"
              name="pipeline"
            />
          </button>

</template>

<template>

      <component :is="rightPane" />

          <button
            v-tooltip
            data-container="body"
            data-placement="left"
            :title="__('Pipelines')"
            class="ide-sidebar-link is-right"
            :class="{
              active: pipelinesActive
            }"
            type="button"
            @click="clickTab($event, $options.rightSidebarViews.pipelines)"
          >
            <icon
              :size="16"
              name="pipeline"
            />
          </button>

</template>


Missing Comma added

Missing Comma added

<script>

/**
 * Renders each stage of the pipeline mini graph.
 *
 * Given the provided endpoint will make a request to
 * fetch the dropdown data when the stage is clicked.
 *
 * Request is made inside this component to make it reusable between:
 * 1. Pipelines main table
 * 2. Pipelines table in commit and Merge request views
 * 3. Merge request widget
 * 4. Commit widget
 */

import Flash from '../../flash';
import icon from '../../vue_shared/components/icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';

export default {
  props: {
    stage: {
      type: Object,
      required: true,
    },

    updateDropdown: {
      type: Boolean,
      required: false,
      default: false,
    },
  },

  directives: {
    tooltip,
  },

  data() {
    return {
      isLoading: false,
      dropdownContent: '',
    };
  },

  components: {
    loadingIcon,
    icon,
  },

  updated() {
    if (this.dropdownContent.length > 0) {
      this.stopDropdownClickPropagation();
    }
  },

  watch: {
    updateDropdown() {
      if (this.updateDropdown &&
        this.isDropdownOpen() &&
        !this.isLoading) {
        this.fetchJobs();
      }
    },
  },

  methods: {
    onClickStage() {
      if (!this.isDropdownOpen()) {
        this.isLoading = true;
        this.fetchJobs();
      }
    },

    fetchJobs() {
      this.$http.get(this.stage.dropdown_path)
        .then(response => response.json())
        .then((data) => {
          this.dropdownContent = data.html;
          this.isLoading = false;
        })
        .catch(() => {
          this.closeDropdown();
          this.isLoading = false;

          const flash = new Flash('Something went wrong on our end.');
          return flash;
        });
    },

    /**
     * When the user right clicks or cmd/ctrl + click in the job name
     * the dropdown should not be closed and the link should open in another tab,
     * so we stop propagation of the click event inside the dropdown.
     *
     * Since this component is rendered multiple times per page we need to guarantee we only
     * target the click event of this component.
     */
    stopDropdownClickPropagation() {
      $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item'))
        .on('click', (e) => {
          e.stopPropagation();
        });
    },

    closeDropdown() {
      if (this.isDropdownOpen()) {
        $(this.$refs.dropdown).dropdown('toggle');
      }
    },

    isDropdownOpen() {
      return this.$el.classList.contains('open');
    },
  },

  computed: {
    dropdownClass() {
      return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading';
    },

    triggerButtonClass() {
      return `ci-status-icon-${this.stage.status.group}`;
    },

    borderlessIcon() {
      return `${this.stage.status.icon}_borderless`;
    },
  },
};
</script>

<template>
  <div class="dropdown">
    <button
      v-tooltip
      :class="triggerButtonClass"
      @click="onClickStage"
      class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
      :title="stage.title"
      data-placement="top"
      data-toggle="dropdown"
      type="button"
      id="stageDropdown"
      aria-haspopup="true"
      aria-expanded="false">

      <span
        aria-hidden="true"
        :aria-label="stage.title">
        <icon
          :name="borderlessIcon"/>
      </span>

      <i
        class="fa fa-caret-down"
        aria-hidden="true">
      </i>
    </button>

    <ul
      class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"
      aria-labelledby="stageDropdown">

      <li
        :class="dropdownClass"
        class="js-builds-dropdown-list scrollable-menu">

        <loading-icon v-if="isLoading"/>

        <ul
          v-else
          v-html="dropdownContent">
        </ul>
      </li>
    </ul>
  </div>
</script>

<script>

/**
 * Renders each stage of the pipeline mini graph.
 *
 * Given the provided endpoint will make a request to
 * fetch the dropdown data when the stage is clicked.
 *
 * Request is made inside this component to make it reusable between:
 * 1. Pipelines main table
 * 2. Pipelines table in commit and Merge request views
 * 3. Merge request widget
 * 4. Commit widget
 */

import Flash from '../../flash';
import icon from '../../vue_shared/components/icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';

export default {
  props: {
    stage: {
      type: Object,
      required: true,
    },

    updateDropdown: {
      type: Boolean,
      required: false,
      default: false,
    },
  },

  directives: {
    tooltip,
  },

  data() {
    return {
      isLoading: false,
      dropdownContent: '',
    };
  },

  components: {
    loadingIcon,
    icon,
  },

  updated() {
    if (this.dropdownContent.length > 0) {
      this.stopDropdownClickPropagation();
    }
  },

  watch: {
    updateDropdown() {
      if (this.updateDropdown &&
        this.isDropdownOpen() &&
        !this.isLoading) {
        this.fetchJobs();
      }
    },
  },

  methods: {
    onClickStage() {
      if (!this.isDropdownOpen()) {
        this.isLoading = true;
        this.fetchJobs();
      }
    },

    fetchJobs() {
      this.$http.get(this.stage.dropdown_path)
        .then(response => response.json())
        .then((data) => {
          this.dropdownContent = data.html;
          this.isLoading = false;
        })
        .catch(() => {
          this.closeDropdown();
          this.isLoading = false;

          const flash = new Flash('Something went wrong on our end.');
          return flash;
        });
    },

    /**
     * When the user right clicks or cmd/ctrl + click in the job name
     * the dropdown should not be closed and the link should open in another tab,
     * so we stop propagation of the click event inside the dropdown.
     *
     * Since this component is rendered multiple times per page we need to guarantee we only
     * target the click event of this component.
     */
    stopDropdownClickPropagation() {
      $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item'))
        .on('click', (e) => {
          e.stopPropagation();
        });
    },

    closeDropdown() {
      if (this.isDropdownOpen()) {
        $(this.$refs.dropdown).dropdown('toggle');
      }
    },

    isDropdownOpen() {
      return this.$el.classList.contains('open');
    },
  },

  computed: {
    dropdownClass() {
      return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading';
    },

    triggerButtonClass() {
      return `ci-status-icon-${this.stage.status.group}`;
    },

    borderlessIcon() {
      return `${this.stage.status.icon}_borderless`;
    }
  },
};
</script>

<template>
  <div class="dropdown">
    <button
      v-tooltip
      :class="triggerButtonClass"
      @click="onClickStage"
      class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
      :title="stage.title"
      data-placement="top"
      data-toggle="dropdown"
      type="button"
      id="stageDropdown"
      aria-haspopup="true"
      aria-expanded="false">

      <span
        aria-hidden="true"
        :aria-label="stage.title">
        <icon
          :name="borderlessIcon"/>
      </span>

      <i
        class="fa fa-caret-down"
        aria-hidden="true">
      </i>
    </button>

    <ul
      class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"
      aria-labelledby="stageDropdown">

      <li
        :class="dropdownClass"
        class="js-builds-dropdown-list scrollable-menu">

        <loading-icon v-if="isLoading"/>

        <ul
          v-else
          v-html="dropdownContent">
        </ul>
      </li>
    </ul>
  </div>
</script>


Changed breakpoint for notification.

Changed breakpoint for notification.

<template>

            <show-question-request :notification="notification" v-if="notification.type === 'showQuestionRequest'">
            </show-question-request>
            <add-to-trust-circle :notification="notification" v-if="notification.type === 'addedToTrustCircle'">
            </add-to-trust-circle>
            <watch-commitment :notification="notification" v-if="notification.type === 'watchingCommitment'">
            </watch-commitment>
            <watch-question :notification="notification" v-if="notification.type === 'watchingQuestion'">
            </watch-question>
            <created-answer :notification="notification" v-if="notification.type === 'createdAnswer'">
            </created-answer>

            {{$t("pages:notifications.noNotifications")}}

</template>


Added lazy loading and removed youtube button.

<template>

{{$t('pages:landingPagePublic.donation.title')}}

                {{$t('pages:landingPagePublic.donation.description')}}

            <v-btn color="secondary" class="action-button" @click="$router.push({name: 'donation'})">
                {{$t('common:button.donate')}}
            </v-btn>

</template>


Avoid flashing IPs when removing

Avoid flashing IPs when removing

<template>

        {{ $t('DBConnection.description_fl') }}

          QGIS

          Tableau

          Qlik

          Power BI

          Databricks

        <SettingsTitle :title="$t('DBConnection.ipsSection.title')"></SettingsTitle>

        <Toggle
          class="dbconnection__formsection"
          :disabled="true"
          :label="$t('DBConnection.ipsSection.allowAllIPs')">
        </Toggle>

        <InputList
          ref="ipInputList"
          class="dbconnection__formsection"
          :title="$t('DBConnection.ipsSection.ipList.title')"
          :placeholder="$t('DBConnection.ipsSection.ipList.placeholder')"
          :values="ipList"
          :fieldValidator="checkIfIPIsValid"
          :addElementToState="false"
          @removeElement="onIPRemoved">
          {{ $t('DBConnection.ipsSection.ipList.description') }}
          {{ $t('DBConnection.ipsSection.ipList.getCurrentIP') }}.
        </InputList>

      <SettingsTitle :title="$t('DBConnection.certificatesSection.title', {certificatesLength, certificateLimit})">

          <button
            class="button button--small"
            :disabled="!canCreateCertificates"
            @click="goToCertificateCreation">
            {{ $t('DBConnection.certificatesSection.createNewCertificate') }}
          </button>

      </SettingsTitle>

        <template v-if="certificatesLength">
          <CertificateCard
            v-for="certificate in certificates"
            :key="certificate.id"
            :certificate="certificate"
            @revoke="onCertificateRevoked"></CertificateCard>
        </template>

          {{ $t('DBConnection.certificatesSection.noCertificates') }}

      <SettingsTitle title="Generate your Certificate" class="dbconnection-creationform">

          <button class="button__back" style="margin-right: 20px;" @click="goToCertificateList">

          </button>

      </SettingsTitle>
      <CertificateCreation class="creation__component" @create="onCertificateCreated"></CertificateCreation>

    <CertificateDownloadModal :certificate="newCertificate" ref="certificateDownload"></CertificateDownloadModal>

</template>


frontend fixes and improvements

frontend fixes and improvements

<template>

                         {{templatePart.name}}

                            <AdditionalField
                              v-bind:content="localContent[templatePart.name]"
                              v-bind:template="templatePart"
                              @changed="newContent => {

                                //update full content
                                content[index][templatePart.name] = newContent;

                                //send all content back
                                updateValue(content);
                              }"
                            />

                             {
                                content.splice(index, 1);
                                updateValue(content);
                            }" class="action delete">-

            add

            <input
                ref="input"
                v-bind:value="content"
                v-on:input="updateValue($event.target.value)"/>

</template>


Fix editor and viewer view on mobile

Fix editor and viewer view on mobile

<template>

    <navbar v-model="navbarInput"></navbar>

        <editor
          v-model="mdContent"
        ></editor>

        <viewer
          :editor-content="mdContent"
          :file-format="fileFormat"
          >
        </viewer>

</template>

<template>

    <navbar v-model="action"></navbar>

        <editor
          v-model="mdContent"
        ></editor>

        <viewer
          :editor-content="mdContent"
          :file-format="fileFormat"
          >
        </viewer>

</template>


Render preview of blog post in overview

Render preview of blog post in overview

<template>

          <post-details :post="new Post(post)" />

</template>

<template>

          <post-details :post="new Post(post)" />

      Edit post

</template>


Fixed hiding the page content when there are quicksearch results.

Refs #78.

<template>
	<v-app id="demyo">
		<v-navigation-drawer
			v-if="!$vuetify.breakpoint.smAndDown"
			v-model="displayDetailsPane" right app clipped
			width="33vw" mobile-breakpoint="sm"
			:disable-resize-watcher="true"
		>
			<portal-target name="appSidePane" />
		</v-navigation-drawer>

		<v-navigation-drawer v-model="mainMenu" app temporary width="20em">
			<v-list class="l-DefaultLayout__menuList">
				<v-list-group v-if="readerLoaded">
					<template #activator>
						<v-list-item-icon>
							<LetterIcon :letter="currentReader.name[0]" :color="currentReader.colour" />
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ currentReader.name }}</v-list-item-title>
						</v-list-item-content>
					</template>

					<v-list-item :to="`/readers/${currentReader.id}/favourites`">
						<v-list-item-icon>
							<v-icon>mdi-heart</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.faves') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item :to="`/readers/${currentReader.id}/readingList`">
						<v-list-item-icon>
							<v-icon>mdi-library</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.readingList') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item :to="`/readers/${currentReader.id}/configuration`">
						<v-list-item-icon>
							<v-icon>mdi-cog</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.configuration') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item @click="promptReaderSelection = true">
						<v-list-item-icon>
							<v-icon>mdi-account-convert</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.switch') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item :href="`/readers/`">
						<v-list-item-icon>
							<v-icon>mdi-account-multiple</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.manage') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
				</v-list-group>

				<v-divider />

				<v-list-item id="l-DefaultLayout__menuSearch">
					<v-list-item-content>
						<v-text-field
							v-model="quicksearchQuery" clearable hide-details
							prepend-icon="mdi-magnify" @keyup="performSearch"
							@click:clear="clearSearch"
						/>
					</v-list-item-content>
				</v-list-item>

				<v-list-item to="/">
					<v-list-item-icon>
						<v-icon>mdi-home</v-icon>
					</v-list-item-icon>
					<v-list-item-content>
						<v-list-item-title>{{ $t('menu.main.home') }}</v-list-item-title>
					</v-list-item-content>
				</v-list-item>

				<v-list-group v-for="section in menuItems" :key="section.title" v-model="section.active">
					<template #activator>
						<v-list-item-content>
							<v-list-item-title v-text="$t(section.title)" />
						</v-list-item-content>
					</template>

					<v-list-item v-for="item in section.subItems" :key="item.title" :to="item.url" exact>
						<v-list-item-icon>
							<v-icon v-text="item.icon" />
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title v-text="$t(item.title)" />
						</v-list-item-content>
					</v-list-item>
				</v-list-group>
			</v-list>
		</v-navigation-drawer>
		<v-app-bar color="primary" dark app clipped-right>
			<v-app-bar-nav-icon @click.stop="mainMenu = !mainMenu" />
			<v-toolbar-title>{{ pageTitle }}</v-toolbar-title>
			<v-spacer />
			<template v-if="!suppressSearch">
				<v-btn id="l-DefaultLayout__toolbarSearchButton" icon @click="showQuicksearch = true; focus()">
					<v-icon>mdi-magnify</v-icon>
				</v-btn>
				<v-expand-x-transition>

						<v-text-field
							ref="toolbarSearch"
							v-model="quicksearchQuery" clearable hide-details
							@click:clear="showQuicksearch = false; clearSearch()"
							@blur="blur" @keyup="performSearch"
						/>

				</v-expand-x-transition>
			</template>
			<portal-target name="appBarAddons" />
			<portal-target name="appTasks" />
		</v-app-bar>

		<v-main id="l-DefaultLayout__mainContent">

			<v-dialog v-if="$vuetify.breakpoint.smAndDown" v-model="displayDetailsPane">
				<v-card>
					<portal-target name="appSidePane" />
				</v-card>
			</v-dialog>
			<v-container id="l-DefaultLayout__mainContainer" fluid>
				<ReaderSelection
					v-if="requireReaderSelection || promptReaderSelection" :require-selection="requireReaderSelection"
					@cancel="promptReaderSelection = false" @select="promptReaderSelection = false"
				/>
				<v-overlay absolute z-index="4" :value="globalOverlay" class="l-DefaultLayout__overlay">
					<v-progress-circular :indeterminate="true" color="primary" size="96" width="8" />
					{{ $t('core.loading') }}
				</v-overlay>
				<QuickSearchResults
					v-if="isRelevantSearchQuery" :results="quicksearchResults"
					:loading="quicksearchLoading" @click="showQuicksearch = false; clearSearch()"
				/>

					<slot />

				<AppSnackbar :shown="displaySnackbar" :message="snackbarMessage" @close="closeSnackbar" />
			</v-container>
		</v-main>
		<v-footer color="secondary" dark>
			<v-col>
				Demyo "{{ demyoCodename }}"
			</v-col>
		</v-footer>
	</v-app>
</template>

<template>
	<v-app id="demyo">
		<v-navigation-drawer
			v-if="!$vuetify.breakpoint.smAndDown"
			v-model="displayDetailsPane" right app clipped
			width="33vw" mobile-breakpoint="sm"
			:disable-resize-watcher="true"
		>
			<portal-target name="appSidePane" />
		</v-navigation-drawer>

		<v-navigation-drawer v-model="mainMenu" app temporary width="20em">
			<v-list class="l-DefaultLayout__menuList">
				<v-list-group v-if="readerLoaded">
					<template #activator>
						<v-list-item-icon>
							<LetterIcon :letter="currentReader.name[0]" :color="currentReader.colour" />
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ currentReader.name }}</v-list-item-title>
						</v-list-item-content>
					</template>

					<v-list-item :to="`/readers/${currentReader.id}/favourites`">
						<v-list-item-icon>
							<v-icon>mdi-heart</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.faves') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item :to="`/readers/${currentReader.id}/readingList`">
						<v-list-item-icon>
							<v-icon>mdi-library</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.readingList') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item :to="`/readers/${currentReader.id}/configuration`">
						<v-list-item-icon>
							<v-icon>mdi-cog</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.configuration') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item @click="promptReaderSelection = true">
						<v-list-item-icon>
							<v-icon>mdi-account-convert</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.switch') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
					<v-list-item :href="`/readers/`">
						<v-list-item-icon>
							<v-icon>mdi-account-multiple</v-icon>
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title>{{ $t('menu.reader.manage') }}</v-list-item-title>
						</v-list-item-content>
					</v-list-item>
				</v-list-group>

				<v-divider />

				<v-list-item id="l-DefaultLayout__menuSearch">
					<v-list-item-content>
						<v-text-field
							v-model="quicksearchQuery" clearable hide-details
							prepend-icon="mdi-magnify" @keyup="performSearch"
							@click:clear="clearSearch"
						/>
					</v-list-item-content>
				</v-list-item>

				<v-list-item to="/">
					<v-list-item-icon>
						<v-icon>mdi-home</v-icon>
					</v-list-item-icon>
					<v-list-item-content>
						<v-list-item-title>{{ $t('menu.main.home') }}</v-list-item-title>
					</v-list-item-content>
				</v-list-item>

				<v-list-group v-for="section in menuItems" :key="section.title" v-model="section.active">
					<template #activator>
						<v-list-item-content>
							<v-list-item-title v-text="$t(section.title)" />
						</v-list-item-content>
					</template>

					<v-list-item v-for="item in section.subItems" :key="item.title" :to="item.url" exact>
						<v-list-item-icon>
							<v-icon v-text="item.icon" />
						</v-list-item-icon>
						<v-list-item-content>
							<v-list-item-title v-text="$t(item.title)" />
						</v-list-item-content>
					</v-list-item>
				</v-list-group>
			</v-list>
		</v-navigation-drawer>
		<v-app-bar color="primary" dark app clipped-right>
			<v-app-bar-nav-icon @click.stop="mainMenu = !mainMenu" />
			<v-toolbar-title>{{ pageTitle }}</v-toolbar-title>
			<v-spacer />
			<template v-if="!suppressSearch">
				<v-btn id="l-DefaultLayout__toolbarSearchButton" icon @click="showQuicksearch = true; focus()">
					<v-icon>mdi-magnify</v-icon>
				</v-btn>
				<v-expand-x-transition>

						<v-text-field
							ref="toolbarSearch"
							v-model="quicksearchQuery" clearable hide-details
							@click:clear="showQuicksearch = false; clearSearch()"
							@blur="blur" @keyup="performSearch"
						/>

				</v-expand-x-transition>
			</template>
			<portal-target name="appBarAddons" />
			<portal-target name="appTasks" />
		</v-app-bar>

		<v-main id="l-DefaultLayout__mainContent">

			<v-dialog v-if="$vuetify.breakpoint.smAndDown" v-model="displayDetailsPane">
				<v-card>
					<portal-target name="appSidePane" />
				</v-card>
			</v-dialog>
			<v-container id="l-DefaultLayout__mainContainer" fluid>
				<ReaderSelection
					v-if="requireReaderSelection || promptReaderSelection" :require-selection="requireReaderSelection"
					@cancel="promptReaderSelection = false" @select="promptReaderSelection = false"
				/>
				<v-overlay absolute z-index="4" :value="globalOverlay" class="l-DefaultLayout__overlay">
					<v-progress-circular :indeterminate="true" color="primary" size="96" width="8" />
					{{ $t('core.loading') }}
				</v-overlay>
				<QuickSearchResults
					v-if="isRelevantSearchQuery" :results="quicksearchResults"
					:loading="quicksearchLoading" @click="showQuicksearch = false; clearSearch()"
				/>

				<slot v-show="!isRelevantSearchQuery" />
				<AppSnackbar :shown="displaySnackbar" :message="snackbarMessage" @close="closeSnackbar" />
			</v-container>
		</v-main>
		<v-footer color="secondary" dark>
			<v-col>
				Demyo "{{ demyoCodename }}"
			</v-col>
		</v-footer>
	</v-app>
</template>


Update color and opacity to meet WCAG AA contrast standard 4.5:1

<template>

    <router-link
      class="tab"
      :class="$computedClass(tabStyles)"
      :to="link"
    >

        <slot></slot>

        {{ title }}

    </router-link>

</template>


Refactore

Refactore

<template>

		<template v-for="i in 10">
			<mk-note-skeleton :key="i"/>
		</template>

	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div">
		<template v-for="(notification, i) in _notifications">
			<x-notification class="notification" :notification="notification" :key="notification.id"/>

				<fa icon="angle-up"/>{{ notification._datetext }}
				<fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}

		</template>
	</component>
	<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMoreNotifications" :disabled="moreFetching">
		<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? this.$t('@.loading') : this.$t('@.load-more') }}
	</button>

{{ $t('empty') }}

	<mk-error v-if="error" @retry="init()"/>

</template>

<template>

		<template v-for="i in 10">
			<mk-note-skeleton :key="i"/>
		</template>

	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div">
		<template v-for="(notification, i) in _notifications">
			<x-notification class="notification" :notification="notification" :key="notification.id"/>

				<fa icon="angle-up"/>{{ notification._datetext }}
				<fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}

		</template>
	</component>
	<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
		<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? this.$t('@.loading') : this.$t('@.load-more') }}
	</button>

{{ $t('empty') }}

</template>


show or hide contracts depending on permalink content

<template>

      {{ labelQuestionDescription }}

        <ContractsShowTableFooterTr
          :label="labelEntity"
          :value="calculatePercentage('contractor')"
        />
        <ContractsShowTableFooterTr
          :label="labelType"
          :value="calculatePercentage('contract_type')"
        />
        <ContractsShowTableFooterTr
          :label="labelProcess"
          :value="calculatePercentage('process_type')"
        />

        {{ labelLink }}

</template>


added country code to location autocomplete

<template>

    <form class="pk-panel-teaser uk-form uk-form-stacked" v-show="editing" v-on="submit: $event.preventDefault()">

            {{ 'Location' | trans }}

                    <input id="form-city" class="uk-width-1-1" type="text" v-model="widget.city" autocomplete="off">

            {{ 'Unit' | trans }}

                    <input type="radio" value="metric" v-model="widget.units"> {{ 'Metric' | trans }}

                    <input type="radio" value="imperial" v-model="widget.units"> {{ 'Imperial' | trans }}

    </form>

{{ time | date format }}

{{ time | date 'medium' }}

{{ widget.city }}

{{ temperature }}

<v-loader></v-loader>

{{ 'Unable to retrieve weather data.' | trans }}

{{ 'No location given.' | trans }}

</template>


Update Team edit modal to use displayName (#6215)

<template>
  <modal @close="$emit('close')" :title="modalTitle" id="clan-creation-modal">

        Team name:
        <input id="input-name" type="text" v-model="name" />

        Description:
        <textarea id="input-description" type="text" rows=2 v-model="description" />

        Public:
        <input id="input-is-public" type="checkbox" v-model="isPublic" />

      <button @click.prevent="submit">{{ modalTitle }}</button>

  </modal>
</template>


site page setting title updated to navigation title

<template>

                <input class="uk-width-1-1 uk-form-large" type="text" name="page[title]" placeholder="{{ 'Enter Title' | trans }}" v-model="page.title">

                <v-editor name="page[content]" value="{{@ page.content }}" options="{{ {markdown : page.data.markdown} }}"></v-editor>

                    {{ 'Navigation Title' | trans }}

                        <input id="form-navigation-title" class="uk-form-width-large" type="text" name="node[title]" v-model="node.title" v-valid="required">

{{ 'Invalid name.' | trans }}

                    {{ 'Slug' | trans }}

                        <input id="form-slug" class="uk-form-width-large" type="text" name="node[slug]" v-model="node.slug">

                    {{ 'Status' | trans }}

                        <select id="form-status" class="uk-form-width-large" v-model="node.status">
                            <option value="0">{{ 'Disabled' | trans }}</option>
                            <option value="1">{{ 'Enabled' | trans }}</option>
                        </select>

                    {{ 'Options' | trans }}

                        <input type="checkbox" name="page[data][title]" v-model="page.data.title"> {{ 'Show Title' | trans }}

                        <input type="checkbox" name="page[data][markdown]" v-model="page.data.markdown"> {{ 'Enable Markdown' | trans }}

</template>

<template>

                <input class="uk-width-1-1 uk-form-large" type="text" name="page[title]" placeholder="{{ 'Enter Title' | trans }}" v-model="page.title">

                <v-editor name="page[content]" value="{{@ page.content }}" options="{{ {markdown : page.data.markdown} }}"></v-editor>

                    {{ 'Options' | trans }}

                        <input type="checkbox" name="page[data][title]" v-model="page.data.title"> {{ 'Show Title' | trans }}

                        <input type="checkbox" name="page[data][markdown]" v-model="page.data.markdown"> {{ 'Enable Markdown' | trans }}

</template>


Add queue chart

Add queue chart

<template>

	<ui-card>
		<template #title>{{ $t('operation') }}</template>

			Deliver
			<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
				<ui-input :value="latestStats.deliver.waiting | number" type="text" readonly>
					Waiting
				</ui-input>
				<ui-input :value="latestStats.deliver.delayed | number" type="text" readonly>
					Delayed
				</ui-input>
				<ui-input :value="latestStats.deliver.active | number" type="text" readonly>
					Active
				</ui-input>
			</ui-horizon-group>

			Inbox
			<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
				<ui-input :value="latestStats.inbox.waiting | number" type="text" readonly>
					Waiting
				</ui-input>
				<ui-input :value="latestStats.inbox.delayed | number" type="text" readonly>
					Delayed
				</ui-input>
				<ui-input :value="latestStats.inbox.active | number" type="text" readonly>
					Active
				</ui-input>
			</ui-horizon-group>

			<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>

	</ui-card>

</template>

<template>

	<ui-card>
		<template #title>{{ $t('operation') }}</template>

			Deliver
			<ui-horizon-group inputs v-if="stats">
				<ui-input :value="stats.deliver.waiting | number" type="text" readonly>
					Waiting
				</ui-input>
				<ui-input :value="stats.deliver.delayed | number" type="text" readonly>
					Delayed
				</ui-input>
				<ui-input :value="stats.deliver.active | number" type="text" readonly>
					Active
				</ui-input>
			</ui-horizon-group>

			Inbox
			<ui-horizon-group inputs v-if="stats">
				<ui-input :value="stats.inbox.waiting | number" type="text" readonly>
					Waiting
				</ui-input>
				<ui-input :value="stats.inbox.delayed | number" type="text" readonly>
					Delayed
				</ui-input>
				<ui-input :value="stats.inbox.active | number" type="text" readonly>
					Active
				</ui-input>
			</ui-horizon-group>

			<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>

	</ui-card>

</template>


known user account listing fix

known user account listing fix

<template>

        <form class="u-form kiwi-invitelist" @submit.prevent="">
            {{ $t('invites_refresh') }}

                This is a public channel.
                Only allow invited users

                Only invited users can join.
                Allow anybody to join

            Only registered users can join

Invited users ({{ inviteListAccounts.length }})

 0 && areWeAnOp">
                    <select ref="addInviteList">
                        <option
                            v-for="user in knownAccounts"
                            :key="user.nick" :value="user.account"
                        >{{ user.account }}</option>
                    </select>
                    <button @click="addAccountInvite($refs.addInviteList.value)">Add invite</button>

 0" class="kiwi-invitelist-table">

                        {{ $t('invites_user') }}
                        {{ $t('invites_by') }}

                            {{ displayMask(invite) }}

                            {{ invite.invited_by }}

                            {{ (new Date(invite.invited_at * 1000)).toDateString() }}

 0">

Invite matches

                    <input ref="addInviteText" type="text" class="u-input">
                    <button @click="addInvite($refs.addInviteText.value)">Add invite</button>

                        {{ $t('invites_user') }}

                            {{ invite.invited }}

                            {{ (new Date(invite.invited_at * 1000)).toDateString() }}

                {{ $t('invites_refreshing') }}

        </form>

</template>

<template>

        <form class="u-form kiwi-invitelist" @submit.prevent="">
            {{ $t('invites_refresh') }}

                This is a public channel.
                Only allow invited users

            Any registered user can join

Invited users ({{ inviteListAccounts.length }})

 0 && areWeAnOp">
                    <select ref="addInviteList">
                        <option
                            v-for="user in knownAccounts"
                            :key="user.nick" :value="user.account"
                        >{{ user.account }}</option>
                    </select>
                    <button @click="addAccountInvite($refs.addInviteList.value)">Add invite</button>

 0" class="kiwi-invitelist-table">

                        {{ $t('invites_user') }}
                        {{ $t('invites_by') }}

                            {{ displayMask(invite) }}

                            {{ invite.invited_by }}

                            {{ (new Date(invite.invited_at * 1000)).toDateString() }}

 0">

Invite matches

                    <input ref="addInviteText" type="text" class="u-input">
                    <button @click="addInvite($refs.addInviteText.value)">Add invite</button>

                        {{ $t('invites_user') }}

                            {{ invite.invited }}

                            {{ (new Date(invite.invited_at * 1000)).toDateString() }}

                {{ $t('invites_refreshing') }}

        </form>

</template>


rename functionto better match the content

<template>

    <transition name="md-ripple" appear @after-enter="clearWave" v-if="!mdDisabled">

    </transition>

    <slot />

</template>


Fix issue w/ command warnings regarding predicates

<template>

 0">
    <q-table
      class="my-sticky-header-table"
      :data="commandData"
      :columns="columns"
      row-key="name"
      dense
      virtual-scroll
      flat
      binary-state-sort
      :pagination.sync="pagination"
    >
      <template v-slot:header="props">
        <q-tr :props="props">
          <q-th
            v-for="col in props.cols"
            :key="col.name"
            :props="props"
          >
            {{ col.label }}
          </q-th>
        </q-tr>
      </template>
      <template v-slot:body="props">
        <q-tr :props="props">
                  <q-td key="status" :props="props" class="q-px-none">
            <q-icon
              v-show="displayCommandWarning(props.row)"
              name="warning"
              class="text-amber"
              style="font-size: 1.5rem"
            >
            <q-tooltip>
                This command is mandatory for the cluster and device type configuration you have enabled
              </q-tooltip>
            </q-icon>
          </q-td>
          <q-td key="out" :props="props" auto-width>
            <q-checkbox
              class="q-mt-xs"
              v-model="selectionOut"
              :val="hashCommandIdClusterId(props.row.id, selectedCluster.id)"
              v-show="
                (selectionClients.includes(selectedCluster.id) &&
                  props.row.source == 'client') ||
                (selectionServers.includes(selectedCluster.id) &&
                  props.row.source == 'server')
              "
              indeterminate-value="false"
              keep-color
              @input="
                handleCommandSelection(
                  selectionOut,
                  'selectedOut',
                  props.row,
                  selectedCluster.id
                )
              "
            />
          </q-td>
          <q-td key="in" :props="props" auto-width>
            <q-checkbox
              class="q-mt-xs"
              v-model="selectionIn"
              :val="hashCommandIdClusterId(props.row.id, selectedCluster.id)"
              indeterminate-value="false"
              keep-color
              v-show="
                (selectionServers.includes(selectedCluster.id) &&
                  props.row.source == 'client') ||
                (selectionClients.includes(selectedCluster.id) &&
                  props.row.source == 'server')
              "
              @input="
                handleCommandSelection(
                  selectionIn,
                  'selectedIn',
                  props.row,
                  selectedCluster.id
                )
              "
            />
          </q-td>
          <q-td key="direction" :props="props" auto-width>{{
            props.row.source === 'client'
              ? 'Client ➞ Server'
              : 'Server ➞ Client'
          }}</q-td>
          <q-td key="commandId" :props="props" auto-width>{{
            asHex(props.row.code, 2)
          }}</q-td>
          <q-td key="commandName" :props="props" auto-width>{{
            props.row.label
          }}</q-td>
          <q-td key="required" :props="props" auto-width>
            {{ isCommandRequired(props.row) ? 'Yes' : '' }}
          </q-td>
          <q-td key="mfgId" :props="props" auto-width
            >{{
              selectedCluster.manufacturerCode
                ? asHex(selectedCluster.manufacturerCode, 4)
                : props.row.manufacturerCode
                ? asHex(props.row.manufacturerCode, 4)
                : '-'
            }}
          </q-td>
        </q-tr>
      </template>
    </q-table>

</template>


Fix undefined on carousel (#2202)

Fix undefined on carousel (#2202)

- Add a condition before deactivating an item

<template>
    <div
        class="carousel"
        :class="{'is-overlay': overlay}"
        @mouseenter="pauseTimer"
        @mouseleave="startTimer">
        <progress
            v-if="progress"
            class="progress"
            :class="progressType"
            :value="activeItem"
            :max="carouselItems.length - 1">
            {{ carouselItems.length - 1 }}
        </progress>
        <div
            class="carousel-items"
            @mousedown="dragStart"
            @mouseup="dragEnd"
            @touchstart.stop="dragStart"
            @touchend.stop="dragEnd">
            <slot />
            <div
                v-if="arrow"
                class="carousel-arrow"
                :class="{'is-hovered': arrowHover}">
                <b-icon
                    v-if="checkArrow(0)"
                    class="has-icons-left"
                    @click.native.prevent="prev"
                    :pack="iconPack"
                    :icon="iconPrev"
                    :size="iconSize"
                    both />
                <b-icon
                    v-if="checkArrow(carouselItems.length - 1)"
                    class="has-icons-right"
                    @click.native.prevent="next"
                    :pack="iconPack"
                    :icon="iconNext"
                    :size="iconSize"
                    both />
            </div>
        </div>
        <div
            v-if="autoplay && pauseHover && pauseInfo && isPause"
            class="carousel-pause">
            <span
                class="tag"
                :class="pauseInfoType">
                {{ pauseText }}
            </span>
        </div>
        <template v-if="withCarouselList && !indicator">
            <slot
                :active="activeItem"
                :switch="changeItem"
                name="list"/>
        </template>
        <div
            v-if="indicator"
            class="carousel-indicator"
            :class="indicatorClasses">
            <a
                v-for="(item, index) in carouselItems"
                class="indicator-item"
                :class="{'is-active': index === activeItem}"
                @mouseover="modeChange('hover', index)"
                @click="modeChange('click', index)"
                :key="index">
                <slot
                    :i="index"
                    name="indicators">
                    <span class="indicator-style" :class="indicatorStyle"/>
                </slot>
            </a>
        </div>
        <template v-if="overlay">
            <slot name="overlay"/>
        </template>
    </div>
</template>

<script>
import config from '../../utils/config'

import Icon from '../icon/Icon'

export default {
    name: 'BCarousel',
    components: {
        [Icon.name]: Icon
    },
    props: {
        value: {
            type: Number,
            default: 0
        },
        animated: {
            type: String,
            default: 'slide'
        },
        interval: Number,
        hasDrag: {
            type: Boolean,
            default: true
        },
        autoplay: {
            type: Boolean,
            default: true
        },
        pauseHover: {
            type: Boolean,
            default: true
        },
        pauseInfo: {
            type: Boolean,
            default: true
        },
        pauseInfoType: {
            type: String,
            default: 'is-white'
        },
        pauseText: {
            type: String,
            default: 'Pause'
        },
        arrow: {
            type: Boolean,
            default: true
        },
        arrowBoth: {
            type: Boolean,
            default: true
        },
        arrowHover: {
            type: Boolean,
            default: true
        },
        repeat: {
            type: Boolean,
            default: true
        },
        iconPack: String,
        iconSize: String,
        iconPrev: {
            type: String,
            default: config.defaultIconPrev
        },
        iconNext: {
            type: String,
            default: config.defaultIconNext
        },
        indicator: {
            type: Boolean,
            default: true
        },
        indicatorBackground: Boolean,
        indicatorCustom: Boolean,
        indicatorCustomSize: {
            type: String,
            default: 'is-small'
        },
        indicatorInside: {
            type: Boolean,
            default: true
        },
        indicatorMode: {
            type: String,
            default: 'click'
        },
        indicatorPosition: {
            type: String,
            default: 'is-bottom'
        },
        indicatorStyle: {
            type: String,
            default: 'is-dots'
        },
        overlay: Boolean,
        progress: Boolean,
        progressType: {
            type: String,
            default: 'is-primary'
        },
        withCarouselList: Boolean
    },
    data() {
        return {
            _isCarousel: true,
            activeItem: this.value,
            carouselItems: [],
            isPause: false,
            dragX: 0,
            timer: null
        }
    },
    computed: {
        indicatorClasses() {
            return [
                {
                    'has-background': this.indicatorBackground,
                    'has-custom': this.indicatorCustom,
                    'is-inside': this.indicatorInside
                },
                this.indicatorCustom && this.indicatorCustomSize,
                this.indicatorInside && this.indicatorPosition
            ]
        }
    },
    watch: {
        /**
         * When v-model is changed set the new active item.
         */
        value(value) {
            if (value < this.activeItem) {
                this.changeItem(value)
            } else {
                this.changeItem(value, false)
            }
        },
        /**
         * When carousel-items are updated, set active one.
         */
        carouselItems() {
            if (this.activeItem < this.carouselItems.length) {
                this.carouselItems[this.activeItem].isActive = true
            }
        },
        /**
         *  When autoplay is change, set by status
         */
        autoplay(status) {
            status ? this.startTimer() : this.pauseTimer()
        }
    },
    methods: {
        startTimer() {
            if (!this.autoplay || this.timer) return
            this.isPause = false
            this.timer = setInterval(() => {
                if (!this.repeat && this.activeItem === this.carouselItems.length - 1) {
                    this.pauseTimer()
                } else {
                    this.next()
                }
            }, (this.interval || config.defaultCarouselInterval))
        },
        pauseTimer() {
            this.isPause = true
            if (this.timer) {
                clearInterval(this.timer)
                this.timer = null
            }
        },
        checkPause() {
            if (this.pauseHover && this.autoplay) {
                return this.pauseTimer()
            }
        },
        /**
         * Change the active item and emit change event.
         * action only for animated slide, there true = next, false = prev
         */
        changeItem(newIndex, action = true) {
            if (this.activeItem === newIndex) return

            if (this.activeItem < this.carouselItems.length) {
                this.carouselItems[this.activeItem].status(false, action)
            }
            this.carouselItems[newIndex].status(true, action)
            this.activeItem = newIndex
            this.$emit('change', newIndex)
        },
        // Indicator trigger when change active item.
        modeChange(trigger, value) {
            if (this.indicatorMode === trigger) {
                this.$emit('input', value)
                return value < this.activeItem
                    ? this.changeItem(value)
                    : this.changeItem(value, false)
            }
        },
        prev() {
            if (this.activeItem === 0) {
                if (this.repeat) this.changeItem(this.carouselItems.length - 1)
            } else {
                this.changeItem(this.activeItem - 1)
            }
        },
        next() {
            if (this.activeItem === this.carouselItems.length - 1) {
                if (this.repeat) this.changeItem(0, false)
            } else {
                this.changeItem(this.activeItem + 1, false)
            }
        },
        // checking arrow between both
        checkArrow(value) {
            if (this.arrowBoth) return true
            if (this.activeItem !== value) return true
        },
        // handle drag event
        dragStart(event) {
            if (!this.hasDrag) return
            this.dragx = event.touches ? event.changedTouches[0].pageX : event.pageX
            if (event.touches) {
                this.pauseTimer()
            } else {
                event.preventDefault()
            }
        },
        dragEnd(event) {
            if (!this.hasDrag) return
            const detected = event.touches ? event.changedTouches[0].pageX : event.pageX
            const diffX = detected - this.dragx
            if (Math.abs(diffX) > 50) {
                if (diffX < 0) {
                    this.next()
                } else {
                    this.prev()
                }
            }
            if (event.touches) {
                this.startTimer()
            }
        }
    },
    mounted() {
        if (this.activeItem < this.carouselItems.length) {
            this.carouselItems[this.activeItem].isActive = true
        }
        this.startTimer()
    },
    beforeDestroy() {
        this.pauseTimer()
    }
}
</script>

<template>
    <div
        class="carousel"
        :class="{'is-overlay': overlay}"
        @mouseenter="pauseTimer"
        @mouseleave="startTimer">
        <progress
            v-if="progress"
            class="progress"
            :class="progressType"
            :value="activeItem"
            :max="carouselItems.length - 1">
            {{ carouselItems.length - 1 }}
        </progress>
        <div
            class="carousel-items"
            @mousedown="dragStart"
            @mouseup="dragEnd"
            @touchstart.stop="dragStart"
            @touchend.stop="dragEnd">
            <slot />
            <div
                v-if="arrow"
                class="carousel-arrow"
                :class="{'is-hovered': arrowHover}">
                <b-icon
                    v-if="checkArrow(0)"
                    class="has-icons-left"
                    @click.native.prevent="prev"
                    :pack="iconPack"
                    :icon="iconPrev"
                    :size="iconSize"
                    both />
                <b-icon
                    v-if="checkArrow(carouselItems.length - 1)"
                    class="has-icons-right"
                    @click.native.prevent="next"
                    :pack="iconPack"
                    :icon="iconNext"
                    :size="iconSize"
                    both />
            </div>
        </div>
        <div
            v-if="autoplay && pauseHover && pauseInfo && isPause"
            class="carousel-pause">
            <span
                class="tag"
                :class="pauseInfoType">
                {{ pauseText }}
            </span>
        </div>
        <template v-if="withCarouselList && !indicator">
            <slot
                :active="activeItem"
                :switch="changeItem"
                name="list"/>
        </template>
        <div
            v-if="indicator"
            class="carousel-indicator"
            :class="indicatorClasses">
            <a
                v-for="(item, index) in carouselItems"
                class="indicator-item"
                :class="{'is-active': index === activeItem}"
                @mouseover="modeChange('hover', index)"
                @click="modeChange('click', index)"
                :key="index">
                <slot
                    :i="index"
                    name="indicators">
                    <span class="indicator-style" :class="indicatorStyle"/>
                </slot>
            </a>
        </div>
        <template v-if="overlay">
            <slot name="overlay"/>
        </template>
    </div>
</template>

<script>
import config from '../../utils/config'

import Icon from '../icon/Icon'

export default {
    name: 'BCarousel',
    components: {
        [Icon.name]: Icon
    },
    props: {
        value: {
            type: Number,
            default: 0
        },
        animated: {
            type: String,
            default: 'slide'
        },
        interval: Number,
        hasDrag: {
            type: Boolean,
            default: true
        },
        autoplay: {
            type: Boolean,
            default: true
        },
        pauseHover: {
            type: Boolean,
            default: true
        },
        pauseInfo: {
            type: Boolean,
            default: true
        },
        pauseInfoType: {
            type: String,
            default: 'is-white'
        },
        pauseText: {
            type: String,
            default: 'Pause'
        },
        arrow: {
            type: Boolean,
            default: true
        },
        arrowBoth: {
            type: Boolean,
            default: true
        },
        arrowHover: {
            type: Boolean,
            default: true
        },
        repeat: {
            type: Boolean,
            default: true
        },
        iconPack: String,
        iconSize: String,
        iconPrev: {
            type: String,
            default: config.defaultIconPrev
        },
        iconNext: {
            type: String,
            default: config.defaultIconNext
        },
        indicator: {
            type: Boolean,
            default: true
        },
        indicatorBackground: Boolean,
        indicatorCustom: Boolean,
        indicatorCustomSize: {
            type: String,
            default: 'is-small'
        },
        indicatorInside: {
            type: Boolean,
            default: true
        },
        indicatorMode: {
            type: String,
            default: 'click'
        },
        indicatorPosition: {
            type: String,
            default: 'is-bottom'
        },
        indicatorStyle: {
            type: String,
            default: 'is-dots'
        },
        overlay: Boolean,
        progress: Boolean,
        progressType: {
            type: String,
            default: 'is-primary'
        },
        withCarouselList: Boolean
    },
    data() {
        return {
            _isCarousel: true,
            activeItem: this.value,
            carouselItems: [],
            isPause: false,
            dragX: 0,
            timer: null
        }
    },
    computed: {
        indicatorClasses() {
            return [
                {
                    'has-background': this.indicatorBackground,
                    'has-custom': this.indicatorCustom,
                    'is-inside': this.indicatorInside
                },
                this.indicatorCustom && this.indicatorCustomSize,
                this.indicatorInside && this.indicatorPosition
            ]
        }
    },
    watch: {
        /**
         * When v-model is changed set the new active item.
         */
        value(value) {
            if (value < this.activeItem) {
                this.changeItem(value)
            } else {
                this.changeItem(value, false)
            }
        },
        /**
         * When carousel-items are updated, set active one.
         */
        carouselItems() {
            if (this.activeItem < this.carouselItems.length) {
                this.carouselItems[this.activeItem].isActive = true
            }
        },
        /**
         *  When autoplay is change, set by status
         */
        autoplay(status) {
            status ? this.startTimer() : this.pauseTimer()
        }
    },
    methods: {
        startTimer() {
            if (!this.autoplay || this.timer) return
            this.isPause = false
            this.timer = setInterval(() => {
                if (!this.repeat && this.activeItem === this.carouselItems.length - 1) {
                    this.pauseTimer()
                } else {
                    this.next()
                }
            }, (this.interval || config.defaultCarouselInterval))
        },
        pauseTimer() {
            this.isPause = true
            if (this.timer) {
                clearInterval(this.timer)
                this.timer = null
            }
        },
        checkPause() {
            if (this.pauseHover && this.autoplay) {
                return this.pauseTimer()
            }
        },
        /**
         * Change the active item and emit change event.
         * action only for animated slide, there true = next, false = prev
         */
        changeItem(newIndex, action = true) {
            if (this.activeItem === newIndex) return
            this.carouselItems[this.activeItem].status(false, action)
            this.carouselItems[newIndex].status(true, action)
            this.activeItem = newIndex
            this.$emit('change', newIndex)
        },
        // Indicator trigger when change active item.
        modeChange(trigger, value) {
            if (this.indicatorMode === trigger) {
                this.$emit('input', value)
                return value < this.activeItem
                    ? this.changeItem(value)
                    : this.changeItem(value, false)
            }
        },
        prev() {
            if (this.activeItem === 0) {
                if (this.repeat) this.changeItem(this.carouselItems.length - 1)
            } else {
                this.changeItem(this.activeItem - 1)
            }
        },
        next() {
            if (this.activeItem === this.carouselItems.length - 1) {
                if (this.repeat) this.changeItem(0, false)
            } else {
                this.changeItem(this.activeItem + 1, false)
            }
        },
        // checking arrow between both
        checkArrow(value) {
            if (this.arrowBoth) return true
            if (this.activeItem !== value) return true
        },
        // handle drag event
        dragStart(event) {
            if (!this.hasDrag) return
            this.dragx = event.touches ? event.changedTouches[0].pageX : event.pageX
            if (event.touches) {
                this.pauseTimer()
            } else {
                event.preventDefault()
            }
        },
        dragEnd(event) {
            if (!this.hasDrag) return
            const detected = event.touches ? event.changedTouches[0].pageX : event.pageX
            const diffX = detected - this.dragx
            if (Math.abs(diffX) > 50) {
                if (diffX < 0) {
                    this.next()
                } else {
                    this.prev()
                }
            }
            if (event.touches) {
                this.startTimer()
            }
        }
    },
    mounted() {
        if (this.activeItem < this.carouselItems.length) {
            this.carouselItems[this.activeItem].isActive = true
        }
        this.startTimer()
    },
    beforeDestroy() {
        this.pauseTimer()
    }
}
</script>