[playlists] Fix repeat button

[playlists] Fix repeat button

<template>

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

      {{ playlist.name }}

    <button-simple
      @click="$emit('show-add-entities')"
      class="playlist-button add-entities-button flexrow-item"
      icon="plus"
      :text="addEntitiesText"
      v-if="isCurrentUserManager && !isAddingEntity"
    />
    <button-simple
      @click="$emit('edit-clicked')"
      class="edit-button playlist-button flexrow-item"
      :title="$t('playlists.actions.edit')"
      icon="edit"
      v-if="isCurrentUserManager"
    />
    <button-simple
      @click="showDeleteModal"
      class="delete-button playlist-button flexrow-item"
      :title="$t('playlists.actions.delete')"
      icon="delete"
      v-if="isCurrentUserManager"
    />

    <raw-video-player
      ref="raw-player"
      class="raw-player"
      :entities="entityList"
      :is-repeating="isRepeating"
      @repeat="onVideoRepeated"
      @metadata-loaded="onMetadataLoaded"
      @entity-change="onPlayerEntityChange"
      @time-update="onTimeUpdate"
      @max-duration-update="onMaxDurationUpdate"
      v-show="isCurrentEntityMovie && !isLoading"
    />
    <raw-video-player
      ref="raw-player-comparison"
      class="raw-player"
      :entities="entityListToCompare"
      :is-repeating="isRepeating"
      :muted="true"
      name="comparison"
      v-show="isComparing && isMovieComparison && !isLoading"
    />

        <download-icon class="icon" />

          {{ $t('tasks.download_pdf_file', {extension: currentEntity.preview_file_extension}) }}

      <spinner />

      <canvas
        id="playlist-annotation-canvas"
        ref="annotation-canvas"
        class="canvas"
      >
      </canvas>

    <task-info
      ref="task-info"
      :class="{
        'flexrow-item': true,
        'task-info-column': true,
        'hidden': isCommentsHidden
      }"
      :task="task"
      :is-preview="false"
    />

      <progress
        ref="progress"
        value="0"
        min="0"
        @click="onProgressBarClicked"
      >

      </progress>

  <annotation-bar
    class="playlist-annotations"
    ref="playlist-annotation"
    :annotations="annotations"
    :max-duration-raw="maxDurationRaw"
    @select-annotation="loadAnnotation"
    v-show="isCurrentEntityMovie && playlist.id && !isAddingEntity"
  />

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayPreviousEntityClicked"
      :title="$t('playlists.actions.previous_shot')"
      icon="back"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onPlayNextEntityClicked"
      :title="$t('playlists.actions.next_shot')"
      icon="forward"
    />

      {{ entityList.length > 0 ? playingEntityIndex + 1 : 0 }}

    /

      {{ entityList.length }}

      <button-simple
        class="button playlist-button flexrow-item"
        icon="left"
        :title="$t('playlists.actions.files_previous')"
        @click="onPreviousPictureClicked"
      />

        {{ currentEntityPictureIndex + 1 }} / {{ currentPicturePreviewLength }}

      <button-simple
        class="button playlist-button flexrow-item"
        icon="right"
        :title="$t('playlists.actions.files_next')"
        @click="onNextPictureClicked"
      />

        <arrow-up-right-icon class="icon" />

      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPlayClicked"
        :title="$t('playlists.actions.play')"
        icon="play"
        v-if="!isPlaying"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPlayClicked"
        :title="$t('playlists.actions.pause')"
        icon="pause"
        v-else
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x1.00"
        v-if="speed === 3"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x0.50"
        v-else-if="speed === 2"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x0.25"
        v-else
      />

      <button-simple
        class="button playlist-button flexrow-item"
        :active="isRepeating"
        :title="$t('playlists.actions.looping')"
        icon="repeat"
        @click="onRepeatClicked"
      />

        {{ currentTime }}

      /

        {{ maxDuration }}

        ({{ currentFrame }})

      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPreviousFrameClicked"
        :title="$t('playlists.actions.previous_frame')"
        icon="left"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onNextFrameClicked"
        :title="$t('playlists.actions.next_frame')"
        icon="right"
      />

      <button-simple
        :class="{
          'comparison-button': true,
          'flexrow-item': true,
          'playlist-button': true,
          active: isComparing
        }"
        :title="$t('playlists.actions.split_screen')"
        icon="compare"
        @click="onCompareClicked"
        v-if="taskTypeOptions"
      />
      <combobox
        class="playlist-button flexrow-item comparison-list"
        :options="taskTypeOptions"
        v-model="taskTypeToCompare"
        v-if="isComparing"
      />

 1
      "
    >
      <button-simple
        class="button playlist-button flexrow-item"
        icon="left"
        @click="onPreviousComparisonPictureClicked"
      />
      {{ currentComparisonEntityPictureIndex + 1 }} / {{ currentComparisonPicturePreviewLength }}
      <button-simple
        class="button playlist-button flexrow-item"
        icon="right"
        @click="onNextComparisonPictureClicked"
      />

    <button-simple
      @click="$emit('save-clicked')"
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.save_playlist')"
      icon="save"
      v-if="isCurrentUserManager && tempMode"
    />

      <button-simple
        class="playlist-button flexrow-item"
        icon="undo"
        :title="$t('playlists.actions.annotation_undo')"
        @click="undoLastAction"
      />

      <button-simple
        class="playlist-button flexrow-item"
        :title="$t('playlists.actions.annotation_redo')"
        icon="redo"
        @click="redoLastAction"
      />

      <transition name="slide">

          <color-picker
            :isOpen="isShowingPalette"
            :color="this.textColor"
            :palette="this.palette"
            @TogglePalette="onPickColor"
            @change="onChangeTextColor"
          />

      </transition>
      <button-simple
        :class="{
          'playlist-button': true,
          'flexrow-item': true,
          active: isTyping
        }"
        :title="$t('playlists.actions.annotation_text')"
        @click="onTypeClicked"
        icon="type"
      />

      <transition name="slide">

          <pencil-picker
            :isOpen="isShowingPencilPalette"
            :pencil="pencil"
            :sizes="this.pencilPalette"
            @toggle-palette="onPickPencil"
            @change="onChangePencil"
          />

          <color-picker
            :isOpen="isShowingPalette"
            :color="this.color"
            :palette="this.palette"
            @TogglePalette="onPickColor"
            @change="onChangeColor"
          />

      </transition>
      <button-simple
        :class="{
          'playlist-button': true,
          'flexrow-item': true,
          active: isDrawing
        }"
        :title="$t('playlists.actions.annotation')"
        @click="onAnnotateClicked"
        icon="pencil"
      />
      <button-simple
        class="playlist-button flexrow-item"
        icon="remove"
        :title="$t('playlists.actions.annotation_delete')"
        @click="onDeleteClicked"
      />

    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.change_task_type')"
      icon="layers"
      @click="showTaskTypeModal"
      v-if="!tempMode"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      :title="$t('playlists.actions.comments')"
      @click="onCommentClicked"
      icon="comment"
    />
    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.entity_list')"
      @click="onFilmClicked"
      icon="film"
    />

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

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

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

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

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

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

              {{ formatDate(job.created_at) }}

            <spinner
              class="build-spinner"
              v-if="job.status === 'running'"
            />
            <button
              class="delete-job-button"
              @click="onRemoveBuildJob(job)"
              v-else
            >
              x
            </button>

      <button-simple
        class="playlist-button"
        :title="$t('playlists.actions.download')"
        icon="download"
        @click="toggleDlButtons"
      />

    <button-simple
      class="button playlist-button flexrow-item"
      :title="$t('playlists.actions.fullscreen')"
      @click="onFullscreenClicked"
      icon="maximize"
      v-if="isFullScreenEnabled"
    />

    <spinner class="spinner" v-if="isLoading" />

      <playlisted-entity
        :ref="'entity-' + index"
        :index="index"
        :entity="entity"
        :is-playing="playingEntityIndex === index"
        @play-click="playEntity"
        @remove-entity="removeEntity"
        @preview-changed="onPreviewChanged"
        @entity-dropped="onEntityDropped"
      />

  <delete-modal
    :active="modals.delete"
    :is-loading="loading.deletePlaylist"
    :is-error="errors.deletePlaylist"
    :text="deleteText"
    :error-text="$t('playlists.delete_error')"
    @confirm="confirmRemovePlaylist"
    @cancel="hideDeleteModal"
  />

  <select-task-type-modal
    :active="modals.taskType"
    :task-type-list="entityTaskTypes"
    @confirm="confirmChangeTaskType"
    @cancel="hideTaskTypeModal"
  />

</template>

<template>

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

      {{ playlist.name }}

    <button-simple
      @click="$emit('show-add-entities')"
      class="playlist-button add-entities-button flexrow-item"
      icon="plus"
      :text="addEntitiesText"
      v-if="isCurrentUserManager && !isAddingEntity"
    />
    <button-simple
      @click="$emit('edit-clicked')"
      class="edit-button playlist-button flexrow-item"
      :title="$t('playlists.actions.edit')"
      icon="edit"
      v-if="isCurrentUserManager"
    />
    <button-simple
      @click="showDeleteModal"
      class="delete-button playlist-button flexrow-item"
      :title="$t('playlists.actions.delete')"
      icon="delete"
      v-if="isCurrentUserManager"
    />

    <raw-video-player
      ref="raw-player"
      class="raw-player"
      :entities="entityList"
      :is-repeating="isRepeating"
      @repeat="onVideoRepeated"
      @metadata-loaded="onMetadataLoaded"
      @entity-change="onPlayerEntityChange"
      @time-update="onTimeUpdate"
      @max-duration-update="onMaxDurationUpdate"
      v-show="isCurrentEntityMovie && !isLoading"
    />
    <raw-video-player
      ref="raw-player-comparison"
      class="raw-player"
      :entities="entityListToCompare"
      :is-repeating="isRepeating"
      :muted="true"
      name="comparison"
      v-show="isComparing && isMovieComparison && !isLoading"
    />

        <download-icon class="icon" />

          {{ $t('tasks.download_pdf_file', {extension: currentEntity.preview_file_extension}) }}

      <spinner />

      <canvas
        id="playlist-annotation-canvas"
        ref="annotation-canvas"
        class="canvas"
      >
      </canvas>

    <task-info
      ref="task-info"
      :class="{
        'flexrow-item': true,
        'task-info-column': true,
        'hidden': isCommentsHidden
      }"
      :task="task"
      :is-preview="false"
    />

      <progress
        ref="progress"
        value="0"
        min="0"
        @click="onProgressBarClicked"
      >

      </progress>

  <annotation-bar
    class="playlist-annotations"
    ref="playlist-annotation"
    :annotations="annotations"
    :max-duration-raw="maxDurationRaw"
    @select-annotation="loadAnnotation"
    v-show="isCurrentEntityMovie && playlist.id && !isAddingEntity"
  />

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayPreviousEntityClicked"
      :title="$t('playlists.actions.previous_shot')"
      icon="back"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onPlayNextEntityClicked"
      :title="$t('playlists.actions.next_shot')"
      icon="forward"
    />

      {{ entityList.length > 0 ? playingEntityIndex + 1 : 0 }}

    /

      {{ entityList.length }}

      <button-simple
        class="button playlist-button flexrow-item"
        icon="left"
        :title="$t('playlists.actions.files_previous')"
        @click="onPreviousPictureClicked"
      />

        {{ currentEntityPictureIndex + 1 }} / {{ currentPicturePreviewLength }}

      <button-simple
        class="button playlist-button flexrow-item"
        icon="right"
        :title="$t('playlists.actions.files_next')"
        @click="onNextPictureClicked"
      />

        <arrow-up-right-icon class="icon" />

      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPlayClicked"
        :title="$t('playlists.actions.play')"
        icon="play"
        v-if="!isPlaying"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPlayClicked"
        :title="$t('playlists.actions.pause')"
        icon="pause"
        v-else
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x1.00"
        v-if="speed === 3"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x0.50"
        v-else-if="speed === 2"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x0.25"
        v-else
      />

      <button-simple
        :active="isRepeating"
        :title="$t('playlists.actions.looping')"
        icon="repeat"
        @click="onRepeatClicked"
        v-if="isFullScreen()"
      />

        {{ currentTime }}

      /

        {{ maxDuration }}

        ({{ currentFrame }})

      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPreviousFrameClicked"
        :title="$t('playlists.actions.previous_frame')"
        icon="left"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onNextFrameClicked"
        :title="$t('playlists.actions.next_frame')"
        icon="right"
      />

      <button-simple
        :class="{
          'comparison-button': true,
          'flexrow-item': true,
          'playlist-button': true,
          active: isComparing
        }"
        :title="$t('playlists.actions.split_screen')"
        icon="compare"
        @click="onCompareClicked"
        v-if="taskTypeOptions"
      />
      <combobox
        class="playlist-button flexrow-item comparison-list"
        :options="taskTypeOptions"
        v-model="taskTypeToCompare"
        v-if="isComparing"
      />

 1
      "
    >
      <button-simple
        class="button playlist-button flexrow-item"
        icon="left"
        @click="onPreviousComparisonPictureClicked"
      />
      {{ currentComparisonEntityPictureIndex + 1 }} / {{ currentComparisonPicturePreviewLength }}
      <button-simple
        class="button playlist-button flexrow-item"
        icon="right"
        @click="onNextComparisonPictureClicked"
      />

    <button-simple
      @click="$emit('save-clicked')"
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.save_playlist')"
      icon="save"
      v-if="isCurrentUserManager && tempMode"
    />

      <button-simple
        class="playlist-button flexrow-item"
        icon="undo"
        :title="$t('playlists.actions.annotation_undo')"
        @click="undoLastAction"
      />

      <button-simple
        class="playlist-button flexrow-item"
        :title="$t('playlists.actions.annotation_redo')"
        icon="redo"
        @click="redoLastAction"
      />

      <transition name="slide">

          <color-picker
            :isOpen="isShowingPalette"
            :color="this.textColor"
            :palette="this.palette"
            @TogglePalette="onPickColor"
            @change="onChangeTextColor"
          />

      </transition>
      <button-simple
        :class="{
          'playlist-button': true,
          'flexrow-item': true,
          active: isTyping
        }"
        :title="$t('playlists.actions.annotation_text')"
        @click="onTypeClicked"
        icon="type"
      />

      <transition name="slide">

          <pencil-picker
            :isOpen="isShowingPencilPalette"
            :pencil="pencil"
            :sizes="this.pencilPalette"
            @toggle-palette="onPickPencil"
            @change="onChangePencil"
          />

          <color-picker
            :isOpen="isShowingPalette"
            :color="this.color"
            :palette="this.palette"
            @TogglePalette="onPickColor"
            @change="onChangeColor"
          />

      </transition>
      <button-simple
        :class="{
          'playlist-button': true,
          'flexrow-item': true,
          active: isDrawing
        }"
        :title="$t('playlists.actions.annotation')"
        @click="onAnnotateClicked"
        icon="pencil"
      />
      <button-simple
        class="playlist-button flexrow-item"
        icon="remove"
        :title="$t('playlists.actions.annotation_delete')"
        @click="onDeleteClicked"
      />

    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.change_task_type')"
      icon="layers"
      @click="showTaskTypeModal"
      v-if="!tempMode"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      :title="$t('playlists.actions.comments')"
      @click="onCommentClicked"
      icon="comment"
    />
    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.entity_list')"
      @click="onFilmClicked"
      icon="film"
    />

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

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

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

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

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

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

              {{ formatDate(job.created_at) }}

            <spinner
              class="build-spinner"
              v-if="job.status === 'running'"
            />
            <button
              class="delete-job-button"
              @click="onRemoveBuildJob(job)"
              v-else
            >
              x
            </button>

      <button-simple
        class="playlist-button"
        :title="$t('playlists.actions.download')"
        icon="download"
        @click="toggleDlButtons"
      />

    <button-simple
      class="button playlist-button flexrow-item"
      :title="$t('playlists.actions.fullscreen')"
      @click="onFullscreenClicked"
      icon="maximize"
      v-if="isFullScreenEnabled"
    />

    <spinner class="spinner" v-if="isLoading" />

      <playlisted-entity
        :ref="'entity-' + index"
        :index="index"
        :entity="entity"
        :is-playing="playingEntityIndex === index"
        @play-click="playEntity"
        @remove-entity="removeEntity"
        @preview-changed="onPreviewChanged"
        @entity-dropped="onEntityDropped"
      />

  <delete-modal
    :active="modals.delete"
    :is-loading="loading.deletePlaylist"
    :is-error="errors.deletePlaylist"
    :text="deleteText"
    :error-text="$t('playlists.delete_error')"
    @confirm="confirmRemovePlaylist"
    @cancel="hideDeleteModal"
  />

  <select-task-type-modal
    :active="modals.taskType"
    :task-type-list="entityTaskTypes"
    @confirm="confirmChangeTaskType"
    @cancel="hideTaskTypeModal"
  />

</template>


Tags.vue: validation work, (so far)

Tags.vue: validation work, (so far)

<template id="tag-template">
    <legend>
        Taggar

    </legend>

                {{ tag.name }}

            <input type="text" name="tags" id="inputTags" class="form-control" list="tags" v-model="new_tag" v-el="tagInput" v-on:keyup.enter="add_tag">
            <datalist id="tags">
                <option v-for="tag in tags" value="{{ tag.name }}">{{ tag.name }}</option>
            </datalist>

                <button class="btn btn-default {{ asdfasdf }}" type="button" @click="add_tag">Lägg till</button>

</template>

<template id="tag-template">
    <legend>
        Taggar

    </legend>

                {{ tag.name }}

            <input type="text" name="tags" id="inputTags" class="form-control" list="tags" v-model="new_tag" v-el="tagInput" v-on:keyup.enter="add_tag">
            <datalist id="tags">
                <option v-for="tag in tags" value="{{ tag.name }}">{{ tag.name }}</option>
            </datalist>

                <button class="btn btn-default" type="button" @click="add_tag">Lägg till</button>

</template>


search debounce

search debounce

<template>

                <input class="form-control" placeholder="Search hash" type="text" v-model="search">

{{error[0]}}

Search result:

                            Hash
                            Type
                            Block

                            <router-link :to="'/hash/'+hash.hash">
                                {{hash.hash}}
                            </router-link>

                        {{hash.type | hashType}}

                            <router-link :to="'/block/'+hash.blocks[0].height">
                                {{hash.blocks[0].height}}
                            </router-link>

Latest operations

                            Hash
                            Type
                            Block

                            <router-link :to="'/hash/'+hash.hash">
                                {{hash.hash}}
                            </router-link>

                        {{hash.type | hashType}}

                            <router-link :to="'/block/'+hash.height">
                                {{hash.height}}
                            </router-link>

</template>


fix empty layers type check in map plugin

<template>
    <div class="h-100" v-if="dataInitialized">
        <ol-map
            :epsg="epsg"
            :init-geojson="geojson"
            :init-projection="'EPSG:4326'"
            :layers="layers"
            :event-bus="eventBus"
            :on-deleteend="deleteFeatures"
            :on-drawend="addFeature"
            :on-modifyend="updateFeatures"
            :reset="false"
            :selected-entity="selectedEntity"
            @update:link="(geoId, entityId) => $emit('update:link', geoId, entityId)"
            @feature-selected="handleFeatureSelection">
        </ol-map>
    </div>
    <div v-else v-html="$t('plugins.map.tab.loading')">
    </div>
</template>

<script>
    import GeoJSON from 'ol/format/GeoJSON';

    export default {
        props: {
            selectedEntity: {
                type: Object,
                required: true
            },
            eventBus: {
                type: Object,
                required: true
            }
        },
        mounted() {
            this.initData();
        },
        methods: {
            initData() {
                this.epsg = this.$getPreference('prefs.map-projection');
                this.dataInitialized = false;
                $httpQueue.add(() => $http.get('/map').then(response => {
                    const mapData = response.data;
                    // empty objects returned as json by php are []
                    // thus if no layers exist, set to real empty {}
                    if(Array.isArray(mapData.layers) && !mapData.layers.length) {
                        this.layers = {};
                    } else {
                        this.layers = mapData.layers;
                    }
                    this.geodata = mapData.geodata;
                    for(let k in this.geodata) {
                        const curr = this.geodata[k];
                        let geo = {
                            geom: curr.geom,
                            props: this.getProperties(curr)
                        };
                        this.geojson.push(geo);
                    }
                    this.dataInitialized = true;
                }));
            },
            getProperties(geodata) {
                return {
                    id: geodata.id,
                    entity: geodata.entity
                };
            },
            deleteFeatures(features, wkt) {
                features.forEach(f => {
                    $httpQueue.add(() => $http.delete(`/map/${f.getProperties().id}`).then(response => {

                    }));
                });
            },
            addFeature(feature, wkt) {
                const collection = this.geoJsonFormat.writeFeatures([feature]);
                const srid = 3857;
                const data = {
                    collection: collection,
                    srid: srid
                };
                return $http.post('/map', data).then(response => {
                    if(response.data.length) {
                        const geodata = response.data[0];
                        // TODO update feature
                        feature.setProperties(this.getProperties(geodata));
                        return feature;
                    } else {
                        return;
                    }
                });
            },
            updateFeatures(features, wkt) {
                const vm = this;
                features.forEach(f => {
                    let data = {
                        feature: vm.geoJsonFormat.writeFeature(f),
                        srid: 3857
                    };
                    vm.$http.patch(`/map/${f.getProperties().id}`, data).then(function(response) {

                    });
                });
            },
            handleFeatureSelection(e) {
                const p = e.properties;
                if(p.entity) {
                    this.$router.push({
                        append: true,
                        name: 'entitydetail',
                        params: {
                            id: p.entity.id
                        },
                        query: this.$route.query
                    });
                }
            }
        },
        data() {
            return {
                dataInitialized: false,
                epsg: {},
                layers:{},
                geodata: {},
                geojson: [],
                geoJsonFormat: new GeoJSON()
            }
        }
    }
</script>

<template>
    <div class="h-100" v-if="dataInitialized">
        <ol-map
            :epsg="epsg"
            :init-geojson="geojson"
            :init-projection="'EPSG:4326'"
            :layers="layers"
            :event-bus="eventBus"
            :on-deleteend="deleteFeatures"
            :on-drawend="addFeature"
            :on-modifyend="updateFeatures"
            :reset="false"
            :selected-entity="selectedEntity"
            @update:link="(geoId, entityId) => $emit('update:link', geoId, entityId)"
            @feature-selected="handleFeatureSelection">
        </ol-map>
    </div>
    <div v-else v-html="$t('plugins.map.tab.loading')">
    </div>
</template>

<script>
    import GeoJSON from 'ol/format/GeoJSON';

    export default {
        props: {
            selectedEntity: {
                type: Object,
                required: true
            },
            eventBus: {
                type: Object,
                required: true
            }
        },
        mounted() {
            this.initData();
        },
        methods: {
            initData() {
                const vm = this;
                vm.epsg = vm.$getPreference('prefs.map-projection');
                vm.dataInitialized = false;
                $httpQueue.add(() => vm.$http.get('/map').then(function(response) {
                    const mapData = response.data;
                    vm.layers = mapData.layers;
                    vm.geodata = mapData.geodata;
                    for(let k in vm.geodata) {
                        const curr = vm.geodata[k];
                        let geo = {
                            geom: curr.geom,
                            props: vm.getProperties(curr)
                        };
                        vm.geojson.push(geo);
                    }
                    vm.dataInitialized = true;
                }));
            },
            getProperties(geodata) {
                return {
                    id: geodata.id,
                    entity: geodata.entity
                };
            },
            deleteFeatures(features, wkt) {
                features.forEach(f => {
                    $httpQueue.add(() => $http.delete(`/map/${f.getProperties().id}`).then(response => {

                    }));
                });
            },
            addFeature(feature, wkt) {
                const collection = this.geoJsonFormat.writeFeatures([feature]);
                const srid = 3857;
                const data = {
                    collection: collection,
                    srid: srid
                };
                return $http.post('/map', data).then(response => {
                    if(response.data.length) {
                        const geodata = response.data[0];
                        // TODO update feature
                        feature.setProperties(this.getProperties(geodata));
                        return feature;
                    } else {
                        return;
                    }
                });
            },
            updateFeatures(features, wkt) {
                const vm = this;
                features.forEach(f => {
                    let data = {
                        feature: vm.geoJsonFormat.writeFeature(f),
                        srid: 3857
                    };
                    vm.$http.patch(`/map/${f.getProperties().id}`, data).then(function(response) {

                    });
                });
            },
            handleFeatureSelection(e) {
                const p = e.properties;
                if(p.entity) {
                    this.$router.push({
                        append: true,
                        name: 'entitydetail',
                        params: {
                            id: p.entity.id
                        },
                        query: this.$route.query
                    });
                }
            }
        },
        data() {
            return {
                dataInitialized: false,
                epsg: {},
                layers:{},
                geodata: {},
                geojson: [],
                geoJsonFormat: new GeoJSON()
            }
        }
    }
</script>


asset/currency on the same row

asset/currency on the same row

<template lang='jade'>
div
  .mx1
    label(for='exchange').wrapper Exchange:
    .custom-select.button
      select(v-model='exchange')
        option(v-for='(market, e) in exchanges') {{ e }}
  .grd-row
    .grd-row-col-3-6.mx1
      label(for='currency') Currency:
      .custom-select.button
        select(v-model='currency')
          option(v-for='cur in currencies') {{ cur }}
    .grd-row-col-3-6.mx1
      label(for='asset') Asset:
      .custom-select.button
        select(v-model='asset')
          option(v-for='asst in assets') {{ asst }}
</template>

<script>

import _ from 'lodash'
import markets from './markets'
import rangePicker from './rangepicker.vue'
import rangeCreator from './rangecreator.vue'

export default {
  props: ['has'],
  data: () => {
    return {
      exchanges: markets,

      // defaults:
      exchange: 'poloniex',
      currency: 'BTC',
      asset: 'USDT',
    };
  },

  created: function() {
    this.emitConfig();
  },
  computed: {
    markets: function() {
      return this.exchanges[ this.exchange ];
    },

    assets: function() {
      return markets[this.exchange][this.currency];
    },

    currencies: function() {
      return _.keys( markets[this.exchange] );
    },
    watchConfig: function() {
      return {
        watch: {
          exchange: this.exchange,
          currency: this.currency,
          currencies: this.currencies,
          asset: this.asset,
          assets: this.assets,
        }
      }
    }
  },

  watch: {
    currency: function() { this.emitConfig() },
    asset: function() { this.emitConfig() },
    market: function() { this.emitConfig() }
  },

  methods: {
    emitConfig: function() {
      this.$emit('market', this.watchConfig);
    }
  }
}
</script>
</style>

<template lang='jade'>
div
  div
    label(for='exchange').wrapper Exchange:
    .custom-select.button
      select(v-model='exchange')
        option(v-for='(market, e) in exchanges') {{ e }}
  div
    label(for='currency') Currency:
    .custom-select.button
      select(v-model='currency')
        option(v-for='cur in currencies') {{ cur }}

  div
    label(for='asset') Asset:
    .custom-select.button
      select(v-model='asset')
        option(v-for='asst in assets') {{ asst }}

</template>

<script>

import _ from 'lodash'
import markets from './markets'
import rangePicker from './rangepicker.vue'
import rangeCreator from './rangecreator.vue'

export default {
  props: ['has'],
  data: () => {
    return {
      exchanges: markets,

      // defaults:
      exchange: 'poloniex',
      currency: 'BTC',
      asset: 'USDT',
    };
  },

  created: function() {
    this.emitConfig();
  },
  computed: {
    markets: function() {
      return this.exchanges[ this.exchange ];
    },

    assets: function() {
      return markets[this.exchange][this.currency];
    },

    currencies: function() {
      return _.keys( markets[this.exchange] );
    },
    watchConfig: function() {
      return {
        watch: {
          exchange: this.exchange,
          currency: this.currency,
          currencies: this.currencies,
          asset: this.asset,
          assets: this.assets,
        }
      }
    }
  },

  watch: {
    currency: function() { this.emitConfig() },
    asset: function() { this.emitConfig() },
    market: function() { this.emitConfig() }
  },

  methods: {
    emitConfig: function() {
      this.$emit('market', this.watchConfig);
    }
  }
}
</script>
</style>


Add top margin to submit buttons on request page

<template>

    <request-form-breadcrumbs
      :i18n="i18n"
      :multiRequest="multiRequest"
      :hasPublicBodies="hasPublicBodies"
      :hidePublicbodyChooser="hidePublicbodyChooser"></request-form-breadcrumbs>

      <slot name="messages"></slot>

          <fieldset v-if="stepSelectPublicBody" id="step-publicbody" class="mt-5">

              <publicbody-multi-chooser
                name="publicbody"
                :defaultsearch="publicBodySearch"
                :scope="pbScope"
                :config="config">
                <template slot="publicbody-missing">
                  <slot name="publicbody-missing"></slot>
                </template>
              </publicbody-multi-chooser>

                  <slot name="publicbody-legend-title"></slot>
                  <slot name="publicbody-help-text"></slot>

                  <publicbody-chooser
                    name="publicbody"
                    :defaultsearch="publicBodySearch"
                    :scope="pbScope"
                    :config="config"
                    :list-view="publicBodyListView"></publicbody-chooser>

                  <slot name="publicbody-missing"></slot>

          </fieldset>

          <fieldset v-if="stepReviewPublicBodies && !stepWriteRequest" id="step-review-publicbody" class="mt-5">
            <pb-multi-review
              name="publicbody"
              :i18n="i18n"
              :scope="pbScope"
            >
            </pb-multi-review>
          </fieldset>

          <fieldset v-if="stepWriteRequest" id="step-request" class="mt-3">

            <request-form
              :config="config"
              :publicbodies="publicBodies"
              :user="user.id ? user : null"
              :request-form="requestForm"
              :user-form="userForm"
              :hide-publicbody-chooser="hidePublicbodyChooser"
              :hide-full-text="hideFullText"
              :hide-editing="hideEditing"
              :multi-request="multiRequest"
              :default-law="defaultLaw"
              :law-type="lawType"
              :initial-subject.sync="subject"
              :initial-body.sync="body"
              :initial-full-text.sync="fullText"
              :initial-first-name.sync="firstName"
              :initial-last-name.sync="lastName"
              @setStepSelectPublicBody="setStepSelectPublicBody"
            >
              <template slot="request-hints">
                <slot name="request-hints"></slot>
              </template>
              <template slot="request-legend-title">
                <slot name="request-legend-title"></slot>
              </template>
            </request-form>

            <user-registration
              :form="userForm"
              :config="config"
              :user="user.id ? user : null"
              :default-law="defaultLaw"
              :initial-email.sync="email"
              :initial-address.sync="address"
              :initial-private.sync="userPrivate"
            ></user-registration>

            <request-public
              :form="requestForm"
              :hide-public="hidePublic"
            ></request-public>

            <user-terms v-if="!user.id"
              :form="userForm"
            ></user-terms>

          </fieldset>

          <similar-requests v-if="showSimilar && stepWriteRequest"
            :publicbodies="publicBodies"
            :subject="subject"
            :config="config"
          ></similar-requests>

          <review-request
            v-if="stepWriteRequest && showReview"
            :i18n="i18n"
            :publicbodies="publicBodies"
            :user="user"
            :defaultLaw="defaultLaw"
            :subject="subject"
            :body="body"
            :fullText="fullText"
            @close="showReview = false"
          ></review-request>

          <button v-if="stepWriteRequest && shouldCheckRequest" type="button" id="review-button" class="btn btn-primary mt-3" @click="showReview = true">

            {{ i18n.reviewRequest }}
          </button>
          <button v-else-if="stepWriteRequest" type="submit" id="send-request-button" class="btn btn-primary mt-3">

            {{ i18n.submitRequest }}
          </button>
          <button v-if="stepWriteRequest && user.id && showDraft" type="submit" class="btn btn-secondary mt-3" name="save_draft" value="true">

            {{ i18n.saveAsDraft }}
          </button>

</template>

<template>

    <request-form-breadcrumbs
      :i18n="i18n"
      :multiRequest="multiRequest"
      :hasPublicBodies="hasPublicBodies"
      :hidePublicbodyChooser="hidePublicbodyChooser"></request-form-breadcrumbs>

      <slot name="messages"></slot>

          <fieldset v-if="stepSelectPublicBody" id="step-publicbody" class="mt-5">

              <publicbody-multi-chooser
                name="publicbody"
                :defaultsearch="publicBodySearch"
                :scope="pbScope"
                :config="config">
                <template slot="publicbody-missing">
                  <slot name="publicbody-missing"></slot>
                </template>
              </publicbody-multi-chooser>

                  <slot name="publicbody-legend-title"></slot>
                  <slot name="publicbody-help-text"></slot>

                  <publicbody-chooser
                    name="publicbody"
                    :defaultsearch="publicBodySearch"
                    :scope="pbScope"
                    :config="config"
                    :list-view="publicBodyListView"></publicbody-chooser>

                  <slot name="publicbody-missing"></slot>

          </fieldset>

          <fieldset v-if="stepReviewPublicBodies && !stepWriteRequest" id="step-review-publicbody" class="mt-5">
            <pb-multi-review
              name="publicbody"
              :i18n="i18n"
              :scope="pbScope"
            >
            </pb-multi-review>
          </fieldset>

          <fieldset v-if="stepWriteRequest" id="step-request" class="mt-3">

            <request-form
              :config="config"
              :publicbodies="publicBodies"
              :user="user.id ? user : null"
              :request-form="requestForm"
              :user-form="userForm"
              :hide-publicbody-chooser="hidePublicbodyChooser"
              :hide-full-text="hideFullText"
              :hide-editing="hideEditing"
              :multi-request="multiRequest"
              :default-law="defaultLaw"
              :law-type="lawType"
              :initial-subject.sync="subject"
              :initial-body.sync="body"
              :initial-full-text.sync="fullText"
              :initial-first-name.sync="firstName"
              :initial-last-name.sync="lastName"
              @setStepSelectPublicBody="setStepSelectPublicBody"
            >
              <template slot="request-hints">
                <slot name="request-hints"></slot>
              </template>
              <template slot="request-legend-title">
                <slot name="request-legend-title"></slot>
              </template>
            </request-form>

            <user-registration
              :form="userForm"
              :config="config"
              :user="user.id ? user : null"
              :default-law="defaultLaw"
              :initial-email.sync="email"
              :initial-address.sync="address"
              :initial-private.sync="userPrivate"
            ></user-registration>

            <request-public
              :form="requestForm"
              :hide-public="hidePublic"
            ></request-public>

            <user-terms v-if="!user.id"
              :form="userForm"
            ></user-terms>

          </fieldset>

          <similar-requests v-if="showSimilar && stepWriteRequest"
            :publicbodies="publicBodies"
            :subject="subject"
            :config="config"
          ></similar-requests>

          <review-request
            v-if="stepWriteRequest && showReview"
            :i18n="i18n"
            :publicbodies="publicBodies"
            :user="user"
            :defaultLaw="defaultLaw"
            :subject="subject"
            :body="body"
            :fullText="fullText"
            @close="showReview = false"
          ></review-request>

          <button v-if="stepWriteRequest && shouldCheckRequest" type="button" id="review-button" class="btn btn-primary" @click="showReview = true">

            {{ i18n.reviewRequest }}
          </button>
          <button v-else-if="stepWriteRequest" type="submit" id="send-request-button" class="btn btn-primary">

            {{ i18n.submitRequest }}
          </button>
          <button v-if="stepWriteRequest && user.id && showDraft" type="submit" class="btn btn-secondary" name="save_draft" value="true">

            {{ i18n.saveAsDraft }}
          </button>

</template>


Update media-banner.vue (#4287)

Update media-banner.vue (#4287)

<template>

		<fa icon="exclamation-triangle"/>
		{{ $t('sensitive') }}
		{{ $t('click-to-show') }}

		<audio class="audio"
			:src="media.url"
			:title="media.name"
			controls
			ref="audio"
			@volumechange="volumechange"
			preload="metadata" />

		<fa icon="download"/>
		{{ media.name }}

</template>


fix: how it works tokens

fix: how it works tokens

<template>

    <v-container>
      <v-row>
        <v-col cols="12" lg="7">
          <v-img
            src="@/assets/images/snippets/my-tokens-table.png"
            class="mb-n7"
          />
        </v-col>
        <v-col cols="12" lg="5" class="text-content pl-lg-12 mt-12 mt-lg-0">
          <app-block-title
            no-page-title
            :data="titleData"
            class="mb-12 pl-12"
          />

              <v-img src="@/assets/images/icons/icon-buy-token-white.svg" />

                Buy Tokens

                Swap for ERC20 tokens and stablecoins through our decentralized
                providers.

              <v-img src="@/assets/images/currencies/icon-eth-white.svg" />

                Custom Tokens

                MEW automatically supports most ERC20 tokens through our API,
                but if you don’t see the token you’re looking for, you can add
                it as a custom token right into the interface!

        </v-col>
      </v-row>
    </v-container>

</template>

<template>

    <v-container>
      <v-row>
        <v-col cols="12" lg="7">
          <v-img
            src="@/assets/images/snippets/my-tokens-table.png"
            class="mb-n7"
          />
        </v-col>
        <v-col cols="12" lg="5" class="text-content pl-lg-12 mt-12 mt-lg-0">
          <app-block-title
            no-page-title
            :data="titleData"
            class="mb-12 pl-12"
          />

              <v-img src="@/assets/images/icons/icon-buy-token-white.svg" />

                Buy Tokens

                Dolor tempor ipsum lorem sadipscing, erat sadipscing et dolor
                sed stet takimata ipsum invidunt, est ea no dolor.

              <v-img src="@/assets/images/currencies/icon-eth-white.svg" />

                Add My Tokens

                Dolor tempor ipsum lorem sadipscing, erat sadipscing et dolor
                sed stet takimata ipsum invidunt, est ea no dolor.

                Watch tutorial

        </v-col>
      </v-row>
    </v-container>

</template>


样式：修改时事日历中table的下拉单选文字颜色，固定左半部分宽度

样式：修改时事日历中table的下拉单选文字颜色，固定左半部分宽度

<template>

          用户概览

JOHN DOE

1234666688

邀请码：8888888886

邀请链接：https://www.douban.com/group/topic/97123912

持有MT账号

2个

持有MT账号

4个

持有MT账号

3个

            资产概况

          <pie-echart-comp ref="positionStructureComp" :pieOption="positionsStructureOption"></pie-echart-comp>

总资产

$888,888,888

$123,123,212

$453,465

$548,343

$12,213

              交易概况

总开仓数：12

$999,999.00

总平仓数：13

$999,999.00

出金总数：$6,545.00

出金

入金总数：$6,545.00

入金

            持仓概况

            占用保证金：$222,222.00
            跟单手数：$22,222.00

浮动盈亏：$88,888.00

          <trend-line-echart-comp ref="chartCompWrap" :positionsOptions="positionsOptions"></trend-line-echart-comp>

              我的佣金

推广赠金

$888,788.00

分销赠金

$88,788.00

              最新活动

            <swiper :options="activitySwiperOption">
              <swiper-slide></swiper-slide>
              <swiper-slide></swiper-slide>
              <swiper-slide></swiper-slide>
              <swiper-slide></swiper-slide>
              <swiper-slide></swiper-slide>

            </swiper>

          时事日历

          <calendar-comp :date="dateToSet" :newsTableData="newsTableData"></calendar-comp>

          <el-table id="news-table" :data="newsTableData" style="width: 100%" max-height="320" header-align="center" :row-class-name="tableRowClassName">
            <el-table-column prop="time" label="时间" min-width="75" width="115">
              <template scope="scope">
                {{ formatDate(scope.row.time) }}
              </template>
            </el-table-column>
            <el-table-column prop="country" label="国家/地区" min-width="96">
              <template scope="scope">

              </template>
            </el-table-column>
            <el-table-column prop="theme" label="指标名称" min-width="160">
              <template scope="scope">
                {{scope.row.theme}}
              </template>
            </el-table-column>
            <el-table-column prop="level" label="重要程度" width="170">
              <template scope="scope">
                <el-rate v-model="scope.row.level" disabled text-color="#ff9900"></el-rate>
              </template>
            </el-table-column>
            <el-table-column prop="influence" label="影响" width="110" :render-header="newsTablerenderHeader">
              <template scope="scope">

 0 ? 'up' : 'down'">{{scope.row.status > 0 ? '↑' : '↓'}} {{ scope.row.influence }} {{influenceStatus}}

              </template>
            </el-table-column>
            <el-table-column label="分享" width="74">
              <template scope="scope">

              </template>
            </el-table-column>
          </el-table>

          交易行情

            添加行情

        <dealer-detail-comp v-for="(item, index) in dealerDetailArr" ref="dealerDetailComp" :key="index" :compOption="item" :dealerDetailOption="dealerDetailOption"></dealer-detail-comp>

</template>


Remove copy-pasta code from origin component

Signed-off-by: Joas Schilling <ab43a7c9cb5b2380afc4ddf8b3e2583169b39a02@schilljs.com>

<template>

			{{ t('spreed', 'Locking the conversation prevents anyone to post messages or start calls.') }}

			<input id="moderation_settings_lock_conversation_checkbox"
				aria-describedby="moderation_settings_lock_conversation_hint"
				type="checkbox"
				class="checkbox"
				name="moderation_settings_lock_conversation_checkbox"
				:checked="isReadOnly"
				:disabled="isReadOnlyStateLoading"
				@change="toggleReadOnly">
			{{ t('spreed', 'Lock conversation') }}

</template>


Close popup after bookmark

Close popup after bookmark


Update style

Update style

<template>
  <uw-card
    v-if="showCard"
    :loaded="true"
  >
    <template #card-heading>

        Professional and Continuing Education (PCE)

    </template>
    <template #card-body>

        Whether you want to update your skills or delve into a new field,
        UW Professional & Continuing Education can help.

About PCE

        Available programs

Career Accelerator Certificate Programs

Certificate Programs

Degree Programs

Courses

    </template>
  </uw-card>
</template>


Fix worm import path in vue project

Fix worm import path in vue project

<template>

{{ projectName }}

</template>


KIcon Html5AppRendererIndex (html5_viewer)

<template>

  <CoreFullscreen
    ref="html5Renderer"
    class="html5-renderer"
    @changeFullscreen="isInFullscreen = $event"
  >

    <div
      class="fullscreen-header"
      :style="{ backgroundColor: this.$themePalette.grey.v_100 }"
    >
    <UiIconButton>
      <KIcon
        v-if="isInFullscreen"
        :color="$themeTokens.textInverted"
        icon="fullscreen_exit"
        style="top: 0; width: 24px; height: 24px;"
      />
      <KIcon
        v-else
        :color="$themeTokens.textInverted"
        icon="fullscreen"
        style="top: 0; width: 24px; height: 24px;"
      />
    </UiIconButton>
    </div>
    <div class="iframe-container">
      <iframe
        ref="iframe"
        class="iframe"
        :style="{ backgroundColor: $themePalette.grey.v_100 }"
        :sandbox="sandbox"
        frameBorder="0"
        :name="name"
        :src="rooturl"
      >
      </iframe>
    </div>
  </CoreFullscreen>

</template>

<script>

  import { now } from 'kolibri.utils.serverClock';
  import CoreFullscreen from 'kolibri.coreVue.components.CoreFullscreen';
  import Hashi from 'hashi';
  import { nameSpace } from 'hashi/src/hashiBase';
  import plugin_data from 'plugin_data';

  // Regex vendored from https://github.com/faisalman/ua-parser-js/blob/master/src/ua-parser.js
  const iOSTest = /ip[honead]{2,4}(?:.*os\s([\w]+)\slike\smac|;\sopera)/i;
  const IE11Test = /(trident).+rv[:\s]([\w.]+).+like\sgecko/i;

  export default {
    name: 'Html5AppRendererIndex',
    components: {
      CoreFullscreen,
    },
    data() {
      return {
        isInFullscreen: false,
      };
    },
    computed: {
      name() {
        return nameSpace;
      },
      rooturl() {
        const iOS = iOSTest.test(navigator.userAgent);
        const iOSorIE11 = iOS || IE11Test.test(navigator.userAgent);
        // Skip hashi on requests for these browsers
        return this.defaultFile.storage_url + (iOSorIE11 ? '?SKIP_HASHI=true' : '');
      },
      sandbox() {
        return plugin_data.html5_sandbox_tokens;
      },
      fullscreenText() {
        return this.isInFullscreen ? this.$tr('exitFullscreen') : this.$tr('enterFullscreen');
      },
      userData() {
        return {
          userId: this.userId,
          userFullName: this.userFullName,
          progress: this.progress,
          complete: this.progress >= 1,
          language: this.lang.id,
          timeSpent: this.timeSpent,
        };
      },
      containerStyle() {
        if (this.isInFullscreen) {
          return {
            position: 'absolute',
            top: '50px',
            bottom: 0,
          };
        }
        return { height: '560px' };
      },
    },
    watch: {
      userData(newValue) {
        if (newValue && this.hashi) {
          this.hashi.updateData({ userData: newValue });
        }
      },
    },
    mounted() {
      this.hashi = new Hashi({ iframe: this.$refs.iframe, now });
      this.hashi.onStateUpdate(data => {
        this.$emit('updateContentState', data);
      });
      this.hashi.initialize(
        (this.extraFields && this.extraFields.contentState) || {},
        this.userData
      );
      this.$emit('startTracking');
      this.startTime = now();
      this.pollProgress();
    },
    beforeDestroy() {
      if (this.timeout) {
        clearTimeout(this.timeout);
      }
      this.$emit('stopTracking');
    },
    methods: {
      recordProgress() {
        const totalTime = now() - this.startTime;
        const hashiProgress = this.hashi ? this.hashi.getProgress() : null;
        this.$emit(
          'updateProgress',
          hashiProgress === null ? Math.max(0, totalTime / 3000000) : hashiProgress
        );
        this.pollProgress();
      },
      pollProgress() {
        this.timeout = setTimeout(() => {
          this.recordProgress();
        }, 15000);
      },
    },
    $trs: {
      exitFullscreen: 'Exit Fullscreen',
      enterFullscreen: 'View Fullscreen',
    },
  };

</script>

<style lang="scss" scoped>

  @import '~kolibri-design-system/lib/styles/definitions';

  .fullscreen-header {
    text-align: right;
  }

  .fs-icon {
    position: relative;
    top: 6px;
  }

  .html5-renderer {
    position: relative;
    text-align: center;
  }

  .iframe {
    width: 100%;
    height: 100%;
  }

  .iframe-container {
    @extend %momentum-scroll;

    width: 100%;
    overflow: visible;
  }

</style>

<template>

  <CoreFullscreen
    ref="html5Renderer"
    class="html5-renderer"
    @changeFullscreen="isInFullscreen = $event"
  >

    <div
      class="fullscreen-header"
      :style="{ backgroundColor: this.$themePalette.grey.v_100 }"
    >
      <KButton
        :primary="false"
        appearance="flat-button"
        @click="$refs.html5Renderer.toggleFullscreen()"
      >
        <mat-svg
          v-if="isInFullscreen"
          name="fullscreen_exit"
          category="navigation"
          class="fs-icon"
        />
        <mat-svg
          v-else
          name="fullscreen"
          category="navigation"
          class="fs-icon"
        />
        {{ fullscreenText }}
      </KButton>
    </div>
    <div class="iframe-container" :style="containerStyle">
      <iframe
        ref="iframe"
        class="iframe"
        :style="{ backgroundColor: $themePalette.grey.v_100 }"
        :sandbox="sandbox"
        frameBorder="0"
        :name="name"
        :src="rooturl"
      >
      </iframe>
    </div>
  </CoreFullscreen>

</template>

<script>

  import { now } from 'kolibri.utils.serverClock';
  import CoreFullscreen from 'kolibri.coreVue.components.CoreFullscreen';
  import Hashi from 'hashi';
  import { nameSpace } from 'hashi/src/hashiBase';
  import plugin_data from 'plugin_data';

  // Regex vendored from https://github.com/faisalman/ua-parser-js/blob/master/src/ua-parser.js
  const iOSTest = /ip[honead]{2,4}(?:.*os\s([\w]+)\slike\smac|;\sopera)/i;
  const IE11Test = /(trident).+rv[:\s]([\w.]+).+like\sgecko/i;

  export default {
    name: 'Html5AppRendererIndex',
    components: {
      CoreFullscreen,
    },
    data() {
      return {
        isInFullscreen: false,
      };
    },
    computed: {
      name() {
        return nameSpace;
      },
      rooturl() {
        const iOS = iOSTest.test(navigator.userAgent);
        const iOSorIE11 = iOS || IE11Test.test(navigator.userAgent);
        // Skip hashi on requests for these browsers
        return this.defaultFile.storage_url + (iOSorIE11 ? '?SKIP_HASHI=true' : '');
      },
      sandbox() {
        return plugin_data.html5_sandbox_tokens;
      },
      fullscreenText() {
        return this.isInFullscreen ? this.$tr('exitFullscreen') : this.$tr('enterFullscreen');
      },
      userData() {
        return {
          userId: this.userId,
          userFullName: this.userFullName,
          progress: this.progress,
          complete: this.progress >= 1,
          language: this.lang.id,
          timeSpent: this.timeSpent,
        };
      },
      containerStyle() {
        if (this.isInFullscreen) {
          return {
            position: 'absolute',
            top: '50px',
            bottom: 0,
          };
        }
        return { height: '560px' };
      },
    },
    watch: {
      userData(newValue) {
        if (newValue && this.hashi) {
          this.hashi.updateData({ userData: newValue });
        }
      },
    },
    mounted() {
      this.hashi = new Hashi({ iframe: this.$refs.iframe, now });
      this.hashi.onStateUpdate(data => {
        this.$emit('updateContentState', data);
      });
      this.hashi.initialize(
        (this.extraFields && this.extraFields.contentState) || {},
        this.userData
      );
      this.$emit('startTracking');
      this.startTime = now();
      this.pollProgress();
    },
    beforeDestroy() {
      if (this.timeout) {
        clearTimeout(this.timeout);
      }
      this.$emit('stopTracking');
    },
    methods: {
      recordProgress() {
        const totalTime = now() - this.startTime;
        const hashiProgress = this.hashi ? this.hashi.getProgress() : null;
        this.$emit(
          'updateProgress',
          hashiProgress === null ? Math.max(0, totalTime / 3000000) : hashiProgress
        );
        this.pollProgress();
      },
      pollProgress() {
        this.timeout = setTimeout(() => {
          this.recordProgress();
        }, 15000);
      },
    },
    $trs: {
      exitFullscreen: 'Exit Fullscreen',
      enterFullscreen: 'View Fullscreen',
    },
  };

</script>

<style lang="scss" scoped>

  @import '~kolibri-design-system/lib/styles/definitions';

  .fullscreen-header {
    text-align: right;
  }

  .fs-icon {
    position: relative;
    top: 6px;
  }

  .html5-renderer {
    position: relative;
    text-align: center;
  }

  .iframe {
    width: 100%;
    height: 100%;
  }

  .iframe-container {
    @extend %momentum-scroll;

    width: 100%;
    overflow: visible;
  }

</style>


docs(Home/MadeWithVuetify): improve ux

docs(Home/MadeWithVuetify): improve ux

<template>
  <v-card
    v-if="featured.length > 0"
    class="text-xs-center py-4"
    flat
  >
    <h4
      class="font-weight-medium grey--text"
      v-text="$t('Vuetify.Home.madeWithVuetify')"
    />

    <v-container
      grid-list-xl
      mb-3
    >
      <v-layout
        align-center
        fill-height
        justify-center
        wrap
      >
        <v-flex
          v-for="(feature, i) in computedFeatured"
          :key="i"
          xs12
          sm6
          md4
        >
          <v-hover>
            <template v-slot:default="{ hover }">
              <v-card
                elevation="24"
                @click="$ga.event('home', 'click', 'mwvjs', feature.title)"
              >
                <v-img
                  :alt="feature.title"
                  :src="feature.image"
                  height="300px"
                  width="100%"
                >
                  <v-fade-transition>
                    <v-overlay
                      v-show="hover"
                      absolute
                      opacity="0.9"
                    >
                      <h3
                        class="headline mb-2"
                        v-text="feature.title"
                      />
                      <div
                        class="overline grey--text mb-5 px-4"
                        v-text="feature.teaser"
                      />
                      <v-btn
                        :title="`Link to ${feature.title}`"
                        :href="`${feature.url}?ref=vuetifyjs.com`"
                        color="success"
                        fab
                        large
                        target="_blank"
                        rel="noopener"
                      >
                        <v-icon>mdi-open-in-new</v-icon>
                      </v-btn>
                    </v-overlay>
                  </v-fade-transition>
                </v-img>
              </v-card>
            </template>
          </v-hover>
        </v-flex>
      </v-layout>
    </v-container>

    <div class="text-xs-center">
      <a
        href="https://madewithvuejs.com/vuetify?ref=vuetifyjs.com"
        target="_blank"
        rel="noopener"
        @click="$ga.event('home', 'click', ' mwvjs')"
      >
        <v-img
          alt="Powered by madewithvuejs.com"
          contain
          src="https://cdn.vuetifyjs.com/images/home/powered-by-madewithvue-1.svg"
          height="65px"
        />
      </a>
    </div>
  </v-card>
</template>

<script>
  export default {
    data: () => ({
      featured: []
    }),

    computed: {
      computedFeatured () {
        return this.featured.slice(0, 6)
      }
    },

    // TODO: Remove when v-img
    // supports lazy loading
    mounted () {
      this.$nextTick(this.init)
    },

    methods: {
      init () {
        fetch('https://madewithvuejs.com/api/tag/vuetify', {
          method: 'get',
          headers: {
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/json'
          }
        })
          .then(res => res.json())
          .then(res => {
            this.featured = this.setFeatured(res)
          })
          .catch(err => console.log(err))
      },
      setFeatured (data) {
        if (!data) return []
        const featured = data.data.map(f => {
          f.hover = false
          return f
        })
        return this.shuffle(featured)
      },
      shuffle (array) {
        let currentIndex = array.length
        let temporaryValue
        let randomIndex
        // While there remain elements to shuffle...
        while (currentIndex !== 0) {
          // Pick a remaining element...
          randomIndex = Math.floor(Math.random() * currentIndex)
          currentIndex -= 1
          // And swap it with the current element.
          temporaryValue = array[currentIndex]
          array[currentIndex] = array[randomIndex]
          array[randomIndex] = temporaryValue
        }
        return array
      }
    }
  }
</script>

<template>
  <v-card
    v-if="featured.length > 0"
    class="text-xs-center py-4"
    flat
  >
    <h4
      class="font-weight-medium grey--text"
      v-text="$t('Vuetify.Home.madeWithVuetify')"
    />

    <v-container grid-list-xl mb-3>
      <v-layout
        align-center
        fill-height
        justify-center
        wrap
      >
        <v-flex
          v-for="(feature, i) in computedFeatured"
          :key="i"
          xs12
          sm6
          md4
        >
          <v-card
            :href="`${feature.url}?ref=vuetifyjs.com`"
            elevation="24"
            target="_blank"
            rel="noopener"
            @click="$ga.event('home', 'click', 'mwvjs', feature.title)"
          >
            <v-img
              :alt="feature.title"
              :src="feature.image"
              height="300px"
              width="100%"
            />
          </v-card>
        </v-flex>
      </v-layout>
    </v-container>

    <div class="text-xs-center">
      <a
        href="https://madewithvuejs.com/vuetify?ref=vuetifyjs.com"
        target="_blank"
        rel="noopener"
        @click="$ga.event('home', 'click', ' mwvjs')"
      >
        <v-img
          alt="Powered by madewithvuejs.com"
          contain
          src="https://cdn.vuetifyjs.com/images/home/powered-by-madewithvue-1.svg"
          height="65px"
        />
      </a>
    </div>
  </v-card>
</template>

<script>
  export default {
    data: () => ({
      featured: []
    }),

    computed: {
      computedFeatured () {
        return this.featured.slice(0, 6)
      },
      features () {
        return this.$t('Vuetify.Home.features', this.$i18n.fallbackLocale)
      }
    },

    // TODO: Remove when v-img
    // supports lazy loading
    mounted () {
      this.$nextTick(this.init)
    },

    methods: {
      init () {
        fetch('https://madewithvuejs.com/api/tag/vuetify', {
          method: 'get',
          headers: {
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/json'
          }
        })
          .then(res => res.json())
          .then(res => {
            this.featured = this.setFeatured(res)
          })
          .catch(err => console.log(err))
      },
      setFeatured (data) {
        if (!data) return []
        const featured = data.data.map(f => {
          f.hover = false
          return f
        })
        return this.shuffle(featured)
      },
      shuffle (array) {
        let currentIndex = array.length
        let temporaryValue
        let randomIndex
        // While there remain elements to shuffle...
        while (currentIndex !== 0) {
          // Pick a remaining element...
          randomIndex = Math.floor(Math.random() * currentIndex)
          currentIndex -= 1
          // And swap it with the current element.
          temporaryValue = array[currentIndex]
          array[currentIndex] = array[randomIndex]
          array[randomIndex] = temporaryValue
        }
        return array
      }
    }
  }
</script>


Added toggle mute button to VideoPlayer.

<template>

      <spinner class="spinner" />

      <canvas
        id="annotation-canvas"
        ref="annotation-canvas"
        class="canvas"
        v-if="!readOnly"
      >
      </canvas>

    <video
      id="annotation-movie"
      ref="movie"
      class="annotation-movie"
      preload="auto"
      :style="{
        display: isLoading ? 'none' : 'block'
      }"
      :src="moviePath"
      :poster="posterPath"
    >
    </video>
    <video
      id="comparison-movie"
      ref="comparison-movie"
      class="annotation-movie"
      preload="auto"
      :src="comparisonMoviePath"
      :poster="comparisonPosterPath"
      v-if="isComparing && previewToCompareId"
    >
    </video>

      <progress
        ref="progress"
        value="0"
        min="0"
        @click="onProgressClicked"
      >

      </progress>

    <annotation-bar
      :annotations="annotations"
      :maxDurationRaw="videoDuration"
      @select-annotation="loadAnnotation"
      ref="annotation-bar"
      v-if="!readOnly"
    />

        <button
          class="button flexrow-item"
          @click="onPlayPauseClicked"
        >
          <pause-icon class="icon" v-if="isPlaying" />
          <play-icon class="icon" v-if="!isPlaying" />
        </button>

        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isRepeating
          }"
          @click="onRepeatClicked"
          v-if="isFullScreen()"
        >
          <repeat-icon class="icon smaller" />
        </button>

        <button
          class="button flexrow-item"
          @click="onToggleSoundClicked">
          <volume-x-icon class="icon" v-if="isMuted" />
          <volume-2-icon class="icon" v-if="!isMuted" />
        </button>

          {{ currentTime }}

        /

         {{ maxDuration }}

          ({{ currentFrame }})

        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isComparing,
            'comparison-button': true
          }"
          @click="onCompareClicked"
          v-if="!readOnly && taskTypeOptions.length > 0 && (!light || isFullScreen())"
        >
          <copy-icon class="icon smaller" />
        </button>

        <combobox
          class="comparison-combobox"
          :options="taskTypeOptions"
          :is-dark="true"
          v-model="taskTypeId"
          v-if="!readOnly && isComparing && (!light || isFullScreen())"
        />
        <combobox
          class="comparison-combobox"
          :options="previewFileOptions"
          :is-dark="true"
          v-model="previewToCompareId"
          v-if="!readOnly && isComparing && (!light || isFullScreen())"
        />

          <span
            :class="{
              'previous-preview-file': true,
              'current-preview-file': previewFile.revision === preview.revision
            }"
            :key="`last-preview-${previewFile.id}`"
            @click="changeCurrentPreview(previewFile)"
            v-for="previewFile in lastPreviewFiles"
          >
            {{ previewFile.revision }}

        <button
          class="button flexrow-item"
          @click="undoLastAction"
          v-if="!readOnly && isFullScreen()"
        >
          <corner-left-down-icon class="icon" />
        </button>

        <button
          class="button flexrow-item"
          @click="redoLastAction"
          v-if="!readOnly && isFullScreen()"
        >
          <corner-right-down-icon class="icon" />
        </button>

        <button
          class="button flexrow-item"
          @click="onDeleteClicked"
          v-if="!readOnly && isFullScreenEnabled"
        >
          <x-icon class="icon" />
        </button>

        <transition name="slide">

            <color-picker
              :isOpen="isShowingPalette"
              :color="this.textColor"
              :palette="this.palette"
              @TogglePalette="onPickColor"
              @change="onChangeTextColor"
            />

        </transition>
        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isTyping
          }"
          :title="$t('playlists.actions.annotation_text')"
          @click="onTypeClicked"
          v-if="!readOnly && isFullScreen()"
        >
          <type-icon class="icon" />
        </button>

        <transition name="slide">

            <pencil-picker
              :isOpen="isShowingPencilPalette"
              :pencil="pencil"
              :sizes="this.pencilPalette"
              @toggle-palette="onPickPencil"
              @change="onChangePencil"
            />

            <color-picker
              :isOpen="isShowingPalette"
              :color="this.color"
              :palette="this.palette"
              @TogglePalette="onPickColor"
              @change="onChangeColor"
            />

        </transition>

        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isDrawing
          }"
          @click="onPencilAnnotateClicked"
          v-if="!readOnly && isFullScreenEnabled"
        >
          <edit-2-icon class="icon" />
        </button>

          <download-icon class="icon" />

        <button
          class="button flexrow-item"
          @click="onFullscreenClicked"
          v-if="isFullScreenEnabled"
        >
          <maximize-icon class="icon" />
        </button>

</template>

<template>

      <spinner class="spinner" />

      <canvas
        id="annotation-canvas"
        ref="annotation-canvas"
        class="canvas"
        v-if="!readOnly"
      >
      </canvas>

    <video
      id="annotation-movie"
      ref="movie"
      class="annotation-movie"
      preload="auto"
      :style="{
        display: isLoading ? 'none' : 'block'
      }"
      :src="moviePath"
      :poster="posterPath"
    >
    </video>
    <video
      id="comparison-movie"
      ref="comparison-movie"
      class="annotation-movie"
      preload="auto"
      :src="comparisonMoviePath"
      :poster="comparisonPosterPath"
      v-if="isComparing && previewToCompareId"
    >
    </video>

      <progress
        ref="progress"
        value="0"
        min="0"
        @click="onProgressClicked"
      >

      </progress>

    <annotation-bar
      :annotations="annotations"
      :maxDurationRaw="videoDuration"
      @select-annotation="loadAnnotation"
      ref="annotation-bar"
      v-if="!readOnly"
    />

        <button
          class="button flexrow-item"
          @click="onPlayPauseClicked"
        >
          <pause-icon class="icon" v-if="isPlaying" />
          <play-icon class="icon" v-if="!isPlaying" />
        </button>

        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isRepeating
          }"
          @click="onRepeatClicked"
          v-if="isFullScreen()"
        >
          <repeat-icon class="icon smaller" />
        </button>

          {{ currentTime }}

        /

         {{ maxDuration }}

          ({{ currentFrame }})

        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isComparing,
            'comparison-button': true
          }"
          @click="onCompareClicked"
          v-if="!readOnly && taskTypeOptions.length > 0 && (!light || isFullScreen())"
        >
          <copy-icon class="icon smaller" />
        </button>

        <combobox
          class="comparison-combobox"
          :options="taskTypeOptions"
          :is-dark="true"
          v-model="taskTypeId"
          v-if="!readOnly && isComparing && (!light || isFullScreen())"
        />
        <combobox
          class="comparison-combobox"
          :options="previewFileOptions"
          :is-dark="true"
          v-model="previewToCompareId"
          v-if="!readOnly && isComparing && (!light || isFullScreen())"
        />

          <span
            :class="{
              'previous-preview-file': true,
              'current-preview-file': previewFile.revision === preview.revision
            }"
            :key="`last-preview-${previewFile.id}`"
            @click="changeCurrentPreview(previewFile)"
            v-for="previewFile in lastPreviewFiles"
          >
            {{ previewFile.revision }}

        <button
          class="button flexrow-item"
          @click="undoLastAction"
          v-if="!readOnly && isFullScreen()"
        >
          <corner-left-down-icon class="icon" />
        </button>

        <button
          class="button flexrow-item"
          @click="redoLastAction"
          v-if="!readOnly && isFullScreen()"
        >
          <corner-right-down-icon class="icon" />
        </button>

        <button
          class="button flexrow-item"
          @click="onDeleteClicked"
          v-if="!readOnly && isFullScreenEnabled"
        >
          <x-icon class="icon" />
        </button>

        <transition name="slide">

            <color-picker
              :isOpen="isShowingPalette"
              :color="this.textColor"
              :palette="this.palette"
              @TogglePalette="onPickColor"
              @change="onChangeTextColor"
            />

        </transition>
        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isTyping
          }"
          :title="$t('playlists.actions.annotation_text')"
          @click="onTypeClicked"
          v-if="!readOnly && isFullScreen()"
        >
          <type-icon class="icon" />
        </button>

        <transition name="slide">

            <pencil-picker
              :isOpen="isShowingPencilPalette"
              :pencil="pencil"
              :sizes="this.pencilPalette"
              @toggle-palette="onPickPencil"
              @change="onChangePencil"
            />

            <color-picker
              :isOpen="isShowingPalette"
              :color="this.color"
              :palette="this.palette"
              @TogglePalette="onPickColor"
              @change="onChangeColor"
            />

        </transition>

        <button
          :class="{
            button: true,
            'flexrow-item': true,
            active: isDrawing
          }"
          @click="onPencilAnnotateClicked"
          v-if="!readOnly && isFullScreenEnabled"
        >
          <edit-2-icon class="icon" />
        </button>

          <download-icon class="icon" />

        <button
          class="button flexrow-item"
          @click="onFullscreenClicked"
          v-if="isFullScreenEnabled"
        >
          <maximize-icon class="icon" />
        </button>

</template>


docs(Footer): add privacy and cookie policy

<template>
  <v-footer
    id="footer"
    class="justify-center align-center py-4"
    color="grey darken-4"
    dark
    height="auto"
  >
    <v-container>
      <v-layout wrap>
        <v-flex xs12>

            <v-layout
              align-center
              column
              justify-center
            >
              <v-img
                contain
                height="64px"
                src="https://cdn.vuetifyjs.com/images/logos/v-alt.svg"
                width="64px"
              />
            </v-layout>

        </v-flex>
        <v-flex xs12>
          <v-layout justify-center mb-3>

              <v-icon
                dark
                v-text="social.icon"
              />

          </v-layout>
        </v-flex>
        <v-flex xs12>
          <v-layout
            column
            text-xs-center
          >

              Released under the

                MIT License

                Copyright © 2016-{{ (new Date()).getFullYear() }} Vuetify, LLC

                Privacy Policy

               •

                Cookie Policy

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

<template>
  <v-footer
    id="footer"
    class="justify-center align-center py-5"
    color="grey darken-4"
    dark
    height="auto"
  >
    <v-container>
      <v-layout wrap>
        <v-flex xs12>

            <v-layout
              align-center
              column
              justify-center
            >
              <v-img
                contain
                height="64px"
                src="https://cdn.vuetifyjs.com/images/logos/v-alt.svg"
                width="64px"
              />
            </v-layout>

        </v-flex>
        <v-flex xs12>
          <v-layout justify-center mb-3>

              <v-icon
                dark
                v-text="social.icon"
              />

          </v-layout>
        </v-flex>
        <v-flex xs12>
          <v-layout
            column
            text-xs-center
          >

              Released under the

                Copyright © 2016-{{ (new Date()).getFullYear() }} Vuetify, LLC

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


Fixed DropDown component

Fixed DropDown component

<template>

            {{name}}:
            <el-select v-model="selectedValue"
                       :multiple="multiple"
                       :size="size">
                <el-option
                        v-for="item in items" :key="item.Value"
                        :label="getColumnName(item.Title)"
                        :value="item.Value">
                </el-option>
            </el-select>

        <el-button type="text" icon="el-icon-close" :size="size" circle
                   @click="onClose"></el-button>

</template>


make all content icon centered and added in default thumbnail

<template>

    <grid-item v-link="link" :title="title">

        <content-icon
          class="content-icon-center"
          v-if="kind"
          :size="60"
          :kind="kind"
          :progress="progress">
        </content-icon>

    </grid-item>

</template>

<template>

    <grid-item v-link="link" :title="title">

        <content-icon
          :class='thumb ? "content-icon" : "content-icon-center" '
          v-if="kind"
          :size="thumb ? 30 : 60"
          :kind="kind"
          :progress="progress">
        </content-icon>

    </grid-item>

</template>


Styles thank you notice text.

Styles thank you notice text.

<template>
  <uw-card
    v-if="showThankYou(notices)"
    :loaded="isReady"
    :errored="isErrored"
    :errored-show="showError"
    class="myuw-thank-you"
  >
    <template #card-heading>

        Payment Received, Thank You

    </template>
    <template #card-body>

    </template>
  </uw-card>
</template>


XWIKI-18332: Live Data macro is overlapping Actions section in an application

<!--
  * See the NOTICE file distributed with this work for additional
  * information regarding copyright ownership.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->

<!--
  The LivedataAdvancedPanels component contains all the Livedata
  advanced panels, so that they can be included at once.
  It provides advance panels for configuration (sort, filter, ...)
  whatever layout is being used.
  It is included inside the livedata root component.

  Advanced panels can be opened from the DropdownMenu component.
-->
<template>
  <div class="livedata-advanced-panels">

    <!-- Properties Panel -->
    <LivedataAdvancedPanelProperties/>

    <!-- Sort Panel -->
    <LivedataAdvancedPanelSort/>

    <!-- Filter Panel -->
    <LivedataAdvancedPanelFilter/>

  </div>
</template>

<script>
import LivedataAdvancedPanelProperties from "./LivedataAdvancedPanelProperties.vue";
import LivedataAdvancedPanelSort from "./LivedataAdvancedPanelSort.vue";
import LivedataAdvancedPanelFilter from "./LivedataAdvancedPanelFilter.vue";

export default {

  name: "LivedataAdvancedPanels",

  components: {
    LivedataAdvancedPanelProperties,
    LivedataAdvancedPanelSort,
    LivedataAdvancedPanelFilter,
  },

  inject: ["logic"],

};
</script>

<style>
.livedata-advanced-panels {
  display: flex;
  flex-direction: column;
}
</style>

<!--
  * See the NOTICE file distributed with this work for additional
  * information regarding copyright ownership.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->

<!--
  The LivedataAdvancedPanels component contains all the Livedata
  advanced panels, so that they can be included at once.
  It provides advance panels for configuration (sort, filter, ...)
  whatever layout is being used.
  It is included inside the livedata root component.

  Advanced panels can be opened from the DropdownMenu component.
-->
<template>
  <div class="livedata-advanced-panels">

    <!-- Properties Panel -->
    <LivedataAdvancedPanelProperties/>

    <!-- Sort Panel -->
    <LivedataAdvancedPanelSort/>

    <!-- Filter Panel -->
    <LivedataAdvancedPanelFilter/>

  </div>
</template>

<script>
import LivedataAdvancedPanelProperties from "./LivedataAdvancedPanelProperties.vue";
import LivedataAdvancedPanelSort from "./LivedataAdvancedPanelSort.vue";
import LivedataAdvancedPanelFilter from "./LivedataAdvancedPanelFilter.vue";

export default {

  name: "LivedataAdvancedPanels",

  components: {
    LivedataAdvancedPanelProperties,
    LivedataAdvancedPanelSort,
    LivedataAdvancedPanelFilter,
  },

  inject: ["logic"],

};
</script>

<style>

</style>


small update to add link to cfp

small update to add link to cfp

<template>

Speakers

                <div
                  class="speaker-img"
                  :class="{active: selectedSpeaker.id === speaker.id}"
                  :style="{ backgroundImage: `url('${speaker.image || '/img/no-photo.png'}')` }"
                >

                    {{selectedSpeakerIndex}} / {{totalSpeakers}}

                      Close

                    {{ this.selectedSpeaker.name }}

                      {{ this.selectedSpeaker.job }}

{{ this.selectedSpeaker.title }}

{{ this.selectedSpeaker.abstract }}

                      <a
                        class="speaker-link wow fadeIn"
                        data-wow-delay="0.8s"
                        id="speaker-github"
                        v-if="selectedSpeaker.social.github"
                        :href="`https://github.com/${selectedSpeaker.social.github}`"
                        target="_blank"
                        rel="noopener noreferrer"
                      >

                      <a
                        class="speaker-link wow fadeIn"
                        data-wow-delay="1s"
                        id="speaker-twitter"
                        v-if="selectedSpeaker.social.twitter"
                        :href="`https://github.com/${selectedSpeaker.social.twitter}`"
                        target="_blank"
                        rel="noopener noreferrer"
                      >

          Submit Talk

</template>


bug: fix keystore password allowing users to input less than 7

<template>
  <v-sheet
    color="transparent"
    max-width="800px"
    class="mx-auto mew-component--keystore"
  >
    <mew-stepper :items="items" :on-step="step" class="mx-md-0">

      <template v-if="step === 1" #stepperContent1>
        <v-sheet color="white" class="border-radius--10px pa-4 pa-sm-12">

STEP 1.

Create password

          <mew-input
            v-model="password"
            hint="Password must be 8 or more characters"
            label="Password"
            placeholder="Enter Password"
            :has-clear-btn="true"
            class="mr-3 flex-grow-1 mb-2"
            :rules="passwordRulles"
            type="password"
          />

          <mew-input
            v-model="cofirmPassword"
            hint=""
            label="Confirm Password"
            placeholder="Confirm password"
            class="mr-3 flex-grow-1"
            :rules="passwordConfirmRulles"
            type="password"
          />

            <mew-button
              title="Create Wallet"
              btn-size="xlarge"
              :has-full-width="false"
              :disabled="!enableCreateButton"
              @click.native="createWallet"
            />

          <v-row v-else justify="center" align="center">
            <v-progress-circular
              indeterminate
              color="primary"
            ></v-progress-circular>

              Sit tight while we are encrypting your wallet

          </v-row>
        </v-sheet>

        <mew-warning-sheet
          class="mt-4 mb-0"
          title="NOT RECOMMENDED"
          description='This information is sensitive, and these options should only be used in offline settings by experienced crypto users. And MEW "CAN NOT" change your password. Please "DO NOT FORGET" to save your password, and it is your private key. You will need this "Password + Keystore file" to access your wallet.'
        />
      </template>

      <template v-if="step === 2" #stepperContent2>
        <v-sheet color="white" class="border-radius--10px pa-4 pa-sm-12">

STEP 2.

Download keystore file

            Important things to know before downloading your keystore file.

          <v-row class="align-stretch">
            <v-col v-for="(d, key) in warningData" :key="key" cols="12" sm="4">

                  <mew-icon :icon-name="d.icon" :img-height="70" />

{{ d.title }}

{{ d.description }}

            </v-col>
          </v-row>

            <mew-button
              title="Back"
              btn-style="outline"
              btn-size="xlarge"
              class="mx-md-1 my-1"
              @click.native="updateStep(1)"
            />
            <mew-button
              title="Acknowledge & Download"
              btn-size="xlarge"
              :has-full-width="false"
              class="mx-md-1 my-1"
              @click.native="downloadWallet"
            />

        </v-sheet>
        <mew-warning-sheet
          class="mt-4 mb-0"
          title="NOT RECOMMENDED"
          description='This information is sensitive, and these options should only be used in offline settings by experienced crypto users. And MEW "CAN NOT" change your password. Please "DO NOT FORGET" to save your password, and it is your private key. You will need this "Password + Keystore file" to access your wallet.'
        />
      </template>

      <template v-if="step === 3" #stepperContent3>
        <v-sheet color="white" class="border-radius--10px pa-4 pa-sm-12">

STEP 3.

You are done!

                Congratulation! Please use the MEWconnect App to scan this QR
                code in order to access your new wallet. And you are done!

              <v-img
                class="d-block d-sm-none mx-auto mt-12 mb-12"
                max-width="170px"
                src="@/assets/images/icons/icon-keystore-mew.png"
              />

                <mew-button
                  title="Access Wallet"
                  btn-size="xlarge"
                  :has-full-width="false"
                  class="mb-3"
                  @click.native="goToAccess"
                />
                <mew-button
                  title="Create Another Wallet"
                  :has-full-width="false"
                  btn-style="transparent"
                  @click.native="updateStep(1)"
                />

            <v-img
              class="d-none d-sm-block ml-8"
              max-width="250px"
              src="@/assets/images/icons/icon-keystore-mew.png"
            />

        </v-sheet>
      </template>
    </mew-stepper>
  </v-sheet>
</template>


add line height

add line height

<template>

      <template v-if="isExpired">
        Member benefits
      </template>
      <template v-else>
        {{ level }} benefits
      </template>

      <template v-if="isExpired">
        In addition to the happiness that comes with supporting mission-driven
        journalism, we like to give our members a little something extra as
        tokens of our appreciation. Renew your membership now and you’ll be
        eligible to receive:
      </template>
      <template v-else>
        As a thank you for supporting our journalism, you receive:
      </template>

 ceiling"
        :class="{
          'has-xs-btm-marg': index !== benefits.length - 1,
          'c-appeal__item--no': index > ceiling,
        }"
        class="c-appeal__item"
      >
        <icon :name="index > ceiling ? 'close' : 'check'" />
        <component :is="benefit.component" />

      Ready to take your giving to the next level?

      <template v-if="isExpired">
        Renew your support
      </template>
      <template v-else-if="isHighest">
        Join our giving Circles
      </template>
      <template v-else>
        Contact us to upgrade your membership
      </template>

</template>