Remove useless call to resize headers for asset pages

<template>

              <search-field
                ref="asset-search-field"
                :can-save="true"
                @change="onSearchChange"
                @save="saveSearchQuery"
                placeholder="ex: props modeling=wip"
              />

              <show-assignations-button class="flexrow-item" />
              <show-infos-button class="flexrow-item" />

            <button-simple
              class="flexrow-item"
              :title="$t('entities.thumbnails.title')"
              icon="image"
              @click="showAddThumbnailsModal"
            />
            <button-simple
              class="flexrow-item"
              :title="$t('main.csv.import_file')"
              icon="upload"
              @click="showImportModal"
            />
            <button-simple
              class="flexrow-item"
              icon="download"
              :title="$t('main.csv.export_file')"
              @click="onExportClick"
            />
            <button-link
              class="flexrow-item"
              :text="$t('assets.new_asset')"
              icon="plus"
              :path="newAssetPath"
            />

          <search-query-list
            :queries="assetSearchQueries"
            @changesearch="changeSearch"
            @removesearch="removeSearchQuery"
            v-if="!isAssetsLoading && !initialLoading"
          />

      <sorting-info
        :label="$t('main.sorted_by')"
        :sorting="assetSorting"
        @clear-sorting="onChangeSortClicked(null)"
      />
      <asset-list
        ref="asset-list"
        :displayed-assets="displayedAssetsByType"
        :is-loading="isAssetsLoading || initialLoading"
        :is-error="isAssetsLoadingError"
        :validation-columns="assetValidationColumns"
        @add-metadata="onAddMetadataClicked"
        @change-sort="onChangeSortClicked"
        @create-tasks="showCreateTasksModal"
        @delete-all-tasks="onDeleteAllTasksClicked"
        @delete-metadata="onDeleteMetadataClicked"
        @edit-metadata="onEditMetadataClicked"
        @scroll="saveScrollPosition"
      />

    <task-info
      :task="Object.values(selectedTasks)[0]"
    />

  <edit-asset-modal
    ref="edit-asset-modal"
    :active="modals.isNewDisplayed"
    :is-loading="loading.edit"
    :is-loading-stay="loading.stay"
    :is-error="editAsset.isCreateError"
    :is-success="editAsset.isSuccess"
    :cancel-route="assetsPath"
    :asset-to-edit="assetToEdit"
    @confirm="confirmEditAsset"
    @confirmAndStay="confirmNewAssetStay"
  />

  <delete-modal
    ref="delete-asset-modal"
    :active="modals.isDeleteDisplayed"
    :is-loading="deleteAsset.isLoading"
    :is-error="deleteAsset.isError"
    :cancel-route="assetsPath"
    :text="deleteText()"
    :error-text="$t('assets.delete_error')"
    @confirm="confirmDeleteAsset"
  />

  <delete-modal
    ref="restore-asset-modal"
    :active="modals.isRestoreDisplayed"
    :is-loading="restoreAsset.isLoading"
    :is-error="restoreAsset.isDeleteError"
    :cancel-route="assetsPath"
    :text="restoreText()"
    :error-text="$t('assets.restore_error')"
    @confirm="confirmRestoreAsset"
  />

  <hard-delete-modal
    ref="delete-all-tasks-modal"
    :active="modals.isDeleteAllTasksDisplayed"
    :is-loading="loading.deleteAllTasks"
    :is-error="errors.deleteAllTasks"
    :cancel-route="assetsPath"
    :text="deleteAllTasksText()"
    :error-text="$t('tasks.delete_all_error')"
    :lock-text="deleteAllTasksLockText"
    @confirm="confirmDeleteAllTasks"
  />

  <delete-modal
    ref="delete-metadata-modal"
    :active="modals.isDeleteMetadataDisplayed"
    :is-loading="loading.deleteMetadata"
    :is-error="errors.deleteMetadata"
    @cancel="modals.isDeleteMetadataDisplayed = false"
    :text="$t('productions.metadata.delete_text')"
    :error-text="$t('productions.metadata.delete_error')"
    @confirm="confirmDeleteMetadata"
  />

  <import-render-modal
    :active="modals.isImportRenderDisplayed"
    :is-loading="loading.importing"
    :is-error="errors.importing"
    :parsed-csv="parsedCSV"
    :form-data="assetsCsvFormData"
    :columns="columns"
    :dataMatchers="dataMatchers"
    :database="filteredAssets"
    @reupload="resetImport"
    @cancel="hideImportRenderModal"
    @confirm="uploadImportFile"
  />

  <import-modal
    ref="import-modal"
    :active="modals.isImportDisplayed"
    :is-loading="loading.importing"
    :is-error="errors.importing"
    :form-data="assetsCsvFormData"
    :columns="columns"
    @confirm="renderImport"
    @cancel="hideImportModal"
  />

  <create-tasks-modal
    :active="modals.isCreateTasksDisplayed"
    :is-loading="loading.creatingTasks"
    :is-loading-stay="loading.taskStay"
    :is-error="errors.creatingTasks"
    :title="$t('tasks.create_tasks_asset')"
    :text="$t('tasks.create_tasks_asset_explaination')"
    :error-text="$t('tasks.create_tasks_asset_failed')"
    @cancel="hideCreateTasksModal"
    @confirm="confirmCreateTasks"
    @confirm-and-stay="confirmCreateTasksAndStay"
  />

  <add-metadata-modal
    :active="modals.isAddMetadataDisplayed"
    :is-loading="loading.addMetadata"
    :is-loading-stay="loading.addMetadata"
    :is-error="errors.addMetadata"
    :descriptor-to-edit="descriptorToEdit"
    @cancel="modals.isAddMetadataDisplayed = false"
    @confirm="confirmAddMetadata"
  />

  <add-thumbnails-modal
    ref="add-thumbnails-modal"
    :active="modals.isAddThumbnailsDisplayed"
    :is-loading="loading.addThumbnails"
    :is-error="errors.addThumbnails"
    @cancel="hideAddThumbnailsModal"
    @confirm="confirmAddThumbnails"
  />

</template>


Picker: Code optimization  var => let

Picker: Code optimization  var => let

<template>

  <flexbox :margin-left=0>
    <flexbox-item v-for="(index, one) in data" style="margin-left:0;">

    </flexbox-item>
  </flexbox>

</template>


Add v-if to <slot> child of ButtonDropdown. This triggers a rerender of any child component passed in, which is more convenient for more complicated bits of UI that may be contained in a dropdown.  I don't believe its expensive enough to worry about, but quite open to alternative approaches.

<template>

    <button
       ref="button"
       :class="['button-dropdown-button', buttonClass]"
      @click="toggleVisibility"
    >

        {{ buttonText }}

    </button>

      <slot v-if="isOpen"></slot>

</template>

<template>

    <button
       ref="button"
       :class="['button-dropdown-button', buttonClass]"
      @click="toggleVisibility"
    >

        {{ buttonText }}

    </button>

      <slot></slot>

</template>


Fix messed up rebase. Add back sendbar icons.

<template>

            <input id="attach" class="mdl-button mdl-js-button mdl-button--icon attach-button" type="image" src="../../assets/images/ic_attach.png"/>
            <input id="emoji" class="mdl-button mdl-js-button mdl-button--icon emoji-button" type="image" src="../../assets/images/ic_mood.png"/>

                <textarea class="mdl-textfield__input disabled" type="text" id="message-entry" autofocus @keydown.shift.enter.stop @keydown.enter.prevent.stop="dispatchSend" v-model="message"></textarea>
                Type message...

            <button class="send mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-button--colored mdl-js-ripple-effect" id="send-button" @click="dispatchSend">
                send
            </button>

</template>

<template>

            <input id="attach" class="mdl-button mdl-js-button mdl-button--icon attach-button" type="image" src="../assets/images/ic_attach.png"/>
            <input id="emoji" class="mdl-button mdl-js-button mdl-button--icon emoji-button" type="image" src="../assets/images/ic_mood.png"/>

                <textarea class="mdl-textfield__input disabled" type="text" id="message-entry" autofocus @keydown.shift.enter.stop @keydown.enter.prevent.stop="dispatchSend" v-model="message"></textarea>
                Type message...

            <button class="send mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-button--colored mdl-js-ripple-effect" id="send-button" @click="dispatchSend">
                send
            </button>

</template>


Fix for client combobox

Fix for client combobox

<template>

        <button
          :class="{
            button: true,
            'is-loading': loading.addPlaylist
          }"
          @click="addPlaylist"
          key="new-playlist-button"
          v-if="isCurrentUserManager"
        >
          <plus-icon class="icon is-small" />
          {{ $t('playlists.new_playlist') }}
        </button>

          <router-link
            :key="playlist.id"
            :to="getPlaylistPath(playlist.id)"
            :class="{
              'playlist-item': true,
              'for-client': playlist.for_client || false,
              selected: playlist.id === currentPlaylist.id
            }"
            v-for="playlist in playlists"
          >

              {{ playlist.name }}

              {{ formatDate(playlist.updated_at)}}

          </router-link>

        <spinner
          class="mt2"
          v-else
        />
        <error-text
          text="$t('playlists.loading_error')"
          v-if="errors.playlistLoading"
        />

        <playlist-player
          ref="playlist-player"
          :playlist="currentPlaylist"
          :shots="currentShots"
          :is-loading="loading.playlist"
          @preview-changed="onPreviewChanged"
          @playlist-deleted="goFirstPlaylist"
          @remove-shot="removeShot"
          @order-change="onOrderChange"
          @annotationchanged="onAnnotationChanged"
          @for-client-changed="onForClientChanged"
        />

        <spinner
          class="mt2"
          key="shot-loader"
          v-if="isShotsLoading"
        />

            <page-subtitle
              :text="$t('playlists.add_shots')"
            />
            <button
              :class="{
                button: true,
                'add-sequence': true,
                'is-loading': this.loading.addWeekly
              }"
              :disabled="isAdditionLoading"
              @click="addAllPending"
            >
              {{ $t('playlists.build_weekly') }}
            </button>
            <button
              :class="{
                button: true,
                'add-sequence': true,
                'is-loading': this.loading.addDaily
              }"
              :disabled="isAdditionLoading"
              @click="addDailyPending"
            >
              {{ $t('playlists.build_daily') }}
            </button>

 0 || !isTVShow"
            >

                <combobox
                  class="select-sequence-combobox"
                  :label="$t('shots.fields.sequence')"
                  :options="sequenceOptions"
                  v-model="sequenceId"
                  v-if="sequenceOptions.length > 0"
                />

                  {{ $t('playlists.no_sequence_for_episode') }}

                <button
                  :class="{
                    button: true,
                    'add-sequence': true,
                    'is-loading': this.loading.addSequence
                  }"
                  :disabled="isAdditionLoading"
                  @click="addSequence"
                  v-if="sequenceOptions.length > 0"
                >
                  {{ $t('playlists.add_sequence') }}
                </button>

              {{ $t('playlists.no_shot_for_production') }}

              {{ $t('playlists.no_shot_for_sequence') }}

              <light-entity-thumbnail
                :preview-file-id="shot.preview_file_id"
                :width="150"
                :height="100"
              />

            {{ shot.name }}

</template>

<template>

        <button
          :class="{
            button: true,
            'is-loading': loading.addPlaylist
          }"
          @click="addPlaylist"
          key="new-playlist-button"
          v-if="isCurrentUserManager"
        >
          <plus-icon class="icon is-small" />
          {{ $t('playlists.new_playlist') }}
        </button>

          <router-link
            :key="playlist.id"
            :to="getPlaylistPath(playlist.id)"
            :class="{
              'playlist-item': true,
              'for-client': playlist.for_client,
              selected: playlist.id === currentPlaylist.id
            }"
            v-for="playlist in playlists"
          >

              {{ playlist.name }}

              {{ formatDate(playlist.updated_at)}}

          </router-link>

        <spinner
          class="mt2"
          v-else
        />
        <error-text
          text="$t('playlists.loading_error')"
          v-if="errors.playlistLoading"
        />

        <playlist-player
          ref="playlist-player"
          :playlist="currentPlaylist"
          :shots="currentShots"
          :is-loading="loading.playlist"
          @preview-changed="onPreviewChanged"
          @playlist-deleted="goFirstPlaylist"
          @remove-shot="removeShot"
          @order-change="onOrderChange"
          @annotationchanged="onAnnotationChanged"
          @for-client-changed="onForClientChanged"
        />

        <spinner
          class="mt2"
          key="shot-loader"
          v-if="isShotsLoading"
        />

            <page-subtitle
              :text="$t('playlists.add_shots')"
            />
            <button
              :class="{
                button: true,
                'add-sequence': true,
                'is-loading': this.loading.addWeekly
              }"
              :disabled="isAdditionLoading"
              @click="addAllPending"
            >
              {{ $t('playlists.build_weekly') }}
            </button>
            <button
              :class="{
                button: true,
                'add-sequence': true,
                'is-loading': this.loading.addDaily
              }"
              :disabled="isAdditionLoading"
              @click="addDailyPending"
            >
              {{ $t('playlists.build_daily') }}
            </button>

 0 || !isTVShow"
            >

                <combobox
                  class="select-sequence-combobox"
                  :label="$t('shots.fields.sequence')"
                  :options="sequenceOptions"
                  v-model="sequenceId"
                  v-if="sequenceOptions.length > 0"
                />

                  {{ $t('playlists.no_sequence_for_episode') }}

                <button
                  :class="{
                    button: true,
                    'add-sequence': true,
                    'is-loading': this.loading.addSequence
                  }"
                  :disabled="isAdditionLoading"
                  @click="addSequence"
                  v-if="sequenceOptions.length > 0"
                >
                  {{ $t('playlists.add_sequence') }}
                </button>

              {{ $t('playlists.no_shot_for_production') }}

              {{ $t('playlists.no_shot_for_sequence') }}

              <light-entity-thumbnail
                :preview-file-id="shot.preview_file_id"
                :width="150"
                :height="100"
              />

            {{ shot.name }}

</template>


Reduce document formatter throttle time from 100ms


Fix password field type

Fix password field type

<template>

Enregistrement

        <form v-on:submit.prevent="register" class="userForm">
            <input type="hidden" name="post" value="post"/>

Informations de connexion

                    Adresse e-mail :

                    <input id="email" type="email" v-model="user.password" required="required"/>

                    Mot de passe :

                    <input id="password" type="password" v-model="user.password" required="required"/>

Informations personnelles

                    Prénom :

                    <input id="firstName" type="text" v-model="user.firstName" required="required"/>

                    Nom :

                    <input id="lastName" type="text" v-model="user.lastName" required="required"/>

                    Date de naissance :

                    <input id="birthday" type="date" v-model="user.birthday" placeholder="dd/mm/aaaa"
                           pattern="(0[1-9]|[12][0-9]|3[01])[/](0[1-9]|1[012])[/](19|20)\d\d"/>

                    Biographie :

                    <textarea id="bio" v-model="user.biographyw"></textarea>

Logistique

                Comptes-tu apporter ton propre matériel :
                <input type="checkbox" id="hasMaterial" v-model="user.material.hasMaterial"/>

                Est-ce que tu apporteras un ordinateur fixe :
                <input type="checkbox" id="isDesktop" v-model="user.material.isDesktop"/>

                Est ce que ton matériel est équipé du WiFi :
                <input type="checkbox" id="hasWiFi" v-model="user.material.hasWiFi"/>

Compte CREMI

                Possèdes-tu un compte au CREMI :
                <input type="checkbox" id="isDesktop" v-model="user.cremiAccount.needed"/>

                        Numéro étudiant :

                        <input id="studentNumber" type="text" v-model="user.cremiAccount.studentNumber"/>

                        Adresse e-mail étudiante :

                        <input id="studentMail" type="text" v-model="user.cremiAccount.studentMail"/>

                    Je déclare avoir pris connaissance de la charte d'utilisation du CREMI et
                        de la suivre :
                    <input type="checkbox" id="cremiCharter" v-model="user.cremiAccount.charter"/>

Recrutement

                J'accepte que mon e-mail soit communiqué à nos partenaires
                    uniquement à des fins de recrutement :
                <input type="checkbox" id="mailForRecruitment" v-model="user.mailForRecruitment"/>

                <router-link class="special" :to="{name: 'home'}">back();</router-link>
                <input type="submit" class="special" value="submit(this);"/>

        </form>

</template>

<template>

Enregistrement

        <form v-on:submit.prevent="register" class="userForm">
            <input type="hidden" name="post" value="post"/>

Informations de connexion

                    Email :

                    <input id="email" type="email" v-model="user.email" required="required"/>

                    Mot de passe :

                    <input id="email" type="email" v-model="user.password" required="required"/>

Informations personnelles

                    Prénom :

                    <input id="firstName" type="text" v-model="user.firstName" required="required"/>

                    Nom :

                    <input id="lastName" type="text" v-model="user.lastName" required="required"/>

                    Date de naissance :

                    <input id="birthday" type="date" v-model="user.birthday" placeholder="dd/mm/aaaa"
                           pattern="(0[1-9]|[12][0-9]|3[01])[/](0[1-9]|1[012])[/](19|20)\d\d"/>

                    Biographie :

                    <textarea id="bio" v-model="user.biographyw"></textarea>

Logistique

                Comptes-tu apporter ton propre matériel :
                <input type="checkbox" id="hasMaterial" v-model="user.material.hasMaterial"/>

                Est-ce que tu apporteras un ordinateur fixe :
                <input type="checkbox" id="isDesktop" v-model="user.material.isDesktop"/>

                Est ce que ton matériel est équipé du WiFi :
                <input type="checkbox" id="hasWiFi" v-model="user.material.hasWiFi"/>

Compte CREMI

                Possèdes-tu un compte au CREMI :
                <input type="checkbox" id="isDesktop" v-model="user.cremiAccount.needed"/>

                        Numéro étudiant :

                        <input id="studentNumber" type="text" v-model="user.cremiAccount.studentNumber"/>

                        Adresse e-mail étudiante :

                        <input id="studentMail" type="text" v-model="user.cremiAccount.studentMail"/>

                    Je déclare avoir pris connaissance de la charte d'utilisation du CREMI et
                        de la suivre :
                    <input type="checkbox" id="cremiCharter" v-model="user.cremiAccount.charter"/>

Recrutement

                J'accepte que mon e-mail soit communiqué à nos partenaires
                    uniquement à des fins de recrutement :
                <input type="checkbox" id="mailForRecruitment" v-model="user.mailForRecruitment"/>

                <router-link class="special" :to="{name: 'home'}">back();</router-link>
                <input type="submit" class="special" value="submit(this);"/>

        </form>

</template>


Fix orphanage search

Fix orphanage search

<template>

        <Navigation></Navigation>

Buscar orfanatos

Encontre orfanatos para ajudar

            <input v-model="search" type="text" class="search-home" name="search" placeholder="Digite o nome do orfanato"/>

		<md-layout md-row>
			<md-layout md-flex="25" class="cards-teacher" v-for="orphanage in filteredOrphanages">
				<md-card>
					<md-card-header>

{{ orphanage.name }}

Cidade Nova - Feira de Santana

					</md-card-header>

					<md-card-expand>
						<md-card-actions>
							<router-link :to="{ name: 'OrfanatosPerfil', params: { id: orphanage['.key'] }}">Ver Perfil</md-button>

							<md-button class="md-icon-button" md-expand-trigger>
								<md-icon>keyboard_arrow_down</md-icon>
							</md-button>
						</md-card-actions>

						<md-card-content>

Responsável: Pedro Mota

Alunos: 35

Média de idade: 14 anos

						</md-card-content>
					</md-card-expand>
				</md-card>
			</md-layout>

Nada encontrado

		</md-layout>

</template>

<template>

        <Navigation></Navigation>

Buscar orfanatos

Encontre orfanatos para ajudar

            <input v-model="search" type="text" class="search-home" name="search" placeholder="Digite o nome do orfanato"/>

		<md-layout md-row>
			<md-layout md-flex="25" class="cards-teacher" v-for="orphanage in filteredOrphanages">
				<md-card>
					<md-card-header>

{{ orphanage.name }}

Cidade Nova - Feira de Santana

					</md-card-header>

					<md-card-expand>
						<md-card-actions>
							<md-button>Ver Perfil</md-button>

							<md-button class="md-icon-button" md-expand-trigger>
								<md-icon>keyboard_arrow_down</md-icon>
							</md-button>
						</md-card-actions>

						<md-card-content>

Contato: 75 9 9999-0000

Redes sociais: Twitter, Facebook

						</md-card-content>
					</md-card-expand>
				</md-card>
			</md-layout>

Nada encontrado

		</md-layout>

</template>


Fixes bug in setting intro-open state

Fixes bug in setting intro-open state

<template>
	<div
		class="intro slide-loadable"
		id="site-intro"
		ref="intro"
		role="presentation"
		:style="{ filter: 'grayscale(' + introFade + '%)' }">
	</div>
</template>
<script>
	import PhotoMixin from './mixins/Photo';
	import PhotoSizes from '../api/flickr/PhotoSizes';

	const FEATURE_IMAGES = [
		"514080236",
		"4402846583",
		"514080242",
		"1802622748",
		"1801772595",
		"4308243203",
		"3053276533",
	];

	export default {
		created() {
			this.requestSizes(this.getFeaturedImageId());

			let component = this;
			window.addEventListener('scroll', function() {
				component.handleScroll.apply(this, arguments);
			});
			this.setIntroScrollPosition();
		},
		data() {
			return {
				introFade: 0,
			};
		},
		methods: {
			handleScroll(e) {
				this.setIntroScrollPosition();
			},
			getFeaturedImageId() {
				return FEATURE_IMAGES[new Date().getDay()];
			},
			setIntroScrollPosition() {
				let scrollPos = window.scrollY;
				let $intro = this.$refs.intro;
				if ($intro) {
					let introHeight = $intro.clientHeight ;
					this.introFade = Math.min((scrollPos / introHeight) * 100, 100);
				} else {
					this.setIntroOpen(true);
				}
			},
			setIntroOpen(isOpen){
				if(isOpen) {
					document.body.classList.add("intro-open");
				} else {
					document.body.classList.remove("intro-open");
				}
			},
		},
		mixins: [PhotoMixin],
		watch: {
			hasSizesData(newVal) {
				if (newVal) {
					this.fadeBackgroundOnLoad(
						this.getImageSource(PhotoSizes.LARGE),
						this.$refs.intro
					);
				}
			},
			introFade(){
				this.setIntroOpen(this.introFade < 100);

			}
		}
	}
</script>

<template>
	<div
		class="intro slide-loadable"
		id="site-intro"
		ref="intro"
		role="presentation"
		:style="{ filter: 'grayscale(' + introFade + '%)' }">
	</div>
</template>
<script>
	import PhotoMixin from './mixins/Photo';
	import PhotoSizes from '../api/flickr/PhotoSizes';

	const FEATURE_IMAGES = [
		"514080236",
		"4402846583",
		"514080242",
		"1802622748",
		"1801772595",
		"4308243203",
		"3053276533",
	];

	export default {
		created() {
			this.requestSizes(this.getFeaturedImageId());

			let component = this;
			window.addEventListener('scroll', function() {
				component.handleScroll.apply(this, arguments);
			});
			this.setIntroScrollPosition();
		},
		data() {
			return {
				introFade: 0,
			};
		},
		methods: {
			handleScroll(e) {
				this.setIntroScrollPosition();
			},
			getFeaturedImageId() {
				return FEATURE_IMAGES[new Date().getDay()];
			},
			setIntroScrollPosition() {
				let scrollPos = window.scrollY;
				let $intro = this.$refs.intro;
				if ($intro) {
					let introHeight = $intro.clientHeight ;
					this.introFade = Math.min((scrollPos / introHeight) * 100, 100);
				} else {
					this.introFade = 0;
				}
			},
		},
		mixins: [PhotoMixin],
		watch: {
			hasSizesData(newVal) {
				if (newVal) {
					this.fadeBackgroundOnLoad(
						this.getImageSource(PhotoSizes.LARGE),
						this.$refs.intro
					);
				}
			},
			introFade(){
				let introOpen = this.introFade < 100;
				if(introOpen) {
					document.body.classList.add("intro-open");
				} else {
					document.body.classList.remove("intro-open");
				}
			}
		}
	}
</script>


use new markdown lib function

use new markdown lib function

<template>

    <loading-spinner></loading-spinner>

    <sidebar></sidebar>

            <bread-crumb :ticket="ticket" />

          {{ ticket.summary }}

              Description

          <comments :comments="ticket.comments" />
          <comment-form />
          <comment-form v-if="currentUser" />

              You must be logged in to comment.

          <ticket-details :ticket="ticket" />
          <ticket-fields :fields="ticket.fields" />

</template>

<template>

Loading your ticket...

    <sidebar></sidebar>

            <bread-crumb :ticket="ticket" />

          {{ ticket.summary }}

              Description

          <comments :comments="ticket.comments" />
          <comment-form />
          <comment-form v-if="currentUser" />

              You must be logged in to comment.

          <ticket-details :ticket="ticket" />
          <ticket-fields :fields="ticket.fields" />

</template>


format page.vue file

format page.vue file

<template>

上一页

1

 1">...

 1" @click="changePage(currentPage - 2)">{{currentPage - 2}}

 1" @click="changePage(currentPage - 1)">{{currentPage - 1}}

{{currentPage}}

      <li v-if="currentPage + 1 < allPages" @click="changePage(currentPage + 1)">{{currentPage + 1}}

      <li v-if="currentPage + 2 < allPages" @click="changePage(currentPage + 2)">{{currentPage + 2}}

      <li v-if="currentPage + 3 < allPages">...

 1" :class="lastPageClasses" @click="changePage(allPages)">{{allPages}}

下一页

    <Options
      :showJump="showJump"
      :total="total"
      :allPages="allPages"
      :current="currentPage"
      :_current.once="currentPage"
      @on-page="onPage">
      </Options>

</template>


Styling/formatting of the "unique RTK kanji" text

<template>

Create a Review Deck from Japanese Text

Paste japanese text (or any selection of kanji) below :

    <form :action="actionUrl" method="post">

        <textarea v-model="japText" class="form-control mb-1" rows="5"></textarea>

          {{ count }} unique RTK kanji in text

          <input type="checkbox" name="shuffle" value="1" />
          Shuffle cards

          <input type="checkbox" name="reverse" value="1" />
          Kanji to Keyword (reverse mode)

      <button type="submit" class="ko-Btn ko-Btn--success">Start Review</button>
    </form>

</template>

<template>

Create a Review Deck from Japanese Text

Paste japanese text (or any selection of kanji) below :

    <form :action="actionUrl" method="post">

        <textarea v-model="japText" class="form-control mb-1" rows="5"></textarea>

          {{ `${count} unique RTK kanji` }}

          <input type="checkbox" name="shuffle" value="1" />
          Shuffle cards

          <input type="checkbox" name="reverse" value="1" />
          Kanji to Keyword (reverse mode)

      <button type="submit" class="ko-Btn ko-Btn--success">Start Review</button>
    </form>

</template>


devop: 🔧 reduce debouce time

devop: 🔧 reduce debouce time

<template>
  <div class="wallet-main">
    <the-wallet-side-menu />
    <v-main>
      <v-container class="pa-2 pa-md-3 mb-14" fluid>
        <the-wallet-header />
        <module-confirmation />
        <the-enkrypt-popup v-if="!isOfflineApp" :show="walletEnkryptPopup" />
        <router-view />
      </v-container>
    </v-main>
    <the-wallet-footer />
    <enkrypt-promo-snackbar v-if="!isOfflineApp" />
  </div>
</template>

<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { toBN } from 'web3-utils';
import Web3 from 'web3';
import moment from 'moment';
import { debounce } from 'lodash';
import handlerWallet from '@/core/mixins/handlerWallet.mixin';
import nodeList from '@/utils/networks';
import {
  ERROR,
  SUCCESS,
  Toast,
  WARNING
} from '@/modules/toast/handler/handlerToast';
import { Web3Wallet } from '@/modules/access-wallet/common';
import { ROUTES_HOME } from '@/core/configs/configRoutes';
import handlerAnalytics from '@/modules/analytics-opt-in/handlers/handlerAnalytics.mixin';
import matchNetwork from '@/core/helpers/matchNetwork';

import WALLET_TYPES from '@/modules/access-wallet/common/walletTypes';

export default {
  components: {
    TheWalletSideMenu: () => import('./components-wallet/TheWalletSideMenu'),
    TheWalletHeader: () => import('./components-wallet/TheWalletHeader'),
    TheWalletFooter: () => import('./components-wallet/TheWalletFooter'),
    ModuleConfirmation: () =>
      import('@/modules/confirmation/ModuleConfirmation'),
    EnkryptPromoSnackbar: () =>
      import('@/views/components-wallet/EnkryptPromoSnackbar'),
    TheEnkryptPopup: () =>
      import('@/views/components-default/TheEnkryptPopup.vue')
  },
  mixins: [handlerWallet, handlerAnalytics],
  computed: {
    ...mapState('wallet', [
      'address',
      'web3',
      'identifier',
      'isOfflineApp',
      'instance'
    ]),
    ...mapState('global', ['online', 'gasPriceType', 'baseGasPrice']),
    ...mapState('external', ['coinGeckoTokens']),
    ...mapState('popups', [
      'enkryptWalletPopup',
      'enkryptLandingPopup',
      'enkryptLandingPopupClosed'
    ]),
    ...mapGetters('global', [
      'network',
      'gasPrice',
      'isEIP1559SupportedNetwork'
    ]),
    ...mapGetters('wallet', ['balanceInWei']),
    walletEnkryptPopup() {
      return (
        !this.enkryptLandingPopup &&
        moment(new Date()).diff(this.enkryptLandingPopupClosed, 'hours') >=
          24 &&
        this.enkryptWalletPopup
      );
    }
  },
  watch: {
    address() {
      if (!this.address) {
        this.$router.push({ name: ROUTES_HOME.HOME.NAME });
      }
    },
    network() {
      if (this.online && !this.isOfflineApp) {
        this.setup();
        this.web3.eth.clearSubscriptions();
      }
    },
    web3() {
      if (this.online && !this.isOfflineApp) this.setup();
    },
    coinGeckoTokens() {
      this.setTokenAndEthBalance();
    }
  },
  mounted() {
    if (this.online && !this.isOfflineApp) {
      this.setup();
      if (this.identifier === WALLET_TYPES.WEB3_WALLET) {
        this.web3Listeners();
      }
      this.checkNetwork();
    }
  },
  beforeDestroy() {
    if (window.ethereum) {
      if (this.findAndSetNetwork instanceof Function)
        window.ethereum.removeListener('chainChanged', this.findAndSetNetwork);
      if (this.setWeb3Account instanceof Function)
        window.ethereum.removeListener('accountsChanged', this.setWeb3Account);
    }
  },
  destroyed() {
    if (this.online && !this.isOfflineApp) this.web3.eth.clearSubscriptions();
  },
  methods: {
    ...mapActions('wallet', ['setBlockNumber', 'setTokens', 'setWallet']),
    ...mapActions('global', [
      'setNetwork',
      'setBaseFeePerGas',
      'updateGasPrice',
      'setValidNetwork'
    ]),
    ...mapActions('external', ['setTokenAndEthBalance', 'setNetworkTokens']),
    setup() {
      this.processNetworkTokens();
      this.setTokensAndBalance();
      this.subscribeToBlockNumber();
    },
    async checkNetwork() {
      const matched = await matchNetwork(
        this.network.type.chainID,
        this.identifier
      );
      this.setValidNetwork(matched);
    },
    processNetworkTokens() {
      this.network.type.tokens.then(res => {
        const tokenMap = new Map();
        res.forEach(item => {
          tokenMap.set(item.address.toLowerCase(), item);
        });
        this.setNetworkTokens(tokenMap);
      });
    },
    setTokensAndBalance() {
      if (this.coinGeckoTokens?.get) {
        this.setTokenAndEthBalance();
      } else {
        this.setTokens([]);
      }
    },
    checkAndSetBaseFee(baseFee) {
      if (baseFee) {
        this.setBaseFeePerGas(toBN(baseFee));
      } else {
        this.setBaseFeePerGas(toBN('0'));
      }
      this.updateGasPrice();
    },
    subscribeToBlockNumber: debounce(function () {
      this.web3.eth.getBlockNumber().then(bNumber => {
        this.setBlockNumber(bNumber);
        this.web3.eth.getBlock(bNumber).then(block => {
          if (block) {
            this.checkAndSetBaseFee(block.baseFeePerGas);
          }
          this.web3.eth
            .subscribe('newBlockHeaders')
            .on('data', res => {
              if (this.isEIP1559SupportedNetwork && res.baseFeePerGas) {
                this.checkAndSetBaseFee(toBN(res.baseFeePerGas));
              }
              this.setBlockNumber(res.number);
            })
            .on('error', err => {
              Toast(
                err.message === 'Load failed'
                  ? 'eth_subscribe is not supported. Please make sure your provider supports eth_subscribe'
                  : 'Network Subscription Error: Please wait a few seconds before continuing.',
                {},
                ERROR
              );
            });
        });
      });
    }, 500),
    /**
     * Checks Metamask chainID on load, switches current network if it doesn't match
     * and setup listeners for metamask changes
     */
    web3Listeners() {
      if (window.ethereum?.on) {
        window.ethereum.on('chainChanged', this.findAndSetNetwork);
        window.ethereum.on('accountsChanged', this.setWeb3Account);
      }
    },
    async findAndSetNetwork() {
      if (
        window.ethereum &&
        this.instance.identifier === WALLET_TYPES.WEB3_WALLET
      ) {
        const networkId = await window.ethereum?.request({
          method: 'eth_chainId'
        });
        const foundNetwork = Object.values(nodeList).find(item => {
          if (toBN(networkId).eq(toBN(item[0].type.chainID))) return item;
        });
        if (window.ethereum.isMetaMask) {
          try {
            if (foundNetwork) {
              await this.setNetwork({
                network: foundNetwork[0],
                walletType: this.instance.identifier
              });
              this.setValidNetwork(true);
              await this.setTokenAndEthBalance();
              this.trackNetworkSwitch(foundNetwork[0].type.name);
              this.$emit('newNetwork');
              Toast(
                `Switched network to: ${foundNetwork[0].type.name}`,
                {},
                SUCCESS
              );
            } else {
              this.setValidNetwork(false);
              Toast("Current wallet's network is unsupported", {}, ERROR);
            }
          } catch (er) {
            Toast('There was an error switching networks', {}, ERROR);
          }
        } else {
          Toast(
            "Can't find matching nodes for selected MetaMask node! MetaMask may not function properly. Please select a supported node",
            {},
            WARNING
          );
        }
      }
    },
    setWeb3Account(acc) {
      const web3 = new Web3(window.ethereum);
      const wallet = new Web3Wallet(acc[0]);
      this.setWallet([wallet, web3.currentProvider]);
    }
  }
};
</script>

<style lang="scss" scoped>
.box-shadow {
  box-shadow: 0 0 15px var(--v-greyMedium-base) !important;
}
.wallet-main {
  background-color: var(--v-greyLight-base);
  height: 100%;
}
</style>

<template>
  <div class="wallet-main">
    <the-wallet-side-menu />
    <v-main>
      <v-container class="pa-2 pa-md-3 mb-14" fluid>
        <the-wallet-header />
        <module-confirmation />
        <the-enkrypt-popup v-if="!isOfflineApp" :show="walletEnkryptPopup" />
        <router-view />
      </v-container>
    </v-main>
    <the-wallet-footer />
    <enkrypt-promo-snackbar v-if="!isOfflineApp" />
  </div>
</template>

<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { toBN } from 'web3-utils';
import Web3 from 'web3';
import moment from 'moment';
import { debounce } from 'lodash';
import handlerWallet from '@/core/mixins/handlerWallet.mixin';
import nodeList from '@/utils/networks';
import {
  ERROR,
  SUCCESS,
  Toast,
  WARNING
} from '@/modules/toast/handler/handlerToast';
import { Web3Wallet } from '@/modules/access-wallet/common';
import { ROUTES_HOME } from '@/core/configs/configRoutes';
import handlerAnalytics from '@/modules/analytics-opt-in/handlers/handlerAnalytics.mixin';
import matchNetwork from '@/core/helpers/matchNetwork';

import WALLET_TYPES from '@/modules/access-wallet/common/walletTypes';

export default {
  components: {
    TheWalletSideMenu: () => import('./components-wallet/TheWalletSideMenu'),
    TheWalletHeader: () => import('./components-wallet/TheWalletHeader'),
    TheWalletFooter: () => import('./components-wallet/TheWalletFooter'),
    ModuleConfirmation: () =>
      import('@/modules/confirmation/ModuleConfirmation'),
    EnkryptPromoSnackbar: () =>
      import('@/views/components-wallet/EnkryptPromoSnackbar'),
    TheEnkryptPopup: () =>
      import('@/views/components-default/TheEnkryptPopup.vue')
  },
  mixins: [handlerWallet, handlerAnalytics],
  computed: {
    ...mapState('wallet', [
      'address',
      'web3',
      'identifier',
      'isOfflineApp',
      'instance'
    ]),
    ...mapState('global', ['online', 'gasPriceType', 'baseGasPrice']),
    ...mapState('external', ['coinGeckoTokens']),
    ...mapState('popups', [
      'enkryptWalletPopup',
      'enkryptLandingPopup',
      'enkryptLandingPopupClosed'
    ]),
    ...mapGetters('global', [
      'network',
      'gasPrice',
      'isEIP1559SupportedNetwork'
    ]),
    ...mapGetters('wallet', ['balanceInWei']),
    walletEnkryptPopup() {
      return (
        !this.enkryptLandingPopup &&
        moment(new Date()).diff(this.enkryptLandingPopupClosed, 'hours') >=
          24 &&
        this.enkryptWalletPopup
      );
    }
  },
  watch: {
    address() {
      if (!this.address) {
        this.$router.push({ name: ROUTES_HOME.HOME.NAME });
      }
    },
    network() {
      if (this.online && !this.isOfflineApp) {
        this.setup();
        this.web3.eth.clearSubscriptions();
      }
    },
    web3() {
      if (this.online && !this.isOfflineApp) this.setup();
    },
    coinGeckoTokens() {
      this.setTokenAndEthBalance();
    }
  },
  mounted() {
    if (this.online && !this.isOfflineApp) {
      this.setup();
      if (this.identifier === WALLET_TYPES.WEB3_WALLET) {
        this.web3Listeners();
      }
      this.checkNetwork();
    }
  },
  beforeDestroy() {
    if (window.ethereum) {
      if (this.findAndSetNetwork instanceof Function)
        window.ethereum.removeListener('chainChanged', this.findAndSetNetwork);
      if (this.setWeb3Account instanceof Function)
        window.ethereum.removeListener('accountsChanged', this.setWeb3Account);
    }
  },
  destroyed() {
    if (this.online && !this.isOfflineApp) this.web3.eth.clearSubscriptions();
  },
  methods: {
    ...mapActions('wallet', ['setBlockNumber', 'setTokens', 'setWallet']),
    ...mapActions('global', [
      'setNetwork',
      'setBaseFeePerGas',
      'updateGasPrice',
      'setValidNetwork'
    ]),
    ...mapActions('external', ['setTokenAndEthBalance', 'setNetworkTokens']),
    setup() {
      this.processNetworkTokens();
      this.setTokensAndBalance();
      this.subscribeToBlockNumber();
    },
    async checkNetwork() {
      const matched = await matchNetwork(
        this.network.type.chainID,
        this.identifier
      );
      this.setValidNetwork(matched);
    },
    processNetworkTokens() {
      this.network.type.tokens.then(res => {
        const tokenMap = new Map();
        res.forEach(item => {
          tokenMap.set(item.address.toLowerCase(), item);
        });
        this.setNetworkTokens(tokenMap);
      });
    },
    setTokensAndBalance() {
      if (this.coinGeckoTokens?.get) {
        this.setTokenAndEthBalance();
      } else {
        this.setTokens([]);
      }
    },
    checkAndSetBaseFee(baseFee) {
      if (baseFee) {
        this.setBaseFeePerGas(toBN(baseFee));
      } else {
        this.setBaseFeePerGas(toBN('0'));
      }
      this.updateGasPrice();
    },
    subscribeToBlockNumber: debounce(function () {
      this.web3.eth.getBlockNumber().then(bNumber => {
        this.setBlockNumber(bNumber);
        this.web3.eth.getBlock(bNumber).then(block => {
          if (block) {
            this.checkAndSetBaseFee(block.baseFeePerGas);
          }
          this.web3.eth
            .subscribe('newBlockHeaders')
            .on('data', res => {
              if (this.isEIP1559SupportedNetwork && res.baseFeePerGas) {
                this.checkAndSetBaseFee(toBN(res.baseFeePerGas));
              }
              this.setBlockNumber(res.number);
            })
            .on('error', err => {
              Toast(
                err.message === 'Load failed'
                  ? 'eth_subscribe is not supported. Please make sure your provider supports eth_subscribe'
                  : 'Network Subscription Error: Please wait a few seconds before continuing.',
                {},
                ERROR
              );
            });
        });
      });
    }, 1000),
    /**
     * Checks Metamask chainID on load, switches current network if it doesn't match
     * and setup listeners for metamask changes
     */
    web3Listeners() {
      if (window.ethereum?.on) {
        window.ethereum.on('chainChanged', this.findAndSetNetwork);
        window.ethereum.on('accountsChanged', this.setWeb3Account);
      }
    },
    async findAndSetNetwork() {
      if (
        window.ethereum &&
        this.instance.identifier === WALLET_TYPES.WEB3_WALLET
      ) {
        const networkId = await window.ethereum?.request({
          method: 'eth_chainId'
        });
        const foundNetwork = Object.values(nodeList).find(item => {
          if (toBN(networkId).eq(toBN(item[0].type.chainID))) return item;
        });
        if (window.ethereum.isMetaMask) {
          try {
            if (foundNetwork) {
              await this.setNetwork({
                network: foundNetwork[0],
                walletType: this.instance.identifier
              });
              this.setValidNetwork(true);
              await this.setTokenAndEthBalance();
              this.trackNetworkSwitch(foundNetwork[0].type.name);
              this.$emit('newNetwork');
              Toast(
                `Switched network to: ${foundNetwork[0].type.name}`,
                {},
                SUCCESS
              );
            } else {
              this.setValidNetwork(false);
              Toast("Current wallet's network is unsupported", {}, ERROR);
            }
          } catch (er) {
            Toast('There was an error switching networks', {}, ERROR);
          }
        } else {
          Toast(
            "Can't find matching nodes for selected MetaMask node! MetaMask may not function properly. Please select a supported node",
            {},
            WARNING
          );
        }
      }
    },
    setWeb3Account(acc) {
      const web3 = new Web3(window.ethereum);
      const wallet = new Web3Wallet(acc[0]);
      this.setWallet([wallet, web3.currentProvider]);
    }
  }
};
</script>

<style lang="scss" scoped>
.box-shadow {
  box-shadow: 0 0 15px var(--v-greyMedium-base) !important;
}
.wallet-main {
  background-color: var(--v-greyLight-base);
  height: 100%;
}
</style>


fix the broken home page on the demo site

<template>

        Website Logo

          Home

          Products

          About

          Contacts

      <div class="background-image" :style="{'background-image': `url(${require('./assets/img/hero.jpg')})`}">

        {{settings.title}}

{{settings.description}}

        Download it Here

Some of our work

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices. Morbi vitae pulvinar velit. Sed aliquam dictum sapien, id sagittis augue malesuada eu.

        <li class="small" :style="{'background-image': `url(${require('./assets/img/coast.jpg')})`}">

        <li class="large" :style="{'background-image': `url(${require('./assets/img/island.jpg')})`}">

        <li class="large" :style="{'background-image': `url(${require('./assets/img/balloon.jpg')})`}">

        <li class="small" :style="{'background-image': `url(${require('./assets/img/mountain.jpg')})`}">

Features and services

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices. Morbi vitae pulvinar velit. Sed aliquam dictum sapien, id sagittis augue malesuada eu.

Photography

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices vitae pulvinar velit.

Web Development

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices vitae pulvinar velit.

Content Editing

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices vitae pulvinar velit.

What others say:

Mauris sit amet mauris a arcu eleifend ultricies eget ut dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

— Patrick Farrell

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices. Morbi vitae pulvinar velit. Sed aliquam dictum sapien, id sagittis augue malesuada eu.

— George Smith

Donec commodo dolor augue, vitae faucibus tortor tincidunt in. Aliquam vitae leo quis mi pulvinar ornare. Integer eu iaculis metus.

— Kevin Blake

Join our newsletter

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id felis et ipsum bibendum ultrices. Morbi vitae pulvinar velit. Sed aliquam dictum sapien, id sagittis augue malesuada eu.

      <form>
        <input type="email" placeholder="Email">
        Subscribe now
      </form>

Made by
        tutorialzine. images courtesy to
        unsplash.

No attribution required. you can remove this footer.

</template>


nicer progress bar in finder

nicer progress bar in finder

<template>

{{ '{0} %count% Files|{1} %count% File|]1,Inf[ %count% Files' | transChoice count {count:count} }}

{{ '{1} %count% File selected|]1,Inf[ %count% Files selected' | transChoice selected.length {count:selected.length} }}

                        <input class="uk-search-field" type="text" v-model="search">

                    <button class="uk-button uk-margin-small-right" v-on="click: createFolder()">{{ 'Add Folder' | trans }}</button>

                        {{ 'Upload' | trans }}
                        <input type="file" name="files[]" multiple="multiple">

                {{ title }}
                {{ title }}

{{ 'No files found.' | trans }}

</template>

<template>

{{ '{0} %count% Files|{1} %count% File|]1,Inf[ %count% Files' | transChoice count {count:count} }}

{{ '{1} %count% File selected|]1,Inf[ %count% Files selected' | transChoice selected.length {count:selected.length} }}

                        <input class="uk-search-field" type="text" v-model="search">

                    <button class="uk-button uk-margin-small-right" v-on="click: createFolder()">{{ 'Add Folder' | trans }}</button>

                        {{ 'Upload' | trans }}
                        <input type="file" name="files[]" multiple="multiple">

                {{ title }}
                {{ title }}

{{ upload.progress }}%

{{ 'No files found.' | trans }}

</template>


fix: chunked upload post

fix: chunked upload post

<template>

			<option
				v-for="album in albums"
				:key="album.id"
				:value="album.id">
				{{ album.name }}
			</option>

		<dropzone
			v-if="showDropzone"
			id="dropzone"
			ref="el"
			:options="dropzoneOptions"
			:include-styling="false"
			@vdropzone-success="dropzoneSuccess"
			@vdropzone-error="dropzoneError"
			@vdropzone-files-added="dropzoneFilesAdded" />

			Add or drop more files

							Link

</template>


fix: 🐛 duplicate tokens in wallet

fix: 🐛 duplicate tokens in wallet

<template>

    <mew6-white-sheet>
      <mew-module
        :has-elevation="true"
        :has-indicator="true"
        class="d-flex flex-grow-1 pt-6"
        title="Swap"
      >
        <template v-if="isAvailable" #moduleBody>

            <v-row class="align-start justify-space-between mt-4">
              <v-col cols="12" sm="5" class="pb-0 pb-sm-3 pr-sm-0">

                  <app-button-balance
                    v-show="!isFromNonChain"
                    :loading="isLoading"
                    :balance="displayBalance"
                  />
                  <mew-select
                    :value="fromTokenType"
                    label="From"
                    :items="actualFromTokens"
                    :is-custom="true"
                    :loading="isLoading"
                    @input="setFromToken"
                  />

                <mew-input
                  ref="amountInput"
                  label="Amount"
                  placeholder="0"
                  type="number"
                  :value="tokenInValue"
                  :persistent-hint="true"
                  :error-messages="amountErrorMessage"
                  :disabled="initialLoad"
                  :buy-more-str="
                    amountErrorMessage === errorMsgs.amountExceedsEthBalance ||
                    amountErrorMessage === errorMsgs.amountEthIsTooLow
                      ? network.type.canBuy
                        ? 'Buy more.'
                        : ''
                      : null
                  "
                  :max-btn-obj="maxBtn"
                  @buyMore="openMoonpay"
                  @input="setTokenInValue"
              /></v-col>
              <v-col
                cols="12"
                align-self="center"
                sm="2"
                class="px-6 py-0 py-sm-3 mb-3 mb-sm-0"
              >

                  <mew-icon-button
                    mdi-icon="swap-horizontal"
                    class="pa-2 d-flex align-center justify-center"
                    color-theme="basic"
                    btn-style="light"
                    :disabled="!enableTokenSwitch"
                    @click.native="switchTokens"
                  />

              </v-col>
              <v-col cols="12" sm="5" class="pb-0 pb-sm-3 pl-sm-0">
                <mew-select
                  :value="toTokenType"
                  :items="actualToTokens"
                  :is-custom="true"
                  :loading="isLoading"
                  label="To"
                  @input="setToToken"
                />
                <mew-input
                  label="Amount"
                  placeholder="0"
                  type="number"
                  :hide-clear-btn="true"
                  :value="tokenOutValue"
                  is-read-only
                />
              </v-col>
            </v-row>

          <app-user-msg-block
            v-if="!hasMinEth"
            class="mt-sm-5"
            :message="msg.lowBalance"
          >

              <mew-button
                btn-size="small"
                btn-style="outline"
                :title="`Buy ${network.type.currencyName}`"
                class="ma-1"
                :has-full-width="$vuetify.breakpoint.xsOnly"
                @click.native="openMoonpay"
              />

          </app-user-msg-block>

            <module-address-book
              v-if="isFromNonChain"
              ref="refundAddressInput"
              :label="nativeLabel"
              :is-valid-address-func="isValidRefundAddress"
              @setAddress="setRefundAddr"
            />
            <module-address-book
              v-show="showToAddress"
              ref="toAddressInput"
              :is-valid-address-func="isValidToAddress"
              :label="toAddressLabel"
              @setAddress="setToAddress"
            />

          <app-user-msg-block
            v-if="
              toTokenType &&
              toTokenType.value &&
              toTokenType.value.toLowerCase() == 'bitcoin' &&
              isEthNetwork
            "
            class="mt-sm-5"
            :message="msg.storeBitcoin"
          >

              <v-expansion-panels
                flat
                class="expansion-panels--remove-paddings"
              >
                <v-expansion-panel>
                  <v-expansion-panel-header
                    color="greyLight"
                    class="textLight--text"
                  >
                    How can I get wrapped Bitcoin?
                  </v-expansion-panel-header>
                  <v-expansion-panel-content color="greyLight" class="pa-0">

                      When you swap to Bitcoin, it is moved to the Bitcoin
                      blockchain, & requires a Bitcoin wallet. In order to keep
                      Bitcoin in MyEtherWallet, you can swap to wrapped Bitcoin
                      instead. Wrapped Bitcoin is an Ethereum token, with a
                      value approximately equal to 1 BTC. Wrapped Bitcoins can
                      be stored in MEW, and can be used as any other Ethereum
                      asset: you can swap it to other tokens, use it as
                      collateral in DeFi apps, etc. There are multiple kinds of
                      wrapped Bitcoins, but they roughly do the same thing.

                        Learn more about Wrapped Bitcoin.

                    <v-row class="mt-6">
                      <v-col cols="12" md="4">
                        <mew-button
                          btn-size="small"
                          btn-style="outline"
                          title="Swap to renBTC"
                          :has-full-width="true"
                          @click.native="swapTo('renBTC')"
                        />
                      </v-col>
                      <v-col cols="12" md="4">
                        <mew-button
                          btn-size="small"
                          btn-style="outline"
                          title="Swap to wBTC"
                          :has-full-width="true"
                          @click.native="swapTo('wBTC')"
                        />
                      </v-col>
                      <v-col cols="12" md="4">
                        <mew-button
                          btn-size="small"
                          btn-style="outline"
                          title="Swap to PBTC"
                          :has-full-width="true"
                          @click.native="swapTo('PBTC')"
                        />
                      </v-col>
                    </v-row>
                  </v-expansion-panel-content>
                </v-expansion-panel>
              </v-expansion-panels>

          </app-user-msg-block>

            <v-slide-y-transition v-if="showAnimation" hide-on-leave group>
              <swap-provider-mentions
                key="showAnimation"
                :is-loading="isLoadingProviders"
                :check-loading="checkLoading"
                @showProviders="showProviders"
              />
            </v-slide-y-transition>

              <swap-providers-list
                :step="step"
                :available-quotes="availableQuotes"
                :set-provider="setProvider"
                :to-token-symbol="toTokenType ? toTokenType.symbol : ''"
                :to-token-icon="toTokenType ? toTokenType.img : ''"
                :is-loading="isLoadingProviders"
                :providers-error="providersErrorMsg"
                :class="isFromNonChain ? '' : 'mt-7'"
                :selected-provider-id="selectedProviderId"
              />

              <app-transaction-fee
                v-if="showNetworkFee"
                :is-from-chain="!isFromNonChain"
                :show-fee="showSwapFee"
                :getting-fee="loadingFee"
                :error="feeError"
                :total-cost="totalCost"
                :tx-fee="txFee"
                :total-gas-limit="totalGasLimit"
                :not-enough-eth="notEnoughEth"
                :from-eth="isFromTokenMain"
                class="mt-10 mt-sm-16"
                @onLocalGasPrice="handleLocalGasPrice"
              />

                <mew-button
                  title="Next"
                  :has-full-width="true"
                  :disabled="disableNext"
                  btn-size="xlarge"
                  style="max-width: 240px"
                  @click.native="showConfirm()"
                />

        </template>

        <template v-else #moduleBody>

            <app-user-msg-block :message="swapNotAvailableMes" />

        </template>
      </mew-module>
    </mew6-white-sheet>

</template>


いらないコメントを消した

いらないコメントを消した

<template lang="html">
  <div :class="{ 'initiative': true, 'dragging': this.stores.ApplicationStore.isDragging }">
    <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
    <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
    <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
    <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
    <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
  </div>
</template>

<style scoped>

.initiative-wrapper{
  width: 560px;
  padding: 0;
  margin: 0;
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  list-style: none;
  flex-wrap: wrap;
}

.initiative-column{
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  font-size: 12px;
}

.initiative-column li{
  flex: 1;
  text-align: center;
  padding: 10px 0;
  width: 80px;
  height: 15px;
  overflow: auto;
}

.initiative{
  width: 300px;
  height: calc(100% - 530px);

  background: #fff;

  overflow: auto;
}

.dragging{
  pointer-events: none;
  opacity: 0.2;
}
</style>

<script>
module.exports = {
	props: ["component"],
  data: ()=>{
    return {
      stores: require("../../stores/Stores")
    }
  },
  computed: {
    getCharactors(){
      return this.stores.ComponentsStore.components.filter((component)=>{
        return component.type == "charactor";
      })
    }
  },
  methods: {
    hoverCharactor(charactor){
      console.log(charactor);
      // this.stores.ComponentsStore.activeId = charactor.id;
      // this.stores.ComponentsStore.active = charactor;
    },
    clickCharactor(charactor){
      console.log(charactor);
      this.stores.ComponentsStore.activeId = charactor.id;
      this.stores.ComponentsStore.active = charactor;
    }
  }
}
</script>

<template lang="html">

    <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
      <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
        <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
          <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>
            <charactor-about :charactor="charactor" v-for="charactor in getCharactors" v-on:click="clickCharactor" v-on:hover="hoverCharactor"></charactor-about>

</template>


Liste produits - animation

Liste produits - animation

<template>

    <template v-for="product in products | filterBy search in 'name' | filterBy type in 'brut' | orderBy 'name' ">

          <svg viewBox="0 0 100 100" class="product-item-icon">
            <use xlink:href="#foods-icon-{{ product.name}}"></use>
          </svg>

            {{ product.quantity }}

{{ product.name }}

Disponible maintenant

Indisponible

    </template>

</template>


Fix transform network-graph after sidebar closing/opening

<template>
  <div class="networkGraph" ref="networkGraph">
    <ConnectionMenu
      :connection="state.connectionMenu.connection"
      :position="state.connectionMenu.position"
      v-if="state.connectionMenu.show"
    />

    <NodeMenu
      :node="state.nodeMenu.node"
      :position="state.nodeMenu.position"
      v-if="state.nodeMenu.show"
    />

    <NetworkGraphToolbar
      :graph="state.graph"
      :network="state.network"
      class="no-print"
    />

    <svg id="networkGraph" width="800" height="600">
      <g class="marker" v-if="state.network">
        <defs
          :key="'defs' + connection.idx"
          v-for="connection of state.network.connections"
        >
          <marker
            :id="'generic' + connection.idx"
            markerHeight="8"
            markerWidth="8"
            orient="auto"
            refX="6"
            refY="4"
          >
            <path
              :style="{ stroke: connection.source.view.color }"
              d="M2,2L6,4L2,6"
              fill="transparent"
            />
          </marker>

          <marker
            :id="'exc' + connection.idx"
            markerHeight="8"
            markerWidth="8"
            orient="auto"
            refX="6"
            refY="4"
          >
            <path
              :style="{
                fill: connection.source.view.color,
                stroke: connection.source.view.color,
              }"
              d="M2,2L6,4L2,6L2,2L6,4"
            />
          </marker>

          <marker
            :id="'inh' + connection.idx"
            markerHeight="8"
            markerWidth="8"
            orient="auto"
            refX="6"
            refY="4"
          >
            <circle
              :style="{
                fill: connection.source.view.color,
                stroke: connection.source.view.color,
              }"
              r="2"
              transform="translate(4,4)"
            />
          </marker>
        </defs>
      </g>

      <rect id="background" fill="white" />
      <!-- <g class="grid no-print" /> -->

      <g id="network" ref="network">
        <g class="grid no-print" />
        <g v-if="state.graph">
          <path
            :style="{ strokeWidth: state.graph.strokeWidth }"
            class="dragline"
            d="M0,0L0,0"
            stroke-linecap="round"
            fill="none"
          />
        </g>

        <g id="connections" />
        <g id="nodes" />
        <g id="panel" />
      </g>
    </svg>

    <v-snackbar :timeout="-1" v-model="state.snackbar.show">
      {{ state.snackbar.text }}

      <template #action="{ attrs }">
        <v-btn
          @click="state.snackbar.show = false"
          outlined
          small
          v-bind="attrs"
          v-if="state.snackbar.actions.length === 0"
        >
          Close
        </v-btn>
        <template v-if="state.snackbar.actions.length > 0">
          <v-btn
            :key="actionIdx"
            @click="action.onClick"
            outlined
            small
            v-bind="attrs"
            v-for="(action, actionIdx) in state.snackbar.actions"
            v-text="action.text"
          />
        </template>
      </template>
    </v-snackbar>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import {
  reactive,
  onBeforeUnmount,
  onMounted,
  watch,
} from '@vue/composition-api';
import * as d3 from 'd3';

import { Connection } from '@/core/connection/connection';
import { NetworkGraph } from '@/core/network/networkGraph';
import { Node } from '@/core/node/node';
import core from '@/core';

import ConnectionMenu from '@/components/connection/ConnectionMenu.vue';
import NodeMenu from '@/components/node/NodeMenu.vue';
import NetworkGraphToolbar from '@/components/network/NetworkGraphToolbar.vue';

export default Vue.extend({
  name: 'NetworkGraph',
  components: {
    ConnectionMenu,
    NetworkGraphToolbar,
    NodeMenu,
  },
  props: {
    networkHash: String,
  },
  setup(props, { refs }) {
    const state = reactive({
      network: core.app.project.network,
      graph: undefined,
      nodeMenu: {
        node: undefined,
        position: { x: 0, y: 0 },
        show: false,
      },
      connectionMenu: {
        connection: undefined,
        position: { x: 0, y: 0 },
        show: false,
      },
      snackbar: {
        actions: [],
        show: false,
        text: '',
      },
    });

    /**
     * Show Menu for connection.
     */
    const showConnectionMenu = (e: MouseEvent, connection: Connection) => {
      e.preventDefault();
      state.connectionMenu.show = false;
      state.connectionMenu.connection = connection;
      state.connectionMenu.position.x = e.clientX;
      state.connectionMenu.position.y = e.clientY;
      setTimeout(() => {
        state.connectionMenu.show = true;
      }, 1);
    };

    /**
     * Show Menu for node.
     */
    const showNodeMenu = (e: MouseEvent, node: Node) => {
      e.preventDefault();
      state.nodeMenu.show = false;
      state.nodeMenu.node = node;
      state.nodeMenu.position.x = e.clientX;
      state.nodeMenu.position.y = e.clientY;
      setTimeout(() => {
        state.nodeMenu.show = true;
      }, 1);
    };

    /**
     * Tigger Menu for node or connection.
     */
    const setMenuTrigger = () => {
      d3.selectAll('g.node').each(
        (node: Node, idx: number, elements: any[]) => {
          d3.select(elements[idx]).on('contextmenu', e => {
            showNodeMenu(e, node);
          });
        }
      );

      d3.selectAll('g.connection').each(
        (connection: Connection, idx: number, elements: any[]) => {
          d3.select(elements[idx]).on('contextmenu', e => {
            showConnectionMenu(e, connection);
          });
        }
      );
    };

    /**
     * Show snackbar
     */
    const showSnackbar = (text: string, actions: any[] = []) => {
      state.snackbar.text = text;
      state.snackbar.actions = actions;
      state.snackbar.show = true;
    };

    const hasAllNodeTypes = () => {
      const types: string[] = state.network.nodes.map(
        node => node.model.elementType
      );
      return (
        types.includes('stimulator') &&
        types.includes('neuron') &&
        types.includes('recorder')
      );
    };

    /**
     * Show help in snackbar.
     */
    const showHelp = () => {
      state.snackbar.show = false;
      if (!state.network.project.config.showHelp) {
        return;
      }
      if (state.network.nodes.length === 0) {
        showSnackbar('Create nodes (use right mouse button).');
      } else if (!hasAllNodeTypes()) {
        showSnackbar('Add at least a stimulator, a neuron and a recorder.');
      } else if (state.network.connections.length < 2) {
        showSnackbar('Connect I/O-devices to neurons.');
      }
    };

    /**
     * Update network graph.
     */
    const update = () => {
      // console.log('Update network graph');
      state.network = core.app.project.network;
      state.graph.network = state.network;
      const elem: any = refs.networkGraph['parentNode'];
      state.graph.reset();
      state.graph.resize(elem.clientWidth, elem.clientHeight);
      state.graph.init();
      state.graph.update();
      state.graph.transform();
      setMenuTrigger();
      showHelp();
    };

    /**
     * Resize network graph.
     */
    const onResize = () => {
      const elem: any = refs.networkGraph['parentNode'];
      if (elem) {
        state.graph.resize(elem.clientWidth, elem.clientHeight);
        state.graph.transform();
      }
    };

    onMounted(() => {
      state.graph = new NetworkGraph('svg#networkGraph');
      update();
      window.addEventListener('resize', onResize);
    });

    onBeforeUnmount(() => {
      window.removeEventListener('resize', onResize);
    });

    watch(
      () => props.networkHash,
      () => update()
    );

    watch(
      () => [
        state.network.view.selectedNode,
        state.network.view.selectedConnection,
      ],
      () => {
        // When (un-)select node or connection button outside of the network graph.
        state.graph.update();
        state.graph.transform();
      }
    );

    return { state };
  },
});
</script>

<style>
.networkGraph {
  display: inline-block;
  position: relative;
  width: 100%;
  overflow: hidden;
}

.connection {
  cursor: pointer;
}

.node .shape {
  fill: white;
  cursor: pointer;
}

.node text {
  pointer-events: none;
  font-size: 12px;
  text-anchor: middle;
}

.dragline {
  pointer-events: none;
}
</style>

<template>
  <div class="networkGraph" ref="networkGraph">
    <ConnectionMenu
      :connection="state.connectionMenu.connection"
      :position="state.connectionMenu.position"
      v-if="state.connectionMenu.show"
    />

    <NodeMenu
      :node="state.nodeMenu.node"
      :position="state.nodeMenu.position"
      v-if="state.nodeMenu.show"
    />

    <NetworkGraphToolbar
      :graph="state.graph"
      :network="state.network"
      class="no-print"
    />

    <svg id="networkGraph" width="800" height="600">
      <g class="marker" v-if="state.network">
        <defs
          :key="'defs' + connection.idx"
          v-for="connection of state.network.connections"
        >
          <marker
            :id="'generic' + connection.idx"
            markerHeight="8"
            markerWidth="8"
            orient="auto"
            refX="6"
            refY="4"
          >
            <path
              :style="{ stroke: connection.source.view.color }"
              d="M2,2L6,4L2,6"
              fill="transparent"
            />
          </marker>

          <marker
            :id="'exc' + connection.idx"
            markerHeight="8"
            markerWidth="8"
            orient="auto"
            refX="6"
            refY="4"
          >
            <path
              :style="{
                fill: connection.source.view.color,
                stroke: connection.source.view.color,
              }"
              d="M2,2L6,4L2,6L2,2L6,4"
            />
          </marker>

          <marker
            :id="'inh' + connection.idx"
            markerHeight="8"
            markerWidth="8"
            orient="auto"
            refX="6"
            refY="4"
          >
            <circle
              :style="{
                fill: connection.source.view.color,
                stroke: connection.source.view.color,
              }"
              r="2"
              transform="translate(4,4)"
            />
          </marker>
        </defs>
      </g>

      <rect id="background" fill="white" />
      <!-- <g class="grid no-print" /> -->

      <g id="network" ref="network">
        <g class="grid no-print" />
        <g v-if="state.graph">
          <path
            :style="{ strokeWidth: state.graph.strokeWidth }"
            class="dragline"
            d="M0,0L0,0"
            stroke-linecap="round"
            fill="none"
          />
        </g>

        <g id="connections" />
        <g id="nodes" />
        <g id="panel" />
      </g>
    </svg>

    <v-snackbar :timeout="-1" v-model="state.snackbar.show">
      {{ state.snackbar.text }}

      <template #action="{ attrs }">
        <v-btn
          @click="state.snackbar.show = false"
          outlined
          small
          v-bind="attrs"
          v-if="state.snackbar.actions.length === 0"
        >
          Close
        </v-btn>
        <template v-if="state.snackbar.actions.length > 0">
          <v-btn
            :key="actionIdx"
            @click="action.onClick"
            outlined
            small
            v-bind="attrs"
            v-for="(action, actionIdx) in state.snackbar.actions"
            v-text="action.text"
          />
        </template>
      </template>
    </v-snackbar>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import {
  reactive,
  onBeforeUnmount,
  onMounted,
  watch,
} from '@vue/composition-api';
import * as d3 from 'd3';

import { Connection } from '@/core/connection/connection';
import { NetworkGraph } from '@/core/network/networkGraph';
import { Node } from '@/core/node/node';
import core from '@/core';

import ConnectionMenu from '@/components/connection/ConnectionMenu.vue';
import NodeMenu from '@/components/node/NodeMenu.vue';
import NetworkGraphToolbar from '@/components/network/NetworkGraphToolbar.vue';

export default Vue.extend({
  name: 'NetworkGraph',
  components: {
    ConnectionMenu,
    NetworkGraphToolbar,
    NodeMenu,
  },
  props: {
    networkHash: String,
  },
  setup(props, { refs }) {
    const state = reactive({
      network: core.app.project.network,
      graph: undefined,
      nodeMenu: {
        node: undefined,
        position: { x: 0, y: 0 },
        show: false,
      },
      connectionMenu: {
        connection: undefined,
        position: { x: 0, y: 0 },
        show: false,
      },
      snackbar: {
        actions: [],
        show: false,
        text: '',
      },
    });

    /**
     * Show Menu for connection.
     */
    const showConnectionMenu = (e: MouseEvent, connection: Connection) => {
      e.preventDefault();
      state.connectionMenu.show = false;
      state.connectionMenu.connection = connection;
      state.connectionMenu.position.x = e.clientX;
      state.connectionMenu.position.y = e.clientY;
      setTimeout(() => {
        state.connectionMenu.show = true;
      }, 1);
    };

    /**
     * Show Menu for node.
     */
    const showNodeMenu = (e: MouseEvent, node: Node) => {
      e.preventDefault();
      state.nodeMenu.show = false;
      state.nodeMenu.node = node;
      state.nodeMenu.position.x = e.clientX;
      state.nodeMenu.position.y = e.clientY;
      setTimeout(() => {
        state.nodeMenu.show = true;
      }, 1);
    };

    /**
     * Tigger Menu for node or connection.
     */
    const setMenuTrigger = () => {
      d3.selectAll('g.node').each(
        (node: Node, idx: number, elements: any[]) => {
          d3.select(elements[idx]).on('contextmenu', e => {
            showNodeMenu(e, node);
          });
        }
      );

      d3.selectAll('g.connection').each(
        (connection: Connection, idx: number, elements: any[]) => {
          d3.select(elements[idx]).on('contextmenu', e => {
            showConnectionMenu(e, connection);
          });
        }
      );
    };

    /**
     * Show snackbar
     */
    const showSnackbar = (text: string, actions: any[] = []) => {
      state.snackbar.text = text;
      state.snackbar.actions = actions;
      state.snackbar.show = true;
    };

    const hasAllNodeTypes = () => {
      const types: string[] = state.network.nodes.map(
        node => node.model.elementType
      );
      return (
        types.includes('stimulator') &&
        types.includes('neuron') &&
        types.includes('recorder')
      );
    };

    /**
     * Show help in snackbar.
     */
    const showHelp = () => {
      state.snackbar.show = false;
      if (!state.network.project.config.showHelp) {
        return;
      }
      if (state.network.nodes.length === 0) {
        showSnackbar('Create nodes (use right mouse button).');
      } else if (!hasAllNodeTypes()) {
        showSnackbar('Add at least a stimulator, a neuron and a recorder.');
      } else if (state.network.connections.length < 2) {
        showSnackbar('Connect I/O-devices to neurons.');
      }
    };

    /**
     * Update network graph.
     */
    const update = () => {
      // console.log('Update network graph');
      state.network = core.app.project.network;
      state.graph.network = state.network;
      const elem: any = refs.networkGraph['parentNode'];
      state.graph.reset();
      state.graph.resize(elem.clientWidth, elem.clientHeight);
      state.graph.init();
      state.graph.update();
      state.graph.transform();
      setMenuTrigger();
      showHelp();
    };

    /**
     * Resize network graph.
     */
    const onResize = () => {
      const elem: any = refs.networkGraph['parentNode'];
      if (elem) {
        state.graph.resize(elem.clientWidth, elem.clientHeight);
      }
    };

    onMounted(() => {
      state.graph = new NetworkGraph('svg#networkGraph');
      update();
      window.addEventListener('resize', onResize);
    });

    onBeforeUnmount(() => {
      window.removeEventListener('resize', onResize);
    });

    watch(
      () => props.networkHash,
      () => update()
    );

    watch(
      () => [
        state.network.view.selectedNode,
        state.network.view.selectedConnection,
      ],
      () => {
        // When (un-)select node or connection button outside of the network graph.
        state.graph.update();
        state.graph.transform();
      }
    );

    return { state };
  },
});
</script>

<style>
.networkGraph {
  display: inline-block;
  position: relative;
  width: 100%;
  overflow: hidden;
}

.connection {
  cursor: pointer;
}

.node .shape {
  fill: white;
  cursor: pointer;
}

.node text {
  pointer-events: none;
  font-size: 12px;
  text-anchor: middle;
}

.dragline {
  pointer-events: none;
}
</style>


revise course link v-out label

revise course link v-out label

<template>

Course Resources

        <template v-if="hasResources">

            <a
              v-out="`Course Website ${section.id}`"
              :href="section.class_website_url"
              :title="`${section.id} Course Website`"
            >
              Course Website

            <a
              v-out="`Library Research Guides ${section.id}`"
              :href="section.lib_subj_guide"
              :title="`${section.id} Library Research Guides`"
            >
              Library Research Guides

            <a
              v-out="`Course Canvas ${section.id}`"
              :href="section.canvas_url"
              :title="`${section.id} Course Canvas`"
            >
              Course Canvas

        </template>

          <a
            v-out="`Textbooks ${section.id}`"
            :href="textbookHref"
            :title="`${section.id} Textbooks`"
          >
            Textbooks

</template>

<template>

Course Resources

        <template v-if="hasResources">

            <a
              v-out="`Course Website ${section.section_label}`"
              :href="section.class_website_url"
              :title="`${section.section_labe} Course Website`"
            >
              Course Website

            <a
              v-out="`Library Research Guides ${section.section_label}`"
              :href="section.lib_subj_guide"
              :title="`${section.section_labe} Library Research Guides`"
            >
              Library Research Guides

            <a
              v-out="`Course Canvas ${section.section_label}`"
              :href="section.canvas_url"
              :title="`${section.section_labe} Course Canvas`"
            >
              Course Canvas

        </template>

          <a
            v-out="`Textbooks ${section.section_label}`"
            :href="textbookHref"
            :title="`${section.section_label} Textbooks`"
          >
            Textbooks

</template>


docs: optimize select demo

docs: optimize select demo

<template>

选中值：{{select}}
      <button class="h-btn h-btn-s" @click="update">修改值</button>

      <Select v-model="select" :datas="param" placeholder="自定义placeholder" null-option-text="自定义请选择项"></Select>

选中值：{{select2}}
      <button class="h-btn h-btn-s" @click="update2">修改值</button>

      <Select v-model="select2" :datas="param2"></Select>

根据内容自适应长度，选中值：{{select3}}
      <button class="h-btn h-btn-s" @click="update3">修改值</button>

      <Select v-model="select3" autosize :datas="param3"></Select>

自定义key, title字段名

      <Select v-model="select4" :datas="param4" keyName="code" titleName="name"></Select>

</template>

<template>

选中值：{{select}}
      <button class="h-btn h-btn-s" @click="update">修改值</button>

      <Select v-model="select" :datas="param" placeholder="请选择您的内容" null-option-text="自定义请选择项"></Select>

选中值：{{select2}}
      <button class="h-btn h-btn-s" @click="update2">修改值</button>

      <Select v-model="select2" :datas="param2" placeholder="请选择您的内容2"></Select>

根据内容自适应长度，选中值：{{select3}}
      <button class="h-btn h-btn-s" @click="update3">修改值</button>

      <Select v-model="select3" autosize :datas="param3" placeholder="请选择您的内容3"></Select>

自定义key, title字段名

      <Select v-model="select4" :datas="param4" keyName="code" titleName="name"></Select>

</template>


Added comment.

Added comment.

<template>
    <div :style="dimensions">
        <svg class="combination-canvas"
            :width="scrollWidth"
            :height="scrollHeight"
            :viewBox="viewBox"
            :transform="'translate(' + (scrollWidth / 2) * (globalScale - 1)
            + ' ' + (scrollHeight / 2) * (globalScale - 1)
            + ') scale(' + globalScale +  ')'"
            @mousemove="mousemove"
            @mousedown="mousedown"
            ref="svgCanvas">
            <artefact v-for="(artefact, index) in artefacts"
                :key="artefact.id"
                :artefact-data="artefact"
                :base-d-p-i="baseDPI"
                :index="index"
                ></artefact>
        </svg>
    </div>
</template>

<script>
import {geoJsonPolygonToSvg, geoJsonPointToSvg, geoJsonParseRect} from '../utils/VectorFactory'
import Artefact from './Artefact.vue'

export default {
  props: {
    globalScale:{
            type: Number,
            default: 1.0,
        },
  },
  components: {
      'artefact': Artefact
  },
  data() {
    return {
        scrollWidth: 10000,
        scrollHeight: 1500,
        artefacts: [],
        baseDPI: 1215,
        clickOrigin: undefined,
        selectedArtefactIndex: undefined,
        selectedArtefactLoc: undefined,
    }
  },
  computed: {
      combCanvasStyle(){
          return {
              width: `${this.scrollWidth * this.globalScale}px`,
              height: `${this.scrollHeight * this.globalScale}px`,
              background: 'maroon',
          }
      },
      canvasWidth() {
          return `${this.scrollWidth * this.globalScale}`
      },
      canvasHeight() {
          return `${this.scrollHeight * this.globalScale}`
      },
      viewBox() {
          return `0 0 ${this.scrollWidth} ${this.scrollHeight}`
      },
      dimensions() {
          return `width: ${this.canvasWidth}px; height: ${this.canvasHeight}px;`
      },
      svgCanvas() {
          return this.$refs['svgCanvas']
      }
  },
  methods: {
      setScrollDimensions(scrollID, versionID) {
        this.$post('resources/cgi-bin/GetImageData.pl', {
        transaction: 'getScrollWidth',
        scroll_id: scrollID,
        scroll_version_id: versionID,
        SESSION_ID: this.$store.getters.sessionID
        })
        .then(res => {
            if (res.status === 200 && res.data.results) {
                this.scrollWidth = res.data.results[0].max_x
            }
        })
        this.$post('resources/cgi-bin/GetImageData.pl', {
        transaction: 'getScrollHeight',
        scroll_id: scrollID,
        scroll_version_id: versionID,
        SESSION_ID: this.$store.getters.sessionID
        })
        .then(res => {
            if (res.status === 200 && res.data.results) {
                this.scrollHeight = res.data.results[0].max_y
            }
        })
        this.loadFragments(scrollID, versionID)
      },
      loadFragments(scrollID, versionID) {
          this.$post('resources/cgi-bin/GetImageData.pl', {
        transaction: 'getScrollArtefacts',
        scroll_id: scrollID,
        scroll_version_id: versionID,
        SESSION_ID: this.$store.getters.sessionID
        })
        .then(res => {
            if (res.status === 200 && res.data.results) {
                this.artefacts = res.data.results
                this.artefacts.forEach(artefact => {
                    artefact.rect = geoJsonParseRect(artefact.rect)
                    artefact.poly = geoJsonPolygonToSvg(artefact.poly, artefact.rect)
                    artefact.pos = geoJsonPointToSvg(artefact.pos)
                })
            }
        })
      },
      mousedown(event) {
          if (event.target.nodeName === 'image') {
            this.selectedArtefactIndex = event.target.dataset.index
            this.selectedArtefactLoc = this.artefacts[this.selectedArtefactIndex].pos
            this.clickOrigin = this.pointInSvg(event.clientX, event.clientY)
            window.addEventListener('mouseup',this.mouseup); // Attach listener to window.
          }
      },
      mousemove(event) {
          if (this.clickOrigin && this.selectedArtefactIndex) {
              const currentLoc = this.pointInSvg(event.clientX, event.clientY)
              this.artefacts[this.selectedArtefactIndex].pos = {
                  x: this.selectedArtefactLoc.x + currentLoc.x - this.clickOrigin.x,
                  y: this.selectedArtefactLoc.y + currentLoc.y - this.clickOrigin.y
              }
          }
      },
      mouseup(event) {
        if (this.clickOrigin  && this.selectedArtefactIndex) {
            this.clickOrigin = this.selectedArtefactLoc = undefined
            window.removeEventListener('mouseup',this.mouseup);
            this.$post('resources/cgi-bin/GetImageData.pl', {
            transaction: 'setArtPosition',
            art_id: this.artefacts[this.selectedArtefactIndex].id,
            x: this.artefacts[this.selectedArtefactIndex].pos.x,
            y: this.artefacts[this.selectedArtefactIndex].pos.y,
            version_id: this.$route.params.scrollVersionID,
            SESSION_ID: this.$store.getters.sessionID
            })
            .then(res => {
                if (res.status === 200 && res.data.returned_info) {
                    this.artefacts[this.selectedArtefactIndex].id = res.data.returned_info
                    this.selectedArtefactIndex = undefined
                }
            })
          }
      },
      pointInSvg(x, y) {
        const pt = this.svgCanvas.createSVGPoint();
        pt.x = x;
        pt.y = y;
        return pt.matrixTransform(this.svgCanvas.getScreenCTM().inverse());
    },
  },
  watch: {
    '$route' (to, from) {
      if (to.params.scrollVersionID && to.params.scrollVersionID !== from.params.scrollVersionID) {
        this.artefacts = []
        this.setScrollDimensions(to.params.scrollID, to.params.scrollVersionID)
      }
    }
  }
}
</script>

<style lang="sass" scoped>
    .combination-canvas {
        max-height: initial;
    }
</style>

<template>
    <div :style="dimensions">
        <svg class="combination-canvas"
            :width="scrollWidth"
            :height="scrollHeight"
            :viewBox="viewBox"
            :transform="'translate(' + (scrollWidth / 2) * (globalScale - 1)
            + ' ' + (scrollHeight / 2) * (globalScale - 1)
            + ') scale(' + globalScale +  ')'"
            @mousemove="mousemove"
            @mousedown="mousedown"
            ref="svgCanvas">
            <artefact v-for="(artefact, index) in artefacts"
                :key="artefact.id"
                :artefact-data="artefact"
                :base-d-p-i="baseDPI"
                :index="index"
                ></artefact>
        </svg>
    </div>
</template>

<script>
import {geoJsonPolygonToSvg, geoJsonPointToSvg, geoJsonParseRect} from '../utils/VectorFactory'
import Artefact from './Artefact.vue'

export default {
  props: {
    globalScale:{
            type: Number,
            default: 1.0,
        },
  },
  components: {
      'artefact': Artefact
  },
  data() {
    return {
        scrollWidth: 10000,
        scrollHeight: 1500,
        artefacts: [],
        baseDPI: 1215,
        clickOrigin: undefined,
        selectedArtefactIndex: undefined,
        selectedArtefactLoc: undefined,
    }
  },
  computed: {
      combCanvasStyle(){
          return {
              width: `${this.scrollWidth * this.globalScale}px`,
              height: `${this.scrollHeight * this.globalScale}px`,
              background: 'maroon',
          }
      },
      canvasWidth() {
          return `${this.scrollWidth * this.globalScale}`
      },
      canvasHeight() {
          return `${this.scrollHeight * this.globalScale}`
      },
      viewBox() {
          return `0 0 ${this.scrollWidth} ${this.scrollHeight}`
      },
      dimensions() {
          return `width: ${this.canvasWidth}px; height: ${this.canvasHeight}px;`
      },
      svgCanvas() {
          return this.$refs['svgCanvas']
      }
  },
  methods: {
      setScrollDimensions(scrollID, versionID) {
        this.$post('resources/cgi-bin/GetImageData.pl', {
        transaction: 'getScrollWidth',
        scroll_id: scrollID,
        scroll_version_id: versionID,
        SESSION_ID: this.$store.getters.sessionID
        })
        .then(res => {
            if (res.status === 200 && res.data.results) {
                this.scrollWidth = res.data.results[0].max_x
            }
        })
        this.$post('resources/cgi-bin/GetImageData.pl', {
        transaction: 'getScrollHeight',
        scroll_id: scrollID,
        scroll_version_id: versionID,
        SESSION_ID: this.$store.getters.sessionID
        })
        .then(res => {
            if (res.status === 200 && res.data.results) {
                this.scrollHeight = res.data.results[0].max_y
            }
        })
        this.loadFragments(scrollID, versionID)
      },
      loadFragments(scrollID, versionID) {
          this.$post('resources/cgi-bin/GetImageData.pl', {
        transaction: 'getScrollArtefacts',
        scroll_id: scrollID,
        scroll_version_id: versionID,
        SESSION_ID: this.$store.getters.sessionID
        })
        .then(res => {
            if (res.status === 200 && res.data.results) {
                this.artefacts = res.data.results
                this.artefacts.forEach(artefact => {
                    artefact.rect = geoJsonParseRect(artefact.rect)
                    artefact.poly = geoJsonPolygonToSvg(artefact.poly, artefact.rect)
                    artefact.pos = geoJsonPointToSvg(artefact.pos)
                })
            }
        })
      },
      mousedown(event) {
          if (event.target.nodeName === 'image') {
            this.selectedArtefactIndex = event.target.dataset.index
            this.selectedArtefactLoc = this.artefacts[this.selectedArtefactIndex].pos
            this.clickOrigin = this.pointInSvg(event.clientX, event.clientY)
            window.addEventListener('mouseup',this.mouseup);
          }
      },
      mousemove(event) {
          if (this.clickOrigin && this.selectedArtefactIndex) {
              const currentLoc = this.pointInSvg(event.clientX, event.clientY)
              this.artefacts[this.selectedArtefactIndex].pos = {
                  x: this.selectedArtefactLoc.x + currentLoc.x - this.clickOrigin.x,
                  y: this.selectedArtefactLoc.y + currentLoc.y - this.clickOrigin.y
              }
          }
      },
      mouseup(event) {
        if (this.clickOrigin  && this.selectedArtefactIndex) {
            this.clickOrigin = this.selectedArtefactLoc = undefined
            window.removeEventListener('mouseup',this.mouseup);
            this.$post('resources/cgi-bin/GetImageData.pl', {
            transaction: 'setArtPosition',
            art_id: this.artefacts[this.selectedArtefactIndex].id,
            x: this.artefacts[this.selectedArtefactIndex].pos.x,
            y: this.artefacts[this.selectedArtefactIndex].pos.y,
            version_id: this.$route.params.scrollVersionID,
            SESSION_ID: this.$store.getters.sessionID
            })
            .then(res => {
                if (res.status === 200 && res.data.returned_info) {
                    this.artefacts[this.selectedArtefactIndex].id = res.data.returned_info
                    this.selectedArtefactIndex = undefined
                }
            })
          }
      },
      pointInSvg(x, y) {
        const pt = this.svgCanvas.createSVGPoint();
        pt.x = x;
        pt.y = y;
        return pt.matrixTransform(this.svgCanvas.getScreenCTM().inverse());
    },
  },
  watch: {
    '$route' (to, from) {
      if (to.params.scrollVersionID && to.params.scrollVersionID !== from.params.scrollVersionID) {
        this.artefacts = []
        this.setScrollDimensions(to.params.scrollID, to.params.scrollVersionID)
      }
    }
  }
}
</script>

<style lang="sass" scoped>
    .combination-canvas {
        max-height: initial;
    }
</style>


Fix overflow on feed loader

Fix overflow on feed loader

<template lang="pug">
  .feed-container
    transition(name='fade', mode='out-in')
      loader(v-if='!instanciate', key='loading')

      entries(v-else-if='instanciate && !current', key='entries')

      info-displayer(v-else-if='instanciate && current', key='current', :current='current', :return-cb='resetCurrent')
</template>

<script>
// Components
import Entries from '@/components/feed/entries.vue'
import Loader from '@/components/feed/loader.vue'
import InfoDisplayer from '@/components/info/layout.vue'

export default {
  name: 'Feed',

  components: { Entries, Loader, InfoDisplayer },

  computed: {
    params: {
      get () {
        return this.$store.state.releases.params
      },
      set () {}
    },
    current: {
      get () {
        return this.$store.state.releases.current
      },
      set () {}
    },
    instanciate: {
      get () {
        return this.$store.state.releases.instanciate
      },
      set () {}
    }
  },

  methods: {
    resetCurrent () {
      this.$store.commit('releases/setCurrent', null)
    }
  }
}
</script>

<style lang="stylus" scoped>
  .fade-enter, .fade-leave-to
    opacity 0

  .fade-enter-active, .fade-leave
    transition all .5s

  .feed-container
    overflow hidden
</style>

<template lang="pug">
  div
    transition(name='fade', mode='out-in')
      loader(v-if='!instanciate', key='loading')

      entries(v-else-if='instanciate && !current', key='entries')

      info-displayer(v-else-if='instanciate && current', key='current', :current='current', :return-cb='resetCurrent')
</template>

<script>
// Components
import Entries from '@/components/feed/entries.vue'
import Loader from '@/components/feed/loader.vue'
import InfoDisplayer from '@/components/info/layout.vue'

export default {
  name: 'Feed',

  components: { Entries, Loader, InfoDisplayer },

  computed: {
    params: {
      get () {
        return this.$store.state.releases.params
      },
      set () {}
    },
    current: {
      get () {
        return this.$store.state.releases.current
      },
      set () {}
    },
    instanciate: {
      get () {
        return this.$store.state.releases.instanciate
      },
      set () {}
    }
  },

  methods: {
    resetCurrent () {
      this.$store.commit('releases/setCurrent', null)
    }
  }
}
</script>

<style lang="stylus" scoped>
  .fade-enter, .fade-leave-to
    opacity 0

  .fade-enter-active, .fade-leave
    transition all .5s
</style>


maint: Use api client for getting game sources

<template>

            <ButtonIcon
                icon="chevron-left"
                class="link white"
                :to="`/games?game=${game.id}&season=${game.season}`"
            >
                Game Details
            </ButtonIcon>

                0
                VS
                0

                <Button class="link white">Objectives</Button>

            <GameProjectTeam
                v-for="(players, teamId) in teams"
                :key="teamId"
                :game="game"
                :players="players"
                :class="[teamClassMap[teamId], expandedClass(teamId)]"
                :team-id="Number(teamId)"
                :sources="sources"
                :expanded="expandedTeamId === Number(teamId)"
                @expandTeam="(teamId) => expandedTeamId = teamId"
            />

</template>