OPEN-242: Remove conversion from 00:00 to 23:59.

<template>

                Geldig vanaf
                <pikaday class="form-control inp-startDate" v-model="eventStartDate" :options="pikadayStart"/>

                tot en met
                <pikaday class="form-control inp-until" v-model="eventUntil" :options="pikadayUntil"/>

×

                    Regelmaat
                    <select v-model="optionFreq" class="form-control" aria-label="Regelmaat">
                        <option :value="RRule.YEARLY">Jaarlijks</option>
                        <option :value="RRule.MONTHLY">Maandelijks</option>
                        <option :value="RRule.WEEKLY">Wekelijks</option>
                        <option :value="RRule.DAILY">Dagelijks</option>
                    </select>

                    Frequentie
                    <select v-model="optionInterval" class="form-control" aria-label="Frequentie">
                        <option :value="null">elke maand</option>
                        <option :value="2">tweemaandelijks</option>
                        <option :value="3">elk kwartaal</option>
                        <option :value="4">viermaandelijks</option>
                    </select>

                    Frequentie
                    <select v-model="optionInterval" class="form-control" aria-label="Frequentie">
                        <option :value="null">elke week</option>
                        <option :value="2">tweewekelijks</option>
                        <option :value="3">driewekelijks</option>
                        <option :value="4">vierwekelijks</option>
                    </select>

                    Frequentie
                    <select v-model="optionInterval" class="form-control" aria-label="Frequentie">
                        <option :value="null">elke dag</option>
                        <option :value="2">om de dag</option>
                        <option :value="3">om de drie dagen</option>
                        <option :value="4">om de vier dagen</option>
                        <option :value="5">om de vijf dagen</option>
                        <option :value="6">om de zes dagen</option>
                    </select>

                    Op

                        <input type="radio" :name="event.start_date" v-model="weekOrMonth" value="monthday">
                         {{ eventStartDayMonth }}

                            <input type="radio" :name="event.start_date" v-model="weekOrMonth" value="weekday">
                             de

                        <select v-model="optionBysetpos" class="form-control">
                            <option value="-" disabled></option>
                            <option value="1">eerste</option>
                            <option value="2">tweede</option>
                            <option value="3">derde</option>
                            <option value="4">vierde</option>
                            <option value="-2">voorlaatste</option>
                            <option value="-1">laatste</option>
                        </select>

                        <select v-model="optionByweekday" class="form-control">
                            <option value="0">maandag</option>
                            <option value="1">dinsdag</option>
                            <option value="2">woensdag</option>
                            <option value="3">donderdag</option>
                            <option value="4">vrijdag</option>
                            <option value="5">zaterdag</option>
                            <option value="6">zondag</option>
                            <option value="0,1,2,3,4,5,6">dag</option>
                            <option value="0,1,2,3,4">weekdag</option>
                            <option value="5,6">weekenddag</option>
                        </select>

                        <select v-model="options.bymonth" class="form-control">
                            <option v-for="(month, index) in fullMonths" :value="index + 1">{{ month }}</option>
                        </select>

                    Op

                        <input type="radio" :name="event.start_date" v-model="weekOrMonth" value="monthday">
                         dag {{ eventStartDayMonth.slice(0, 2) }}

                            <input type="radio" :name="event.start_date" v-model="weekOrMonth" value="weekday">
                             de

                        <select v-model="optionBysetpos" class="form-control">
                            <option value="-" disabled></option>
                            <option value="1">eerste</option>
                            <option value="2">tweede</option>
                            <option value="3">derde</option>
                            <option value="4">vierde</option>
                            <option value="-2">voorlaatste</option>
                            <option value="-1">laatste</option>
                        </select>

                        <select v-model="optionByweekday" class="form-control">
                            <option value="0">maandag</option>
                            <option value="1">dinsdag</option>
                            <option value="2">woensdag</option>
                            <option value="3">donderdag</option>
                            <option value="4">vrijdag</option>
                            <option value="5">zaterdag</option>
                            <option value="6">zondag</option>
                            <option value="0,1,2,3,4,5,6">dag</option>
                            <option value="0,1,2,3,4">weekdag</option>
                            <option value="5,6">weekend</option>
                        </select>

                    Op

×

                    <multi-day-select :options="fullDays" v-model="optionByweekday"></multi-day-select>

                    Van
                    <input type="text" class="form-control control-time inp-startTime"
                           aria-label="Van"
                           v-model.lazy="eventStartTime"
                           placeholder="_ _ : _ _">

                    <label :aria-describedby="`next_day_${this._uid}`">tot
                    <input type="text" class="form-control control-time inp-endTime"
                           aria-label="tot"
                           v-model.lazy="eventEndTime"
                           placeholder="_ _ : _ _">

                <span class="col-xs-9  col-sm-offset-3 text-danger" :id="`next_day_${this._uid}`" v-if="eventStartTime >= eventEndTime">volgende dag

            <button type="button" class="btn btn-link" @click="$emit('add-event', prop, event)"
                    :disabled="$root.isRecreatex">+ Voeg meer dagen toe
            </button>

</template>


fix: use sender instead of recipient for sender links (#658)

<template>
  <ModalWindow
    :title="$t('TRANSACTION.TRANSACTION')"
    container-classes="TransactionShow"
    @close="emitClose"
  >
    <ListDivided>
      <ListDividedItem :label="$t('TRANSACTION.ID')">

          {{ transaction.id | truncateMiddle }}

        <ButtonClipboard
          :value="transaction.id"
          class="text-theme-page-text-light mx-1"
        />
        <button
          v-if="transaction.confirmations > 0"
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openTransaction"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem
        v-if="transaction.blockId"
        :label="$t('TRANSACTION.BLOCK_ID')"
      >
        {{ transaction.blockId }}
        <button
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openBlock"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem :label="$t('TRANSACTION.SENDER')">
        {{ wallet_formatAddress(transaction.sender, 10) }}
        <ButtonClipboard
          :value="transaction.sender"
          class="text-theme-page-text-light mx-1"
        />
        <button
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openAddress(transaction.sender)"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem
        v-if="transaction.recipient"
        :label="$t('TRANSACTION.RECIPIENT')"
      >
        {{ wallet_formatAddress(transaction.recipient, 10) }}
        <ButtonClipboard
          :value="transaction.recipient"
          class="text-theme-page-text-light mx-1"
        />
        <button
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openAddress(transaction.recipient)"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem :label="$t('TRANSACTION.AMOUNT')">
        <TransactionAmount :transaction="transaction" />
      </ListDividedItem>

      <ListDividedItem
        :label="$t('TRANSACTION.FEE')"
        :value="formatter_networkCurrency(transaction.fee)"
      />

      <ListDividedItem :label="$t('TRANSACTION.CONFIRMATIONS')">
        {{ transaction.confirmations }}
        {{ $t('TRANSACTION.WELL_CONFIRMED') }}

          <SvgIcon
            name="time"
            view-box="0 0 12 13"
          />

      </ListDividedItem>

      <ListDividedItem
        :label="$t('TRANSACTION.TIMESTAMP')"
        :value="$d(transaction.timestamp, 'long')"
      />

      <ListDividedItem
        v-if="transaction.vendorField"
        :value="transaction.vendorField"
        :label="$t('TRANSACTION.VENDOR_FIELD')"
      />
    </ListDivided>
  </ModalWindow>
</template>

<template>
  <ModalWindow
    :title="$t('TRANSACTION.TRANSACTION')"
    container-classes="TransactionShow"
    @close="emitClose"
  >
    <ListDivided>
      <ListDividedItem :label="$t('TRANSACTION.ID')">

          {{ transaction.id | truncateMiddle }}

        <ButtonClipboard
          :value="transaction.id"
          class="text-theme-page-text-light mx-1"
        />
        <button
          v-if="transaction.confirmations > 0"
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openTransaction"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem
        v-if="transaction.blockId"
        :label="$t('TRANSACTION.BLOCK_ID')"
      >
        {{ transaction.blockId }}
        <button
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openBlock"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem :label="$t('TRANSACTION.SENDER')">
        {{ wallet_formatAddress(transaction.sender, 10) }}
        <ButtonClipboard
          :value="transaction.sender"
          class="text-theme-page-text-light mx-1"
        />
        <button
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openAddress(transaction.recipient)"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem
        v-if="transaction.recipient"
        :label="$t('TRANSACTION.RECIPIENT')"
      >
        {{ wallet_formatAddress(transaction.recipient, 10) }}
        <ButtonClipboard
          :value="transaction.recipient"
          class="text-theme-page-text-light mx-1"
        />
        <button
          v-tooltip="{
            content: `${$t('TRANSACTION.OPEN_IN_EXPLORER')}`,
            trigger: 'hover'
          }"
          @click="openAddress(transaction.recipient)"
        >
          <SvgIcon
            name="open-external"
            view-box="0 0 12 12"
            class="text-theme-page-text-light"
          />
        </button>
      </ListDividedItem>

      <ListDividedItem :label="$t('TRANSACTION.AMOUNT')">
        <TransactionAmount :transaction="transaction" />
      </ListDividedItem>

      <ListDividedItem
        :label="$t('TRANSACTION.FEE')"
        :value="formatter_networkCurrency(transaction.fee)"
      />

      <ListDividedItem :label="$t('TRANSACTION.CONFIRMATIONS')">
        {{ transaction.confirmations }}
        {{ $t('TRANSACTION.WELL_CONFIRMED') }}

          <SvgIcon
            name="time"
            view-box="0 0 12 13"
          />

      </ListDividedItem>

      <ListDividedItem
        :label="$t('TRANSACTION.TIMESTAMP')"
        :value="$d(transaction.timestamp, 'long')"
      />

      <ListDividedItem
        v-if="transaction.vendorField"
        :value="transaction.vendorField"
        :label="$t('TRANSACTION.VENDOR_FIELD')"
      />
    </ListDivided>
  </ModalWindow>
</template>


Update panel.vue

Update panel.vue

<template>

                    <icon name="mobile" scale="15"/>

                    <icon name="desktop" scale="15"/>

MANAGER\\SHORTCUTS

        <button class="modal-close is-large" @click="closePanel()"/>

</template>


Update messaging-room.vue

Update messaging-room.vue

<template>
<div class="mk-messaging-room-page">
	<mk-messaging-room v-if="user" :user="user" :is-naked="true"/>
</div>
</template>

<script lang="ts">
import Vue from 'vue';
import Progress from '../../../common/scripts/loading';
import parseAcct from '../../../../../acct/parse';
import getUserName from '../../../../../renderers/get-user-name';

export default Vue.extend({
	data() {
		return {
			fetching: true,
			user: null
		};
	},
	watch: {
		$route: 'fetch'
	},
	created() {
		const applyBg = v =>
			document.documentElement.style.setProperty('background', v ? '#191b22' : '#fff', 'important');

		applyBg(this.$store.state.device.darkmode);

		this.unwatchDarkmode = this.$store.watch(s => {
			return s.device.darkmode;
		}, applyBg);

		this.fetch();
	},
	beforeDestroy() {
		document.documentElement.style.removeProperty('background');
		document.documentElement.style.removeProperty('background-color'); // for safari's bug
		this.unwatchDarkmode();
	},
	methods: {
		fetch() {
			Progress.start();
			this.fetching = true;

			(this as any).api('users/show', parseAcct(this.$route.params.user)).then(user => {
				this.user = user;
				this.fetching = false;

				document.title = 'メッセージ: ' + getUserName(this.user);

				Progress.done();
			});
		}
	}
});
</script>

<style lang="stylus" scoped>
.mk-messaging-room-page
	display flex
	flex 1
	flex-direction column
	min-height 100%

</style>

<template>
<div class="mk-messaging-room-page">
	<mk-messaging-room v-if="user" :user="user" :is-naked="true"/>
</div>
</template>

<script lang="ts">
import Vue from 'vue';
import Progress from '../../../common/scripts/loading';
import parseAcct from '../../../../../acct/parse';
import getUserName from '../../../../../renderers/get-user-name';

export default Vue.extend({
	data() {
		return {
			fetching: true,
			user: null
		};
	},
	watch: {
		$route: 'fetch'
	},
	created() {
		const applyBg = v =>
			document.documentElement.style.setProperty('background', v ? '#191b22' : '#fff', 'important');

		applyBg(this.$store.state.device.darkmode);

		this.unwatchDarkmode = this.$store.watch(s => {
			return s.device.darkmode;
		}, applyBg);

		this.fetch();
	},
	beforeDestroy() {
		document.documentElement.style.removeProperty('background');
		this.unwatchDarkmode();
	},
	methods: {
		fetch() {
			Progress.start();
			this.fetching = true;

			(this as any).api('users/show', parseAcct(this.$route.params.user)).then(user => {
				this.user = user;
				this.fetching = false;

				document.title = 'メッセージ: ' + getUserName(this.user);

				Progress.done();
			});
		}
	}
});
</script>

<style lang="stylus" scoped>
.mk-messaging-room-page
	display flex
	flex 1
	flex-direction column
	min-height 100%

</style>


🐛 fix preview scripts load timing issue

🐛 fix preview scripts load timing issue

<template>

      <template v-if="isModeViewports">

            {{ previewTitle(name, width) }}

            <iframe
              ref="iframes"
              :src="path"
              :title="title"
              :style="{ width: `${iframeSize(width)}px` }"
              :width="iframeSize(width)"
              :height="iframeSize(height)"
              :scrolling="height ? 'yes' : 'no'"
              class="preview__iframe"
              frameborder="0"
            />

      </template>
      <template v-else>

          <template v-if="breakpoints">

              <button
                :title="'options.toggle' | localize"
                class="preview__toggle"
                type="button"
                @click.stop="isBreakpointsActive = !isBreakpointsActive"
              >{{ size }}</button>

                <button
                  v-for="(width, breakpoint) in breakpoints"
                  :key="breakpoint"
                  class="preview__option"
                  type="button"
                  @click="setWidth(width)"
                >{{ previewTitle(breakpoint, width) }}</button>
                <button
                  class="preview__option"
                  type="button"
                  @click="setWidth(null)"
                >{{ 'options.reset' | localize }}</button>

          </template>

      </template>

</template>


l10n: Add dot to the end of the sentence

Reported at Transifex.

Signed-off-by: rakekniven <431c871bb5514e240c815672fa1af6e63423a115@users.noreply.github.com>

<template>

			{{ t('spreed', 'Web server setup checks') }}

				{{ t('spreed', 'Files required for background blur can be loaded') }}
				<button
					v-if="backgroundBlurAvailable === false"
					v-tooltip="backgroundBlurAvailableToolTip"
					:aria-label="backgroundBlurAvailableAriaLabel"
					class="icon"
					:class="backgroundBlurAvailableClasses"
					@click="checkBackgroundBlur" />
				<button
					v-else-if="backgroundBlurAvailable === true"
					v-tooltip="backgroundBlurAvailableToolTip"
					:aria-label="backgroundBlurAvailableAriaLabel"
					class="icon"
					:class="backgroundBlurAvailableClasses"
					@click="checkBackgroundBlur" />

</template>


Interactives: Swappable tile text should not be selectable (#813)

<template>
  <base-interactive-layout
    :interactive="interactive"
    :art-url="artUrl"
  >

          <template
            v-for="(label, index) in labels"
          >
            <li
              :key="`${index}-numbers-content`"
            >

                {{ index + 1}}.

            <li
              :key="`${index}-numbers-spacer`"
              class="spacer"
            />
          </template>

        <swapping-vue-draggable
          :list="promptSlots"
          class="slots-container prompt-slots"
          tag="ul"
          :options="{ draggable: '.prompt' }"
          @change="handleSwap"
        >
          <template
            v-for="prompt in promptSlots"
          >
            <li
              :key="`${prompt.elementId}-slot-content`"
              :class="{ 'prompt': true, 'monospaced': (prompt.textStyleCode === true) }"
            >

                {{ prompt.text }}

            <li
              :key="`${prompt.elementId}-slot-spacer`"
              class="spacer"
            />
          </template>
        </swapping-vue-draggable>

          <template
            v-for="(label, index) in labels"
          >
            <li
              :key="`${index}-label-content`"
              :class="{ 'prompt-label': true, 'monospaced': (label.textStyleCode === true) }"
            >

                {{ label.text }}

            <li
              :key="`${index}-label-spacer`"
              class="spacer"
            />
          </template>

      <base-button
        class="submit"
        :on-click="submitSolution"
        :enabled="submitEnabled"
      >
        {{ $t('common.submit') }}
      </base-button>

      <modal-interactive
        v-if="showModal"

        :success="solutionCorrect"
        @close="closeModal"
      >
        {{ $t(modalMessageTag) }}
      </modal-interactive>

  </base-interactive-layout>
</template>


fix incorrectly reported measurement

fix incorrectly reported measurement

<template>

            Optimizing Gesture Keyboards for Clarity, Done Dirt Cheap

            I had the opportunity to do a sort of self directed research project under the
            <v-link to="http://www.sfu.ca/dean-gradstudies/awards/undergraduate-awards/NSERC.html">Canadian NSERC USRA at SFU</v-link>.
            I used this opportunity to tackle a problem I deal with every day; the terrible error rate on gesture keyboards like
            <v-link to="https://www.microsoft.com/en-us/swiftkey?activetab=pivot_1%3aprimaryr2">SwiftKey</v-link>
            and
            <v-link to="https://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin&hl=en_CA">Gboard</v-link>.

            If you are unfamiliar with gesture keyboards, and trying to optimize them, I recommend checking out this
            <v-link to="https://www.youtube.com/watch?v=0PHjN4GjSi8">video</v-link>...
            or read the
            <v-link to='http://www.cs.columbia.edu/~brian/publications/optimizing_keyboards.html'>paper</v-link>

            Explaining all the specifics here would probably be too much to read, but the concept is pretty easy to cover.
            When a gesture keyboard tries to determine what word was being gestured for, it compares the gestures for each possible word, to the gesture given.
            The closest one is then chosen, after taking into consideration the user's history and preferences.
            The problem here is, many words have very similar gestures or sometimes identical gestures.
            I set out to fix this in the most obvious way I could think to.

            If you followed the link in my note at the top there, you would know about the main method already used in gesture keyboard clarity optimization.
            The biggest problem with the existing method, is it takes a very long time.
            If you take into consideration the number of words in the dictionary, square that, you are now in the ballpark of how many comparisons need to be done to rate a single keyboard for gesture clarity.

            In other words, scoring a single keyboard takes O(n2) time, which is as bad as using <v-link to='https://en.wikipedia.org/wiki/Bubble_sort'>bubble sort</v-link> on the entire dictionary, for every single keyboard.

            Getting around this restriction is pretty difficult, so instead I went about changing the goal.
            Rather than performing this analysis directly, I created a new <v-link to='https://en.wikipedia.org/wiki/Heuristic'>heuristic</v-link> that matched the old heuristic closely.
            Instead of comparing every word against every other word for each keyboard, we do this step once, analyzing the lexical structures of the words.
            Based on what parts of the words are similar, we can extract the features which must differ for gestures to be different.
            This reduces the time to evaluate a keyboard for its clarity down to the number of letters in the alphabet cubed.

            This actually makes the
            <v-link to='https://en.wikipedia.org/wiki/Asymptotic_computational_complexity'>asymptotic complexity</v-link>
            worse... if we have no way of comparing the number of letters in the alphabet to words in dictionary.
            However, in practice this theoretical problem is a non-starter.

            Rough comparison of the speed of my heuristic and the one made by Smith.
            The scale in this graph is logarithmic, like the
            <v-link to='https://en.wikipedia.org/wiki/Richter_magnitude_scale'>Richter scale</v-link>
            or
            <v-link to='https://en.wikipedia.org/wiki/Decibel'>Decibels</v-link>.
            For 3000 keyboards, with a dictionary of 8000 words, it took more than 4 hours for my computer to evaluate using my version of Smith's method, and less than 20 seconds using my method.

            The mysterious <v-link to='http://www.cs.columbia.edu/~brian/index.html'>Smith</v-link> mentioned here and in the graph is the the person that presents in that
            <v-link to="https://www.youtube.com/watch?v=0PHjN4GjSi8">video</v-link>
            and wrote the
            <v-link to='http://www.cs.columbia.edu/~brian/publications/optimizing_keyboards.html'>paper</v-link>!

            In the end, it turns out that my heuristic is pretty close to the analysis we would otherwise perform.
            Which means, that it is a good method for filtering out bad keyboards. Sometimes we may discard a good keyboard, but most of the time the keyboard wasn't worth considering.
            Moreover, throwing away a few good keyboards doesn't really matter, because there are lots and lots of keyboards.
            There are plenty of fish in the sea as it were.

            By lots and lots of keyboards I mean 26 factorial... 400,000 times more than the number of
            <v-link to='https://helios.gsfc.nasa.gov/qa_star.html#howmany'>stars in the observable universe</v-link>.
            There are infinitely more than that if we don't stick to the three row design of
            <v-link to='https://en.wikipedia.org/wiki/QWERTY'>QWERTY</v-link>.

            Correlation between my heuristic and my implementation of Smith's.
            Each data point is a keyboard, Y is the ranking according to Smith's, and X is the ranking according to mine.
            On both axes, a bigger number means the keyboard is better.
            A better ranking here doesn't say how much better a keyboard is, just that it is better.
            Just like when you say some sports team is the best in their league, you don't necessarily say by how much, just that they are the best team.

            I am working on publishing by the way, and making a keyboard that will do this optimization available via Google Play.
            I will update this page when I can provide links to those things!

</template>

<template>

            Optimizing Gesture Keyboards for Clarity, Done Dirt Cheap

            I had the opportunity to do a sort of self directed research project under the
            <v-link to="http://www.sfu.ca/dean-gradstudies/awards/undergraduate-awards/NSERC.html">Canadian NSERC USRA at SFU</v-link>.
            I used this opportunity to tackle a problem I deal with every day; the terrible error rate on gesture keyboards like
            <v-link to="https://www.microsoft.com/en-us/swiftkey?activetab=pivot_1%3aprimaryr2">SwiftKey</v-link>
            and
            <v-link to="https://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin&hl=en_CA">Gboard</v-link>.

            If you are unfamiliar with gesture keyboards, and trying to optimize them, I recommend checking out this
            <v-link to="https://www.youtube.com/watch?v=0PHjN4GjSi8">video</v-link>...
            or read the
            <v-link to='http://www.cs.columbia.edu/~brian/publications/optimizing_keyboards.html'>paper</v-link>

            Explaining all the specifics here would probably be too much to read, but the concept is pretty easy to cover.
            When a gesture keyboard tries to determine what word was being gestured for, it compares the gestures for each possible word, to the gesture given.
            The closest one is then chosen, after taking into consideration the user's history and preferences.
            The problem here is, many words have very similar gestures or sometimes identical gestures.
            I set out to fix this in the most obvious way I could think to.

            If you followed the link in my note at the top there, you would know about the main method already used in gesture keyboard clarity optimization.
            The biggest problem with the existing method, is it takes a very long time.
            If you take into consideration the number of words in the dictionary, square that, you are now in the ballpark of how many comparisons need to be done to rate a single keyboard for gesture clarity.

            In other words, scoring a single keyboard takes O(n2) time, which is as bad as using <v-link to='https://en.wikipedia.org/wiki/Bubble_sort'>bubble sort</v-link> on the entire dictionary, for every single keyboard.

            Getting around this restriction is pretty difficult, so instead I went about changing the goal.
            Rather than performing this analysis directly, I created a new <v-link to='https://en.wikipedia.org/wiki/Heuristic'>heuristic</v-link> that matched the old heuristic closely.
            Instead of comparing every word against every other word for each keyboard, we do this step once, analyzing the lexical structures of the words.
            Based on what parts of the words are similar, we can extract the features which must differ for gestures to be different.
            This reduces the time to evaluate a keyboard for its clarity down to the number of letters in the alphabet cubed.

            This actually makes the
            <v-link to='https://en.wikipedia.org/wiki/Asymptotic_computational_complexity'>asymptotic complexity</v-link>
            worse... if we have no way of comparing the number of letters in the alphabet to words in dictionary.
            However, in practice this theoretical problem is a non-starter.

            Rough comparison of the speed of my heuristic and the one made by Smith.
            The scale in this graph is logarithmic, like the
            <v-link to='https://en.wikipedia.org/wiki/Richter_magnitude_scale'>Richter scale</v-link>
            or
            <v-link to='https://en.wikipedia.org/wiki/Decibel'>Decibels</v-link>.
            For 3000 keyboards, with a dictionary of 8000 words, it took 12 hours for my computer to evaluate using my version of Smith's method, and less than 20 seconds using my method.

            The mysterious <v-link to='http://www.cs.columbia.edu/~brian/index.html'>Smith</v-link> mentioned here and in the graph is the the person that presents in that
            <v-link to="https://www.youtube.com/watch?v=0PHjN4GjSi8">video</v-link>
            and wrote the
            <v-link to='http://www.cs.columbia.edu/~brian/publications/optimizing_keyboards.html'>paper</v-link>!

            In the end, it turns out that my heuristic is pretty close to the analysis we would otherwise perform.
            Which means, that it is a good method for filtering out bad keyboards. Sometimes we may discard a good keyboard, but most of the time the keyboard wasn't worth considering.
            Moreover, throwing away a few good keyboards doesn't really matter, because there are lots and lots of keyboards.
            There are plenty of fish in the sea as it were.

            By lots and lots of keyboards I mean 26 factorial... 400,000 times more than the number of
            <v-link to='https://helios.gsfc.nasa.gov/qa_star.html#howmany'>stars in the observable universe</v-link>.
            There are infinitely more than that if we don't stick to the three row design of
            <v-link to='https://en.wikipedia.org/wiki/QWERTY'>QWERTY</v-link>.

            Correlation between my heuristic and my implementation of Smith's.
            Each data point is a keyboard, Y is the ranking according to Smith's, and X is the ranking according to mine.
            On both axes, a bigger number means the keyboard is better.
            A better ranking here doesn't say how much better a keyboard is, just that it is better.
            Just like when you say some sports team is the best in their league, you don't necessarily say by how much, just that they are the best team.

            I am working on publishing by the way, and making a keyboard that will do this optimization available via Google Play.
            I will update this page when I can provide links to those things!

</template>


updating spacing

updating spacing

<template>

      <form id="DuesRequiredInfoForm" v-on:submit.prevent="submit">

GT Directory Info

Information obtained via GT Single Sign-On. Update at Passport.

          Full Name

            <input v-model="localUser.full_name" type="text" readonly class="form-control" id="user-name">

          GT Username

            <input v-model="localUser.uid" type="text" readonly class="form-control" id="user-uid">

          GT Email

            <input v-model="localUser.gt_email" type="text" readonly class="form-control" id="user-gtemail">

Membership Information

          Dues Term

            <select id="duesPackage" v-model="duesPackageChoice" class="custom-select"
                    :class="{ 'is-invalid': $v.duesPackageChoice.$error }" @input="$v.duesPackageChoice.$touch()">
              <option value="" style="display:none" v-if="!duesPackages">Loading...</option>
              <option value="" style="display:none" v-if="duesPackages && duesPackages.length === 0">No Dues Packages
                Available
              </option>
              <option value="" style="display:none" v-if="duesPackages && duesPackages.length > 0">Select One</option>
              <option v-for="duesPackage in duesPackages" :value="duesPackage.id">{{ duesPackage.name }} -
                ${{ duesPackage.cost }}
              </option>
            </select>

              Select a dues package.

Graduation Information

            Graduation Date

              <term-input
                v-model="localUser.graduation_semester"
                id="user-graduationsemester"
                :is-error="$v.localUser.graduation_semester.$error"
                @touch="$v.localUser.graduation_semester.$touch()">
              </term-input>

                Select a valid graduation date.

Information for Merchandise

          T-Shirt Size

            <custom-radio-buttons
              v-model="localUser.shirt_size"
              :options="shirtSizeOptions"
              id="user-shirtsize"
              :is-error="$v.localUser.shirt_size.$error"
              @input="$v.localUser.shirt_size.$touch()">
            </custom-radio-buttons>

              You must choose a shirt size.

          Polo Size

            <custom-radio-buttons
              v-model="localUser.polo_size"
              :options="shirtSizeOptions"
              id="user-polosize"
              :is-error="$v.localUser.polo_size.$error"
              @input="$v.localUser.polo_size.$touch()">
            </custom-radio-buttons>

              You must choose a polo size.

Merchandise Selection

 0)">One item of RoboJackets merch from each group below
          is included with your dues payment. {{ merchDependencyText }}

Only students are eligible for RoboJackets merch.

          {{ group }}

            <select :id="'merch-'+group" class="custom-select" v-model="merchlist.selection"
                    :class="{ 'is-invalid': $v.merchGroups.$each[group].$error }"
                    @input="$v.merchGroups.$each[group].$touch()">
              <option value="" style="display:none">Select One</option>
              <option v-for="merch in merchlist.list" :value="merch.id">{{ merch.name }}</option>
            </select>

              You must choose an item.

            <button type="submit" class="btn btn-primary float-right">Continue</button>

      </form>

</template>


Menu show/hide on scroll

Menu show/hide on scroll

<template>

                        <hamburger-icon class="p15 icon bg-lightgray"/>

                        <logo width="36px" height="41px"/>

                        <search-icon class="p15 icon hidden-xs" />
                        <wishlist-icon class="p15 icon hidden-xs" />
                        <microcart-icon class="p15 icon" />
                        <account-icon class="p15 icon hidden-xs" />

                        <router-link to="/">Return to the shopping</router-link>

</template>


Disable rule in  modal.vue breaking tests

<template>

            <slot name="header">

                {{ title }}

              <button
                type="button"
                class="close pull-right"
                @click="emitCancel($event)"
                data-dismiss="modal"
                aria-label="Close"
              >
                ×
              </button>
            </slot>

            <slot
              name="body"
              :text="text"
            >

{{ text }}

            </slot>

            <button
              type="button"
              class="btn pull-left"
              :class="btnCancelKindClass"
              @click="emitCancel($event)"
              data-dismiss="modal"
            >
              {{ closeButtonLabel }}
            </button>
            <button
              v-if="primaryButtonLabel"
              type="button"
              class="btn pull-right js-primary-button"
              :disabled="submitDisabled"
              :class="btnKindClass"
              @click="emitSubmit($event)"
              data-dismiss="modal"
            >
              {{ primaryButtonLabel }}
            </button>

</template>


Adjust menu.

Adjust menu.

<template>
    <div id="navigator">
        <el-row type="flex">
            <el-col :span="22">
                <el-menu :default-active="activeIndex" mode="horizontal"
                    background-color="#1199cc"
                    text-color="#fff"
                    menu-trigger="click"
                    active-text-color="#FFB400"
                    :router="true">
                    <el-menu-item index="/">首页</el-menu-item>
                    <el-menu-item index="/travel">游记</el-menu-item>
                    <el-menu-item index="/articles">文章</el-menu-item>
                </el-menu>
            </el-col>
            <el-col :span="2">
                <el-menu :default-active="activeIndex" mode="horizontal"
                    background-color="#1199cc"
                    text-color="#fff"
                    menu-trigger="click"
                    active-text-color="#FFB400"
                    :router="true">
                    <el-submenu index="/">
                        <template slot="title">Map</template>
                        <el-menu-item index="/map-place">地点</el-menu-item>
                        <el-menu-item index="/map-province">省份</el-menu-item>
                    </el-submenu>
                </el-menu>
            </el-col>
        </el-row>
    </div>
</template>

<script>
export default {
    name: 'Navigator',
    data() {
        return {
            user: 'Primo'
        }
    },
    computed: {
        activeIndex: function() {
            // use main menu
            return this.$route.path.split('/').slice(0, 2).join('/')
        }
    },
    methods: {
    }
}
</script>

<style lang="scss" scoped>
#navigator {
    color: white;
    box-shadow: 0 0px 8px 0px #363636;
    background-color: #0099CC;

    .el-row {
        height: 60px;

        .el-from {
            .el-form-item {
                margin-bottom: 0;
            }
        }

        .el-button {
            color: white;
        }
    }
}
</style>

<template>
    <div id="navigator">
        <el-menu :default-active="activeIndex" mode="horizontal"
            background-color="#1199cc"
            text-color="#fff"
            menu-trigger="click"
            active-text-color="#FFB400"
            :router="true">
            <el-menu-item index="/">首页</el-menu-item>
            <el-submenu index="/">
                <template slot="title">Map</template>
                <el-menu-item index="/map-place">地点</el-menu-item>
                <el-menu-item index="/map-province">省份</el-menu-item>
            </el-submenu>
            <el-menu-item index="/travel">游记</el-menu-item>
            <el-menu-item index="/articles">文章</el-menu-item>

            <el-row type="flex" :gutter="15" align="middle">
                <el-col :span="16" :offset="7">
                    <el-row class="search" type="flex" align="middle">
                        <el-col class="button-wrap" :span="2" :offset="22" v-show="!onSearch">
                            <el-button type="text" icon="el-icon-search" class="search-button" @click="search"></el-button>
                        </el-col>
                        <el-col v-show="onSearch" :span="24">
                            <transition name="slide-fade">
                                <el-form v-show="onSearch">
                                    <el-form-item>
                                        <el-input placeholder="Search" suffix-icon="el-icon-search" @blur="search" ref="searchBox"></el-input>
                                    </el-form-item>
                                </el-form>
                            </transition>
                        </el-col>
                    </el-row>
                </el-col>
            </el-row>
        </el-menu>
    </div>
</template>

<script>
export default {
    name: 'Navigator',
    data() {
        return {
            user: 'Primo',
            onSearch: false
        }
    },
    computed: {
        activeIndex: function() {
            // use main menu
            return this.$route.path.split('/').slice(0, 2).join('/')
        }
    },
    methods: {
        search: function() {
            this.onSearch = !this.onSearch
            if (this.onSearch) {
                this.$nextTick(function() {
                    this.$refs.searchBox.focus()
                })
            }
        }
    }
}
</script>

<style lang="scss" scoped>
.slide-fade-enter-active,
.slide-fade-leave-active {
    transition: all .3s ease;
}

.slide-fade-enter,
.slide-fade-leave-to {
    transform: translateX(100px);
    opacity: 0;
}

#navigator {
    color: white;
    box-shadow: 0 0px 8px 0px #363636;
    background-color: #0099CC;

    .el-row {
        height: 60px;

        .el-from {
            .el-form-item {
                margin-bottom: 0;
            }
        }

        .el-button {
            color: white;
        }

        .search {
            .el-form-item {
                margin-bottom: 0;
            }
            .button-wrap {
                height: 60px;
                position: relative;
                .search-button {
                    width: 35px;
                    position: absolute;
                    right: 0;
                    top: 0;
                    padding: 23px 0;
                }
            }
        }
    }
}
</style>


tweaks

tweaks


edit svgeditor

edit svgeditor


Fix tab debouce update issue

Fix tab debouce update issue

<template>
  <div :class="bem( `tab-search`, { theme } )">
    <label class="tab-search__label">
      <svg class="tab-search__icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
        <path d="M15.707 14.293l-4.822-4.822a6.019 6.019 0 1 0-1.414 1.414l4.822 4.822a1 1 0 0 0 1.414-1.414zM6 10a4 4 0 1 1 4-4 4 4 0 0 1-4 4z"></path>
      </svg>
      <input class="tab-search__input" type="search" v-model="search_text" @input="onUpdateSearchText" :placeholder="__MSG_tab_search_placeholder__"/>
      <svg class="tab-search__clear-icon" @click="clearSearchText()" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
        <path fill-rule="evenodd" d="M6.586 8l-2.293 2.293a1 1 0 0 0 1.414 1.414L8 9.414l2.293 2.293a1 1 0 0 0 1.414-1.414L9.414 8l2.293-2.293a1 1 0 1 0-1.414-1.414L8 6.586 5.707 4.293a1 1 0 0 0-1.414 1.414L6.586 8zM8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0z"></path>
      </svg>
    </label>
  </div>
</template>

<script>
import {
  getWindow,
} from '../store/helpers.mjs'
import {
  bem,
  debounce,
  onStateChange,
} from "./helpers.mjs"

export default {
  name: "tab-search",
  props: [ "theme" ],
  data() {
    return {
      search_text: '',
      search_resolved: true,
      window_id: window.current_window_id,
      search_history: []
    }
  },
  computed: {
    __MSG_tab_search_placeholder__() {
      return window.background.getMessage( "tab_search_placeholder" )
    }
  },
  created() {
    onStateChange( state => {
      const state_window = getWindow( state, this.window_id )
      if( state_window && state_window.search != null ) {
        const index = this.search_history.indexOf( state_window.search.text )
        if( index > -1 ) {
          this.search_history.splice( index, 1 )
        } else {
          this.search_text = state_window.search.text
        }
        this.search_resolved = state_window.search.resolved
      } else {
        this.search_text = ""
        this.search_resolved = true
      }
    })
  },
  methods: {
    bem,
    onUpdateSearchText: debounce( function() {
      console.info('runSearch', this.search_text)
      this.search_history.push( this.search_text )
      window.background.runTabSearch( window.store, this.window_id, this.search_text )
    }, 250 ),
    clearSearchText() {
      console.info('clearSearchText', this.search_text)
      this.search_text = ""
      window.background.runTabSearch( window.store, this.window_id, this.search_text )
    },
  }
}
</script>

<style lang="scss">
@import "../styles/photon";

$dark-awesome-bar-background: #474749;

%tab-search__icon {
  position: absolute;
  top: 11px;
  height: 16px;
  width: 16px;
  opacity: 0.4;
}

$tab-search__theme: (
  light: (
    --background-color: $white-100,
    --color: $grey-90,
  ),
  dark: (
    --background-color: $dark-awesome-bar-background,
    --color: $white-100,
  ),
);

.tab-search {
  flex: 1;
  padding: 4px 0 4px 4px;
  position: relative;

  &__input {
    @extend %text-body-10;
    width: 100%;
    height: 30px;
    padding-left: 28px;
    border: 1px solid $grey-90-a30;
    border-radius: 4px;

    &::-moz-placeholder {
      @extend %text-body-10;
    }
  }

  &__label:hover &__input {
    border-color: $grey-90-a50;
  }

  &__icon {
    @extend %tab-search__icon;
    left: 11px;
  }

  &__clear-icon {
    @extend %tab-search__icon;
    @extend %slow-transition;
    transition-property: opacity;
    right: 8px;
    cursor: pointer;
  }

  &__input:placeholder-shown + &__clear-icon {
    display: none;
    opacity: 0;
  }
}

@each $theme, $colors in $tab-search__theme {
  .tab-search {
    &--theme-#{$theme} &__input {
      background-color: map-get( $colors, --background-color );
      color: map-get( $colors, --color );
      border: 1px solid rgba( map-get( $colors, --color ), 0.3 );

      &::-moz-placeholder {
        color: map-get( $colors, --color );
      }
    }

    &--theme-#{$theme} &__label:hover &__input {
      border-color: rgba( map-get( $colors, --color ), 0.5 );
    }

    &--theme-#{$theme} &__icon {
      fill: map-get( $colors, --color );
    }

    &--theme-#{$theme} &__clear-icon {
      fill: map-get( $colors, --color );
    }
  }
}
</style>

<template>
  <div :class="bem( `tab-search`, { theme } )">
    <label class="tab-search__label">
      <svg class="tab-search__icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
        <path d="M15.707 14.293l-4.822-4.822a6.019 6.019 0 1 0-1.414 1.414l4.822 4.822a1 1 0 0 0 1.414-1.414zM6 10a4 4 0 1 1 4-4 4 4 0 0 1-4 4z"></path>
      </svg>
      <input class="tab-search__input" type="search" v-model="search_text" @input="onUpdateSearchText" :placeholder="__MSG_tab_search_placeholder__"/>
      <svg class="tab-search__clear-icon" @click="clearSearchText()" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
        <path fill-rule="evenodd" d="M6.586 8l-2.293 2.293a1 1 0 0 0 1.414 1.414L8 9.414l2.293 2.293a1 1 0 0 0 1.414-1.414L9.414 8l2.293-2.293a1 1 0 1 0-1.414-1.414L8 6.586 5.707 4.293a1 1 0 0 0-1.414 1.414L6.586 8zM8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0z"></path>
      </svg>
    </label>
  </div>
</template>

<script>
import {
  getWindow,
} from '../store/helpers.mjs'
import {
  bem,
  debounce,
  onStateChange,
} from "./helpers.mjs"

export default {
  name: "tab-search",
  props: [ "theme" ],
  data() {
    return {
      search_text: '',
      search_resolved: true,
      window_id: window.current_window_id,
    }
  },
  computed: {
    __MSG_tab_search_placeholder__() {
      return window.background.getMessage( "tab_search_placeholder" )
    }
  },
  created() {
    onStateChange( state => {
      const state_window = getWindow( state, this.window_id )
      if( state_window && state_window.search != null ) {
        this.search_text = state_window.search.text
        this.search_resolved = state_window.search.resolved
      } else {
        this.search_text = ""
        this.search_resolved = true
      }
    })
  },
  methods: {
    bem,
    onUpdateSearchText: debounce( function() {
      console.info('runSearch', this.search_text)
      window.background.runTabSearch( window.store, this.window_id, this.search_text )
    }, 250 ),
    clearSearchText() {
      console.info('clearSearchText', this.search_text)
      this.search_text = ""
      window.background.runTabSearch( window.store, this.window_id, this.search_text )
    },
  }
}
</script>

<style lang="scss">
@import "../styles/photon";

$dark-awesome-bar-background: #474749;

%tab-search__icon {
  position: absolute;
  top: 11px;
  height: 16px;
  width: 16px;
  opacity: 0.4;
}

$tab-search__theme: (
  light: (
    --background-color: $white-100,
    --color: $grey-90,
  ),
  dark: (
    --background-color: $dark-awesome-bar-background,
    --color: $white-100,
  ),
);

.tab-search {
  flex: 1;
  padding: 4px 0 4px 4px;
  position: relative;

  &__input {
    @extend %text-body-10;
    width: 100%;
    height: 30px;
    padding-left: 28px;
    border: 1px solid $grey-90-a30;
    border-radius: 4px;

    &::-moz-placeholder {
      @extend %text-body-10;
    }
  }

  &__label:hover &__input {
    border-color: $grey-90-a50;
  }

  &__icon {
    @extend %tab-search__icon;
    left: 11px;
  }

  &__clear-icon {
    @extend %tab-search__icon;
    @extend %slow-transition;
    transition-property: opacity;
    right: 8px;
    cursor: pointer;
  }

  &__input:placeholder-shown + &__clear-icon {
    display: none;
    opacity: 0;
  }
}

@each $theme, $colors in $tab-search__theme {
  .tab-search {
    &--theme-#{$theme} &__input {
      background-color: map-get( $colors, --background-color );
      color: map-get( $colors, --color );
      border: 1px solid rgba( map-get( $colors, --color ), 0.3 );

      &::-moz-placeholder {
        color: map-get( $colors, --color );
      }
    }

    &--theme-#{$theme} &__label:hover &__input {
      border-color: rgba( map-get( $colors, --color ), 0.5 );
    }

    &--theme-#{$theme} &__icon {
      fill: map-get( $colors, --color );
    }

    &--theme-#{$theme} &__clear-icon {
      fill: map-get( $colors, --color );
    }
  }
}
</style>


feat(food组件): title部分完成

feat(food组件): title部分完成

<template>
  <transition name="fly-in">

{{food.name}}

          月售{{food.sellCount}}份
          好评率{{food.rating}}%

            {{food.price}}
            {{food.oldPrice}}

加入购物车

  </transition>
</template>

<template>
  <transition name="fly-in">

  </transition>
</template>


Added Info about AlphaPOC RIC assignment

<template>

FAQ

I do not have an account. How do I get one?

Due to the reason that you can trigger amateur radio transmissions with your account, please apply for an account by emailing to rwth-afu [at] online.de and provide some prove that you are a licensed amateur radio operator. Also give your Pager ID (RIC) as you can see here:

I emailed 10 minutes ago. No answer yet. How long does it take to get an account?

We review the applications in our free time. So please be patient for some days.

What is a callsign?

A callsign is logical connection between a name (the callsign) and one or multiple RIC numbers.

What is a transmitter?

A transmitter is a device that puts the POCSAG signal on the air. There are many hardware configurations, like C9000, RasPager. Have a look at https://www.afu.rwth-aachen.de/projekte/funkruf-pager-pocsag.

What is a RIC?

A RIC is the identifiaction number in the POCSAG world. They can have special meaning like the clock-RIC to set the date and time on your pager. Mostly they identify personally your pager. The assignment between a personal RIC and you is the "Callsign".

I cannot add a callsign or a transmitter.

Due to abuse reasons, only admins can add callsigns or transmitters. Please contact rwth-afu [at] online.de. Once your callsign or transmitter is assigned to you, you can edit it by yourself.

I want to add a transmitter to the network.

Very nice. We like the network growing. Please send an email to rwth-afu [at] online.de and give the following information:

Callsign of the transmitter

Location Latitude and Longitude (Please in decimal form, e.g. 50.12345)

Type of transmitter: Widerange (> 1 watt) oder Personal (<= 1 watt, e.g. RasPager)

Transmitter output power in watt

Antenna type (omni or directional)

Height of the antenna above ground (!), not above sea level

Gain of the antenna minus cable losses in dBi

Desired time slots (Please have a look here http://www.hampager.de/#/transmitters/map and find out, which timeslots in your neighborhood are already used.) If you are unsure, just let us know and we will find a suitable solution.

I want to add a node to the network.

Please send an email to rwth-afu [at] online.de and give the following information:

Amateur radio callsign.

Location Latitude and Longitude (Please in decimal form, e.g. 50.12345)

Some reason, why it makes sense to add your node.

Keep in mind that you need a stable HAMNET connection (44.0.0.0/8) to join the cluster network.

I do not receive the rubrics on my skyper.

Please activate your skyper on http://www.hampager.de/#/rubrics using the "Activate Rubrics" function. You have to provide your pager ID (RIC).

I do not have skyper but a AlphaPOC receiver. How can I receive the rubric's content?

It is now common sense to take your DMR ID an substract 60000 from it. So DMR ID 2621234 will result in POCSAG RIC 2021234. If you don't have a DMR ID assigned to you, please contact us and we will assign you a sepearte RIC.

What RIC should I assign to my AlphaPOC receiver, as there is no pre-defined RIC on these devices?

New rubric content is send both in skyper format and to RIC number (1000 + <rubric-number>). So Rubric number 31 is RIC 1031. Add this RIC to your receiver.

I want to add a rubric to the network.

Please send an email to rwth-afu [at] online.de and give the following information:

The desired name

The content you want to provide

The transmitter group on which you want to send out your data

I want to send automated messages from third party sources.

You can use the REST API to do so. Description here: https://github.com/DecentralizedAmateurPagingNetwork/Core/wiki/Beschreibung-der-REST-API. Please be aware that the traffic capability is limited on the air interface. So make fair use and use a transmitter group that fits to your needs. Nobody wants in transmitter group "all" the information that someone has phoned you or your washing machine is done.

What is a timeslot?

As all transmitters work on the same frequency (TDMA), only one transmitter can be active at a time to avoid interference. This is why there are 16 timeslots (0 to F) assigned to each transmitter. If you operate a transmitter it is mandatory to have your clock synced by NTP. Otherwise you will destroy the network.

How do I know which timeslots to use on a transmitter?

Please have a look here http://www.hampager.de/#/transmitters/map and find out, which timeslots in your neighborhood are already used.)

I want to get a plug-and-play hardware for a new transmitter site. Where can I get it from?

There is an easy solution with GM1200 and a RasPi. If you like to get a 19'' Rack, please send an email to dh3wr [at] darc.de. An example can be found here https://www.afu.rwth-aachen.de/projekte/funkruf-pager-pocsag/funkrufsender-19-zoll-mit-gm1200.

Where can I get a Skyper?

Please help yourself. There is a possiblity to buy at www.funkhandel.com.

Why is the time displayed on my Skyper wrong?

It is not wrong, it's UTC. We know, there are pros and cons of using UTC, but this is the most common understanding.

Some rubics are not transmitted on my transmitter.

Please check, if your transmitter is in a member of all desired transmitter groups that are assigned to a rubric. Especially for the DX-Cluster messages, there were complains about heavy load on some transmitters. So we added another transmitter group of the DX-Cluster messages. If you want to transmit those messages, have your transmitter added to the group.

Sometimes my transmitter transmits longer than the assigned timeslots.

We are aware of this bug. It is tracked in this issue: https://github.com/rwth-afu/UniPager/issues/66 and will be hopefully solved soon.

Is there an SMTP-Interface to DAPNET, so I can send calls with my email program?

Not yet. Feel free to contribute. Use the open documented REST-API and write a simple SMTP server generating REST calls to the API.

</template>

<template>

FAQ

I do not have an account. How do I get one?

Due to the reason that you can trigger amateur radio transmissions with your account, please apply for an account by emailing to rwth-afu [at] online.de and provide some prove that you are a licensed amateur radio operator. Also give your Pager ID (RIC) as you can see here:

I emailed 10 minutes ago. No answer yet. How long does it take to get an account?

We review the applications in our free time. So please be patient for some days.

What is a callsign?

A callsign is logical connection between a name (the callsign) and one or multiple RIC numbers.

What is a transmitter?

A transmitter is a device that puts the POCSAG signal on the air. There are many hardware configurations, like C9000, RasPager. Have a look at https://www.afu.rwth-aachen.de/projekte/funkruf-pager-pocsag.

What is a RIC?

A RIC is the identifiaction number in the POCSAG world. They can have special meaning like the clock-RIC to set the date and time on your pager. Mostly they identify personally your pager. The assignment between a personal RIC and you is the "Callsign".

I cannot add a callsign or a transmitter.

Due to abuse reasons, only admins can add callsigns or transmitters. Please contact rwth-afu [at] online.de. Once your callsign or transmitter is assigned to you, you can edit it by yourself.

I want to add a transmitter to the network.

Very nice. We like the network growing. Please send an email to rwth-afu [at] online.de and give the following information:

Callsign of the transmitter

Location Latitude and Longitude (Please in decimal form, e.g. 50.12345)

Type of transmitter: Widerange (> 1 watt) oder Personal (<= 1 watt, e.g. RasPager)

Transmitter output power in watt

Antenna type (omni or directional)

Height of the antenna above ground (!), not above sea level

Gain of the antenna minus cable losses in dBi

Desired time slots (Please have a look here http://www.hampager.de/#/transmitters/map and find out, which timeslots in your neighborhood are already used.) If you are unsure, just let us know and we will find a suitable solution.

I want to add a node to the network.

Please send an email to rwth-afu [at] online.de and give the following information:

Amateur radio callsign.

Location Latitude and Longitude (Please in decimal form, e.g. 50.12345)

Some reason, why it makes sense to add your node.

Keep in mind that you need a stable HAMNET connection (44.0.0.0/8) to join the cluster network.

I do not receive the rubrics on my skyper.

Please activate your skyper on http://www.hampager.de/#/rubrics using the "Activate Rubrics" function. You have to provide your pager ID (RIC).

I do not have skyper but a AlphaPOC receiver. How can I receive the rubric's content.

New rubric content is send both in skyper format and to RIC number (1000 + <rubric-number>). So Rubric number 31 is RIC 1031. Add this RIC to your receiver.

I want to add a rubric to the network.

Please send an email to rwth-afu [at] online.de and give the following information:

The desired name

The content you want to provide

The transmitter group on which you want to send out your data

I want to send automated messages from third party sources.

You can use the REST API to do so. Description here: https://github.com/DecentralizedAmateurPagingNetwork/Core/wiki/Beschreibung-der-REST-API. Please be aware that the traffic capability is limited on the air interface. So make fair use and use a transmitter group that fits to your needs. Nobody wants in transmitter group "all" the information that someone has phoned you or your washing machine is done.

What is a timeslot?

As all transmitters work on the same frequency (TDMA), only one transmitter can be active at a time to avoid interference. This is why there are 16 timeslots (0 to F) assigned to each transmitter. If you operate a transmitter it is mandatory to have your clock synced by NTP. Otherwise you will destroy the network.

How do I know which timeslots to use on a transmitter?

Please have a look here http://www.hampager.de/#/transmitters/map and find out, which timeslots in your neighborhood are already used.)

I want to get a plug-and-play hardware for a new transmitter site. Where can I get it from?

There is an easy solution with GM1200 and a RasPi. If you like to get a 19'' Rack, please send an email to dh3wr [at] darc.de. An example can be found here https://www.afu.rwth-aachen.de/projekte/funkruf-pager-pocsag/funkrufsender-19-zoll-mit-gm1200.

Where can I get a Skyper?

Please help yourself. There is a possiblity to buy at www.funkhandel.com.

Why is the time displayed on my Skyper wrong?

It is not wrong, it's UTC. We know, there are pros and cons of using UTC, but this is the most common understanding.

Some rubics are not transmitted on my transmitter.

Please check, if your transmitter is in a member of all desired transmitter groups that are assigned to a rubric. Especially for the DX-Cluster messages, there were complains about heavy load on some transmitters. So we added another transmitter group of the DX-Cluster messages. If you want to transmit those messages, have your transmitter added to the group.

Sometimes my transmitter transmits longer than the assigned timeslots.

We are aware of this bug. It is tracked in this issue: https://github.com/rwth-afu/UniPager/issues/66 and will be hopefully solved soon.

Is there an SMTP-Interface to DAPNET, so I can send calls with my email program?

Not yet. Feel free to contribute. Use the open documented REST-API and write a simple SMTP server generating REST calls to the API.

</template>


omim  - update vue demo

omim  - update vue demo

<template>

    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

The switch is {{result? 'on' : 'off'}}.

    <m-icon-button color="red" icons="['favorite', 'favorite_border']" @change="myEvent"></m-icon-button>

</template>


Select: use initial height to reset height of input (#12719)

* Select: use initial height to reset height of input

* Update select.vue

<template>

        <el-tag
          :closable="!selectDisabled"
          :size="collapseTagSize"
          :hit="selected[0].hitState"
          type="info"
          @close="deleteTag($event, selected[0])"
          disable-transitions>
          {{ selected[0].currentLabel }}
        </el-tag>
        <el-tag
          v-if="selected.length > 1"
          :closable="false"
          :size="collapseTagSize"
          type="info"
          disable-transitions>
          + {{ selected.length - 1 }}
        </el-tag>

      <transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
        <el-tag
          v-for="item in selected"
          :key="getValueKey(item)"
          :closable="!selectDisabled"
          :size="collapseTagSize"
          :hit="item.hitState"
          type="info"
          @close="deleteTag($event, item)"
          disable-transitions>
          {{ item.currentLabel }}
        </el-tag>
      </transition-group>

      <input
        type="text"
        class="el-select__input"
        :class="[selectSize ? `is-${ selectSize }` : '']"
        :disabled="selectDisabled"
        :autocomplete="autoComplete || autocomplete"
        @focus="handleFocus"
        @blur="softFocus = false"
        @click.stop
        @keyup="managePlaceholder"
        @keydown="resetInputState"
        @keydown.down.prevent="navigateOptions('next')"
        @keydown.up.prevent="navigateOptions('prev')"
        @keydown.enter.prevent="selectOption"
        @keydown.esc.stop.prevent="visible = false"
        @keydown.delete="deletePrevTag"
        @compositionstart="handleComposition"
        @compositionupdate="handleComposition"
        @compositionend="handleComposition"
        v-model="query"
        @input="debouncedQueryChange"
        v-if="filterable"
        :style="{ width: inputLength + 'px', 'max-width': inputWidth - 42 + 'px' }"
        ref="input">

    <el-input
      ref="reference"
      v-model="selectedLabel"
      type="text"
      :placeholder="currentPlaceholder"
      :name="name"
      :id="id"
      :autocomplete="autoComplete || autocomplete"
      :size="selectSize"
      :disabled="selectDisabled"
      :readonly="readonly"
      :validate-event="false"
      :class="{ 'is-focus': visible }"
      @focus="handleFocus"
      @blur="handleBlur"
      @keyup.native="debouncedOnInputChange"
      @keydown.native.down.stop.prevent="navigateOptions('next')"
      @keydown.native.up.stop.prevent="navigateOptions('prev')"
      @keydown.native.enter.prevent="selectOption"
      @keydown.native.esc.stop.prevent="visible = false"
      @keydown.native.tab="visible = false"
      @paste.native="debouncedOnInputChange"
      @mouseenter.native="inputHovering = true"
      @mouseleave.native="inputHovering = false">
      <template slot="prefix" v-if="$slots.prefix">
        <slot name="prefix"></slot>
      </template>

    </el-input>
    <transition
      name="el-zoom-in-top"
      @before-enter="handleMenuEnter"
      @after-leave="doDestroy">
      <el-select-menu
        ref="popper"
        :append-to-body="popperAppendToBody"
        v-show="visible && emptyText !== false">
        <el-scrollbar
          tag="ul"
          wrap-class="el-select-dropdown__wrap"
          view-class="el-select-dropdown__list"
          ref="scrollbar"
          :class="{ 'is-empty': !allowCreate && query && filteredOptionsCount === 0 }"
          v-show="options.length > 0 && !loading">
          <el-option
            :value="query"
            created
            v-if="showNewOption">
          </el-option>
          <slot></slot>
        </el-scrollbar>

          {{ emptyText }}

      </el-select-menu>
    </transition>

</template>


fix select prop value

fix select prop value

<template>

      <transition-group @after-leave="resetInputHeight">
        <el-tag
          v-for="item in selected"
          :key="getValueKey(item)"
          :closable="!disabled"
          :hit="item.hitState"
          type="primary"
          @close="deleteTag($event, item)"
          close-transition>
          {{ item.currentLabel }}
        </el-tag>
      </transition-group>

      <input
        type="text"
        class="el-select__input"
        :class="`is-${ size }`"
        @focus="visible = true"
        :disabled="disabled"
        @keyup="managePlaceholder"
        @keydown="resetInputState"
        @keydown.down.prevent="navigateOptions('next')"
        @keydown.up.prevent="navigateOptions('prev')"
        @keydown.enter.prevent="selectOption"
        @keydown.esc.stop.prevent="visible = false"
        @keydown.delete="deletePrevTag"
        v-model="query"
        :debounce="remote ? 300 : 0"
        v-if="filterable"
        :style="{ width: inputLength + 'px', 'max-width': inputWidth - 42 + 'px' }"
        ref="input">

    <el-input
      ref="reference"
      v-model="selectedLabel"
      type="text"
      :placeholder="currentPlaceholder"
      :name="name"
      :size="size"
      :disabled="disabled"
      :readonly="!filterable || multiple"
      :validate-event="false"
      @focus="handleFocus"
      @click="handleIconClick"
      @mousedown.native="handleMouseDown"
      @keyup.native="debouncedOnInputChange"
      @keydown.native.down.prevent="navigateOptions('next')"
      @keydown.native.up.prevent="navigateOptions('prev')"
      @keydown.native.enter.prevent="selectOption"
      @keydown.native.esc.stop.prevent="visible = false"
      @keydown.native.tab="visible = false"
      @paste.native="debouncedOnInputChange"
      @mouseenter.native="inputHovering = true"
      @mouseleave.native="inputHovering = false"
      :icon="iconClass">
    </el-input>
    <transition
      name="el-zoom-in-top"
      @before-enter="handleMenuEnter"
      @after-leave="doDestroy">
      <el-select-menu
        ref="popper"
        v-show="visible && emptyText !== false">
        <el-scrollbar
          tag="ul"
          wrap-class="el-select-dropdown__wrap"
          view-class="el-select-dropdown__list"
          :class="{ 'is-empty': !allowCreate && filteredOptionsCount === 0 }"
          v-show="options.length > 0 && !loading">
          <el-option
            :value="query"
            created
            v-if="showNewOption">
          </el-option>
          <slot></slot>
        </el-scrollbar>

{{ emptyText }}

      </el-select-menu>
    </transition>

</template>


color picker radio and color choice

color picker radio and color choice

<template>

    <HubHeader title="Image Generator" :expanded="true"></HubHeader>

Quickly and easily create app icons for various platforms in the right size and format

        <form
          id="imageFileInputForm"
          class="form"
          enctype="multipart/form-data"
          role="form"
          ref="form"
        >

Image Details

Specify the image details below.

Input Image

              <input
                id="fileName"
                class="file-chooser"
                name="fileName"
                type="file"
                ref="fileName"
                aria-label="Choose File"
                @change="changeLabel"
              />

Padding

              <input
                class="padding-input"
                name="padding"
                type="number"
                value="0.3"
                min="0"
                step="any"
              />
              0 is no padding, 1 is 100% of the source image. 0.3 is a typical value for most icons

Background Color

                  <input
                    type="radio"
                    name="colorOption"
                    value="transparent"
                    @change="colorRadioChanged"
                    :checked="colorRadio === 'transparent'"
                  />
                  Transparent

                  <input
                    type="radio"
                    name="colorOption"
                    value="choose"
                    @change="colorRadioChanged"
                    :checked="colorRadio === 'choose'"
                  />
                  Custom color

                <input
                  id="colorPicker"
                  class="color-picker"
                  type="color"
                  :value="colorPickerColor"
                  @change="colorPickerChanged"
                />
                <input
                  id="color"
                  class="color-text"
                  type="text"
                  name="color"
                  placeholder="#000000"
                  @change="colorPickerChanged"
                  :value="colorPickerColor"
                />
                Choose a color for the background (leave blank to default to the pixel at the top left)

Platforms

Select the platforms to generate images for.

            (*) windows platform generates images for Windows 8.1 and Windows Phone 8.1 apps.

                <input type="checkbox" name="platform" value="windows10" v-model="platforms" />
                windows10

                <input type="checkbox" name="platform" value="windows" v-model="platforms" />
                windows *

                <input type="checkbox" name="platform" value="android" v-model="platforms" />
                android

                <input type="checkbox" name="platform" value="ios" v-model="platforms" />
                ios

                <input type="checkbox" name="platform" value="chrome" v-model="platforms" />
                chrome

                <input type="checkbox" name="platform" value="firefox" v-model="platforms" />
                firefox

            <button
              id="selectPlatforms"
              class="secondary"
              @click.prevent="checkAll"
            >Select/Clear All</button>

            <button
              id="downloadButton"
              class="primary"
              ref="downloadButton"
              v-on:click.prevent="useAPI"
              :disabled="disableDownload"
            >
              Download

            </button>

        </form>

        PWA Builder was founded by Microsoft as a community guided, open source
        project to help move PWA adoption forward.
        Our Privacy Statement

</template>

<template>

    <HubHeader title="Image Generator" :expanded="true"></HubHeader>

Quickly and easily create app icons for various platforms in the right size and format

        <form
          id="imageFileInputForm"
          class="form"
          enctype="multipart/form-data"
          role="form"
          ref="form"
        >

Image Details

Specify the image details below.

Input Image

              <input
                id="fileName"
                class="file-chooser"
                name="fileName"
                type="file"
                ref="fileName"
                aria-label="Choose File"
                @change="changeLabel"
              />

Padding

              <input
                class="padding-input"
                name="padding"
                type="number"
                value="0.3"
                min="0"
                step="any"
              />
              0 is no padding, 1 is 100% of the source image. 0.3 is a typical value for most icons

Background Color

                  <input type="radio" name="colorOption" checked="checked" value="transparent" />
                  Transparent

                  <input type="radio" name="colorOption" value="choose" />
                  Custom color

                <input id="colorPicker" class="color-picker" type="color" />
                <input id="color" class="color-text" type="text" name="color" placeholder="#000000" />
                Choose a color for the background (leave blank to default to the pixel at the top left)

Platforms

Select the platforms to generate images for.

            (*) windows platform generates images for Windows 8.1 and Windows Phone 8.1 apps.

                <input type="checkbox" name="platform" value="windows10" v-model="platforms" />
                windows10

                <input type="checkbox" name="platform" value="windows" v-model="platforms" />
                windows *

                <input type="checkbox" name="platform" value="android" v-model="platforms" />
                android

                <input type="checkbox" name="platform" value="ios" v-model="platforms" />
                ios

                <input type="checkbox" name="platform" value="chrome" v-model="platforms" />
                chrome

                <input type="checkbox" name="platform" value="firefox" v-model="platforms" />
                firefox

            <button
              id="selectPlatforms"
              class="secondary"
              @click.prevent="checkAll"
            >Select/Clear All</button>

            <button
              id="downloadButton"
              class="primary"
              ref="downloadButton"
              v-on:click.prevent="useAPI"
              :disabled="disableDownload"
            >
              Download

            </button>

        </form>

        PWA Builder was founded by Microsoft as a community guided, open source
        project to help move PWA adoption forward.
        Our Privacy Statement

</template>


修改组织机构管理

修改组织机构管理

/*
* @Author: ycl
* @Date:
* @description
*/
<template>
  <div class="org-content">

  </div>
</template>

<script>

</script>

<style lang="scss" scoped>

</style>

/*
* @Author: ycl
* @Date:
* @description
*/
<template>

    <panel-split :leftWidth="400"
                 :left-max-width="600">

        <el-tree :data="data" :props="defaultProps"
                 @node-click="handleNodeClick" :render-content="renderContent"></el-tree>

            <el-button type="primary" size="small">编  辑</el-button>
            <el-button type="primary" size="small">添加同级机构</el-button>
            <el-button type="primary" size="small">添加下级机构</el-button>

            <el-row>
              <el-col :span="6">
                机构名称：
              </el-col>
              <el-col :span="18">
                <el-input :disabled="!edit"></el-input>
              </el-col>
            </el-row>
            <el-row>
              <el-col :span="6">
                机构编码：
              </el-col>
              <el-col :span="18">
                <el-input disabled></el-input>
              </el-col>
            </el-row>
            <el-row>
              <el-col :span="6">
                机构描述：
              </el-col>
              <el-col :span="18">
                <el-input :disabled="!edit" type="textarea"></el-input>
              </el-col>
            </el-row>
            <el-row>
              <el-col :span="6">
                &nbsp
              </el-col>
              <el-col :span="18">
                <el-button type="primary" size="small">保 存</el-button>
                <el-button type="primary" size="small">取 消</el-button>
              </el-col>
            </el-row>

    </panel-split>

</template>


delays link component editor until active

<template>
  <div class="apos-link-control">
    <AposButton
      type="rich-text"
      @click="click"
      :class="{ 'apos-active': buttonActive }"
      :label="tool.label"
      :icon-only="!!tool.icon"
      :icon="tool.icon ? tool.icon : false"
      :modifiers="['no-border', 'no-motion']"
    />
    <editor-menu-bubble
      v-if="active"
      :editor="editor"
      :keep-in-bounds="keepInBounds"
      v-slot="{ menu }"
    >
      <div
        class="apos-link-control__dialog"
        :class="{
          'is-triggered': active,
          'has-selection': hasSelection
        }"
        :style="
          `left: ${menu.left}px;
          bottom: ${menu.bottom}px;
          transform: translate3d(-25px, calc(100% + ${offset}), 0);
          `"
      >
        <AposContextMenuDialog
          menu-placement="bottom-start"
          v-if="active"
        >
          <form>
            <AposSchema
              :schema="schema"
              v-model="value"
              :modifiers="formModifiers"
            />
            <footer class="apos-link-control__footer">
              <AposButton
                type="default" label="Cancel"
                @click="close"
                :modifiers="formModifiers"
              />
              <AposButton
                type="primary" label="Save"
                @click="save"
                :modifiers="formModifiers"
              />
            </footer>
          </form>
        </AposContextMenuDialog>
      </div>
    </editor-menu-bubble>
  </div>
</template>

<script>

import { EditorMenuBubble } from 'tiptap';
import { isEmpty } from 'lodash';

export default {
  name: 'AposTiptapLink',
  components: {
    EditorMenuBubble
  },
  props: {
    name: {
      type: String,
      required: true
    },
    tool: {
      type: Object,
      required: true
    },
    editor: {
      type: Object,
      required: true
    }
  },
  data () {
    return {
      keepInBounds: true,
      href: null,
      id: null,
      target: null,
      active: false,
      value: {
        data: {}
      },
      formModifiers: [ 'small' ],
      schema: [
        {
          name: 'href',
          label: 'URL',
          help: 'Where should the link go?',
          placeholder: 'http://calm.com',
          type: 'string'
        },
        {
          name: 'id',
          label: 'Anchor Name',
          help: 'This becomes the ID of the anchor',
          type: 'string'
        },
        {
          name: 'target',
          label: 'Target',
          help: 'Where should this link open?',
          type: 'select',
          def: '_self',
          choices: [
            {
              label: 'Current tab (_self)',
              value: '_self'
            },
            {
              label: 'New tab (_blank)',
              value: '_blank'
            }
          ]
        }
      ]
    };
  },
  computed: {
    buttonActive() {
      return !isEmpty(this.value.data);
    },
    hasSelection() {
      return this.editor.selection.from !== this.editor.selection.to;
    },
    offset() {
      const selection = window.getSelection();
      const range = selection.getRangeAt(0);
      const rect = range.getBoundingClientRect();
      return (rect.height + 15) + 'px';
    }
  },
  watch: {
    'editor.selection.from': {
      handler(newVal, oldVal) {
        this.populateFields();
      }
    },
    hasSelection(newVal, oldVal) {
      if (!newVal) {
        this.close();
      }
    }
  },
  methods: {
    click() {
      if (this.hasSelection) {
        this.active = !this.active;
        this.populateFields();
      }
    },
    close() {
      this.active = false;
      this.editor.focus();
    },
    save() {
      this.editor.commands[this.name](this.value.data);
      this.active = false;
    },
    populateFields() {
      const attrs = this.editor.getMarkAttrs('link');
      this.value.data = {};
      this.schema.forEach((item) => {
        if (attrs[item.name]) {
          this.value.data[item.name] = attrs[item.name];
        }
      });
    }
  }
};
</script>

<style lang="scss" scoped>
  .apos-link-control {
    display: inline-block;
  }

  .apos-link-control__dialog {
    z-index: $z-index-modal-bg;
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  .apos-link-control__dialog.is-triggered.has-selection {
    opacity: 1;
    pointer-events: auto;
  }

  .apos-active {
    background-color: var(--a-brand-blue);
  }

  .apos-link-control__footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 10px;
  }

  .apos-link-control__footer .apos-button {
    margin-left: 7.5px;
  }
</style>

<template>
  <div class="apos-link-control">
    <AposButton
      type="rich-text"
      @click="click"
      :class="{ 'apos-active': buttonActive }"
      :label="tool.label"
      :icon-only="!!tool.icon"
      :icon="tool.icon ? tool.icon : false"
      :modifiers="['no-border', 'no-motion']"
    />
    <editor-menu-bubble
      :editor="editor"
      :keep-in-bounds="keepInBounds"
      v-slot="{ menu }"
    >
      <div
        class="apos-link-control__dialog"
        :class="{
          'is-triggered': active,
          'has-selection': hasSelection
        }"
        :style="
          `left: ${menu.left}px;
          bottom: ${menu.bottom}px;
          transform: translate3d(-25px, calc(100% + ${offset}), 0);
          `"
      >
        <AposContextMenuDialog
          menu-placement="bottom-start"
          v-if="active"
        >
          <form>
            <AposSchema
              :schema="schema"
              v-model="value"
              :modifiers="formModifiers"
            />
            <footer class="apos-link-control__footer">
              <AposButton
                type="default" label="Cancel"
                @click="close"
                :modifiers="formModifiers"
              />
              <AposButton
                type="primary" label="Save"
                @click="save"
                :modifiers="formModifiers"
              />
            </footer>
          </form>
        </AposContextMenuDialog>
      </div>
    </editor-menu-bubble>
  </div>
</template>

<script>

import { EditorMenuBubble } from 'tiptap';
import { isEmpty } from 'lodash';

export default {
  name: 'AposTiptapLink',
  components: {
    EditorMenuBubble
  },
  props: {
    name: {
      type: String,
      required: true
    },
    tool: {
      type: Object,
      required: true
    },
    editor: {
      type: Object,
      required: true
    }
  },
  data () {
    return {
      keepInBounds: true,
      href: null,
      id: null,
      target: null,
      active: false,
      value: {
        data: {}
      },
      formModifiers: [ 'small' ],
      schema: [
        {
          name: 'href',
          label: 'URL',
          help: 'Where should the link go?',
          placeholder: 'http://calm.com',
          type: 'string'
        },
        {
          name: 'id',
          label: 'Anchor Name',
          help: 'This becomes the ID of the anchor',
          type: 'string'
        },
        {
          name: 'target',
          label: 'Target',
          help: 'Where should this link open?',
          type: 'select',
          def: '_self',
          choices: [
            {
              label: 'Current tab (_self)',
              value: '_self'
            },
            {
              label: 'New tab (_blank)',
              value: '_blank'
            }
          ]
        }
      ]
    };
  },
  computed: {
    buttonActive() {
      return !isEmpty(this.value.data);
    },
    hasSelection() {
      return this.editor.selection.from !== this.editor.selection.to;
    },
    offset() {
      const selection = window.getSelection();
      const range = selection.getRangeAt(0);
      const rect = range.getBoundingClientRect();
      return (rect.height + 15) + 'px';
    }
  },
  watch: {
    'editor.selection.from': {
      handler(newVal, oldVal) {
        this.populateFields();
      }
    },
    hasSelection(newVal, oldVal) {
      if (!newVal) {
        this.close();
      }
    }
  },
  methods: {
    click() {
      if (this.hasSelection) {
        this.active = !this.active;
        this.populateFields();
      }
    },
    close() {
      this.active = false;
      this.editor.focus();
    },
    save() {
      this.editor.commands[this.name](this.value.data);
      this.active = false;
    },
    populateFields() {
      const attrs = this.editor.getMarkAttrs('link');
      this.value.data = {};
      this.schema.forEach((item) => {
        if (attrs[item.name]) {
          this.value.data[item.name] = attrs[item.name];
        }
      });
    }
  }
};
</script>

<style lang="scss" scoped>
  .apos-link-control {
    display: inline-block;
  }

  .apos-link-control__dialog {
    z-index: $z-index-modal-bg;
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  .apos-link-control__dialog.is-triggered.has-selection {
    opacity: 1;
    pointer-events: auto;
  }

  .apos-active {
    background-color: var(--a-brand-blue);
  }

  .apos-link-control__footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 10px;
  }

  .apos-link-control__footer .apos-button {
    margin-left: 7.5px;
  }
</style>


max 5 characters

max 5 characters

<template>

Characters

        <router-link class="card" :to="'/character/' + character.id" v-for="(character, index) in createdCharacters" v-bind:key="index">

{{ character.name }}

{{ character.className }}

        </router-link>

{{ cClass.name }}

            {{ cClass.flavorText }}

          <button class="ui button" :disabled="characterCount >= 5">
            <router-link :to="'/characters/new/' + cClass.id">Create {{ cClass.name }}</router-link>
          </button>

</template>

<template>

Characters

        <router-link class="card" :to="'/character/' + character.id" v-for="(character, index) in createdCharacters" v-bind:key="index">

{{ character.name }}

{{ character.className }}

        </router-link>

{{ cClass.name }}

            {{ cClass.flavorText }}

          <button class="ui button" :disabled="characterCount > 1">
            <router-link :to="'/characters/new/' + cClass.id">Create {{ cClass.name }}</router-link>
          </button>

</template>


history: further fix for loading spinner

<template>
  <q-infinite-scroll :handler="loadMore">

        <HistoryEntry v-for="entry in history" :entry="entry" :key="entry.id" />

    <q-card-main v-if="!history.length && status.success"><q-icon name="fa-bug" /> Nothing happened so far...</q-card-main>

<q-spinner-dots slot="message" :size="40"></q-spinner-dots>

  </q-infinite-scroll>
</template>


Fix Mobile referencing dropdown component (#1396)

On mobile, the 4refs.dropdown component doesn't exist, but the keyPress event handler is still referencing it.
Since the only key event being cared about right now is the Escape key, it may make more sense to just not create or destroy the keyPress event listener, but this change touched the least stuff.

<template>

            <b-input
                v-if="!inline"
                ref="input"
                slot="trigger"
                autocomplete="off"
                :value="formatValue(dateSelected)"
                :placeholder="placeholder"
                :size="size"
                :icon="icon"
                :icon-pack="iconPack"
                :rounded="rounded"
                :loading="loading"
                :disabled="disabled"
                :readonly="!editable"
                v-bind="$attrs"
                @click.native.stop="toggle(true)"
                @keyup.native.enter="toggle(true)"
                @change.native="onChange($event.target.value)"
                @focus="handleOnFocus"
                @blur="onBlur" />

                    <template v-if="$slots.header !== undefined && $slots.header.length">
                        <slot name="header" />
                    </template>

                                    <option
                                        v-for="(month, index) in monthNames"
                                        :value="index"
                                        :key="month">
                                        {{ month }}
                                    </option>

                                    <option
                                        v-for="year in listOfYears"
                                        :value="year"
                                        :key="year">
                                        {{ year }}
                                    </option>

                    <b-datepicker-table
                        v-model="dateSelected"
                        :day-names="dayNames"
                        :month-names="monthNames"
                        :first-day-of-week="firstDayOfWeek"
                        :min-date="minDate"
                        :max-date="maxDate"
                        :focused="focusedDateData"
                        :disabled="disabled"
                        :unselectable-dates="unselectableDates"
                        :unselectable-days-of-week="unselectableDaysOfWeek"
                        :selectable-dates="selectableDates"
                        :events="events"
                        :indicators="indicators"
                        :date-creator="dateCreator"
                        @close="toggle(false)"/>

                    <slot/>

        <b-input
            v-else
            ref="input"
            type="date"
            autocomplete="off"
            :value="formatYYYYMMDD(value)"
            :placeholder="placeholder"
            :size="size"
            :icon="icon"
            :icon-pack="iconPack"
            :loading="loading"
            :max="formatYYYYMMDD(maxDate)"
            :min="formatYYYYMMDD(minDate)"
            :disabled="disabled"
            :readonly="false"
            v-bind="$attrs"
            @change.native="onChangeNativePicker"
            @click.native.stop="toggle(true)"
            @keyup.native.enter="toggle(true)"
            @focus="handleOnFocus"
            @blur="onBlur"/>

</template>


zone: allow configuring IPv6 default guest network when adding a new Zone (#343)

Advanced Network with Security Groups is a great option for deploying a Zone with IPv6 address support. However, it is not possible to set IPv6 DNS + IPv6 CIDR + IPv6 gateway via the zoneWizard UI.

This PR adds a small enhancement on the UI Zone deployment to allow deploying a Zone with Advanced Network with Security Groups + IPv6 CIDR, IPv6 gateway, and IPv6 DNS.

Note that API [1] offers full support for such action, therefore there is no need for changing the API. It is just a small enhancement on UI that might be useful in the meantime that we wait for the new ACS UI.

[1] https://cloudstack.apache.org/api/apidocs-4.13/apis/createNetwork.html

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

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

<template>

            {{ hypervisor.name }}

            {{ networkOffering.name }}

            {{ dom.path }}

        {{ $t('label.previous') }}

        {{ $t('label.next') }}

</template>