fix for memberships panel

fix for memberships panel

<script lang="coffee">
import Records        from '@/shared/services/records'
import AbilityService from '@/shared/services/ability_service'
import ModalService   from '@/shared/services/modal_service'
import RecordLoader   from '@/shared/services/record_loader'
import AnnouncementModalMixin from '@/mixins/announcement_modal'
import { approximate } from '@/shared/helpers/format_time'

export default
  mixins: [AnnouncementModalMixin]
  props:
    group: Object
    pending: Boolean
  data: ->
    fragment: ''
    plusUser: Records.users.build(avatarKind: 'mdi-plus')
    fetched: false
    searchOpen: false
    loader: new RecordLoader
      collection: 'memberships'
      params:
        per: 20
        pending: @pending
        group_id: @group.id

  methods:
    approximate: approximate

    show: ->
      return false if (@recordCount() == 0 && @pending)
      @initialFetch() if @canView()
      @canView()

    canView: ->
      if @pending
        AbilityService.canViewPendingMemberships(@group)
      else
        AbilityService.canViewMemberships(@group)

    recordCount: ->
      if @pending
        @group.pendingMembershipsCount
      else
        @group.membershipsCount - @group.pendingMembershipsCount

    toggleSearch: ->
      @fragment = ''
      @searchOpen = !@searchOpen
      @$nextTick => document.querySelector('.membership-card__search input').focus()

    showLoadMore: ->
      @loader.numRequested < @recordCount() && _.isEmpty @fragment && !@loader.loading

    canAddMembers: ->
      AbilityService.canAddMembers(@group.targetModel().group() || @group) && !@pending

    memberships: ->
      if !_.isEmpty @fragment
        _.filter @records(), (membership) =>
          _.includes membership.userName().toLowerCase(), @fragment.toLowerCase()
      else
        @records()

    orderedMemberships: ->
      limit = @loader.numRequested || 10
      _.slice(_.orderBy(@memberships(), ['-admin', '-createdAt']), 0, limit)

    recordsDisplayed: ->
      _.min [@loader.numRequested, @recordCount()]

    initialFetch: ->
      @loader.fetchRecords(per: 4) unless @fetched
      @fetched = true

    records: ->
      if @pending
        Records.memberships.find(groupId: @group.id, acceptedAt: null)
      else
        Records.memberships.find(groupId: @group.id, acceptedAt: {$ne: null})

    invite: ->
      @openAnnouncementModal(Records.announcements.buildFromModel(@group.targetModel()))

    fetchMemberships: ->
      return unless !_.isEmpty @fragment
      Records.memberships.fetchByNameFragment(@fragment, @group.key)

    cardTitle: ->
      if @pending
        'membership_card.invitations'
      else
        "membership_card.#{@group.targetModel().constructor.singular}_members"

  computed:
    pollType: ->
      @$t(@group.targetModel().pollTypeKey()) if @group.targetModel().isA('poll')
  watch:
    fragment: ->
      @fetchMemberships()
</script>

<template lang="pug">
v-card.membership-card.lmo-no-print(v-if='show()', :class="{'membership-card--pending': pending}")
  v-list(two-line avatar)
    v-list-item(v-if="!searchOpen")
      v-list-item-content
        span.grey--text(v-t='{ path: cardTitle(), args: { values: { pollType: pollType } } }')
      v-list-item-action
        v-btn.membership-card__search-button(icon @click="toggleSearch()")
          v-icon mdi-magnify

    v-list-item(v-if="searchOpen")
      v-text-field.membership-card__filter(autofocus v-model="fragment" :placeholder="$t('memberships_page.fragment_placeholder')")
        template(slot="append")
          v-btn(icon @click="toggleSearch()")
            v-icon mdi-close

    v-list-item.membership-card__membership.membership-card__invite(v-if='!searchOpen && canAddMembers()', @click="invite()")
      v-list-item-avatar
        v-avatar(:size='40')
          v-icon(color="primary") mdi-plus
      v-list-item-content
        v-list-item-title(v-t="'membership_card.invite_to_' + group.targetModel().constructor.singular")

    v-list-item(v-for='membership in orderedMemberships()', :key='membership.id', data-username='membership.user().username')
      v-list-item-avatar
        user-avatar(:user='membership.user()', size='forty', :coordinator='membership.admin', :no-link='!membership.acceptedAt')
      v-list-item-content
        v-list-item-title {{membership.userName() || membership.user().email }}
        v-list-item-subtitle.membership-card__last-seen
          span(v-if='membership.user().lastSeenAt', v-t="{ path: 'user_page.online_field', args: { value: approximate(membership.user().lastSeenAt) } }")
          span(v-if='!membership.acceptedAt', v-t="{ path: 'user_page.invited', args: { value: approximate(membership.user().createdAt) } }")
      v-list-item-action
        membership-dropdown(:membership="membership")
    loading(v-if='loader.loading')
  v-card-actions(v-if='showLoadMore()')
    v-btn(text color="accent", v-if='showLoadMore()', @click='loader.loadMore()', v-t="'common.action.load_more'")
    span {{recordsDisplayed()}} / {{recordCount()}}
</template>

<script lang="coffee">
import Records        from '@/shared/services/records'
import AbilityService from '@/shared/services/ability_service'
import ModalService   from '@/shared/services/modal_service'
import RecordLoader   from '@/shared/services/record_loader'
import AnnouncementModalMixin from '@/mixins/announcement_modal'
import { approximate } from '@/shared/helpers/format_time'

export default
  mixins: [AnnouncementModalMixin]
  props:
    group: Object
    pending: Boolean
  data: ->
    fragment: ''
    plusUser: Records.users.build(avatarKind: 'mdi-plus')
    fetched: false
    searchOpen: false
    loader: new RecordLoader
      collection: 'memberships'
      params:
        per: 20
        pending: @pending
        group_id: @group.id

  methods:
    approximate: approximate

    show: ->
      return false if (@recordCount() == 0 && @pending)
      @initialFetch() if @canView()
      @canView()

    canView: ->
      if @pending
        AbilityService.canViewPendingMemberships(@group)
      else
        AbilityService.canViewMemberships(@group)

    recordCount: ->
      if @pending
        @group.pendingMembershipsCount
      else
        @group.membershipsCount - @group.pendingMembershipsCount

    toggleSearch: ->
      @fragment = ''
      @searchOpen = !@searchOpen
      @$nextTick => document.querySelector('.membership-card__search input').focus()

    showLoadMore: ->
      @loader.numRequested < @recordCount() && _.isEmpty @fragment && !@loader.loading

    canAddMembers: ->
      AbilityService.canAddMembers(@group.targetModel().group() || @group) && !@pending

    memberships: ->
      if !_.isEmpty @fragment
        _.filter @records(), (membership) =>
          _.includes membership.userName().toLowerCase(), @fragment.toLowerCase()
      else
        @records()

    orderedMemberships: ->
      limit = @loader.numRequested || 10
      _.slice(_.orderBy(@memberships(), ['-admin', '-createdAt']), 0, limit)

    recordsDisplayed: ->
      _.min [@loader.numRequested, @recordCount()]

    initialFetch: ->
      @loader.fetchRecords(per: 4) unless @fetched
      @fetched = true

    records: ->
      if @pending
        @group.pendingMemberships()
      else
        @group.activeMemberships()

    invite: ->
      @openAnnouncementModal(Records.announcements.buildFromModel(@group.targetModel()))

    fetchMemberships: ->
      return unless !_.isEmpty @fragment
      Records.memberships.fetchByNameFragment(@fragment, @group.key)

    cardTitle: ->
      if @pending
        'membership_card.invitations'
      else
        "membership_card.#{@group.targetModel().constructor.singular}_members"

  computed:
    pollType: ->
      @$t(@group.targetModel().pollTypeKey()) if @group.targetModel().isA('poll')
  watch:
    fragment: ->
      @fetchMemberships()
</script>

<template lang="pug">
v-card.membership-card.lmo-no-print(v-if='show()', :class="{'membership-card--pending': pending}")
  v-list(two-line avatar)
    v-list-item(v-if="!searchOpen")
      v-list-item-content
        span.grey--text(v-t='{ path: cardTitle(), args: { values: { pollType: pollType } } }')
      v-list-item-action
        v-btn.membership-card__search-button(icon @click="toggleSearch()")
          v-icon mdi-magnify

    v-list-item(v-if="searchOpen")
      v-text-field.membership-card__filter(autofocus v-model="fragment" :placeholder="$t('memberships_page.fragment_placeholder')")
        template(slot="append")
          v-btn(icon @click="toggleSearch()")
            v-icon mdi-close

    v-list-item.membership-card__membership.membership-card__invite(v-if='!searchOpen && canAddMembers()', @click="invite()")
      v-list-item-avatar
        v-avatar(:size='40')
          v-icon(color="primary") mdi-plus
      v-list-item-content
        v-list-item-title(v-t="'membership_card.invite_to_' + group.targetModel().constructor.singular")

    v-list-item(v-for='membership in orderedMemberships()', :key='membership.id', data-username='membership.user().username')
      v-list-item-avatar
        user-avatar(:user='membership.user()', size='forty', :coordinator='membership.admin', :no-link='!membership.acceptedAt')
      v-list-item-content
        v-list-item-title {{membership.userName() || membership.user().email }}
        v-list-item-subtitle.membership-card__last-seen
          span(v-if='membership.user().lastSeenAt', v-t="{ path: 'user_page.online_field', args: { value: approximate(membership.user().lastSeenAt) } }")
          span(v-if='!membership.acceptedAt', v-t="{ path: 'user_page.invited', args: { value: approximate(membership.user().createdAt) } }")
      v-list-item-action
        membership-dropdown(:membership="membership")
    loading(v-if='loader.loading')
  v-card-actions(v-if='showLoadMore()')
    v-btn(text color="accent", v-if='showLoadMore()', @click='loader.loadMore()', v-t="'common.action.load_more'")
    span {{recordsDisplayed()}} / {{recordCount()}}
</template>


[schedule] Add week option to the production schedule

<template>

          {{ $t('main.start_date') }}

        <datepicker
          wrapper-class="datepicker"
          input-class="date-input input"
          :language="locale"
          :disabled-dates="{ days: [6, 0] }"
          :monday-first="true"
          format="yyyy-MM-dd"
          v-model="selectedStartDate"
        />

          {{ $t('main.end_date') }}

        <datepicker
          wrapper-class="datepicker"
          input-class="date-input input"
          :language="locale"
          :disabled-dates="{ days: [6, 0] }"
          :monday-first="true"
          format="yyyy-MM-dd"
          v-model="selectedEndDate"
        />

      <text-field
        class="flexrow-item overall-man-days"
        type="number"
        v-model="overallManDays"
        :label="$t('schedule.overall_man_days')"
        :disabled="!isCurrentUserAdmin"
        v-show="false"
      />
      <combobox-number
        class="flexrow-item zoom-level"
        :label="$t('schedule.zoom_level')"
        :options="zoomOptions"
        v-model="zoomLevel"
      />

    <schedule
      :start-date="startDate"
      :end-date="endDate"
      :hierarchy="scheduleItems"
      :zoom-level="zoomLevel"
      :is-loading="loading.schedule"
      :is-error="errors.schedule"
      :hide-man-days="true"
      @item-changed="scheduleItemChanged"
      @estimation-changed="estimationChanged"
      @change-zoom="changeZoom"
      @root-element-expanded="expandTaskTypeElement"
    />

    <task-info
      :task="currentTask"
      :is-loading="false"
    />

</template>

<template>

          {{ $t('main.start_date') }}

        <datepicker
          wrapper-class="datepicker"
          input-class="date-input input"
          :language="locale"
          :disabled-dates="{ days: [6, 0] }"
          :monday-first="true"
          format="yyyy-MM-dd"
          v-model="selectedStartDate"
        />

          {{ $t('main.end_date') }}

        <datepicker
          wrapper-class="datepicker"
          input-class="date-input input"
          :language="locale"
          :disabled-dates="{ days: [6, 0] }"
          :monday-first="true"
          format="yyyy-MM-dd"
          v-model="selectedEndDate"
        />

      <text-field
        class="flexrow-item overall-man-days"
        type="number"
        v-model="overallManDays"
        :label="$t('schedule.overall_man_days')"
        :disabled="!isCurrentUserAdmin"
        v-focus
      />
      <combobox-number
        class="flexrow-item zoom-level"
        :label="$t('schedule.zoom_level')"
        :options="zoomOptions"
        v-model="zoomLevel"
      />

    <schedule
      :start-date="startDate"
      :end-date="endDate"
      :hierarchy="scheduleItems"
      :zoom-level="zoomLevel"
      :is-loading="loading.schedule"
      :is-error="errors.schedule"
      @item-changed="scheduleItemChanged"
      @estimation-changed="estimationChanged"
      @change-zoom="changeZoom"
      @root-element-expanded="expandTaskTypeElement"
    />

    <task-info
      :task="currentTask"
      :is-loading="false"
    />

</template>


Fix user padding on mobile width

Fix user padding on mobile width


Chat scroll bottom on load

Chat scroll bottom on load

<template>

{{ trans('Chat') }}

{{ trans('General') }}  0">{{ chat.unread_tab.general }}

{{ trans('Team') }}  0">{{ chat.unread_tab.team }}

{{ tab.name }}  0">{{ chat.unread_tab[tab.id] }} ×

                                                {{ trans(msg.text, msg.context) }}

                                                {{ msg.author_name }} :
                                                {{ msg.text }}

                                    <input id="input-msg" type="text" autocomplete="off" v-model="message" autofocus @keyup="keyup($event)"/>

                                <button class="button success small-10 large-3 round" @click="send()">

                                    {{ trans('Send') }}
                                </button>
                                <button class="button alert small-10 large-3 round" @click="close()">

                                    {{ trans('Close') }}
                                </button>

</template>


Fix language dropdown width

Fix language dropdown width

<template>

  <VAutocomplete
    v-model="language"
    class="language-dropdown"
    v-bind="$attrs"
    :items="languages"
    :label="$tr('labelText')"
    color="primary"
    itemValue="id"
    :itemText="languageText"
    autoSelectFirst
    :allowOverflow="false"
    persistentHint
    :rules="rules"
    :required="required"
    :no-data-text="$tr('noDataText')"
    :search-input.sync="input"
    :menu-props="menuProps"
    @change="input=''"
  >
    <template #item="{item}">
      <VTooltip bottom>
        <template v-slot:activator="{ on }">
          {{ languageText(item) }}
        </template>
        {{ languageText(item) }}
      </VTooltip>

    </template>
  </VAutocomplete>

</template>

<template>

  <VAutocomplete
    v-model="language"
    class="language-dropdown"
    v-bind="$attrs"
    :items="languages"
    :label="$tr('labelText')"
    color="primary"
    itemValue="id"
    :itemText="languageText"
    autoSelectFirst
    :allowOverflow="false"
    persistentHint
    :rules="rules"
    :required="required"
    :no-data-text="$tr('noDataText')"
    :search-input.sync="input"
    @change="input=''"
  />

</template>

<script>

  import Constants from 'edit_channel/constants/index';

  export default {
    name: 'LanguageDropdown',
    props: {
      value: {
        type: [String, Array],
        required: false,
      },
      required: {
        type: Boolean,
        default: false,
      },
      excludeLanguages: {
        type: Array,
        default() {
          return [];
        },
      },
    },
    data() {
      return {
        input: '',
      };
    },
    computed: {
      language: {
        get() {
          return this.value;
        },
        set(value) {
          this.$emit('input', value);
        },
      },
      languages() {
        return Constants.Languages.filter(
          l => !this.excludeLanguages.includes(l.id)
        ).sort((langA, langB) => langA.native_name.localeCompare(langB.native_name));
      },
      rules() {
        return this.required ? [v => Boolean(v) || this.$tr('languageRequired')] : [];
      },
    },
    methods: {
      languageText(item) {
        return this.$tr('languageItemText', { language: item.native_name, code: item.id });
      },
    },
    $trs: {
      labelText: 'Language',
      languageItemText: '{language} ({code})',
      languageRequired: 'Language is required',
      noDataText: 'No languages found',
    },
  };

</script>

<style lang="less" scoped>

  .language-dropdown {
    display: inline-block;
    width: 100%;
  }

</style>


fix lint issues

fix lint issues

Signed-off-by: Rohit Yadav <902b9eb5dd62819678163bd5e00fbf0cbc7c264b@shapeblue.com>

<template>

              {{ zone.name }}

            <template slot="title">
              Fetch Latest
            </template>

            <template slot="title">
              View Alerts
            </template>

              <router-link :to="{ name: 'alert' }">

              </router-link>

            <template slot="title">
              View Hosts in Alert State
            </template>

              <router-link :to="{ name: 'host', query: {'state': 'Alert'} }">

              </router-link>

          <chart-card :loading="loading">

{{ stat.name }}

            <template slot="footer">{{ stat.capacityused }} / {{ stat.capacitytotal }}</template>
          </chart-card>

      <chart-card>

          <router-link :to="{ name: 'event' }">View Events</router-link>

        <template slot="footer">

                {{ event.created }}

                {{ event.type }}

                ({{ event.username }}) {{ event.description }}

        </template>
      </chart-card>

</template>

<template>

              {{ zone.name }}

            <template slot='title'>
              Fetch Latest
            </template>

            <template slot='title'>
              View Alerts
            </template>

              <router-link :to="{ name: 'alert' }">

              </router-link>

            <template slot='title'>
              View Hosts in Alert State
            </template>

              <router-link :to="{ name: 'host', query: {'state': 'Alert'} }">

              </router-link>

          <chart-card :loading="loading">

{{ stat.name }}

            <template slot="footer">{{ stat.capacityused }} / {{ stat.capacitytotal }}</template>
          </chart-card>

      <chart-card>

          <router-link :to="{ name: 'event' }">View Events</router-link>

        <template slot="footer">

                {{ event.created }}

                {{ event.type }}

                ({{ event.username }}) {{ event.description }}

        </template>
      </chart-card>

</template>


:art:

:art:

<template>
<ui-card>
	<template #title><fa icon="user"/> {{ $t('title') }}</template>

			<mk-avatar class="avatar" :user="$store.state.i" :disable-preview="true" :disable-link="true"/>

		<ui-form :disabled="saving">
			<ui-input v-model="name" :max="30">
				{{ $t('name') }}
			</ui-input>

			<ui-input v-model="username" readonly>
				{{ $t('account') }}
				<template #prefix>@</template>
				<template #suffix>@{{ host }}</template>
			</ui-input>

			<ui-input v-model="location">
				{{ $t('location') }}
				<template #prefix><fa icon="map-marker-alt"/></template>
			</ui-input>

			<ui-input v-model="birthday" type="date">
				<template #title>{{ $t('birthday') }}</template>
				<template #prefix><fa icon="birthday-cake"/></template>
			</ui-input>

			<ui-textarea v-model="description" :max="500">
				{{ $t('description') }}
				<template #desc>{{ $t('you-can-include-hashtags') }}</template>
			</ui-textarea>

			<ui-select v-model="lang">
				<template #label>{{ $t('language') }}</template>
				<template #icon><fa icon="language"/></template>
				<option v-for="lang in unique(Object.values(langmap).map(x => x.nativeName)).map(name => Object.keys(langmap).find(k => langmap[k].nativeName == name))" :value="lang" :key="lang">{{ langmap[lang].nativeName }}</option>
			</ui-select>

			<ui-input type="file" @change="onAvatarChange">
				{{ $t('avatar') }}
				<template #icon><fa icon="image"/></template>
				<template #desc v-if="avatarUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
			</ui-input>

			<ui-input type="file" @change="onBannerChange">
				{{ $t('banner') }}
				<template #icon><fa icon="image"/></template>
				<template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
			</ui-input>

				{{ $t('profile-metadata') }}
				<ui-horizon-group>
					<ui-input v-model="fieldName0">{{ $t('metadata-label') }}</ui-input>
					<ui-input v-model="fieldValue0">{{ $t('metadata-content') }}</ui-input>
				</ui-horizon-group>
				<ui-horizon-group>
					<ui-input v-model="fieldName1">{{ $t('metadata-label') }}</ui-input>
					<ui-input v-model="fieldValue1">{{ $t('metadata-content') }}</ui-input>
				</ui-horizon-group>
				<ui-horizon-group>
					<ui-input v-model="fieldName2">{{ $t('metadata-label') }}</ui-input>
					<ui-input v-model="fieldValue2">{{ $t('metadata-content') }}</ui-input>
				</ui-horizon-group>
				<ui-horizon-group>
					<ui-input v-model="fieldName3">{{ $t('metadata-label') }}</ui-input>
					<ui-input v-model="fieldValue3">{{ $t('metadata-content') }}</ui-input>
				</ui-horizon-group>

			<ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
		</ui-form>

		<fa :icon="faCogs"/> {{ $t('advanced') }}

			<ui-switch v-model="isCat" @change="save(false)">{{ $t('is-cat') }}</ui-switch>
			<ui-switch v-model="isBot" @change="save(false)">{{ $t('is-bot') }}</ui-switch>
			<ui-switch v-model="alwaysMarkNsfw">{{ $t('@._settings.always-mark-nsfw') }}</ui-switch>

		<fa :icon="faUnlockAlt"/> {{ $t('privacy') }}

			<ui-switch v-model="isLocked" @change="save(false)">{{ $t('is-locked') }}</ui-switch>
			<ui-switch v-model="carefulBot" :disabled="isLocked" @change="save(false)">{{ $t('careful-bot') }}</ui-switch>
			<ui-switch v-model="autoAcceptFollowed" :disabled="!isLocked && !carefulBot" @change="save(false)">{{ $t('auto-accept-followed') }}</ui-switch>

		<fa :icon="faEnvelope"/> {{ $t('email') }}

			<template v-if="$store.state.i.email != null">
				<ui-info v-if="$store.state.i.emailVerified">{{ $t('email-verified') }}</ui-info>
				<ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info>
			</template>
			<ui-input v-model="email" type="email">{{ $t('email-address') }}</ui-input>
			<ui-button @click="updateEmail()" :disabled="email === $store.state.i.email"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>

		<fa :icon="faBoxes"/> {{ $t('export-and-import') }}

			<ui-select v-model="exportTarget">
				<option value="notes">{{ $t('export-targets.all-notes') }}</option>
				<option value="following">{{ $t('export-targets.following-list') }}</option>
				<option value="mute">{{ $t('export-targets.mute-list') }}</option>
				<option value="blocking">{{ $t('export-targets.blocking-list') }}</option>
				<option value="user-lists">{{ $t('export-targets.user-lists') }}</option>
			</ui-select>
			<ui-horizon-group class="fit-bottom">
				<ui-button @click="doExport()"><fa :icon="faDownload"/> {{ $t('export') }}</ui-button>
				<ui-button @click="doImport()" :disabled="!['following', 'user-lists'].includes(exportTarget)"><fa :icon="faUpload"/> {{ $t('import') }}</ui-button>
			</ui-horizon-group>

			{{ $t('danger-zone') }}
			<ui-button @click="deleteAccount()">{{ $t('delete-account') }}</ui-button>

</ui-card>
</template>


[gui] Fix memory view

[gui] Fix memory view

<template>
  <v-app id="simulator" dark>

    <v-navigation-drawer
      fixed
      mini-variant
      permanent
      app
    >
      <v-list two-line>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="openFile()">
            <v-list-tile-action>
              <v-icon large>folder_open</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Open File
          <v-list-tile slot="activator" @click="toggleSimulator()">
            <v-list-tile-action>
              <v-icon v-if="!sim.running" large color="green accent-2">play_arrow</v-icon>
              <v-icon v-else large color="red accent-2">pause</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Run
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>refresh</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Restart
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>redo</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Over
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>arrow_downward</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step In
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>arrow_upward</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Out
        </v-tooltip>
      </v-list>
    </v-navigation-drawer>

    <v-content>
      <v-container fluid fill-height>
        <v-layout row wrap>
          <v-flex xs12 shrink class="simulator-wrapper">

                Registers
                <v-data-table hide-headers hide-actions :items="sim.regs">
                  <template slot="items" slot-scope="props">

{{ props.item.name.toUpperCase() }}

                        <v-edit-dialog lazy>
                          {{ toHex(props.item.value) }}
                          <v-text-field
                            slot="input" label="Hex Value"
                            v-bind:value="toHex(props.item.value)"
                            @change="setDataValue($event, props.item)"
                            :rules="[rules.hex]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field
                            slot="input" label="Decimal Value"
                            v-bind:value="props.item.value"
                            @change="setDataValue($event, props.item)"
                            :rules="[rules.dec]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        CC: {{ PSRToCC(props.item.value) }}

                  </template>
                </v-data-table>

Console

              Memory

                <v-layout row wrap>
                    <v-text-field label="Jump To Location" @change="jumpToMemView"></v-text-field>
                    <v-spacer></v-spacer>
                    <v-btn icon @click="jumpToPrevMemView"><v-icon>arrow_back</v-icon></v-btn>
                    <v-btn icon @click="jumpToNextMemView"><v-icon>arrow_forward</v-icon></v-btn>
                </v-layout>

                <v-data-table hide-headers hide-actions :items="mem_view.data">
                  <template slot="items" slot-scope="props">

{{ toHex(props.item.addr) }}

                        <v-edit-dialog lazy>
                          {{ toHex(props.item.value) }}
                          <v-text-field
                            slot="input"
                            v-bind:value="toHex(props.item.value)"
                            @change="setDataValue($event, props.item)"
                            :rules="[rules.hex]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field
                            slot="input" label="Decimal Value"
                            v-bind:value="props.item.value"
                            @change="setDataValue($event, props.item)"
                            :rules="[rules.dec]"
                          >
                          </v-text-field>
                        </v-edit-dialog>

                        {{ props.item.line }}

                  </template>
                </v-data-table>

              Instructions executed: {{ inst_executed }}

          </v-flex>
        </v-layout>
      </v-container>
    </v-content>

  </v-app>
</template>

<template>
  <v-app id="simulator" dark>

    <v-navigation-drawer
      v-model="drawer"
      fixed
      mini-variant
      permanent
      app
    >
      <v-list two-line>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="openFile()">
            <v-list-tile-action>
              <v-icon large>folder_open</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Open File
          <v-list-tile slot="activator" @click="toggleSimulator()">
            <v-list-tile-action>
              <v-icon v-if="!sim.running" large color="green accent-2">play_arrow</v-icon>
              <v-icon v-else large color="red accent-2">pause</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Run
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>refresh</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Restart
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>redo</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Over
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>arrow_downward</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step In
        </v-tooltip>
        <v-tooltip right>
          <v-list-tile slot="activator" @click="">
            <v-list-tile-action>
              <v-icon large>arrow_upward</v-icon>
            </v-list-tile-action>
          </v-list-tile>
          Step Out
        </v-tooltip>
      </v-list>
    </v-navigation-drawer>

    <v-content>
      <v-container fluid fill-height>
        <v-layout row wrap>
          <v-flex xs12 shrink class="simulator-wrapper">

                Registers
                <v-data-table hide-headers hide-actions :items="sim.regs">
                  <template slot="items" slot-scope="props">

{{ props.item.name.toUpperCase() }}

                        <v-edit-dialog lazy>
                          {{ "0x" + props.item.value.toString(16) }}
                          <v-text-field slot="input" label="Hex Value" v-bind:value="'0x'+props.item.value.toString(16)" @change="setRegHex($event, props.item)">
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog lazy>
                          {{ props.item.value }}
                          <v-text-field slot="input" label="Decimal Value" v-bind:value="props.item.value" @change="setRegDec($event, props.item)">
                          </v-text-field>
                        </v-edit-dialog>

                        CC: {{ PSRToCC(props.item.value) }}

                  </template>
                </v-data-table>

                Console

              Memory

                <v-layout row wrap>
                    <v-text-field solo label="Jump To Location" @change="jumpToMemView"></v-text-field>
                    <v-spacer></v-spacer>
                    <v-btn icon @click="jumpToPrevMemView"><v-icon>arrow_back</v-icon></v-btn>
                    <v-btn icon @click="jumpToNextMemView"><v-icon>arrow_forward</v-icon></v-btn>
                </v-layout>

                <v-data-table hide-headers hide-actions :items="mem_view.data">
                  <template slot="items" slot-scope="props">

{{ "0x" +  props.item.addr.toString(16) }}

                        <v-edit-dialog :return-value.sync="props.item.value" lazy>
                          {{ "0x" + props.item.value.toString(16) }}
                          <v-text-field slot="input" label="Hex Value" v-model="props.item.value">
                          </v-text-field>
                        </v-edit-dialog>

                        <v-edit-dialog :return-value.sync="props.item.value" lazy>
                          {{ props.item.value }}
                          <v-text-field slot="input" label="Decimal Value" v-model="props.item.value">
                          </v-text-field>
                        </v-edit-dialog>

                        {{ props.item.line }}

                  </template>
                </v-data-table>

              Instructions executed: {{ inst_executed }}

          </v-flex>
        </v-layout>
      </v-container>
    </v-content>

  </v-app>
</template>


Fix #539

Fix #539

<!--
Displays a field as a color picker (can be used with v-model)
-->
<template>
  <base-picker
    icon="mdi-palette"
    :icon-color="value"
    :value="value"
    v-bind="$attrs"
    :parse-picker="parsePicker"
    @input="$emit('input', $event)">
    <template slot-scope="picker">
      <v-card>
        <v-color-picker v-if="picker.showPicker"
                        :value="picker.value"
                        flat
                        @input="picker.on.input" />
        <v-spacer />
        <v-btn text color="primary" @click="picker.on.close">Cancel</v-btn>
        <v-btn text color="primary" @click="picker.on.save">OK</v-btn>
      </v-card>
    </template>

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

<script>
import BasePicker from './BasePicker'

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

<style scoped>
</style>

<!--
Displays a field as a color picker (can be used with v-model)
-->
<template>
  <base-picker
    icon="mdi-palette"
    :icon-color="value"
    :value="value"
    v-bind="$attrs"
    @input="$emit('input', $event)">
    <template slot-scope="picker">
      <v-card>
        <v-color-picker v-if="picker.showPicker"
                        :value="picker.localValue"
                        flat
                        @input="picker.on.input" />
        <v-spacer />
        <v-btn text color="primary" @click="picker.on.close">Cancel</v-btn>
        <v-btn text color="primary" @click="picker.on.save">OK</v-btn>
      </v-card>
    </template>

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

<script>
import BasePicker from './BasePicker'

export default {
  name: 'ColorPicker',
  components: { BasePicker },
  props: {
    value: { type: String, required: true }
  }
}
</script>

<style scoped>
</style>


fix: 修复 app-v3 cover-view 组件内容无法更新的问题

fix: 修复 app-v3 cover-view 组件内容无法更新的问题

<script>
import native from '../../mixins/native'
import cover from '../../mixins/cover'

export default {
  name: 'CoverView',
  mixins: [native, cover],
  props: {},
  data () {
    return {
      coverType: 'text',
      coverContent: ''
    }
  },
  render (createElement) {
    let coverContent = ''
    const $slots = this.$slots.default || []
    $slots.forEach(node => {
      if (!node.tag) {
        coverContent += node.text || ''
      }
    })
    this.coverContent = coverContent
    return createElement('uni-cover-view', {
      on: {
        on: this.$listeners
      }
    }, [createElement('div', {
      ref: 'container',
      staticClass: 'uni-cover-view'
    }, [coverContent])])
  }
}
</script>
<style>
uni-cover-view {
  display: block;
  line-height: 1.2;
  overflow: hidden;
  white-space: nowrap;
  pointer-events: auto;
}

uni-cover-view[hidden] {
  display: none;
}

uni-cover-view .uni-cover-view {
  width: 100%;
  height: 100%;
}
</style>

<template>
  <uni-cover-view
    v-on="$listeners">
    <div
      ref="container"
      class="uni-cover-view"
      v-text="coverContent"/>
  </uni-cover-view>
</template>
<script>
import native from '../../mixins/native'
import cover from '../../mixins/cover'

export default {
  name: 'CoverView',
  mixins: [native, cover],
  props: {},
  data () {
    return {
      coverType: 'text',
      coverContent: ''
    }
  },
  watch: {},
  mounted () {
    this._updateContent()
    this.$watch('$slot', this._updateContent)
  },
  methods: {
    _updateContent (val) {
      const $slots = this.$slots.default || []
      $slots.forEach(node => {
        if (!node.tag) {
          this.coverContent += node.text || ''
        }
      })
    }
  }
}
</script>
<style>
uni-cover-view {
  display: block;
  line-height: 1.2;
  overflow: hidden;
  white-space: nowrap;
  pointer-events: auto;
}

uni-cover-view[hidden] {
  display: none;
}

uni-cover-view .uni-cover-view {
  width: 100%;
  height: 100%;
}
</style>


add fixdir

add fixdir

<template>

            <ui-icon-button
                @click="onToolBtnClick(index, item)"
                :type="item.type"
                :size="item.size"
                :color="item.color"
                :key="item.id"
                v-if="item.visiable"
                v-for="item, index in actionList"
                >

            </ui-icon-button>

            <ui-confirm
                :autofocus="confirmDialog.autofocus"
                :confirm-button-text="confirmDialog.confirmButtonText"
                :deny-button-text="confirmDialog.denyButtonText"
                :ref="confirmDialog.ref"
                :title="confirmDialog.title"

                @confirm="confirmDialog.callbackConfirm"
                @deny="confirmDialog.callbackDeny"
                @open="confirmDialog.callbackOpen"
                @close="confirmDialog.callbackClose"
                >
                {{ confirmDialog.content }}
            </ui-confirm>

            <ui-confirm
                :autofocus="outputConfigDialog.autofocus"
                :confirm-button-text="outputConfigDialog.confirmButtonText"
                :deny-button-text="outputConfigDialog.denyButtonText"
                :ref="outputConfigDialog.ref"
                :title="outputConfigDialog.title"

                @confirm="outputConfigDialog.callbackConfirm"
                @deny="outputConfigDialog.callbackDeny"
                @open="outputConfigDialog.callbackOpen"
                @close="outputConfigDialog.callbackClose"
                >

                        {{ $t('pages.convert.dialog-config-output.path') }}
                        <ui-select
                            :options="onOutputToLocalStorage()"
                            v-model="outputPathsModel"

                            @change="onOutputToLocalStorage()"
                            >
                        </ui-select>
                        <ui-button
                            type="secondary"
                            size="small"
                            color="primary"
                            @click="onOpenSelectOutDir()"
                            >

                        </ui-button>

                        <ui-checkbox
                            v-model="checkBox"
                            >
                        </ui-checkbox>
                        {{ $t('pages.convert.dialog-config-output.cover') }}

            </ui-confirm>

            <svg
                :id="welcomeContentID"
                class="page__examples-app-doc__welcome"
                v-show="taskList.length <= 0"
                />
            <ui-alert
                :class="getItemStyleClass(item)"
                :type="item.style.type"
                :key="item"
                removeIcon

                @dismiss="onRemoveTaskItem(item, index)"

                v-show="item.style.show"
                v-for="item, index in taskList">

                                {{ item.name }}

                                    ({{ item.size }})

                            <ui-icon-button
                                @click="onOpenParentDir(item.fixOutDir)"
                                type="secondary"
                                color="white"
                                size="small"
                                v-if="item.stateInfo.state > 0"
                                >

                            </ui-icon-button>

                            <ui-icon-button
                                @click="onPreviewFile(item.fixpath)"
                                type="secondary"
                                color="white"
                                size="small"
                                v-if="item.stateInfo.state > 0"
                                >

                            </ui-icon-button>

                        <span
                            :class="['ui-toolbar__top__taskMessage', item.stateInfo.state < 0 ? 'task-item-has-error': '']"
                            :title="item.stateInfo.message"
                            v-show="item.stateInfo.state < 0"
                            >
                            {{ item.stateInfo.message }}

                        {{ item.path }}

                        <ui-progress-linear
                            :color="getItemProgressStyle(item)"
                            type="determinate"
                            :progress="item.progress"
                            v-show="getImageProgressShow(item)"
                            :title=" $t('pages.convert.task-item.progress') + item.progress"
                            >
                        </ui-progress-linear>

            </ui-alert>

= 0">
            {{ $t('pages.convert.footbar.fileCount') }} : {{ taskList.length }}
            {{ $t('pages.convert.footbar.state') }} : {{ isConvertWorking ? $t('pages.convert.footbar.isConvertWorking') : $t('pages.convert.footbar.isWaiting') }}

            {{ $t('pages.convert.footbar.transferState') }} : {{ transferIsNormal ? $t('pages.convert.footbar.transferIsNormal') : $t('pages.convert.footbar.transferIsFault') }}

</template>


modify content beforeCreate

modify content beforeCreate

<template>

            <ui-icon-button
                @click="onToolBtnClick(index, item)"
                :type="item.type"
                :size="item.size"
                :color="item.color"
                :key="item.id"
                v-if="item.visiable"
                v-for="item, index in actionList"
                >

            </ui-icon-button>

            <ui-confirm
                :autofocus="confirmDialog.autofocus"
                :confirm-button-text="confirmDialog.confirmButtonText"
                :deny-button-text="confirmDialog.denyButtonText"
                :ref="confirmDialog.ref"
                :title="confirmDialog.title"

                @confirm="confirmDialog.callbackConfirm"
                @deny="confirmDialog.callbackDeny"
                @open="confirmDialog.callbackOpen"
                @close="confirmDialog.callbackClose"
                >
                {{ confirmDialog.content }}
            </ui-confirm>

            <ui-confirm
                :autofocus="outputConfigDialog.autofocus"
                :confirm-button-text="outputConfigDialog.confirmButtonText"
                :deny-button-text="outputConfigDialog.denyButtonText"
                :ref="outputConfigDialog.ref"
                :title="outputConfigDialog.title"

                @confirm="outputConfigDialog.callbackConfirm"
                @deny="outputConfigDialog.callbackDeny"
                @open="outputConfigDialog.callbackOpen"
                @close="outputConfigDialog.callbackClose"
                >

                        {{ $t('pages.convert.dialog-config-output.path') }}
                        <ui-select
                            :options="onOutputToLocalStorage()"
                            v-model="outputPathsModel"

                            @change="onOutputToLocalStorage()"
                            >
                        </ui-select>
                        <ui-button
                            type="secondary"
                            size="small"
                            color="primary"
                            @click="onOpenSelectOutDir()"
                            >

                        </ui-button>

                        <ui-checkbox
                            v-model="checkBox"
                            >
                        </ui-checkbox>
                        {{ $t('pages.convert.dialog-config-output.cover') }}

            </ui-confirm>

            <svg
                :id="welcomeContentID"
                class="page__examples-app-doc__welcome"
                v-show="taskList.length <= 0"
                />
            <ui-alert
                :class="getItemStyleClass(item)"
                :type="item.style.type"
                :key="item"
                removeIcon

                @dismiss="onRemoveTaskItem(item, index)"

                v-show="item.style.show"
                v-for="item, index in taskList">

                                {{ item.name }}

                                    {{ item.size ? '(' + item.size + ')' : '' }}

                            <ui-icon-button
                                @click="onOpenParentDir(item.fixOutDir)"
                                type="secondary"
                                color="black"
                                size="small"
                                v-if="item.stateInfo.state > 0"
                                >

                            </ui-icon-button>

                            <ui-icon-button
                                @click="onPreviewFile(item.fixpath)"
                                type="secondary"
                                color="black"
                                size="small"
                                v-if="item.stateInfo.state > 0 && checkOutputPathIsFile(item.fixpath)"
                                >

                            </ui-icon-button>

                        <span
                            :class="['ui-toolbar__top__taskMessage', item.stateInfo.state < 0 ? 'task-item-has-error': '']"
                            :title="item.stateInfo.message"
                            v-show="item.stateInfo.state < 0"
                            >
                            {{ item.stateInfo.message }}

                        {{ item.path }}

                        <ui-progress-linear
                            :color="getItemProgressStyle(item)"
                            :progress="item.progress"
                            v-show="getImageProgressShow(item)"
                            :title=" $t('pages.convert.task-item.progress') + item.progress"
                            >
                        </ui-progress-linear>

            </ui-alert>

= 0">
            {{ $t('pages.convert.footbar.fileCount') }} : {{ taskList.length }}
            {{ $t('pages.convert.footbar.state') }} : {{ isConvertWorking ? $t('pages.convert.footbar.isConvertWorking') : $t('pages.convert.footbar.isWaiting') }}

            {{ $t('pages.convert.footbar.transferState') }} : {{ transferIsNormal ? $t('pages.convert.footbar.transferIsNormal') : $t('pages.convert.footbar.transferIsFault') }}

</template>


[doc] update swipe example

[doc] update swipe example

<template>

Swipe

基础用法

      <wd-swipe class="swipe" :auto="4000" :show-indicators="true">
        <wd-swipe-item>page1</wd-swipe-item>
        <wd-swipe-item>page2</wd-swipe-item>
        <wd-swipe-item>page3</wd-swipe-item>
        <wd-swipe-item>page4</wd-swipe-item>
      </wd-swipe>

取消自动播放

      <wd-swipe class="swipe" :auto="0" :show-indicators="true">
        <wd-swipe-item>page1</wd-swipe-item>
        <wd-swipe-item>page2</wd-swipe-item>
        <wd-swipe-item>page3</wd-swipe-item>
        <wd-swipe-item>page4</wd-swipe-item>
      </wd-swipe>

隐藏 indicators

      <wd-swipe class="swipe" :auto="4000" :show-indicators="false">
        <wd-swipe-item>page1</wd-swipe-item>
        <wd-swipe-item>page2</wd-swipe-item>
        <wd-swipe-item>page3</wd-swipe-item>
        <wd-swipe-item>page4</wd-swipe-item>
      </wd-swipe>

</template>

<template>
  <div class="component-wrapper">
    <wd-swipe class="swipe" :auto="4000" :show-indicators="true">
      <wd-swipe-item>page1</wd-swipe-item>
      <wd-swipe-item>page2</wd-swipe-item>
      <wd-swipe-item>page3</wd-swipe-item>
      <wd-swipe-item>page4</wd-swipe-item>
    </wd-swipe>
  </div>
</template>

<script>
  export default {
  }
</script>

<style lang="css">
  .component-wrapper {
    height: 400px;
    /*height: 100%;*/
  }
  .wd-swipe-items-wrap > section:nth-child(1) {
    background: deepskyblue;
  }
  .wd-swipe-items-wrap > section:nth-child(2) {
    background: cyan;
  }
  .wd-swipe-items-wrap > section:nth-child(3) {
    background: chartreuse;
  }
  .wd-swipe-items-wrap > section:nth-child(4) {
    background: green;
  }
</style>


remove rem

remove rem

<template>

      <slot>

{{item}}

      </slot>
      <slot name="pullup"
            :pullUpLoad="pullUpLoad"
            :isPullUpLoad="isPullUpLoad"
      >

            加载更多

            <loading></loading>

      </slot>

    <slot name="pulldown"
          :pullDownRefresh="pullDownRefresh"
          :pullDownStyle="pullDownStyle"
          :beforePullDown="beforePullDown"
          :pulling="pulling"
          :bubbleY="bubbleY"
    >

          <bubble :y="bubbleY"></bubble>

            <loading></loading>

刷新成功

    </slot>

</template>


增加了地址新增和编辑功能

增加了地址新增和编辑功能


Adding button functionality.

Adding button functionality.


Changing the way the data updates are made.


調整手機樣式

調整手機樣式

<template lang="html">

{{ conf.menu.session }}

                      <button v-on:click="changeDate(1)" type="button" class="button day-btn" v-bind:class="{'active': isDay1}">
Day 1 (10/29)
</button>

                      <button v-on:click="changeDate(2)" type="button" class="button day-btn" v-bind:class="{'active': !isDay1}">
Day 2 (10/30)
</button>

                            {{ conf.session.day1.maintrack.morning.timeplace }}
                            {{ conf.session.day1.maintrack.morning.place }}

                                {{ session.time }}

                                <lecture v-for="lecture in session.content" :lecture="lecture" :conf="conf"></lecture>

                                {{ session.content }}

                            {{ conf.session.day1.maintrack.afternoon.timeplace }}
                            {{ conf.session.day1.maintrack.afternoon.place1 }}
                            {{ conf.session.day1.maintrack.afternoon.place2 }}

                                {{ session.time }}

                                {{ session.content }}

                                <lecture :lecture="lecture" :conf="conf"></lecture>

                            {{ conf.session.day1.qa.timeplace }}
                            {{ conf.session.day1.qa.place }}

                                {{ session.time }}

                                <lecture v-for="lecture in session.content" :lecture="lecture" :conf="conf"></lecture>

                                {{ session.content }}

                            {{ conf.session.day1.workshop.timeplace }}
                            {{ conf.session.day1.workshop.place }}

                                {{ session.time }}

                                <lecture v-for="lecture in session.content" :lecture="lecture" :conf="conf"></lecture>

                                {{ session.content }}

                            {{ conf.session.day2.workshop.timeplace }}
                            {{ conf.session.day2.workshop.place }}

                                {{ session.time }}

                                <lecture v-for="lecture in session.content" :lecture="lecture" :conf="conf"></lecture>

                                {{ session.content }}

</template>


Editor load content fix

Editor load content fix

<style scoped>

</style>

<template>

    <div>

        <div
            :id="uniqId"
            :name="name"
            v-model="dataValue">
        </div>

    </div>

</template>

<script>

    export default {

        data(){
            return{
                dataValue: null,
                tinyMCE_options: {},
                editor: null,
                uniqId: Math.random().toString(36).substring(7) + '_',
            };
        },

        props: {
            // "value" is required to use v-model on the component
            value: {
                default: null
            },
            name: {
                type: String,
                default: ''
            },
            css: {
                type: String,
                default: '',
                required: false
            },
            assetsUrl: {
                type: String,
                default: '',
                required: false
            },
            editorOptions: {
                type: Object,
                required: false,
                default(){
                    return {};
                }
            }
        },

        watch: {
            value: function(newValue, oldValue){

                this.dataValue = newValue ? newValue : '';

                // Check if value has just been loaded for the first time
                if(oldValue === null){

                    // Editor has been loaded before the value => set content !
                    // (Otherwise, the content is set at editor init)
                    if(this.editor){
                        this.editor.setContent(this.dataValue);
                    }

                }
            },
        },

        mounted() {
            this.init();
        },

        methods: {

            init(){
                var self = this;

                let elt = $("div#" + this.uniqId);

                //console.log("textarea#" + this.uniqId, elt.length);

                this.tinyMCE_options = {

                    selector: "div#" + this.uniqId,
                    height: '600px',
                    resize: "vertical",
                    language : 'fr_FR',
                    theme: "modern",
                    body_class: "tinymce-body",
                    content_css : this.css,
                    document_base_url: '/',
                    relative_urls: true,
                    convert_urls: false,
                    remove_script_host: true,
                    schema: "html5",
                    inline: false,
                    statusbar: false,
                    forced_root_block: false, // 'p'
                    //media_filter_html: true,
                    //extended_valid_elements:"iframe[src|title|width|height|allowfullscreen|frameborder|class|id],object[classid|width|height|codebase|*],param[name|value|_value|*],embed[type|width|height|src|*]",
                    //extended_valid_elements : "iframe[src|width|height|name|align|allowfullscreen|frameborder]",
                    //fontsize_formats: "8px 9px 10px 11px 12px 13px 14px 15px 16px 18px 20px 22px 24px 26px 28px 30px 36px 42px",
                    //image_list: tinyMceImages,
                    //link_list: tinyMceLinks,
                    plugins: [
                        "textcolor advlist autolink lists link image charmap print preview anchor emoticons", // media
                        "searchreplace visualblocks code fullscreen charmap",
                        "insertdatetime table contextmenu paste autoresize" //moxiemanager
                    ],
                    //menubar: "tools table format view insert edit",
                    menubar: false,
                    //toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
                    toolbar: "undo redo | bold italic | fontsizeselect | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link | blockquote | code",
                    textcolor_map: {},
                    //toolbar2: "fontsizeselect | forecolor backcolor | charmap | emoticons | media",
                    /*style_formats: [
                     {title: 'Bold text', inline: 'b'},
                     {title: 'Red text', inline: 'span', styles: {color: '#ff0000'}},
                     {title: 'Red header', block: 'h1', styles: {color: '#ff0000'}},
                     {title: 'Example 1', inline: 'span', classes: 'example1'},
                     {title: 'Example 2', inline: 'span', classes: 'example2'},
                     {title: 'Table styles'},
                     {title: 'Table row 1', selector: 'tr', classes: 'tablerow1'}
                     ],*/

                    setup: function(editor) {

                        editor.on('Init', function(e) {
                            console.log('Editor initialized', self.uniqId);
                            self.editor = editor;
                            editor.setContent(self.dataValue ? self.dataValue : '');
                        });

                        editor.on('Dirty', function() {
                            self.$emit('edit', editor.getContent());
                        });

                        editor.on('KeyPress', function() {
                            self.$emit('edit', editor.getContent());
                        });

                        editor.on('Change', function() {
                            this.dataValue = editor.getContent();
                            self.$emit('input', this.dataValue);
                        });
                    }
                };

                this.tinyMCE_options = Object.assign({}, this.tinyMCE_options, this.editorOptions);

                if(this.assetsUrl){

                    console.log('Loading editor assets', this.assetsUrl);

                    axios.get(this.assetsUrl).then(response => {

                        var medias = [];

                        _.forEach(response.data, function(media){
                            medias.push({
                                title: media.title + ' (' + media.mime + ')',
                                value: media.filepath
                            });
                        });

                        var opts = {
                            image_list: medias,
                            link_list: medias
                        };

                        this.tinyMCE_options = Object.assign({}, this.tinyMCE_options, opts);

                        this.launchInit();

                    });

                }else{

                    this.launchInit();
                }
            },

            launchInit(){

                tinymce.init(this.tinyMCE_options).then(r => {
                    console.log('Editor init success', this.uniqId, this.tinyMCE_options, r);
                }).catch(r => {
                    console.log('Editor init ERROR', this.uniqId, r);
                });

                console.log('Editor init pending...', this.uniqId);
            },

            insert(text){

                this.editor.insertContent(text);
            }
        }
    }
</script>

<style scoped>

</style>

<template>

    <div>

        <div
            :id="uniqId"
            :name="name"
            v-model="dataValue">
        </div>

    </div>

</template>

<script>

    export default {

        data(){
            return{
                dataValue: this.value ? this.value : '',
                tinyMCE_options: {},
                editor: null,
                uniqId: Math.random().toString(36).substring(7) + '_',
            };
        },

        props: {
            // "value" is required to use v-model on the component
            value: {
                default: null
            },
            name: {
                type: String,
                default: ''
            },
            css: {
                type: String,
                default: '',
                required: false
            },
            assetsUrl: {
                type: String,
                default: '',
                required: false
            },
            editorOptions: {
                type: Object,
                required: false,
                default(){
                    return {};
                }
            }
        },

        watch: {
            value: function(val){
                this.dataValue = val ? val : '';
                if(this.editor){
                    this.editor.setContent(this.dataValue);
                }
            },
        },

        mounted() {
            this.init();
        },

        methods: {

            init(){
                var self = this;

                let elt = $("div#" + this.uniqId);

                //console.log("textarea#" + this.uniqId, elt.length);

                this.tinyMCE_options = {
                    selector: "div#" + this.uniqId,
                    height: '600px',
                    resize: "vertical",
                    language : 'fr_FR',
                    theme: "modern",
                    body_class: "tinymce-body",
                    content_css : this.css,
                    document_base_url: '/',
                    relative_urls: true,
                    convert_urls: false,
                    remove_script_host: true,
                    schema: "html5",
                    inline: false,
                    statusbar: false,
                    forced_root_block: false, // 'p'
                    //media_filter_html: true,
                    //extended_valid_elements:"iframe[src|title|width|height|allowfullscreen|frameborder|class|id],object[classid|width|height|codebase|*],param[name|value|_value|*],embed[type|width|height|src|*]",
                    //extended_valid_elements : "iframe[src|width|height|name|align|allowfullscreen|frameborder]",
                    //fontsize_formats: "8px 9px 10px 11px 12px 13px 14px 15px 16px 18px 20px 22px 24px 26px 28px 30px 36px 42px",
                    //image_list: tinyMceImages,
                    //link_list: tinyMceLinks,
                    plugins: [
                        "textcolor advlist autolink lists link image charmap print preview anchor emoticons", // media
                        "searchreplace visualblocks code fullscreen charmap",
                        "insertdatetime table contextmenu paste autoresize" //moxiemanager
                    ],
                    //menubar: "tools table format view insert edit",
                    menubar: false,
                    //toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
                    toolbar: "undo redo | bold italic | fontsizeselect | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link | blockquote | code",
                    textcolor_map: {},
                    //toolbar2: "fontsizeselect | forecolor backcolor | charmap | emoticons | media",
                    /*style_formats: [
                     {title: 'Bold text', inline: 'b'},
                     {title: 'Red text', inline: 'span', styles: {color: '#ff0000'}},
                     {title: 'Red header', block: 'h1', styles: {color: '#ff0000'}},
                     {title: 'Example 1', inline: 'span', classes: 'example1'},
                     {title: 'Example 2', inline: 'span', classes: 'example2'},
                     {title: 'Table styles'},
                     {title: 'Table row 1', selector: 'tr', classes: 'tablerow1'}
                     ],*/

                    setup: function(editor) {

                        editor.on('Init', function(e) {
                            console.log('Editor initialized', self.uniqId);
                            self.editor = editor;
                            editor.setContent(self.dataValue ? self.dataValue : '');
                        });

                        editor.on('Dirty', function() {
                            self.$emit('edit', editor.getContent());
                        });

                        editor.on('KeyPress', function() {
                            self.$emit('edit', editor.getContent());
                        });

                        editor.on('Change', function() {
                            this.dataValue = editor.getContent();
                            self.$emit('input', this.dataValue);
                        });
                    }
                };

                this.tinyMCE_options = Object.assign({}, this.tinyMCE_options, this.editorOptions);

                if(this.assetsUrl){

                    console.log('Loading editor assets', this.assetsUrl);

                    axios.get(this.assetsUrl).then(response => {

                        var medias = [];

                        _.forEach(response.data, function(media){
                            medias.push({
                                title: media.title + ' (' + media.mime + ')',
                                value: media.filepath
                            });
                        });

                        var opts = {
                            image_list: medias,
                            link_list: medias
                        };

                        this.tinyMCE_options = Object.assign({}, this.tinyMCE_options, opts);

                        this.launchInit();

                    });

                }else{

                    this.launchInit();
                }
            },

            launchInit(){

                tinymce.init(this.tinyMCE_options).then(r => {
                    console.log('Editor init success', this.uniqId, this.tinyMCE_options, r);
                }).catch(r => {
                    console.log('Editor init ERROR', this.uniqId, r);
                });

                console.log('Editor init pending...', this.uniqId);
            },

            insert(text){

                this.editor.insertContent(text);
            }
        }
    }
</script>


fix(user): update the skin model after change type

<template>
  <canvas style="z-index: 10;" :width="width" :height="height" @dragover="$emit('dragover', $event)"
          @drop="$emit('drop', $event)" />
</template>

<script>
import Model from './skin-model';

const THREE = require('three');
const OrbitControls = require('three-orbit-controls')(THREE);

export default {
  props: {
    width: {
      type: Number,
      default: 210,
    },
    height: {
      type: Number,
      default: 400,
    },
    cape: {
      type: Object,
      required: false,
    },
    rotate: {
      type: Boolean,
      default: true,
    },
    maxDistance: {
      type: Number,
      default: 3,
    },
    minDistance: {
      type: Number,
      default: 1.5,
    },
    slim: {
      type: Boolean,
      default: false,
    },
    data: {
      required: true,
    },
  },
  data: () => ({
    disposed: false,
  }),
  watch: {
    data(nskin) {
      if (nskin === undefined) {
        this.$setSkin(undefined);
        return;
      }
      this.$setSkin(nskin, this.slim);
    },
    slim(slim) {
      this.$setSkin(this.data, slim);
    },
  },
  destroyed() {
    this.disposed = true;
  },
  mounted(e) {
    // console.log("===========START===========")
    // let canvas = this.$el;
    // let gl = canvas.getContext("webgl");
    // console.log(gl.getParameter(gl.RENDERER));
    // console.log(gl.getParameter(gl.VENDOR));
    // console.log(getUnmaskedInfo(gl).vendor);
    // console.log(getUnmaskedInfo(gl).renderer);
    // function getUnmaskedInfo(gl) {
    //     let unMaskedInfo = {
    //         renderer: '',
    //         vendor: ''
    //     };
    //     let dbgRenderInfo = gl.getExtension("WEBGL_debug_renderer_info");
    //     if (dbgRenderInfo != null) {
    //         unMaskedInfo.renderer = gl.getParameter(dbgRenderInfo.UNMASKED_RENDERER_WEBGL);
    //         unMaskedInfo.vendor = gl.getParameter(dbgRenderInfo.UNMASKED_VENDOR_WEBGL);
    //     }
    //     return unMaskedInfo;
    // }
    // console.log("===========END===========")
    const renderer = new THREE.WebGLRenderer({ canvas: this.$el, antialias: true, alpha: true });
    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.5, 5);
    camera.position.z = 3;
    const character = new Model();
    character.root.translateY(-0.5);
    this.$setSkin = (skin, slim) => {
      console.log(`Set Skin ${skin}`);
      character.updateSkin(skin, slim);
    };
    this.$setCape = (cape) => {
      character.updateCape(cape);
    };
    scene.add(character.root);
    if (this.data) this.$setSkin(this.data, this.slim);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    const controls = new OrbitControls(camera, this.$el);
    controls.target = new THREE.Vector3(0, 0, 0);
    controls.enablePan = false;
    controls.enableKeys = false;
    controls.maxDistance = this.maxDistance;
    controls.minDistance = this.minDistance;
    if (this.rotate) {
      // controls.autoRotate = true;
      // controls.autoRotateSpeed = 4;
    }
    const self = this;
    requestAnimationFrame(function animate(nowMsec) {
      if (self.disposed) return;
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    });
  },
  methods: {
  },
};
</script>

<template>
  <canvas style="z-index: 10;" :width="width" :height="height" @dragover="$emit('dragover', $event)"
          @drop="$emit('drop', $event)" />
</template>

<script>
import Model from './skin-model';

const THREE = require('three');
const OrbitControls = require('three-orbit-controls')(THREE);

export default {
  props: {
    width: {
      type: Number,
      default: 210,
    },
    height: {
      type: Number,
      default: 400,
    },
    cape: {
      type: Object,
      required: false,
    },
    rotate: {
      type: Boolean,
      default: true,
    },
    maxDistance: {
      type: Number,
      default: 3,
    },
    minDistance: {
      type: Number,
      default: 1.5,
    },
    slim: {
      type: Boolean,
      default: false,
    },
    data: {
      required: true,
    },
  },
  data: () => ({
    disposed: false,
  }),
  watch: {
    data(nskin) {
      if (nskin === undefined) {
        this.$setSkin(undefined);
        return;
      }
      this.$setSkin(nskin, this.slim);
    },
  },
  destroyed() {
    this.disposed = true;
  },
  mounted(e) {
    // console.log("===========START===========")
    // let canvas = this.$el;
    // let gl = canvas.getContext("webgl");
    // console.log(gl.getParameter(gl.RENDERER));
    // console.log(gl.getParameter(gl.VENDOR));
    // console.log(getUnmaskedInfo(gl).vendor);
    // console.log(getUnmaskedInfo(gl).renderer);
    // function getUnmaskedInfo(gl) {
    //     let unMaskedInfo = {
    //         renderer: '',
    //         vendor: ''
    //     };
    //     let dbgRenderInfo = gl.getExtension("WEBGL_debug_renderer_info");
    //     if (dbgRenderInfo != null) {
    //         unMaskedInfo.renderer = gl.getParameter(dbgRenderInfo.UNMASKED_RENDERER_WEBGL);
    //         unMaskedInfo.vendor = gl.getParameter(dbgRenderInfo.UNMASKED_VENDOR_WEBGL);
    //     }
    //     return unMaskedInfo;
    // }
    // console.log("===========END===========")
    const renderer = new THREE.WebGLRenderer({ canvas: this.$el, antialias: true, alpha: true });
    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.5, 5);
    camera.position.z = 3;
    const character = new Model();
    character.root.translateY(-0.5);
    this.$setSkin = (skin, slim) => {
      console.log(`Set Skin ${skin}`);
      character.updateSkin(skin, slim);
    };
    this.$setCape = (cape) => {
      character.updateCape(cape);
    };
    scene.add(character.root);
    if (this.data) this.$setSkin(this.data, this.slim);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    const controls = new OrbitControls(camera, this.$el);
    controls.target = new THREE.Vector3(0, 0, 0);
    controls.enablePan = false;
    controls.enableKeys = false;
    controls.maxDistance = this.maxDistance;
    controls.minDistance = this.minDistance;
    if (this.rotate) {
      // controls.autoRotate = true;
      // controls.autoRotateSpeed = 4;
    }
    const self = this;
    requestAnimationFrame(function animate(nowMsec) {
      if (self.disposed) return;
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    });
  },
  methods: {
  },
};
</script>


Use an actual ellipsis character and update docstrings

<template>

  <!--
    Text is wrapped in two `div`s to allow parent components adding
    padding style directly on `<TextTruncatorCss>` component no matter
    of what truncating technique is used. Otherwise adding padding directly
    would break when using technique (B) since text that should be truncated
    would show in padding area.
  -->
  <div>
    <div :class="$computedClass(truncate)">
      {{ text }}
    </div>
  </div>

</template>

<script>

  /**
   * Truncates text to a certain number of lines
   * and adds an ellipsis character "…"
   *
   * Internet Explorer note:
   * Depending on length of words of the text, there might
   * be a gap between the last visible word and "…"
   */
  export default {
    name: 'TextTruncatorCss',
    props: {
      text: {
        type: String,
        required: true,
      },
      maxLines: {
        type: Number,
        required: false,
        default: 1,
      },
      /**
       * Text line height in rem.
       * Used only for Internet Explorer fallback.
       */
      lineHeightIE: {
        type: Number,
        required: false,
        default: 1.4,
      },
    },
    computed: {
      truncate() {
        /*
          (A)
          For one line, use standard ellipsis text overflow
          that works well for such scenario
        */
        if (this.maxLines === 1) {
          return {
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          };
        }

        // 54 is random number only to be able to define `supports` test condition
        if ('CSS' in window && CSS.supports && CSS.supports('-webkit-line-clamp: 54')) {
          /*
            (B)
            For multiple lines, use line clamp in browsers that support it
            (https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp)
          */
          return {
            overflow: 'hidden',
            display: '-webkit-box',
            '-webkit-line-clamp': `${this.maxLines}`,
            '-webkit-box-orient': 'vertical',
            // needed to make line clamp work for very long word with no spaces
            overflowWrap: 'break-word',
          };
        } else {
          /*
            (C)
            Fallback for multiple lines in Internet Explorer and some older versions
            of other browsers that don't support line clamp
            (https://caniuse.com/mdn-css_properties_-webkit-line-clamp).
            Calculate max height and add "..." in `::before` while covering it with
            white rectangle defined in `::after` when text doesn't need to be truncated.
            Adapted from https://hackingui.com/a-pure-css-solution-for-multiline-text-truncation/
            and https://css-tricks.com/line-clampin/#the-hide-overflow-place-ellipsis-pure-css-way.
          */
          const ellipsisWidth = '1rem';
          return {
            overflow: 'hidden',
            position: 'relative',
            lineHeight: `${this.lineHeightIE}rem`,
            maxHeight: `${this.maxLines * this.lineHeightIE}rem`,
            // needed to make truncation work for very long word with no spaces
            // `word-wrap` is a legacy name for `overflow-wrap` that needs to be used for IE
            wordWrap: 'break-word',
            // create space for "..."
            paddingRight: ellipsisWidth,
            marginRigth: `-${ellipsisWidth}`,
            '::before': {
              content: "'…'",
              position: 'absolute',
              right: 0,
              bottom: 0,
            },
            // cover "..." with white rectangle when text
            // doesn't need to be truncated
            '::after': {
              content: "''",
              position: 'absolute',
              right: 0,
              width: ellipsisWidth,
              height: '50%',
              background: 'white',
            },
          };
        }
      },
    },
  };

</script>

<template>

  <!--
    Text is wrapped in two `div`s to allow parent components adding
    padding style directly on `<TextTruncatorCss>` component no matter
    of what truncating technique is used. Otherwise adding padding directly
    would break when using technique (B) since text that should be truncated
    would show in padding area.
  -->
  <div>
    <div :class="$computedClass(truncate)">
      {{ text }}
    </div>
  </div>

</template>

<script>

  /**
   * Truncates text to a certain number of lines and adds "..."
   *
   * This is a pure CSS alternative to `TextTruncator` for simpler
   * use cases like card titles since `TextTruncator` is causing performance
   * issues (https://github.com/learningequality/kolibri/issues/8124)
   * and thus its usage is currently not recommended for larger
   * amounts of instances on one page.
   *
   * Compared to `TextTruncator` there are two disadvantages:
   * - depending on length of words of the text, there might be a gap
   *   between the last visible word and "..." in Internet Explorer
   *   and old versions of some other browsers (see implementation comments)
   * - it currently doesn't offer "View all"/"View less" functionality
   */
  export default {
    name: 'TextTruncatorCss',
    props: {
      text: {
        type: String,
        required: true,
      },
      maxLines: {
        type: Number,
        required: false,
        default: 1,
      },
      /**
       * Text line height in rem.
       * Used only for Internet Explorer fallback.
       */
      lineHeightIE: {
        type: Number,
        required: false,
        default: 1.4,
      },
    },
    computed: {
      truncate() {
        /*
          (A)
          For one line, use standard ellipsis text overflow
          that works well for such scenario
        */
        if (this.maxLines === 1) {
          return {
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          };
        }

        // 54 is random number only to be able to define `supports` test condition
        if ('CSS' in window && CSS.supports && CSS.supports('-webkit-line-clamp: 54')) {
          /*
            (B)
            For multiple lines, use line clamp in browsers that support it
            (https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp)
          */
          return {
            overflow: 'hidden',
            display: '-webkit-box',
            '-webkit-line-clamp': `${this.maxLines}`,
            '-webkit-box-orient': 'vertical',
            // needed to make line clamp work for very long word with no spaces
            overflowWrap: 'break-word',
          };
        } else {
          /*
            (C)
            Fallback for multiple lines in Internet Explorer and some older versions
            of other browsers that don't support line clamp
            (https://caniuse.com/mdn-css_properties_-webkit-line-clamp).
            Calculate max height and add "..." in `::before` while covering it with
            white rectangle defined in `::after` when text doesn't need to be truncated.
            Adapted from https://hackingui.com/a-pure-css-solution-for-multiline-text-truncation/
            and https://css-tricks.com/line-clampin/#the-hide-overflow-place-ellipsis-pure-css-way.
          */
          const ellipsisWidth = '1rem';
          return {
            overflow: 'hidden',
            position: 'relative',
            lineHeight: `${this.lineHeightIE}rem`,
            maxHeight: `${this.maxLines * this.lineHeightIE}rem`,
            // needed to make truncation work for very long word with no spaces
            // `word-wrap` is a legacy name for `overflow-wrap` that needs to be used for IE
            wordWrap: 'break-word',
            // create space for "..."
            paddingRight: ellipsisWidth,
            marginRigth: `-${ellipsisWidth}`,
            '::before': {
              content: "'...'",
              position: 'absolute',
              right: 0,
              bottom: 0,
            },
            // cover "..." with white rectangle when text
            // doesn't need to be truncated
            '::after': {
              content: "''",
              position: 'absolute',
              right: 0,
              width: ellipsisWidth,
              height: '50%',
              background: 'white',
            },
          };
        }
      },
    },
  };

</script>


frontend: Clean up the image information popup


WIP: Rubric Entry View

WIP: Rubric Entry View

<template>

Entry View

DF

CF

                    Processing {{dataConnector.processingSize}} saves...

                    All changes saved

            <link rel="stylesheet"
                  href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
            <div :style="`--num-levels: ${ rubric.levels.length }`">

{{ rubric.title }}

{{ cluster.title}}

                                <li v-for="category in cluster.categories" class="category-list-item" :style="`--category-color: ${category.color}`">

{{ category.title }}

{{ criterium.title }}

                                                    <template v-for="level in rubric.levels" class="score">

{{ rubric.getChoiceScore(criterium, level) }}

                                                                {{level.title}}

                                                            {{ rubric.getChoice(criterium, level).feedback }}

                                                    </template>

{{ getCriteriumScore(criterium) }}

                                                    <textarea placeholder="Geef Feedback"></textarea>

Totaal {{ category.title }}:

{{ getCategoryScore(category) }}

Totaal {{ cluster.title }}:

{{ getClusterScore(cluster) }}

Totaal Rubric:

{{ getRubricScore() }}

</template>

<template>

Entry View

                    Processing {{dataConnector.processingSize}} saves...

                    All changes saved

            <link rel="stylesheet"
                  href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
            <div :style="`--num-levels: ${ rubric.levels.length }`">

{{ rubric.title }}

{{ cluster.title}}

                                <li v-for="category in cluster.categories" class="category-list-item" :style="`--category-color: ${category.color}`">

{{ category.title }}

{{ criterium.title }}

                                                    <template v-for="level in rubric.levels" class="score">

{{ rubric.getChoiceScore(criterium, level) }}

                                                                {{level.title}}

                                                            {{ rubric.getChoice(criterium, level).feedback }}

                                                    </template>

{{ getCriteriumScore(criterium) }}

                                                    <textarea placeholder="Geef Feedback"></textarea>

Totaal {{ category.title }}:

{{ getCategoryScore(category) }}

</template>


Allow to "Send call notification" for owners

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

<template>

		<AvatarWrapper :id="computedId"
			:disable-tooltip="true"
			:disable-menu="isSearched"
			:size="44"
			:show-user-status="showUserStatus && !isSearched"
			:show-user-status-compact="false"
			:preloaded-user-status="preloadedUserStatus"
			:name="computedName"
			:source="participant.source || participant.actorType"
			:offline="isOffline"
			:menu-container="container" />

				{{ computedName }}
				({{ t('spreed', 'moderator') }})
				({{ t('spreed', 'bot') }})
				({{ t('spreed', 'guest') }})

				{{ statusMessage }}

			{{ callIconTooltip }}
			<Microphone v-if="callIcon === 'audio'"
				:size="20" />
			<Phone v-if="callIcon === 'phone'"
				:size="20" />
			<VideoIcon v-if="callIcon === 'video'"
				:size="20" />

			<HandBackLeft v-if="callIcon === 'hand'"
				:size="18" />

		<NcActions v-if="(canBeModerated || canSendCallNotification) && !isSearched"
			:container="container"
			:aria-label="participantSettingsAriaLabel"
			:force-menu="true"
			class="participant-row__actions">
			<template #icon>
				<LockOpenVariant v-if="actionIcon === 'LockOpenVariant'"
					:size="20" />
				<Lock v-else-if="actionIcon === 'Lock'"
					:size="20" />
				<Tune v-else-if="actionIcon === 'Tune'"
					:size="20" />
				<DotsHorizontal v-else
					:size="20" />
			</template>
			<NcActionText v-if="attendeePin"
				:title="t('spreed', 'Dial-in PIN')"
				icon="icon-password">
				{{ attendeePin }}
			</NcActionText>
			<NcActionButton v-if="canBeDemoted"
				:close-after-click="true"
				@click="demoteFromModerator">
				<template #icon>
					<Account :size="20" />
					{{ t('spreed', 'Demote from moderator') }}
				</template>
			</NcActionButton>
			<NcActionButton v-if="canBePromoted"
				:close-after-click="true"
				@click="promoteToModerator">
				<template #icon>
					<Crown :size="20" />
				</template>
				{{ t('spreed', 'Promote to moderator') }}
			</NcActionButton>

			<template v-if="showPermissionsOptions">
				<NcActionSeparator />
				<NcActionButton v-if="hasNonDefaultPermissions"
					:close-after-click="true"
					@click="applyDefaultPermissions">
					<template #icon>
						<LockReset :size="20" />
					</template>
					{{ t('spreed', 'Reset custom permissions') }}
				</NcActionButton>
				<NcActionButton :close-after-click="true"
					@click="grantAllPermissions">
					<template #icon>
						<LockOpenVariant :size="20" />
					</template>
					{{ t('spreed', 'Grant all permissions') }}
				</NcActionButton>
				<NcActionButton :close-after-click="true"
					@click="removeAllPermissions">
					<template #icon>
						<Lock :size="20" />
					</template>
					{{ t('spreed', 'Remove all permissions') }}
				</NcActionButton>
				<NcActionButton :close-after-click="true"
					@click="showPermissionsEditor">
					<template #icon>
						<Pencil :size="20" />
					</template>
					{{ t('spreed', 'Edit permissions') }}
				</NcActionButton>
			</template>
			<NcActionButton v-if="canBeModerated && isEmailActor"
				icon="icon-mail"
				:close-after-click="true"
				@click="resendInvitation">
				{{ t('spreed', 'Resend invitation') }}
			</NcActionButton>
			<NcActionButton v-if="canSendCallNotification"
				:close-after-click="true"
				@click="sendCallNotification">
				<template #icon>
					<Bell :size="20" />
				</template>
				{{ t('spreed', 'Send call notification') }}
			</NcActionButton>
			<NcActionSeparator v-if="attendeePin || canBePromoted || canBeDemoted || isEmailActor" />
			<NcActionButton v-if="canBeModerated"
				icon="icon-delete"
				:close-after-click="true"
				@click="removeParticipant">
				<template v-if="isGroup">
					{{ t('spreed', 'Remove group and members') }}
				</template>
				<template v-else>
					{{ t('spreed', 'Remove participant') }}
				</template>
			</NcActionButton>
		</NcActions>
		<ParticipantPermissionsEditor v-if="permissionsEditor"
			:actor-id="participant.actorId"
			:close-after-click="true"
			:participant="participant"
			:token="token"
			@close="hidePermissionsEditor" />

</template>

<template>

		<AvatarWrapper :id="computedId"
			:disable-tooltip="true"
			:disable-menu="isSearched"
			:size="44"
			:show-user-status="showUserStatus && !isSearched"
			:show-user-status-compact="false"
			:preloaded-user-status="preloadedUserStatus"
			:name="computedName"
			:source="participant.source || participant.actorType"
			:offline="isOffline"
			:menu-container="container" />

				{{ computedName }}
				({{ t('spreed', 'moderator') }})
				({{ t('spreed', 'bot') }})
				({{ t('spreed', 'guest') }})

				{{ statusMessage }}

			{{ callIconTooltip }}
			<Microphone v-if="callIcon === 'audio'"
				:size="20" />
			<Phone v-if="callIcon === 'phone'"
				:size="20" />
			<VideoIcon v-if="callIcon === 'video'"
				:size="20" />

			<HandBackLeft v-if="callIcon === 'hand'"
				:size="18" />

		<NcActions v-if="canBeModerated && !isSearched"
			:container="container"
			:aria-label="participantSettingsAriaLabel"
			:force-menu="true"
			class="participant-row__actions">
			<template #icon>
				<LockOpenVariant v-if="actionIcon === 'LockOpenVariant'"
					:size="20" />
				<Lock v-else-if="actionIcon === 'Lock'"
					:size="20" />
				<Tune v-else-if="actionIcon === 'Tune'"
					:size="20" />
				<DotsHorizontal v-else
					:size="20" />
			</template>
			<NcActionText v-if="attendeePin"
				:title="t('spreed', 'Dial-in PIN')"
				icon="icon-password">
				{{ attendeePin }}
			</NcActionText>
			<NcActionButton v-if="canBeDemoted"
				:close-after-click="true"
				@click="demoteFromModerator">
				<template #icon>
					<Account :size="20" />
					{{ t('spreed', 'Demote from moderator') }}
				</template>
			</NcActionButton>
			<NcActionButton v-if="canBePromoted"
				:close-after-click="true"
				@click="promoteToModerator">
				<template #icon>
					<Crown :size="20" />
				</template>
				{{ t('spreed', 'Promote to moderator') }}
			</NcActionButton>

			<template v-if="showPermissionsOptions">
				<NcActionSeparator />
				<NcActionButton v-if="hasNonDefaultPermissions"
					:close-after-click="true"
					@click="applyDefaultPermissions">
					<template #icon>
						<LockReset :size="20" />
					</template>
					{{ t('spreed', 'Reset custom permissions') }}
				</NcActionButton>
				<NcActionButton :close-after-click="true"
					@click="grantAllPermissions">
					<template #icon>
						<LockOpenVariant :size="20" />
					</template>
					{{ t('spreed', 'Grant all permissions') }}
				</NcActionButton>
				<NcActionButton :close-after-click="true"
					@click="removeAllPermissions">
					<template #icon>
						<Lock :size="20" />
					</template>
					{{ t('spreed', 'Remove all permissions') }}
				</NcActionButton>
				<NcActionButton :close-after-click="true"
					@click="showPermissionsEditor">
					<template #icon>
						<Pencil :size="20" />
					</template>
					{{ t('spreed', 'Edit permissions') }}
				</NcActionButton>
			</template>
			<NcActionButton v-if="isEmailActor"
				icon="icon-mail"
				:close-after-click="true"
				@click="resendInvitation">
				{{ t('spreed', 'Resend invitation') }}
			</NcActionButton>
			<NcActionButton v-if="canSendCallNotification"
				:close-after-click="true"
				@click="sendCallNotification">
				<template #icon>
					<Bell :size="20" />
				</template>
				{{ t('spreed', 'Send call notification') }}
			</NcActionButton>
			<NcActionSeparator v-if="attendeePin || canBePromoted || canBeDemoted || isEmailActor" />
			<NcActionButton icon="icon-delete"
				:close-after-click="true"
				@click="removeParticipant">
				<template v-if="isGroup">
					{{ t('spreed', 'Remove group and members') }}
				</template>
				<template v-else>
					{{ t('spreed', 'Remove participant') }}
				</template>
			</NcActionButton>
		</NcActions>
		<ParticipantPermissionsEditor v-if="permissionsEditor"
			:actor-id="participant.actorId"
			:close-after-click="true"
			:participant="participant"
			:token="token"
			@close="hidePermissionsEditor" />

</template>


Fix bug

Fix bug

<template>
<x-column>

		%fa:user%{{ title }}

%fa:exclamation-triangle% %i18n:@is-remote%%i18n:@view-remote%

				<button class="menu" @click="menu" ref="menu">%fa:ellipsis-h%</button>
				<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow"/>
				<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
				{{ user | userName }}
				@{{ user | acct }}

				<misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/>

 0">

%fa:thumbtack% %i18n:@pinned-notes%

				<x-note v-for="n in user.pinnedNotes" :key="n.id" :note="n" :mini="true"/>

 0">
			<router-link v-for="image in images"
				:style="`background-image: url(${image.thumbnailUrl})`"
				:key="`${image.id}:${image._note.id}`"
				:to="image._note | notePage"
				:title="`${image.name}\n${(new Date(image.createdAt)).toLocaleString()}`"
			></router-link>

			<x-notes ref="timeline" :more="existMore ? fetchMoreNotes : null"/>

</x-column>
</template>


Fix sections in ExamplesList

Fix sections in ExamplesList

<template>

    <sui-rail class="docs-rail" position="right">

{{ component }}

      <sui-accordion is="sui-menu" vertical following fluid text>
        <sui-menu-item
          :key="section.title"
          v-for="section in sections"
        >

            <sui-icon name="dropdown" />
            {{ section.title }}

          <sui-accordion-content is="sui-menu-menu">
            <router-link
              is="sui-menu-item"
              :key="example.title"
              v-if="example.title"
              v-for="example in section.examples"
              :to="{ hash: kebabCase(example.title) }"
            >
              {{ example.title }}
            </router-link>
          </sui-accordion-content>
        </sui-menu-item>
      </sui-accordion>
    </sui-rail>
    <sui-grid :columns="1" padded>
      <sui-segment is="sui-grid-column">

{{ section.title }}

          <example
            v-for="(example, index) in section.examples"
            v-bind="example"
            :key="index"
            :base-url="exampleBaseUrl"
          />

      </sui-segment>
    </sui-grid>

</template>

<template>

    <sui-rail class="docs-rail" position="right">

{{ component }}

      <sui-accordion is="sui-menu" vertical following fluid text>
        <sui-menu-item
          :key="section.title"
          v-for="section in sections"
        >

            <sui-icon name="dropdown" />
            {{ section.title }}

          <sui-accordion-content is="sui-menu-menu">
            <router-link
              is="sui-menu-item"
              :key="example.title"
              v-if="example.title"
              v-for="example in section.examples"
              :to="{ hash: kebabCase(example.title) }"
            >
              {{ example.title }}
            </router-link>
          </sui-accordion-content>
        </sui-menu-item>
      </sui-accordion>
    </sui-rail>
    <sui-grid
      :key="section.title"
      v-for="section in sections"
      :columns="1"
      padded
    >
      <sui-segment is="sui-grid-column">

{{ section.title }}

        <example
          v-for="(example, index) in section.examples"
          v-bind="example"
          :key="index"
          :base-url="exampleBaseUrl"
        />
      </sui-segment>
    </sui-grid>

</template>