dont show image buttons unless preview has image

<script lang="coffee">

doctypes = []
import("@/../../config/doctypes.yml").then (data) ->
  keys = Object.keys(data).filter (k) -> parseInt(k).toString() == k
  values = keys.map (k) -> data[k]
  doctypes = values

export default
  props:
    model: Object
    preview: Object
    remove:
      default: null
      type: Function

  data: ->
    editing: false

  computed:
    doctype: ->
      doctypes.find((dt) => (new RegExp(dt.regex)).test(@preview.url)) || {name: 'other'}

    icon: ->
      if @doctype
        'mdi-'+@doctype.icon
      else
        'mdi-link-variant'

    iconColor: -> @doctype.icon

    hostname: ->
      new URL(@preview.url).host
</script>
<template lang="pug">
div
  template(v-if="editing")
    v-card.link-preview.mt-3(outlined style="position: relative")
      .link-preview__image(v-if="preview.image" :style="{'background-image': 'url('+preview.image+')', 'background-size': (preview.fit || 'contain'), 'background-position': (preview.align || 'center')}")
      v-btn.link-preview__btn(color="primary" icon outlined
            :title="$t('common.action.done')"
            style="right: 8px"
            @click="editing = false")
        v-icon mdi-check
      v-card-title
        v-text-field(filled v-model="preview.title")
      v-card-subtitle
        v-textarea(filled v-model="preview.description")

  template(v-else)
    v-card.link-preview.link-preview-link.mt-3(outlined style="position: relative")
      template(v-if="remove")
        v-btn.link-preview__btn(color="primary"
              outlined
              style="right: 8px"
              icon
              @click="remove(preview.url)"
              :title="$t('common.action.remove')")
          v-icon mdi-close
        v-btn.link-preview__btn(color="primary"
              style="right: 48px"
              icon
              outlined
              @click="editing = true"
              :title="$t('common.action.edit')")
          v-icon mdi-pencil

        template(v-if="preview.image")
          v-btn.link-preview__btn(@click="preview.fit = (preview.fit == 'cover' ? 'contain' : 'cover')"
                color="primary" icon outlined
                style="right: 88px"
                :title="$t('common.action.zoom')")
            v-icon(v-if="preview.fit == 'contain'") mdi-magnify-plus-outline
            v-icon(v-else) mdi-magnify-minus-outline
          v-btn.link-preview__btn(v-if="preview.fit == 'cover'"
                @click="preview.align = (preview.align == 'top' ? 'center' : 'top')"
                color="primary" icon outlined
                style="right: 128px"
                :title="$t('common.action.align')")
            v-icon(v-if="preview.align == 'top'") mdi-format-vertical-align-center
            v-icon(v-else) mdi-format-vertical-align-top

      a.link-preview-link(:href="preview.url" target="_blank" rel="nofollow ugc noreferrer noopener")
        div.ml-4
        .link-preview__image(v-if="preview.image" :style="{'background-image': 'url('+preview.image+')', 'background-size': (preview.fit || 'contain'), 'background-position': (preview.align || 'center')}")
        v-card-title.text--secondary
          v-icon.mr-1 mdi-open-in-new
          | {{preview.title}}
        v-card-subtitle
          span(v-if="doctype.name != 'other'" v-t="'doctypes.'+doctype.name")
          span.link-preview__hostname(v-else v-html="preview.hostname")
        v-card-text.text--secondary(v-if="preview.description") {{preview.description}}
</template>

<style lang="sass">
.link-preview
  overflow-wrap: break-word
  word-wrap: break-word
  word-break: break-word
//   opacity: 70%
//
// .link-preview-link:hover
//   opacity: 100%

.link-preview__image
  height: 160px
  overflow: none
  background-position: center
  // max-width: 512px
  // margin: 0 auto
.link-preview__btn
  top: 8px
  position: absolute
  z-index: 1000
</style>

<script lang="coffee">

doctypes = []
import("@/../../config/doctypes.yml").then (data) ->
  keys = Object.keys(data).filter (k) -> parseInt(k).toString() == k
  values = keys.map (k) -> data[k]
  doctypes = values

export default
  props:
    model: Object
    preview: Object
    remove:
      default: null
      type: Function

  data: ->
    editing: false

  computed:
    doctype: ->
      doctypes.find((dt) => (new RegExp(dt.regex)).test(@preview.url)) || {name: 'other'}

    icon: ->
      if @doctype
        'mdi-'+@doctype.icon
      else
        'mdi-link-variant'

    iconColor: -> @doctype.icon

    hostname: ->
      new URL(@preview.url).host
</script>
<template lang="pug">
div
  template(v-if="editing")
    v-card.link-preview.mt-3(outlined style="position: relative")
      .link-preview__image(v-if="preview.image" :style="{'background-image': 'url('+preview.image+')', 'background-size': (preview.fit || 'contain'), 'background-position': (preview.align || 'center')}")
      v-btn.link-preview__btn(color="accent" icon outlined
            :title="$t('common.action.done')"
            style="right: 8px"
            @click="editing = false")
        v-icon mdi-check
      v-card-title
        v-text-field(filled v-model="preview.title")
      v-card-subtitle
        v-textarea(filled v-model="preview.description")

  template(v-else)
    v-card.link-preview.link-preview-link.mt-3(outlined style="position: relative")
      template(v-if="remove")
        v-btn.link-preview__btn(color="accent"
              outlined
              style="right: 8px"
              icon
              @click="remove(preview.url)"
              :title="$t('common.action.remove')")
          v-icon mdi-close
        v-btn.link-preview__btn(color="accent"
              style="right: 48px"
              icon
              outlined
              @click="editing = true"
              :title="$t('common.action.edit')")
          v-icon mdi-pencil
        v-btn.link-preview__btn(@click="preview.fit = (preview.fit == 'cover' ? 'contain' : 'cover')"
              color="accent" icon outlined
              style="right: 88px"
              :title="$t('common.action.zoom')")
          v-icon(v-if="preview.fit == 'contain'") mdi-magnify-plus-outline
          v-icon(v-else) mdi-magnify-minus-outline
        v-btn.link-preview__btn(v-if="preview.fit == 'cover'"
              @click="preview.align = (preview.align == 'top' ? 'center' : 'top')"
              color="accent" icon outlined
              style="right: 128px"
              :title="$t('common.action.align')")
          v-icon(v-if="preview.align == 'top'") mdi-format-vertical-align-center
          v-icon(v-else) mdi-format-vertical-align-top

      a.link-preview-link(:href="preview.url" target="_blank" rel="nofollow ugc noreferrer noopener")
        div.ml-4
        .link-preview__image(v-if="preview.image" :style="{'background-image': 'url('+preview.image+')', 'background-size': (preview.fit || 'contain'), 'background-position': (preview.align || 'center')}")
        v-card-title.text--secondary
          v-icon.mr-1 mdi-open-in-new
          | {{preview.title}}
        v-card-subtitle
          span(v-if="doctype.name != 'other'" v-t="'doctypes.'+doctype.name")
          span.link-preview__hostname(v-else v-html="preview.hostname")
        v-card-text.text--secondary(v-if="preview.description") {{preview.description}}
</template>

<style lang="sass">
.link-preview
  overflow-wrap: break-word
  word-wrap: break-word
  word-break: break-word
//   opacity: 70%
//
// .link-preview-link:hover
//   opacity: 100%

.link-preview__image
  height: 160px
  overflow: none
  background-position: center
  // max-width: 512px
  // margin: 0 auto
.link-preview__btn
  top: 8px
  position: absolute
  z-index: 1000
</style>


Update message on video requirements (#683)

<template>
	<app-loading-fade :is-loading="!isLoaded">
		<template v-if="shouldShowFormPlaceholder">
			<app-form-legend compact :deletable="canRemoveUploadingVideo">

			</app-form-legend>

		</template>
		<template v-else-if="videoProvider === FiresidePostVideo.PROVIDER_GAMEJOLT">
			<app-form-legend compact :deletable="canRemoveUploadingVideo" @delete="onDeleteUpload">
				<translate>Video</translate>
			</app-form-legend>

			<template v-if="videoStatus === 'idle'">
				<app-form ref="form" name="postVideoGameJoltForm">
					<app-form-group
						name="video"
						class="sans-margin-bottom"
						hide-label
						optional
						:label="$gettext(`Video`)"
					>

							<translate>
								Only short videos between 3 and 60 seconds are allowed at this time.
							</translate>

							<translate>
								Videos must be bigger than 200x200.
							</translate>
							<translate>
								Video filetypes currently supported:
							</translate>

								{{ filetype }}

										<app-jolticon icon="add" big />

											<translate>Video</translate>

						<app-form-control-upload
							ref="upload"
							class="-upload-input"
							:rules="{
								filesize: maxFilesize,
							}"
							:accept="allowedFiletypesString"
							@changed="videoSelected()"
						/>

						<app-form-control-errors />
					</app-form-group>
				</app-form>

					<app-button trans @click="setVideoProvider(FiresidePostVideo.PROVIDER_YOUTUBE)">
						<translate>Add YouTube video instead</translate>
					</app-button>

			</template>
			<template v-else-if="videoStatus === 'uploading'">
				<app-progress-bar :percent="uploadProgress * 100" />

				<translate>Uploading...</translate>
				{{ number(uploadProgress, { style: 'percent' }) }}

				<app-button
					class="pull-right"
					trans
					:disabled="uploadProgress === 1"
					@click="cancelUpload"
				>
					<translate>Cancel Upload</translate>
				</app-button>
			</template>
			<template v-else-if="videoStatus === 'processing'">
				<app-video-processing-progress
					:post="post"
					@complete="onProcessingComplete"
					@error="onProcessingError"
				/>
			</template>
			<template v-else-if="videoStatus === 'complete'">
				<app-video-player
					class="-video-player"
					:media-item="videoMediaItem"
					:manifests="videoManifestUrls"
				/>
			</template>
		</template>
		<template v-else-if="videoProvider === FiresidePostVideo.PROVIDER_YOUTUBE">
			<app-form-legend compact deletable @delete="emitDelete">
				<translate>YouTube video URL</translate>
			</app-form-legend>

			<app-form name="postVideoYouTubeForm">
				<app-form-group
					v-app-focus-when="!wasPublished"
					name="video_url"
					hide-label
					:label="$gettext(`YouTube Video URL`)"
				>

						<translate>Enter the URL of your YouTube video. For example:</translate>
						https://www.youtube.com/watch?v=dQw4w9WgXcQ

					<app-form-control
						type="text"
						:rules="{
							pattern: YOUTUBE_URL_REGEX,
						}"
					/>

					<app-form-control-errors />

					<template v-if="hasValidYouTubeUrl">

						<app-video-embed
							class="-video-embed"
							:video-provider="FiresidePostVideo.PROVIDER_YOUTUBE"
							:video-id="youtubeVideoId"
						/>
					</template>
				</app-form-group>
			</app-form>

				<app-button trans @click="setVideoProvider(FiresidePostVideo.PROVIDER_GAMEJOLT)">
					<translate>Upload video instead</translate>
				</app-button>

		</template>
	</app-loading-fade>
</template>

<template>
	<app-loading-fade :is-loading="!isLoaded">
		<template v-if="shouldShowFormPlaceholder">
			<app-form-legend compact :deletable="canRemoveUploadingVideo">

			</app-form-legend>

		</template>
		<template v-else-if="videoProvider === FiresidePostVideo.PROVIDER_GAMEJOLT">
			<app-form-legend compact :deletable="canRemoveUploadingVideo" @delete="onDeleteUpload">
				<translate>Video</translate>
			</app-form-legend>

			<template v-if="videoStatus === 'idle'">
				<app-form ref="form" name="postVideoGameJoltForm">
					<app-form-group
						name="video"
						class="sans-margin-bottom"
						hide-label
						optional
						:label="$gettext(`Video`)"
					>

							<translate>
								Only short videos of 60 seconds or less are allowed at this time.
							</translate>

							<translate>
								Video filetypes currently supported:
							</translate>

								{{ filetype }}

										<app-jolticon icon="add" big />

											<translate>Video</translate>

						<app-form-control-upload
							ref="upload"
							class="-upload-input"
							:rules="{
								filesize: maxFilesize,
							}"
							:accept="allowedFiletypesString"
							@changed="videoSelected()"
						/>

						<app-form-control-errors />
					</app-form-group>
				</app-form>

					<app-button trans @click="setVideoProvider(FiresidePostVideo.PROVIDER_YOUTUBE)">
						<translate>Add YouTube video instead</translate>
					</app-button>

			</template>
			<template v-else-if="videoStatus === 'uploading'">
				<app-progress-bar :percent="uploadProgress * 100" />

				<translate>Uploading...</translate>
				{{ number(uploadProgress, { style: 'percent' }) }}

				<app-button
					class="pull-right"
					trans
					:disabled="uploadProgress === 1"
					@click="cancelUpload"
				>
					<translate>Cancel Upload</translate>
				</app-button>
			</template>
			<template v-else-if="videoStatus === 'processing'">
				<app-video-processing-progress
					:post="post"
					@complete="onProcessingComplete"
					@error="onProcessingError"
				/>
			</template>
			<template v-else-if="videoStatus === 'complete'">
				<app-video-player
					class="-video-player"
					:media-item="videoMediaItem"
					:manifests="videoManifestUrls"
				/>
			</template>
		</template>
		<template v-else-if="videoProvider === FiresidePostVideo.PROVIDER_YOUTUBE">
			<app-form-legend compact deletable @delete="emitDelete">
				<translate>YouTube video URL</translate>
			</app-form-legend>

			<app-form name="postVideoYouTubeForm">
				<app-form-group
					v-app-focus-when="!wasPublished"
					name="video_url"
					hide-label
					:label="$gettext(`YouTube Video URL`)"
				>

						<translate>Enter the URL of your YouTube video. For example:</translate>
						https://www.youtube.com/watch?v=dQw4w9WgXcQ

					<app-form-control
						type="text"
						:rules="{
							pattern: YOUTUBE_URL_REGEX,
						}"
					/>

					<app-form-control-errors />

					<template v-if="hasValidYouTubeUrl">

						<app-video-embed
							class="-video-embed"
							:video-provider="FiresidePostVideo.PROVIDER_YOUTUBE"
							:video-id="youtubeVideoId"
						/>
					</template>
				</app-form-group>
			</app-form>

				<app-button trans @click="setVideoProvider(FiresidePostVideo.PROVIDER_GAMEJOLT)">
					<translate>Upload video instead</translate>
				</app-button>

		</template>
	</app-loading-fade>
</template>


fix(threadnav): added discussionId param to query

<script lang="coffee">
import EventBus from '@/shared/services/event_bus'
import UrlFor from '@/mixins/url_for'
import Records from '@/shared/services/records'
import WatchRecords from '@/mixins/watch_records'
import AnnouncementModalMixin from '@/mixins/announcement_modal'
import { debounce } from 'lodash'

export default
  mixins: [UrlFor, WatchRecords, AnnouncementModalMixin]
  data: ->
    discussion: null
    open: null
    keyEvents: []
    inversePosition: 0

  mounted: ->
    # threadPositionRequest (use slides the slider, tell others)
    # threadPositionUpdated (slider position needs updating)
    EventBus.$on 'toggleThreadNav', => @open = !@open
    EventBus.$on 'threadPositionUpdated', (position) =>
      @inversePosition = 0 - position

    EventBus.$on 'currentComponent', (options) =>
      @discussion = options.discussion
      return unless @discussion

      Records.events.fetch
        params:
          discussion_id: @discussion.id
          kind: ['poll_created', 'outcome_created']
          per: 200

      @watchRecords
        key: @discussion.id
        collections: ["events"]
        query: (records) =>
          return unless @discussion
          @keyEvents = records.events.collection.chain()
                        .find({
                          kind: {$in: ['poll_created']}
                          discussionId: @discussion.id
                        })
                        .simplesort('sequenceId')
                        .data()
  computed:
    childCount: -> @discussion.createdEvent().childCount
    position: -> 0 - @inversePosition
    thumbLabel: -> "#{@position} / #{@childCount}"

  methods:
    emitPosition: ->
      EventBus.$emit 'threadPositionRequest', @position

    scrollTo: (selector) ->
      @$vuetify.goTo(selector)

    title: (model) ->
      model.title || model.statement

    refreshThread: debounce ->
      EventBus.$emit('updateThreadPosition', 0 - @requestedPosition)
    ,
      250

    addPeople: ->
      @openAnnouncementModal(Records.announcements.buildFromModel(@discussion))

  watch:
    open: (val) ->
      console.log 'sidebar open', val

</script>

<template lang="pug">
v-navigation-drawer(v-if="discussion" v-model="open" :permanent="$vuetify.breakpoint.mdAndUp" width="210px" app fixed right clipped)
  .thread-nav
    v-subheader Jump to
    v-slider(color="accent" track-color="accent" thumb-color="accent" thumb-size="64" v-model="inversePosition" vertical :max="0" :min="0 - discussion.createdEvent().childCount" thumb-label @change="emitPosition()")
      template(v-slot:thumb-label)
        | {{thumbLabel}}
    v-btn(color="accent" :to="urlFor(discussion)+'/'+discussion.firstUnreadSequenceId()") {{discussion.unreadItemsCount()}} Unread {{discussion.firstUnreadSequenceId()}}
    v-btn.thread-nav__add-people(@click="addPeople()" v-t="'common.action.add_people'")
    //- v-list(dense)
    //-   v-subheader Navigation
    //-   v-list-item(:to="urlFor(discussion)")
    //-     v-list-item-title Context
    //-   v-list-item(:to="urlFor(discussion)+'/'+discussion.firstSequenceId()" :disabled="!discussion.firstSequenceId()")
    //-     v-list-item-title First
    //-   v-list-item(:disabled="!discussion.firstUnreadSequenceId()" :to="urlFor(discussion)+'/'+discussion.firstUnreadSequenceId()")
    //-     v-list-item-title Unread
    //-   v-list-item(:to="urlFor(discussion)+'/'+discussion.lastSequenceId()" :disabled="!discussion.lastSequenceId()")
    //-     v-list-item-title Latest
    //-   v-list-item(@click="scrollTo('.activity-panel__actions')")
    //-     v-list-item-title Add comment
    v-subheader(v-if="keyEvents.length" v-t="'group_page.polls'")
    v-list-item(v-for="event in keyEvents" :key="event.id" :to="urlFor(discussion)+'/'+event.sequenceId")
      v-list-item-avatar
        poll-common-chart-preview(:poll='event.model()' :size="28" :showMyStance="false")
      v-list-item-title
        span {{title(event.model())}}
</template>

<script lang="coffee">
import EventBus from '@/shared/services/event_bus'
import UrlFor from '@/mixins/url_for'
import Records from '@/shared/services/records'
import WatchRecords from '@/mixins/watch_records'
import AnnouncementModalMixin from '@/mixins/announcement_modal'
import { debounce } from 'lodash'

export default
  mixins: [UrlFor, WatchRecords, AnnouncementModalMixin]
  data: ->
    discussion: null
    open: null
    keyEvents: []
    inversePosition: 0

  mounted: ->
    # threadPositionRequest (use slides the slider, tell others)
    # threadPositionUpdated (slider position needs updating)
    EventBus.$on 'toggleThreadNav', => @open = !@open
    EventBus.$on 'threadPositionUpdated', (position) =>
      @inversePosition = 0 - position

    EventBus.$on 'currentComponent', (options) =>
      @discussion = options.discussion
      return unless @discussion

      Records.events.fetch
        params:
          discussion_id: @discussion.id
          kind: ['poll_created', 'outcome_created']
          per: 200

      @watchRecords
        key: @discussion.id
        collections: ["events"]
        query: (records) =>
          @keyEvents = records.events.collection.chain()
                        .find(kind: {$in: ['poll_created']})
                        .simplesort('sequenceId')
                        .data()
  computed:
    childCount: -> @discussion.createdEvent().childCount
    position: -> 0 - @inversePosition
    thumbLabel: -> "#{@position} / #{@childCount}"

  methods:
    emitPosition: ->
      EventBus.$emit 'threadPositionRequest', @position

    scrollTo: (selector) ->
      @$vuetify.goTo(selector)

    title: (model) ->
      model.title || model.statement

    refreshThread: debounce ->
      EventBus.$emit('updateThreadPosition', 0 - @requestedPosition)
    ,
      250

    addPeople: ->
      @openAnnouncementModal(Records.announcements.buildFromModel(@discussion))

  watch:
    open: (val) ->
      console.log 'sidebar open', val

</script>

<template lang="pug">
v-navigation-drawer(v-if="discussion" v-model="open" :permanent="$vuetify.breakpoint.mdAndUp" width="210px" app fixed right clipped)
  .thread-nav
    v-subheader Jump to
    v-slider(color="accent" track-color="accent" thumb-color="accent" thumb-size="64" v-model="inversePosition" vertical :max="0" :min="0 - discussion.createdEvent().childCount" thumb-label @change="emitPosition()")
      template(v-slot:thumb-label)
        | {{thumbLabel}}
    v-btn(color="accent" :to="urlFor(discussion)+'/'+discussion.firstUnreadSequenceId()") {{discussion.unreadItemsCount()}} Unread {{discussion.firstUnreadSequenceId()}}
    v-btn.thread-nav__add-people(@click="addPeople()" v-t="'common.action.add_people'")
    //- v-list(dense)
    //-   v-subheader Navigation
    //-   v-list-item(:to="urlFor(discussion)")
    //-     v-list-item-title Context
    //-   v-list-item(:to="urlFor(discussion)+'/'+discussion.firstSequenceId()" :disabled="!discussion.firstSequenceId()")
    //-     v-list-item-title First
    //-   v-list-item(:disabled="!discussion.firstUnreadSequenceId()" :to="urlFor(discussion)+'/'+discussion.firstUnreadSequenceId()")
    //-     v-list-item-title Unread
    //-   v-list-item(:to="urlFor(discussion)+'/'+discussion.lastSequenceId()" :disabled="!discussion.lastSequenceId()")
    //-     v-list-item-title Latest
    //-   v-list-item(@click="scrollTo('.activity-panel__actions')")
    //-     v-list-item-title Add comment
    v-subheader(v-if="keyEvents.length" v-t="'group_page.polls'")
    v-list-item(v-for="event in keyEvents" :key="event.id" :to="urlFor(discussion)+'/'+event.sequenceId")
      v-list-item-avatar
        poll-common-chart-preview(:poll='event.model()' :size="28" :showMyStance="false")
      v-list-item-title
        span {{title(event.model())}}
</template>


feature: questionlist

feature: questionlist

<template>

    <el-row>
      <el-col :span="20" :offset="2">
        <el-card class="box-card clearfix">

            {{courseName}}试题列表
            <el-button style="float: right;width: 100px" type="primary" size="small">
              下载试题模版
            </el-button>
            <el-button style="float: right;margin-right: 5px;width: 100px" type="primary" size="small"
                       @click="uploadDialogVisible = true">
              上传试题
            </el-button>

          <el-table
            :data="questionListData"
            style="width: 100%;text-align: left">
            <el-table-column
              type="selection"
              width="55">
            </el-table-column>
            <el-table-column type="expand">
              <template slot-scope="props">
                <el-form label-position="left" inline class="demo-table-expand">
                  <el-form-item :label="'选项' + option.optionId+': '" v-for="(option,index) in props.row.options"
                                :key="option.optionId">
                    {{ option.content }}
                  </el-form-item>
                </el-form>
              </template>
            </el-table-column>
            <el-table-column
              label="题目内容"
              prop="content"
            >
            </el-table-column>
            <el-table-column
              label="等级"
              prop="level"
            >
            </el-table-column>
            <el-table-column
              label="答案"
              prop="answer"
            >
            </el-table-column>
          </el-table>

            <el-button style="float: left"
                       type="danger"
                       size="small">删除题目
            </el-button>
            <el-pagination
              @size-change="handleSizeChange"
              @current-change="handleCurrentChange"
              :current-page=page
              :page-sizes="[10, 15, 20, 50]"
              :page-size=size
              layout="total, sizes, prev, pager, next, jumper"
              :total=total>
            </el-pagination>

        </el-card>
      </el-col>
    </el-row>

    <el-dialog
      :close-on-click-modal=false
      title="上传试题"
      :visible.sync="uploadDialogVisible"
      width="60%"
    >
      <el-upload
        action="/question/upload"
        :file-list="fileList">
        <el-button size="small" type="primary">点击上传</el-button>

只能上传excel文件

      </el-upload>
    </el-dialog>

</template>

<template>

    <el-row>
      <el-col :span="20" :offset="2">
        <el-card class="box-card clearfix">

            {{courseName}}试题列表
            <el-button style="float: right;width: 100px" type="primary" size="small">
              下载试题模版
            </el-button>
            <el-button style="float: right;margin-right: 5px;width: 100px" type="primary" size="small"
                       @click="uploadDialogVisible = true">
              上传试题
            </el-button>

          <el-table
            :data="questionListData"
            style="width: 100%;text-align: left">
            <el-table-column type="expand">
              <template slot-scope="props">
                <el-form label-position="left" inline class="demo-table-expand">
                  <el-form-item :label="'选项' + option.optionId+': '" v-for="(option,index) in props.row.options"
                                :key="option.optionId">
                    {{ option.content }}
                  </el-form-item>
                </el-form>
              </template>
            </el-table-column>
            <el-table-column
              label="题目内容"
              prop="content"
            >
            </el-table-column>
            <el-table-column
              label="等级"
              prop="level"
            >
            </el-table-column>
            <el-table-column
              label="答案"
              prop="answer"
            >
            </el-table-column>
          </el-table>
          <el-pagination
            style="margin-top: 20px"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page=page
            :page-sizes="[10, 15, 20, 50]"
            :page-size=size
            layout="total, sizes, prev, pager, next, jumper"
            :total=total>
          </el-pagination>
        </el-card>
      </el-col>
    </el-row>

    <el-dialog
      :close-on-click-modal=false
      title="上传试题"
      :visible.sync="uploadDialogVisible"
      width="60%"
    >
      <el-upload
        action="/question/upload"
        :file-list="fileList">
        <el-button size="small" type="primary">点击上传</el-button>

只能上传excel文件

      </el-upload>
    </el-dialog>

</template>


Theme

Theme

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/JosefFriedrich-nodejs/baldr.git/'


Make fullscreen

Make fullscreen


remove .native modifier

remove .native modifier

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/christianmemije/kolibri-exercise-perseus-plugin.git/'


улучшает UX и реализует функционал кастомных алиасов

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/taxnuke/url-shortener.git/'


Space created.

Space created.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/NathanielDavid/Sumie.git/'


Some cleanup

Some cleanup

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/soal/spDiary.git/'


Update label

Update label

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/LokeCarlsson/lokecarlsson.git/'


Fix thank-you page styles

Fix thank-you page styles

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/wappulehti-apy/diilikone-front-2017.git/'


linter fixes

linter fixes

<template>
    <transition name="collapse">
        <div :class="classObject" v-show="show">
            <slot></slot>
        </div>
    </transition>
</template>

<style scoped>
    .collapse-enter-active, .collapse-leave-active {
        transition: all .35s ease;
        overflow: hidden;
        max-height: 100vh;
    }

    .collapse-enter, .collapse-leave-to {
        max-height: 0;
    }
</style>

<script>
    export default {

        data() {
            return {
                show: false
            };
        },

        computed: {
            classObject() {
                return {
                    'navbar-collapse': this.isNav,
                    show: this.show
                };
            }
        },

        props: {
            isNav: {
                type: Boolean,
                default: false
            },
            id: {
                type: String,
                required: true
            }
        },

        methods: {
            toggle() {
                this.show = !this.show;
            }
        },

        created() {
            this.$root.$on('collapse::toggle', target => {
                if (target !== this.id) {
                    return;
                }
                this.toggle();
            });
        }
    };

</script>

<template>
    <transition name="collapse">
        <div :class="classObject" v-show="show">
            <slot></slot>
        </div>
    </transition>
</template>

<style scoped>
    .collapse-enter-active, .collapse-leave-active {
        transition: all .35s ease;
        overflow: hidden;
        max-height: 100vh;
    }

    .collapse-enter, .collapse-leave-to {
        max-height: 0;
    }
</style>

<script>
    export default {

        data() {
            return {
                show: false,
            };
        },

        computed: {
            classObject() {
                return {
                    'navbar-collapse': this.isNav,
                    show: this.show
                };
            },
        },

        props: {
            isNav: {
                type: Boolean,
                default: false
            },
            id: {
                type: String,
                required: true
            }
        },

        methods: {
            toggle() {
                this.show = !this.show;
            }
        },

        created() {
            this.$root.$on('collapse::toggle', target => {
                if (target !== this.id) {
                    return;
                }
                this.toggle();
            });
        },
    };

</script>


removed comment

removed comment

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jaceks-iRonin/vue-material-test.git/'


EBEAST: vc/button-bar.vue: add <vc-button-bar/> for tight button grouping

Signed-off-by: Tim Janik <6ec6ff229c4e1c5bbd5933ee92e6c5832ff023bb@gnu.org>

<docs>
  ## vc-button-bar - A container for tight packing of buttons
  ### Props:
  ### Events:
  ### Slots:
  - **slot** - All contents passed into this element will be packed tightly and styled as buttons.
</docs>

<template>
  <slot class="vc-slot"></slot>
</template>

error: pathspec 'ebeast/vc/button-bar.vue' did not match any file(s) known to git


update

update

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/iview/iview-doc.git/'


Rework social icons style

Rework social icons style


fix textarea autosize on initial value (#341)

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/praneetloke/vue-material.git/'


Adjust footer

Adjust footer

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/egoconf/egoconf.git/'


Removes transition

Removes transition


주소에 따른 사이드바 show/hide fix #1

주소에 따른 사이드바 show/hide fix #1

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/HedwigProject/Hedwig.git/'


Charts: add menu items

Charts: add menu items

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/f00-/ncompass-node-mockup.git/'


Adds fetch DB on delete

Adds fetch DB on delete


docs(grid): convert markup div to code

docs(grid): convert markup div to code

<template>
  <v-container fluid>
    <v-layout column>
      <v-flex xs12>

          <v-layout v-bind="layoutAttributes">

          </v-layout>

      </v-flex>
      <v-flex xs12>
        <v-layout row xs12 wrap>
          <v-flex xs12 md4>
            <v-radio-group v-model="alignment">
              <v-radio
                v-for="n in alignmentsAvailable"
                :key="n"
                :label="n === '' ? 'Nothing' : n"
                :value="n"
              ></v-radio>
            </v-radio-group>
          </v-flex>
          <v-flex xs12 md4>
            <v-radio-group v-model="justify">
              <v-radio
                v-for="n in justifyAvailable"
                :key="n"
                :label="n === '' ? 'Nothing' : n"
                :value="n"
              ></v-radio>
            </v-radio-group>
          </v-flex>
          <v-flex xs12 md4>
            <v-layout column>
              <v-radio-group v-model="flexDirection">
                <v-checkbox
                  v-model="reverse"
                  label="reverse"
                  hide-details
                ></v-checkbox>
                <v-checkbox
                  v-model="fillHeight"
                  label="fill-height"
                  hide-details
                ></v-checkbox>
                <v-radio
                  v-for="n in flexDirectionAvailable"
                  :key="n"
                  :label="n === '' ? 'Nothing' : n"
                  :value="n"
                ></v-radio>
              </v-radio-group>
            </v-layout>
          </v-flex>
        </v-layout>
      </v-flex>
      <v-flex xs12>

Output:

        {{ formatAttributes(layoutAttributes) }}
      </v-flex>
    </v-layout>
  </v-container>
</template>


update radio

update radio

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/TuhuFE/wepy.git/'


menu back

menu back

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/beyondplus/cms.git/'


Small adjustment

Small adjustment

<template>
  <div id="Category">
    Core Category
  </div>
</template>

<script>
import builder from 'bodybuilder'

import { breadCrumbRoutes } from 'src/lib/filters'
import EventBus from 'src/event-bus/event-bus'
import Sidebar from '../components/core/blocks/Category/Sidebar.vue'
import ProductTile from '../components/core/ProductTile.vue'
import Breadcrumbs from '../components/core/Breadcrumbs.vue'
import { optionLabel } from 'src/store/modules/attribute'

export default {
  name: 'category',

  methods: {

    baseFilterQuery () {
      let self = this
      let searchProductQuery = builder().query('range', 'price', { 'gt': 0 }).andFilter('range', 'visibility', { 'gte': 3, 'lte': 4 }/** Magento visibility in search & categories */).orFilter('term', 'category.category_id', self.category.id)  // FIXME!

      // add filters to query
      for (let attrToFilter of Object.keys(self.filters)) {
        if (attrToFilter !== 'price') {
          searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
        } else {
          searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
          searchProductQuery.aggregation('range', 'price', {
            ranges: [
              { from: 0, to: 50 },
              { from: 50, to: 100 },
              { from: 100, to: 150 },
              { from: 150 }
            ]
          })
        }
      }

      if (self.category.children_data) {
        let recurCatFinderBuilder = (category, searchProductQuery) => {
          if (!category) {
            return searchProductQuery
          }

          if (!category.children_data) {
            return searchProductQuery
          }

          for (let sc of category.children_data) {
            if (sc && sc.id) {
              searchProductQuery = searchProductQuery.orFilter('term', 'category.category_id', sc.id)
            }
            return recurCatFinderBuilder(sc, searchProductQuery)
          }

          return searchProductQuery
        }
        recurCatFinderBuilder(self.category, searchProductQuery)
      }

      return searchProductQuery
    },

    filterData ({ populateAggregations = false, searchProductQuery }) {
      let self = this
      self.$store.dispatch('product/list', {
        query: searchProductQuery.build(),
        start: self.pagination.offset,
        size: self.pagination.pageSize
      }).then(function (res) {
        self.aggregations = res.aggregations
        self.products = res.items
        self.isCategoryEmpty = (self.products.length === 0)

        if (populateAggregations === true) { // populate filter aggregates
          for (let attrToFilter of Object.keys(self.filters)) { // fill out the filter options
            self.filters[attrToFilter] = []

            if (attrToFilter !== 'price') {
              for (let option of res.aggregations['agg_terms_' + attrToFilter].buckets) {
                self.filters[attrToFilter].push({
                  id: option.key,
                  label: optionLabel(self.$store.state.attribute, { attributeKey: attrToFilter, optionId: option.key })
                })
              }
            } else { // special case is range filter for prices
              let index = 0
              let count = res.aggregations['agg_range_' + attrToFilter].buckets.length
              for (let option of res.aggregations['agg_range_' + attrToFilter].buckets) {
                self.filters[attrToFilter].push({
                  id: option.key,
                  from: option.from,
                  to: option.to,
                  label: (index === 0 || (index == count-1)) ? (option.to ? '< $' + option.to  : '> $' + option.from) : '$' + option.from + (option.to ? ' - ' + option.to : '')// TODO: add better way for formatting, extract currency sign
                })
                index++
              }
            }
          }
        }
      })
    },

    fetchData () {
      let self = this
      let searchProductQuery = self.baseFilterQuery()

      if (self.category) { // fill breadcrumb data - TODO: extract it to a helper to be used on product page
        EventBus.$emit('current-category-changed', self.$store.state.category.current_path)
        self.breadcrumbs.routes = breadCrumbRoutes(self.$store.state.category.current_path)

        self.$store.dispatch('attribute/list', { // load filter attributes for this specific category
          filterValues: Object.keys(self.filters) // TODO: assign specific filters/ attribute codes dynamicaly to specific categories
        })
      }
      self.filterData({ searchProductQuery: searchProductQuery, populateAggregations: true })
    },

    validateRoute () {
      let self = this
      let slug = this.$route.params.slug

      self.$store.dispatch('category/list', {}).then((categories) => {
        self.$store.dispatch('category/single', { key: 'slug', value: slug }).then((category) => {
          self.category = category

          if (!self.category) {
            self.$router.push('/')
          } else {
            self.fetchData()
          }
        })
      })
    }
  },
  watch: {
    '$route': 'validateRoute'
  },

  beforeMount () {
    this.validateRoute()
    let self = this // TODO: refactor it to bind()
    EventBus.$on('filter-changed', (filterOption) => { // slection of product variant on product page
      console.log(filterOption)
      if (self.filterSet[filterOption.attribute_code]) {
        delete self.filterSet[filterOption.attribute_code]
      } else {
        self.filterSet[filterOption.attribute_code] = filterOption
      }

      let filterQr = self.baseFilterQuery()
      for (let code of Object.keys(self.filterSet)) {
        const filter = self.filterSet[code]

        if (filter.attribute_code !== 'price') {
          filterQr = filterQr.andFilter('match', filter.attribute_code, filter.id)
        } else { // multi should be possible filter here?
          const rangeqr = {}
          if (filter.from) {
            rangeqr['gte'] = filter.from
          }
          if (filter.to) {
            rangeqr['lte'] = filter.to
          }

          filterQr = filterQr.andFilter('range', filter.attribute_code, rangeqr)
        }
      }
      self.filterData({ populateAggregations: false, searchProductQuery: filterQr }) // because already aggregated
    })
  },
  data () {
    return {
      isCategoryEmpty: false,
      breadcrumbs: { routes: [] },
      products: {},
      category: {},
      aggregations: {}, // facets
      pagination: {
        pageSize: 18,
        offset: 0
      },
      filterSet: {}, // filter set selected by user
      filters: { // filters should be set by category, and should be synchronized with magento
        color: [{'id': 165, 'label': 'red'}, {'id': 166, 'label': 'blue'}],
        size: [
            {'id': 50, 'label': 'XS'},
            {'id': 51, 'label': 'S'},
            {'id': 52, 'label': 'M'},
            {'id': 53, 'label': 'L'},
            {'id': 54, 'label': 'XL'}
        ],
        price: ['0.00 - 50.00', '50.01 - 100.00', '100.01 - 150.00', '150.01 and more']
      }
    }
  },
  components: {
    ProductTile,
    Breadcrumbs,
    Sidebar
  }
}
</script>

<template>
  <div id="Category">
    Core Category
  </div>
</template>

<script>
import builder from 'bodybuilder'

import { breadCrumbRoutes } from 'src/lib/filters'
import EventBus from 'src/event-bus/event-bus'
import Sidebar from '../components/core/blocks/Category/Sidebar.vue'
import ProductTile from '../components/core/ProductTile.vue'
import Breadcrumbs from '../components/core/Breadcrumbs.vue'
import { optionLabel } from 'src/store/modules/attribute'

export default {
  name: 'category',

  methods: {

    baseFilterQuery () {
      let self = this
      let searchProductQuery = builder().query('range', 'price', { 'gt': 0 }).andFilter('range', 'visibility', { 'gte': 3, 'lte': 4 }/** Magento visibility in search & categories */).orFilter('term', 'category.category_id', self.category.id)  // FIXME!

      // add filters to query
      for (let attrToFilter of Object.keys(self.filters)) {
        if (attrToFilter !== 'price') {
          searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
        } else {
          searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
          searchProductQuery.aggregation('range', 'price', {
            ranges: [
              { from: 0, to: 50 },
              { from: 50, to: 100 },
              { from: 100, to: 150 },
              { from: 150 }
            ]
          })
        }
      }

      if (self.category.children_data) {
        let recurCatFinderBuilder = (category, searchProductQuery) => {
          if (!category) {
            return searchProductQuery
          }

          if (!category.children_data) {
            return searchProductQuery
          }

          for (let sc of category.children_data) {
            if (sc && sc.id) {
              searchProductQuery = searchProductQuery.orFilter('term', 'category.category_id', sc.id)
            }
            return recurCatFinderBuilder(sc, searchProductQuery)
          }

          return searchProductQuery
        }
        recurCatFinderBuilder(self.category, searchProductQuery)
      }

      return searchProductQuery
    },

    filterData ({ populateAggregations = false, searchProductQuery }) {
      let self = this
      self.$store.dispatch('product/list', {
        query: searchProductQuery.build(),
        start: self.pagination.offset,
        size: self.pagination.pageSize
      }).then(function (res) {
        self.aggregations = res.aggregations
        self.products = res.items
        self.isCategoryEmpty = (self.products.length === 0)

        if (populateAggregations === true) { // populate filter aggregates
          for (let attrToFilter of Object.keys(self.filters)) { // fill out the filter options
            self.filters[attrToFilter] = []

            if (attrToFilter !== 'price') {
              for (let option of res.aggregations['agg_terms_' + attrToFilter].buckets) {
                self.filters[attrToFilter].push({
                  id: option.key,
                  label: optionLabel(self.$store.state.attribute, { attributeKey: attrToFilter, optionId: option.key })
                })
              }
            } else { // special case is range filter for prices
              let index = 0
              let count = res.aggregations['agg_range_' + attrToFilter].buckets.length
              for (let option of res.aggregations['agg_range_' + attrToFilter].buckets) {
                self.filters[attrToFilter].push({
                  id: option.key,
                  from: option.from,
                  to: option.to,
                  label: (index === 0 || (index == count-1)) ? (option.to ? '<' : '>') + ' $' + option.from : '$' + option.from + (option.to ? ' - ' + option.to : '')// TODO: add better way for formatting, extract currency sign
                })
                index++
              }
            }
          }
        }
      })
    },

    fetchData () {
      let self = this
      let searchProductQuery = self.baseFilterQuery()

      if (self.category) { // fill breadcrumb data - TODO: extract it to a helper to be used on product page
        EventBus.$emit('current-category-changed', self.$store.state.category.current_path)
        self.breadcrumbs.routes = breadCrumbRoutes(self.$store.state.category.current_path)

        self.$store.dispatch('attribute/list', { // load filter attributes for this specific category
          filterValues: Object.keys(self.filters) // TODO: assign specific filters/ attribute codes dynamicaly to specific categories
        })
      }
      self.filterData({ searchProductQuery: searchProductQuery, populateAggregations: true })
    },

    validateRoute () {
      let self = this
      let slug = this.$route.params.slug

      self.$store.dispatch('category/list', {}).then((categories) => {
        self.$store.dispatch('category/single', { key: 'slug', value: slug }).then((category) => {
          self.category = category

          if (!self.category) {
            self.$router.push('/')
          } else {
            self.fetchData()
          }
        })
      })
    }
  },
  watch: {
    '$route': 'validateRoute'
  },

  beforeMount () {
    this.validateRoute()
    let self = this // TODO: refactor it to bind()
    EventBus.$on('filter-changed', (filterOption) => { // slection of product variant on product page
      console.log(filterOption)
      if (self.filterSet[filterOption.attribute_code]) {
        delete self.filterSet[filterOption.attribute_code]
      } else {
        self.filterSet[filterOption.attribute_code] = filterOption
      }

      let filterQr = self.baseFilterQuery()
      for (let code of Object.keys(self.filterSet)) {
        const filter = self.filterSet[code]

        if (filter.attribute_code !== 'price') {
          filterQr = filterQr.andFilter('match', filter.attribute_code, filter.id)
        } else { // multi should be possible filter here?
          const rangeqr = {}
          if (filter.from) {
            rangeqr['gte'] = filter.from
          }
          if (filter.to) {
            rangeqr['lte'] = filter.to
          }

          filterQr = filterQr.andFilter('range', filter.attribute_code, rangeqr)
        }
      }
      self.filterData({ populateAggregations: false, searchProductQuery: filterQr }) // because already aggregated
    })
  },
  data () {
    return {
      isCategoryEmpty: false,
      breadcrumbs: { routes: [] },
      products: {},
      category: {},
      aggregations: {}, // facets
      pagination: {
        pageSize: 18,
        offset: 0
      },
      filterSet: {}, // filter set selected by user
      filters: { // filters should be set by category, and should be synchronized with magento
        color: [{'id': 165, 'label': 'red'}, {'id': 166, 'label': 'blue'}],
        size: [
            {'id': 50, 'label': 'XS'},
            {'id': 51, 'label': 'S'},
            {'id': 52, 'label': 'M'},
            {'id': 53, 'label': 'L'},
            {'id': 54, 'label': 'XL'}
        ],
        price: ['0.00 - 50.00', '50.01 - 100.00', '100.01 - 150.00', '150.01 and more']
      }
    }
  },
  components: {
    ProductTile,
    Breadcrumbs,
    Sidebar
  }
}
</script>


Fix wrong example order

Fix wrong example order


add code sample to switch (#158)

add code sample to switch (#158)

* Adding code sample to switch, fixing #157

* Added basic chips stuff

* "revert chips stuff from merged commit"

This reverts commit 367eea839a5eec7b982c82c2a10d199136999953.


Adds display:flex to heading

Adds display:flex to heading


Adds a version stamp

Adds a version stamp


Delete qrCode.vue

Delete qrCode.vue

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/wtbu703/simple.git/'


remove absolute positioning from input fields

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/aligerum/keyring.git/'


update:上线文件选择增加全选

update:上线文件选择增加全选

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/o2team/deploy.git/'


Update component template

Update component template

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/pigeonfresh/website.git/'


typo when loading components Message

typo when loading components Message

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/shadowRR/kozette.git/'


Add HTML/markdown support for the master slide task

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/JosefFriedrich-nodejs/baldr.git/'


fix typo

fix typo


Adjust logo size

Adjust logo size

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/stefanjudis/stefan-judis-website.git/'


Warn user if their token will expire soon

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/04n0/goldfish.git/'


create date

create date

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/CHCP/toolmaker-web.git/'


add parameter status kind

add parameter status kind


Basic functionalities of bulma modal

Basic functionalities of bulma modal

<template>

      <slot name="modal-content"></slot>

    <button
      class="Modal__close modal-close is-large"
      aria-label="close"
      v-if="!isCard"
      @click="$emit('modal:close')"
    ></button>

          <slot name="title"></slot>

        <button
          class="delete"
          aria-label="close"
          v-if="titleHasClose"
          @click="$emit('modal:close')"
        ></button>

        <slot name="body"></slot>

        <slot name="foot"></slot>

</template>

error: pathspec 'src/components/Modal.vue' did not match any file(s) known to git


Update info.vue

Update info.vue

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/iview/iview-doc.git/'


Added spotify follow

Added spotify follow

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/nikrich/UpperMillBandWebsite.git/'


Add example of variety of icons

Add example of variety of icons

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/praneetloke/vue-material.git/'


feat(MdButton): fix to prop not being passed to router-link

<style lang="scss">
  @import "~components/MdAnimation/variables";
  @import "~components/MdElevation/mixins";
  @import "~components/MdElevation/variables";

  $md-button-min-width: 88px;
  $md-button-height: 36px;
  $md-button-radius: 2px;
  $md-button-font-size: 14px;

  $md-button-dense-height: 32px;

  $md-button-icon-size: 40px;

  .md-button {
    min-width: $md-button-min-width;
    height: $md-button-height;
    margin: 6px 8px;
    padding: 0;
    display: inline-block;
    position: relative;
    overflow: hidden;
    user-select: none;
    outline: none;
    background: transparent;
    border: 0;
    border-radius: $md-button-radius;
    transition-timing-function: $md-transition-stand-timing;
    transition-property: box-shadow, color, background-color;
    transition-duration: $md-elevation-transition-duration,
                         $md-transition-default-duration,
                         $md-transition-default-duration;
    will-change: box-shadow, color, background-color;
    font-family: inherit;
    font-size: $md-button-font-size;
    font-weight: 500;
    text-transform: uppercase;
    text-decoration: none;
    vertical-align: top;
    white-space: nowrap;

    &:active {
      outline: none;
    }

    &[disabled] {
      pointer-events: none;
    }

    &:not([disabled]) {
      cursor: pointer;

      &:hover,
      &:active,
      &.md-focused {
        &:before {
          background-color: currentColor;
          opacity: .15;
        }
      }

      &.md-focused {
        &.md-primary,
        &.md-accent {
          &:before {
            opacity: .2;
          }
        }
      }

      &:active {
        &:before {
          opacity: .2;
        }
      }
    }

    &::-moz-focus-inner {
      padding: 0;
      border: 0;
    }

    &:before {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 1;
      opacity: 0;
      transition: $md-transition-default;
      will-change: background-color, opacity;
      content: " ";
    }

    &.md-dense {
      height: $md-button-dense-height;
      font-size: 13px;
    }

    &.md-raised:not([disabled]) {
      @include md-elevation(2);

      &:active {
        @include md-elevation(8);
      }
    }

    + .md-button {
      margin-left: 0;
    }

    .md-ripple {
      padding: 0 8px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
  }

  .md-button-spaced .md-ripple {
    padding: 0 16px;
  }

  .md-icon-button {
    width: $md-button-icon-size;
    min-width: $md-button-icon-size;
    height: $md-button-icon-size;
    margin: 0 6px;
    border-radius: 50%;

    &:before {
      border-radius: 50%;
    }

    &.md-dense {
      width: $md-button-dense-height;
      min-width: $md-button-dense-height;
      height: $md-button-dense-height;
    }

    .md-ripple {
      border-radius: 50%;
    }

    .md-ripple-wave {
      top: 0 !important;
      right: 0 !important;
      bottom: 0 !important;
      left: 0 !important;
    }

    .md-ripple-enter-active {
      transition-duration: 1.2s;
    }
  }

  .md-button-content {
    position: relative;
    z-index: 2;
  }
</style>

<script>
  import MdComponent from 'core/MdComponent'
  import ripple from 'core/mixins/ripple'
  import MdButtonContent from './MdButtonContent'

  export default new MdComponent({
    name: 'MdButton',
    components: {
      MdButtonContent
    },
    mixins: [ripple],
    props: {
      href: String,
      type: {
        type: String,
        default: 'button'
      },
      disabled: Boolean,
      to: [String, Object]
    },
    render (createElement) {
      const buttonContent = createElement('md-button-content', {
        attrs: {
          mdRipple: this.mdRipple,
          disabled: this.disabled
        }
      }, this.$slots.default)
      let buttonAttrs = {
        staticClass: 'md-button',
        class: [this.$mdActiveTheme],
        attrs: {
          href: this.href,
          disabled: this.disabled,
          type: this.type || 'button'
        },
        on: {
          click: ($event) => {
            this.$emit('click', $event)
          }
        }
      }
      let tag = 'button'

      if (this.href) {
        tag = 'a'
      } else if (this.to) {
        tag = 'router-link'
        buttonAttrs.attrs = {
          ...this.$options.propsData,
          to: this.to
        }
      }

      return createElement(tag, buttonAttrs, [buttonContent])
    }
  })
</script>

<style lang="scss">
  @import "~components/MdAnimation/variables";
  @import "~components/MdElevation/mixins";
  @import "~components/MdElevation/variables";

  $md-button-min-width: 88px;
  $md-button-height: 36px;
  $md-button-radius: 2px;
  $md-button-font-size: 14px;

  $md-button-dense-height: 32px;

  $md-button-icon-size: 40px;

  .md-button {
    min-width: $md-button-min-width;
    height: $md-button-height;
    margin: 6px 8px;
    padding: 0;
    display: inline-block;
    position: relative;
    overflow: hidden;
    user-select: none;
    outline: none;
    background: transparent;
    border: 0;
    border-radius: $md-button-radius;
    transition-timing-function: $md-transition-stand-timing;
    transition-property: box-shadow, color, background-color;
    transition-duration: $md-elevation-transition-duration,
                         $md-transition-default-duration,
                         $md-transition-default-duration;
    will-change: box-shadow, color, background-color;
    font-family: inherit;
    font-size: $md-button-font-size;
    font-weight: 500;
    text-transform: uppercase;
    text-decoration: none;
    vertical-align: top;
    white-space: nowrap;

    &:active {
      outline: none;
    }

    &[disabled] {
      pointer-events: none;
    }

    &:not([disabled]) {
      cursor: pointer;

      &:hover,
      &:active,
      &.md-focused {
        &:before {
          background-color: currentColor;
          opacity: .15;
        }
      }

      &.md-focused {
        &.md-primary,
        &.md-accent {
          &:before {
            opacity: .2;
          }
        }
      }

      &:active {
        &:before {
          opacity: .2;
        }
      }
    }

    &::-moz-focus-inner {
      padding: 0;
      border: 0;
    }

    &:before {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 1;
      opacity: 0;
      transition: $md-transition-default;
      will-change: background-color, opacity;
      content: " ";
    }

    &.md-dense {
      height: $md-button-dense-height;
      font-size: 13px;
    }

    &.md-raised:not([disabled]) {
      @include md-elevation(2);

      &:active {
        @include md-elevation(8);
      }
    }

    + .md-button {
      margin-left: 0;
    }

    .md-ripple {
      padding: 0 8px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
  }

  .md-button-spaced .md-ripple {
    padding: 0 16px;
  }

  .md-icon-button {
    width: $md-button-icon-size;
    min-width: $md-button-icon-size;
    height: $md-button-icon-size;
    margin: 0 6px;
    border-radius: 50%;

    &:before {
      border-radius: 50%;
    }

    &.md-dense {
      width: $md-button-dense-height;
      min-width: $md-button-dense-height;
      height: $md-button-dense-height;
    }

    .md-ripple {
      border-radius: 50%;
    }

    .md-ripple-wave {
      top: 0 !important;
      right: 0 !important;
      bottom: 0 !important;
      left: 0 !important;
    }

    .md-ripple-enter-active {
      transition-duration: 1.2s;
    }
  }

  .md-button-content {
    position: relative;
    z-index: 2;
  }
</style>

<script>
  import MdComponent from 'core/MdComponent'
  import ripple from 'core/mixins/ripple'
  import MdButtonContent from './MdButtonContent'

  export default new MdComponent({
    name: 'MdButton',
    components: {
      MdButtonContent
    },
    mixins: [ripple],
    props: {
      href: String,
      type: {
        type: String,
        default: 'button'
      },
      disabled: Boolean,
      to: [String, Object]
    },
    render (createElement) {
      const buttonContent = createElement('md-button-content', {
        attrs: {
          mdRipple: this.mdRipple,
          disabled: this.disabled
        }
      }, this.$slots.default)
      let buttonAttrs = {
        staticClass: 'md-button',
        class: [this.$mdActiveTheme],
        attrs: {
          href: this.href,
          disabled: this.disabled,
          type: this.type || 'button'
        },
        on: {
          click: ($event) => {
            this.$emit('click', $event)
          }
        }
      }
      let tag = 'button'

      if (this.href) {
        tag = 'a'
      } else if (this.to) {
        tag = 'router-link'
        buttonAttrs.attrs = {
          to: this.to,
          ...this.$options.propsData
        }
      }

      return createElement(tag, buttonAttrs, [buttonContent])
    }
  })
</script>


新增文档页面

新增文档页面

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Sumi-Sakura/Sakura-UI.git/'


Fixing odd negative margin-top behavior on .columns

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/eoinmurphy/field-guide.git/'


Fixes mutation name

Fixes mutation name

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/NathanielDavid/Sumie.git/'


登录使用验证码组件

登录使用验证码组件

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/firery/mirs-web-vue.git/'


Wrote a mixin that can sort objects using nested properties

<script type="text/ecmascript-6">
/*
 * @author ToxicTree
 */
    export default {

        methods: {

            // Sort objects by targeting a deeper property
            deepSort(a, b) {

                var checkA = a;
                var checkB = b;

                // Dig down to the properties to compare
                var level = this.order.split('.');

                for (var i=0; i<level.length ; i++){
                    if (!checkA[level[i]] || !checkB[level[i]])
                        return 0;
                    checkA = checkA[level[i]];
                    checkB = checkB[level[i]];
                }

                // Compare numbers
                if (typeof checkA == 'number'){
                    if (checkA == checkB)
                        return 0;
                    return (checkA - checkB) * this.desc;
                }

                // Compare strings
                if (typeof checkA == 'string'){
                    checkA = checkA.toLowerCase();
                    checkB = checkB.toLowerCase();

                    // Char by char
                    for (var i=0 ; i<checkA.length ; i++){
                        if (checkA[i] < checkB[i])
                            return -1 * this.desc;
                        if (checkA[i] > checkB[i])
                            return 1 * this.desc;
                    }
                    return 0;
                }

                // Compare array lengths
                if (typeof checkA == 'object'){
                    if (checkA.length == checkB.length)
                        return 0;
                    return (checkA.length - checkB.length) * this.desc;
                }
            },

            // Set order & toggle desc
            setOrder(what,desc){
                this.desc = (this.order == what) ? this.desc*=-1 : (desc) ? -1 : 1;
                this.order = what || event.updated_at;
            },

        }

    }
</script>

error: pathspec 'resources/assets/js/mixins/SortingData.vue' did not match any file(s) known to git


Use filterControl to display only the filterable options

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/PayBreak/basket.git/'


Add created_at column as Requested at

Add created_at column as Requested at


Add positive number constraint

Add positive number constraint

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Gamabunta94/GroceryShop.git/'


Add new bitcoin hash

Add new bitcoin hash

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/egoconf/egoconf.git/'


test No1

test No1


music-list

music-list

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/gthulei/qqmusic.git/'


added twitter to contact

added twitter to contact

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/alidcastano/alidcastano.github.io.git/'


correct spacing for login screen inputs

correct spacing for login screen inputs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/aligerum/keyring.git/'


add group-preview component

add group-preview component

<template>

        <sg-avatar :size="64" :entity="group"></sg-avatar>

{{ group.name }}

{{ group.description }}

</template>

error: pathspec 'res/js/content/components/group-preview.vue' did not match any file(s) known to git


Adding support for fetching images from BreweryDB

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/pestbarn/beertasting.git/'


Cleanup layout

Cleanup layout

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/starmage/my-movies.git/'


Update overview

Update overview

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ICJIA/icjia-gata.git/'


Addig text editor component

Addig text editor component

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/soal/spDiary.git/'


Remove mixing used as component

Remove mixing used as component


Fix reports test

Fix reports test

<template>

        <icon :icon="['fas', 'book']" class="fa-sm mr-2"/>
        Reports

      <template v-if="is_staff">
        <select
          v-model="exportFormat"
          class="export-select custom-select form-control-sm w-25 flex-fill mr-2">
          <option value="csv">csv</option>
          <option value="xls">xls</option>
          <option value="tsv">tsv</option>
          <option value="ods">ods</option>
          <option value="json">json</option>
          <option value="yaml">yaml</option>
          <option value="html">html</option>
        </select>
        <button
          id="export-report"
          class="btn btn-light flex-fill text-nowrap mr-2"
          @click="exportReport">
          <icon :icon="['fas', 'download']" class="mr-1"/>
          Export
        </button>
      </template>

      <popover class="flex-fill mr-2">
        <template slot="popover-button">
          <icon :icon="['fas', 'filter']" class="mr-1"/>
          Filter
        </template>
        <template slot="popover-content">
          Filter by column, multiple filters are okay

                User
                <selector
                  id="report-filter-user"
                  v-model="user"
                  :options="users"
                  placeholder="User"
                  allowclear/>

                Client
                <selector
                  id="report-filter-client"
                  v-model="client"
                  :options="clients"
                  placeholder="Client"
                  allowclear/>

                Project
                <selector
                  id="report-filter-project"
                  v-model="project"
                  :options="projects"
                  placeholder="Project"
                  allowclear/>

                Task
                <selector
                  id="report-filter-task"
                  v-model="task"
                  :options="tasks"
                  placeholder="Task"
                  allowclear/>

                Min. Date
                <datepicker
                  id="report-filter-min-date"
                  v-model="minDate"
                  type="text"
                  class="form-control date-input"
                  placeholder="Min. date"
                  allowclear/>

                Max. Date
                <datepicker
                  id="report-filter-max-date"
                  v-model="maxDate"
                  type="text"
                  class="form-control date-input"
                  placeholder="Max. date"
                  allowclear/>

        </template>
      </popover>

      <popover class="flex-fill">
        <template slot="popover-button">
          <icon :icon="['fas', 'sort']" class="mr-1"/>
          Sort
        </template>
        <template slot="popover-content">
          Sort by column and direction

              <selector
                id="report-sort-by"
                v-model="orderBy"
                :options="orderByOptions"
                selected="date"
                placeholder="Order by"/>

              <selector
                id="report-order-dir"
                v-model="orderDir"
                :options="orderDirOptions"
                selected="desc"
                placeholder="Order direction"/>

        </template>
      </popover>

  <template v-if="entries.length">

              <icon :icon="['fas', 'address-book']" class="mr-1"/>
              Client

              <icon :icon="['fas', 'briefcase']" class="mr-1"/>
              Project

              <icon :icon="['fas', 'tasks']" class="mr-1"/>
              Task

              <icon :icon="['fas', 'user-circle']" class="mr-1"/>
              User

              <icon :icon="['fas', 'calendar']" class="mr-1"/>
              Date

              <icon :icon="['fas', 'clock']" class="mr-1"/>
              Duration

              <icon :icon="['fas', 'comment']" class="mr-1"/>
              Note

            {{ entry.project ? getClient(getProject(entry.project).client).name : '' }}
            {{ entry.project ? getProject(entry.project).name : '' }}
            {{ entry.task ? getTask(entry.task).name : '' }}
            {{ entry.user ? getUser(entry.user).username : '' }}
            {{ $moment(entry.date).format('LL') }}
            {{ $moment.duration(entry.duration, 'hours').format('h[h] mm[m]') }}
            {{ entry.note }}

              Count
              {{ entries.length }}

              Sum
              {{ $moment.duration(total, 'hours').format('d[d] h[h] m[m]') }}

  </template>

          Loading...

</template>


Adjust welcome text

Adjust welcome text


add ul to margin zero

add ul to margin zero

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Luncher/BlogAdmin.git/'


Finish (almost) autocomplete/suggest with flashlight and vue-multiselect


Formatting fix

Formatting fix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/LokeCarlsson/lokecarlsson.git/'


tuning slider

tuning slider

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/cold-coder/swm_web.git/'


add atlas

add atlas

<template>

  <foxgis-layout>

        <foxgis-search :placeholder="'搜索'" :value="searchKeyWords" :search-key-words.sync="searchKeyWords"></foxgis-search>
        <mdl-button raised accent v-mdl-ripple-effect>搜索</mdl-button>

          主题词：
          <a v-for="tag in theme_tags" v-if="$index<10"
              @click="conditionClick($event,1)">{{ tag }}

          制图地区：
          <a v-for="location in location_tags" v-if="$index<10"
              @click="conditionClick($event,2)">{{ location }}

          制图年份：
          <a v-for="year in year_tags | orderBy" v-if="$index<10"
              @click="conditionClick($event,3)">{{ year }}

      <div class="mdl-cell mdl-cell--3-col mdl-cell--4-col-tablet mdl-cell--4-col-phone" v-for='u in pageConfig.page_item_num' v-if="((pageConfig.current_page-1)*pageConfig.page_item_num+$index) < displayUploads.length" track-by="$index">
        <foxgis-card>

{{displayUploads[(pageConfig.current_page-1)*pageConfig.page_item_num+$index].name}}

              <mdl-anchor-button accent raised v-mdl-ripple-effect @click="showPreview($event, (pageConfig.current_page-1)*pageConfig.page_item_num+$index)">预览</mdl-anchor-button>

              <mdl-anchor-button colored v-mdl-ripple-effect @click="downloadUpload(displayUploads[(pageConfig.current_page-1)*pageConfig.page_item_num+$index].upload_id)">下载</mdl-anchor-button>

制图地区：{{displayUploads[(pageConfig.current_page-1)*pageConfig.page_item_num+$index].location}}

制图年份：{{displayUploads[(pageConfig.current_page-1)*pageConfig.page_item_num+$index].year}}

        </foxgis-card>

0?true:false">

 1?'':'disabled'">
          navigate_before

{{ pageConfig.first_page + page }}

 1)&&(pageConfig.current_page < parseInt(total_items/pageConfig.page_item_num)+1)?'':'disabled'">
          navigate_next

  </foxgis-layout>

</template>

error: pathspec 'src/views/Atlas.vue' did not match any file(s) known to git


Add a todo

Add a todo


add navigation borders

add navigation borders

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/stefanjudis/stefan-judis-website.git/'


for publish

for publish

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/mulberryx/blog-web.git/'


Added Material component

Added Material component

<template>

              {{material.section}}

{{material.title}}

 Posted by
            <router-link :to="'/profile/' + material.author.user.id">

                  {{material.author.user.username}}

            </router-link>

</template>

error: pathspec 'src/components/materials-components/Material.vue' did not match any file(s) known to git


Vue experiment

Vue experiment

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/itho/home.git/'


Small improvement

Small improvement

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/egoconf/egoconf.git/'


bugFix

bugFix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Esilen/GCBatch.git/'


Made a generic model-selector

Made a generic model-selector

<template>

        {{ trans_choice(model + '.model', 1) }}

            <select name="model" id="model" class="form-control" v-model="selected">
                <option :value="null">{{ trans(model + '.select') }}</option>
                <option v-for="element in models" v-bind:value="element.id">{{ element.name }}</option>
            </select>

</template>

error: pathspec 'resources/assets/js/components/ModelSelector.vue' did not match any file(s) known to git


lint again

lint again

<!--
This template is intended to act as the default wrapper for assessment focused rendering
plugins.

As such, it provides display of current mastery progress, and manages all mastery/attempt log
oriented data synchronization.
-->

<template>

  <div>
    <slot v-if="ready"></slot>
  </div>

</template>

<script>

  const actions = require('kolibri.coreVue.vuex.actions');
  const InteractionTypes = require('kolibri.coreVue.vuex.constants').InteractionTypes;
  const UserKinds = require('kolibri.coreVue.vuex.constants').UserKinds;

  module.exports = {
    props: {
      itemId: {
        type: String,
      },
      masteryModel: {
        type: String,
      },
      masterySpacingTime: {
        type: Number,
      },
      masteryCriterion: {
        type: String,
      },
    },
    data: () => ({
      ready: false,
    }),
    created() {
      this.$on('checkanswer', (correct, complete, firstAttempt, hinted) => {
        this.updateMasetryLogSaveAttemptLog(correct, complete, firstAttempt, hinted);
      });
      this.$on('takehint', (firstAttempt, hinted) => { this.hintTaken(firstAttempt, hinted);});
      this.$on('passexercise', () => { this.exercisePassed();});
      // Once the data for the overall assessment is loaded in the renderer
      // we can initialize the mastery log, as the mastery model and spacing time
      // will be available.
      if (this.isLearner) {
        this.initMasteryLog();
      } else {
        // if userKind is anonymous user or deviceOwner.
        this.createDummyMasteryLogAction(this.Kolibri);
      }
      this.initNewAttemptLog();
    },
    methods: {
      updateMasetryLogSaveAttemptLog(correct, complete, firstAttempt, hinted) {
        this.updateMasteryAttemptStateAction(new Date(), correct, complete, firstAttempt, hinted);
        if (this.masteryLogId || !this.isLearner) {
          this.saveAttemptLogAction(this.Kolibri);
        } else {
          const watchRevoke = this.$watch('masteryLogId', () => {
            if (this.masteryLogId) {
              this.saveAttemptLogAction(this.Kolibri);
              watchRevoke();
            }
          });
        }
      },
      hintTaken(firstAttempt, hinted) {
        this.updateAttemptLogInteractionHistoryAction(InteractionTypes.hint);
        this.updateMasetryLogSaveAttemptLog(0, false, firstAttempt, hinted);
      },
      exercisePassed() {
        if (this.isLearner) {
          this.setMasteryLogCompleteAction(new Date());
          this.saveMasteryLogAction(this.Kolibri);
        }
      },
      initMasteryLog() {
        // Only initialize masteryLogs once the summaryLog is initialized.
        if (!this.summaryLogId) {
          const watchRevoke = this.$watch('summaryLogId', () => {
            if (this.summaryLogId) {
              this.initMasteryLogAction(this.Kolibri, this.masterySpacingTime, this.masteryCriterion);
              watchRevoke();
            }
          });
        } else {
          this.initMasteryLogAction(this.Kolibri, this.masterySpacingTime, this.masteryCriterion);
        }
      },
      initNewAttemptLog() {
        if (this.itemId) {
          // seems sometimes vue does not reset itemId on page reload,
          // therefore the following watch doesn't get triggered and ready is not set properly.
          this.createAttemptLog();
        }
        this.$watch('itemId', () => {
          // every new question has a new attemptlog with the question's itemId
          if (this.itemId) {
            this.createAttemptLog();
          }
        });
      },
      createAttemptLog() {
        this.ready = false;
        if (!this.sessionLogId) {
          const watchRevoke = this.$watch('sessionLogId', () => {
            if (this.sessionLogId) {
              this.createAttemptLogAction(this.Kolibri, this.itemId, this.newAttemptlogReady);
              watchRevoke();
            }
          });
        } else {
          this.createAttemptLogAction(this.Kolibri, this.itemId, this.newAttemptlogReady);
        }
      },
      newAttemptlogReady() {
        this.ready = true;
      },
    },
    computed: {
      isLearner() {
        if (this.userkind.includes(UserKinds.LEARNER)) {
          return true;
        }
        return false;
      },
    },
    vuex: {
      actions: {
        initMasteryLogAction: actions.initMasteryLog,
        createDummyMasteryLogAction: actions.createDummyMasteryLog,
        saveMasteryLogAction: actions.saveMasteryLog,
        setMasteryLogCompleteAction: actions.setMasteryLogComplete,
        createAttemptLogAction: actions.createAttemptLog,
        saveAttemptLogAction: actions.saveAttemptLog,
        updateMasteryAttemptStateAction: actions.updateMasteryAttemptState,
        updateAttemptLogInteractionHistoryAction: actions.updateAttemptLogInteractionHistory,
      },
      getters: {
        summaryLogId: (state) => state.core.logging.summary.id,
        sessionLogId: (state) => state.core.logging.session.id,
        masteryLogId: (state) => state.core.logging.mastery.id,
        attemptLogComplete: (state) => state.core.logging.attempt.complete,
        attemptLogCorrect: (state) => state.core.logging.attempt.correct,
        userkind: (state) => state.core.session.kind,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  @require '~kolibri.styles.coreTheme'

</style>

<!--
This template is intended to act as the default wrapper for assessment focused rendering
plugins.

As such, it provides display of current mastery progress, and manages all mastery/attempt log
oriented data synchronization.
-->

<template>

  <div>
    <slot v-if="ready"></slot>
  </div>

</template>

<script>

  const logging = require('kolibri.lib.logging').getLogger(__filename);
  const actions = require('kolibri.coreVue.vuex.actions');
  const InteractionTypes = require('kolibri.coreVue.vuex.constants').InteractionTypes;
  const UserKinds = require('kolibri.coreVue.vuex.constants').UserKinds;

  module.exports = {
    props: {
      itemId: {
        type: String,
      },
      masteryModel: {
        type: String,
      },
      masterySpacingTime: {
        type: Number,
      },
      masteryCriterion: {
        type: String,
      },
    },
    data: () => ({
      ready: false,
    }),
    created() {
      this.$on('checkanswer', (correct, complete, firstAttempt, hinted) => {
        this.updateMasetryLogSaveAttemptLog(correct, complete, firstAttempt, hinted);
      });
      this.$on('takehint', (firstAttempt, hinted) => { this.hintTaken(firstAttempt, hinted);});
      this.$on('passexercise', () => { this.exercisePassed();});
      // Once the data for the overall assessment is loaded in the renderer
      // we can initialize the mastery log, as the mastery model and spacing time
      // will be available.
      if (this.isLearner) {
        this.initMasteryLog();
      } else {
        // if userKind is anonymous user or deviceOwner.
        this.createDummyMasteryLogAction(this.Kolibri);
      }
      this.initNewAttemptLog();
    },
    methods: {
      updateMasetryLogSaveAttemptLog(correct, complete, firstAttempt, hinted) {
        this.updateMasteryAttemptStateAction(new Date(), correct, complete, firstAttempt, hinted);
        if (this.masteryLogId || !this.isLearner) {
          this.saveAttemptLogAction(this.Kolibri);
        } else {
          const watchRevoke;
          watchRevoke = this.$watch('masteryLogId', () => {
            if (this.masteryLogId) {
              this.saveAttemptLogAction(this.Kolibri);
              watchRevoke();
            }
          });
        }
      },
      hintTaken(firstAttempt, hinted) {
        this.updateAttemptLogInteractionHistoryAction(InteractionTypes.hint);
        this.updateMasetryLogSaveAttemptLog(0, false, firstAttempt, hinted);
      },
      exercisePassed() {
        if (this.isLearner) {
          this.setMasteryLogCompleteAction(new Date());
          this.saveMasteryLogAction(this.Kolibri);
        }
      },
      initMasteryLog() {
        // Only initialize masteryLogs once the summaryLog is initialized.
        if (!this.summaryLogId) {
          const watchRevoke;
          watchRevoke = this.$watch('summaryLogId', () => {
            if (this.summaryLogId) {
              this.initMasteryLogAction(this.Kolibri, this.masterySpacingTime, this.masteryCriterion);
              watchRevoke();
            }
          });
        } else {
          this.initMasteryLogAction(this.Kolibri, this.masterySpacingTime, this.masteryCriterion);
        }
      },
      initNewAttemptLog() {
        if (this.itemId) {
          // seems sometimes vue does not reset itemId on page reload,
          // therefore the following watch doesn't get triggered and ready is not set properly.
          this.createAttemptLog();
        }
        this.$watch('itemId', () => {
          // every new question has a new attemptlog with the question's itemId
          if (this.itemId) {
            this.createAttemptLog();
          }
        });
      },
      createAttemptLog() {
        this.ready = false;
        if (!this.sessionLogId) {
          const watchRevoke;
          watchRevoke = this.$watch('sessionLogId', () => {
            if (this.sessionLogId) {
              this.createAttemptLogAction(this.Kolibri, this.itemId, this.newAttemptlogReady);
              watchRevoke();
            }
          });
        } else {
          this.createAttemptLogAction(this.Kolibri, this.itemId, this.newAttemptlogReady);
        }
      },
      newAttemptlogReady() {
        this.ready = true;
      },
    },
    computed: {
      isLearner() {
        if (this.userkind.includes(UserKinds.LEARNER)) {
          return true;
        }
        return false;
      },
    },
    vuex: {
      actions: {
        initMasteryLogAction: actions.initMasteryLog,
        createDummyMasteryLogAction: actions.createDummyMasteryLog,
        saveMasteryLogAction: actions.saveMasteryLog,
        setMasteryLogCompleteAction: actions.setMasteryLogComplete,
        createAttemptLogAction: actions.createAttemptLog,
        saveAttemptLogAction: actions.saveAttemptLog,
        updateMasteryAttemptStateAction: actions.updateMasteryAttemptState,
        updateAttemptLogInteractionHistoryAction: actions.updateAttemptLogInteractionHistory,
      },
      getters: {
        summaryLogId: (state) => state.core.logging.summary.id,
        sessionLogId: (state) => state.core.logging.session.id,
        masteryLogId: (state) => state.core.logging.mastery.id,
        attemptLogComplete: (state) => state.core.logging.attempt.complete,
        attemptLogCorrect: (state) => state.core.logging.attempt.correct,
        userkind: (state) => state.core.session.kind,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  @require '~kolibri.styles.coreTheme'

</style>


adds component ffor login header

adds component ffor login header

<template>
  <div
    class="apos-login__header"
    :class="{'apos-login__header--tiny': tiny}"
  >
    <label
      class="apos-login__project apos-login__project-env"
      :class="[`apos-login__project-env--${env}`]"
    >
      {{ env }}
    </label>
    <label class="apos-login__project apos-login__project-name">
      {{ name }}
    </label>
    <label class="apos-login--error">
      {{ error }}
    </label>
  </div>
</template>

<script>

export default {
  props: {
    env: {
      type: String,
      default: ''
    },
    name: {
      type: String,
      default: ''
    },
    tiny: {
      type: Boolean,
      default: false
    },
    error: {
      type: String,
      default: ''
    }
  }
};
</script>
<style scoped lang='scss'>

.apos-login {

  &__header {
    z-index: $z-index-manager-display;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: start;
    width: max-content;
  }

  &__project-name {
    @include type-display;
    margin: 0;
    color: var(--a-text-primary);
    text-transform: capitalize;
  }

  &__project-env {
    @include type-base;
    text-transform: capitalize;
    padding: 6px 12px;
    color: var(--a-white);
    background: var(--a-success);
    border-radius: 5px;
    margin-bottom: 15px;

    &--development {
      background: var(--a-danger);
    }

    &--success {
      background: var(--a-warning);
    }
  }

  &--error {
    @include type-help;
    color: var(--a-danger);
    min-height: 13px;
    margin-top: 20px;
    margin-bottom: 15px;
  }

  &__header--tiny {
    flex-direction: row;
    color: #F8F9FA;

    .apos-login__project {
      opacity: 0.7
    }

    .apos-login__project-name {
      font-size: 21px;

    }

    .apos-login__project-env {
      margin-right: 10px;
    }
  }
}
</style>

error: pathspec 'modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue' did not match any file(s) known to git