App.vue: check `resolvedLayouts` in `setLayout`

<template>
  <div id="__nuxt">
    <% if (loading) { %><nuxt-loading ref="loading"></nuxt-loading><% } %>
    <component v-if="layout" :is="layout"></component>
  </div>
</template>

<script>
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
<% css.forEach(function (c) { %>
import '<%= relativeToBuild(resolvePath(c.src || c)) %>'
<% }) %>

let layouts = {
<%
var layoutsKeys = Object.keys(layouts);
layoutsKeys.forEach(function (key, i) { %>
  "_<%= key %>": () => import('<%= layouts[key] %>'  /* webpackChunkName: "layouts/<%= key %>" */).then(m => m.default || m)<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
<% }) %>
}

let resolvedLayouts = {}

export default {
  head: <%= JSON.stringify(head) %>,
  data: () => ({
    layout: null,
    layoutName: ''
  }),
  <% if (loading) { %>
  mounted () {
    this.$loading = this.$refs.loading
    this.$nuxt.$loading = this.$loading
  },
  <% } %>
  methods: {
    setLayout (layout) {
      if (!layout || !resolvedLayouts['_' + layout]) layout = 'default'
      this.layoutName = layout
      let _layout = '_' + layout
      this.layout = resolvedLayouts[_layout]
      return this.layout
    },
    loadLayout (layout) {
      if (!layout || !(layouts['_' + layout] || resolvedLayouts['_' + layout])) layout = 'default'
      let _layout = '_' + layout
      if (resolvedLayouts[_layout]) {
        return Promise.resolve(resolvedLayouts[_layout])
      }
      return layouts[_layout]()
      .then((Component) => {
        resolvedLayouts[_layout] = Component
        delete layouts[_layout]
        return resolvedLayouts[_layout]
      })
      .catch((e) => {
        if (this.$nuxt) {
          return this.$nuxt.error({ statusCode: 500, message: e.message })
        }
      })
    }
  },
  components: {
    <%= (loading ? 'NuxtLoading' : '') %>
  }
}
</script>

<template>
  <div id="__nuxt">
    <% if (loading) { %><nuxt-loading ref="loading"></nuxt-loading><% } %>
    <component v-if="layout" :is="layout"></component>
  </div>
</template>

<script>
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
<% css.forEach(function (c) { %>
import '<%= relativeToBuild(resolvePath(c.src || c)) %>'
<% }) %>

let layouts = {
<%
var layoutsKeys = Object.keys(layouts);
layoutsKeys.forEach(function (key, i) { %>
  "_<%= key %>": () => import('<%= layouts[key] %>'  /* webpackChunkName: "layouts/<%= key %>" */).then(m => m.default || m)<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
<% }) %>
}

let resolvedLayouts = {}

export default {
  head: <%= JSON.stringify(head) %>,
  data: () => ({
    layout: null,
    layoutName: ''
  }),
  <% if (loading) { %>
  mounted () {
    this.$loading = this.$refs.loading
    this.$nuxt.$loading = this.$loading
  },
  <% } %>
  methods: {
    setLayout (layout) {
      if (!layout || !layouts['_' + layout]) layout = 'default'
      this.layoutName = layout
      let _layout = '_' + layout
      this.layout = layouts[_layout]
      return this.layout
    },
    loadLayout (layout) {
      if (!layout || !layouts['_' + layout]) layout = 'default'
      let _layout = '_' + layout
      if (resolvedLayouts[_layout]) {
        return Promise.resolve(resolvedLayouts[_layout])
      }
      return layouts[_layout]()
      .then((Component) => {
        resolvedLayouts[_layout] = Component
        delete layouts[_layout]
        return resolvedLayouts[_layout]
      })
      .catch((e) => {
        if (this.$nuxt) {
          return this.$nuxt.error({ statusCode: 500, message: e.message })
        }
      })
    }
  },
  components: {
    <%= (loading ? 'NuxtLoading' : '') %>
  }
}
</script>


Minor design change.

Minor design change.

<template>

                    <v-icon size="28">mdi-account-outline</v-icon>

                    <v-badge overlap color="secondary" v-model="showNumberOfUnreadNotifications">
                        <template v-slot:badge>{{numberOfUnreadNotifications}}</template>
                        <v-icon size="28" >mdi-bell-outline</v-icon>
                    </v-badge>

        <v-btn color="primary" class="create-button" rounded @click="$emit('show-create-question-dialog')">
            <v-icon dark>mdi-plus</v-icon>
            {{$t('common:toolbar.askQuestion')}}
        </v-btn>

        <v-divider></v-divider>

</template>


add notes density

add notes density

<template lang="pug">
  div
    v-flex(xs6)
      v-text-field(
      v-model="search",
      append-icon="search",
      single-line hide-details label="曲名検索")
    v-flex(xs6)
      v-select(
      :items="items"
      v-model="difficultySelected"
      label="難易度"
      item-value="text")
    v-data-table(:headers = "headers",
    :items = "songs",
    :search="search",
    :custom-sort="sort",
    :pagination.sync="pagination")
      template(slot="items" scope="props")
        tr(:class="typeColor(props.item.type)" v-if="difficulty(props.item, difficultySelected)")
          td {{props.item.title}}
          td {{props.item.type}}
          td(align="right")  {{props.item.bpm}}
          td(align="right")  {{difficulty(props.item, difficultySelected).level}}
          td(align="right")  {{difficulty(props.item, difficultySelected).density}}
          td(align="right")  {{difficulty(props.item, difficultySelected).notes}}

</template>
<script>
  import { mapState } from 'vuex';

  export default {
    data: () => ({
      headers: [
        { text: 'title', align: 'left', value: 'title' },
        { text: 'type', align: 'left', value: 'type' },
        { text: 'bpm', align: 'right', value: 'bpm' },
        { text: 'level', align: 'right', value: 'level' },
        { text: '密度', align: 'right', value: 'density' },
        { text: 'note', align: 'right', value: 'notes' },
      ],
      search: '',
      pagination: {
        descending: false,
        page: 1,
        rowsPerPage: 100,
        sortBy: '',
        totalItems: 0,
      },
      difficultySelected: 'MASTER',
      items: ['DEBUT', 'REGULAR', 'PRO', 'MASTER', 'MASTER+LEGACY', 'MASTER+'],
    }),

    computed: {
      ...mapState({
        songs(state) {
          return state.songs.filter(song => this.difficulty(song, this.difficultySelected));
        },
      }),
    },

    methods: {
      difficulty(song, rank) {
        switch (rank) {
          case 'DEBUT':
            return song.difficulties[0];
          case 'REGULAR':
            return song.difficulties[1];
          case 'PRO':
            return song.difficulties[2];
          case 'MASTER':
            return song.difficulties[3];
          case 'MASTER+LEGACY':
            return song.difficulties.length === 6 ? song.difficulties[4] : null;
          case 'MASTER+':
            return song.difficulties.length === 6 ? song.difficulties[5] : song.difficulties[4];
          default:
            return null;
        }
      },
      typeColor(type) {
        switch (type) {
          case '全タイプ':
            return 'brown--text darken-3';
          case 'キュート':
            return 'pink--text darken-3';
          case 'クール':
            return 'blue--text darken-3';
          case 'パッション':
            return 'orange--text darken-3';
          default:
            return null;
        }
      },
      sort(items, index, isDescending) {
        if (index === null) return items;

        return items.sort((a, b) => {
          let sortA = a[this.pagination.sortBy];
          let sortB = b[this.pagination.sortBy];
          if (this.pagination.sortBy === 'level') {
            sortA = this.difficulty(a, this.difficultySelected).level;
            sortB = this.difficulty(b, this.difficultySelected).level;
          } else if (this.pagination.sortBy === 'density') {
            sortA = this.difficulty(a, this.difficultySelected).density;
            sortB = this.difficulty(b, this.difficultySelected).density;
          } else if (this.pagination.sortBy === 'notes') {
            sortA = this.difficulty(a, this.difficultySelected).notes;
            sortB = this.difficulty(b, this.difficultySelected).notes;
          }
          if (isDescending) {
            [sortA, sortB] = [sortB, sortA];
          }

          // Check if both are numbers
          if (!isNaN(sortA) && !isNaN(sortB)) {
            return sortA - sortB;
          }

          // Check if both cannot be evaluated
          if (sortA === null && sortB === null) {
            return 0;
          }

          [sortA, sortB] = [sortA, sortB]
            .map(s => (
              (s || '').toString().toLocaleLowerCase()
            ));

          if (sortA > sortB) return 1;
          if (sortA < sortB) return -1;

          return 0;
        });
      },
    },

    mounted() {
      this.$store.dispatch('fetchSongs');
    },
  };

</script>
<style lang="postcss">

</style>

<template lang="pug">
  div
    v-flex(xs6)
      v-text-field(
      v-model="search",
      append-icon="search",
      single-line hide-details label="曲名検索")
    v-flex(xs6)
      v-select(
      :items="items"
      v-model="difficultySelected"
      label="難易度"
      item-value="text")
    v-data-table(:headers = "headers",
    :items = "songs",
    :search="search",
    :custom-sort="sort",
    :pagination.sync="pagination")
      template(slot="items" scope="props")
        tr(:class="typeColor(props.item.type)" v-if="difficulty(props.item, difficultySelected)")
          td {{props.item.title}}
          td {{props.item.type}}
          td(align="right")  {{props.item.bpm}}
          td(align="right")  {{difficulty(props.item, difficultySelected).level}}

</template>
<script>
  import { mapState } from 'vuex';

  export default {
    data: () => ({
      headers: [
        { text: 'title', align: 'left', value: 'title' },
        { text: 'type', align: 'left', value: 'type' },
        { text: 'bpm', align: 'right', value: 'bpm' },
        { text: 'level', align: 'right', value: 'level' },
      ],
      search: '',
      pagination: {
        descending: false,
        page: 1,
        rowsPerPage: 100,
        sortBy: '',
        totalItems: 0,
      },
      difficultySelected: 'MASTER',
      items: ['DEBUT', 'REGULAR', 'PRO', 'MASTER', 'MASTER+LEGACY', 'MASTER+'],
    }),

    computed: {
      ...mapState({
        songs(state) {
          return state.songs.filter(song => this.difficulty(song, this.difficultySelected));
        },
      }),
    },

    methods: {
      difficulty(song, rank) {
        switch (rank) {
          case 'DEBUT':
            return song.difficulties[0];
          case 'REGULAR':
            return song.difficulties[1];
          case 'PRO':
            return song.difficulties[2];
          case 'MASTER':
            return song.difficulties[3];
          case 'MASTER+LEGACY':
            return song.difficulties.length === 6 ? song.difficulties[4] : null;
          case 'MASTER+':
            return song.difficulties.length === 6 ? song.difficulties[5] : song.difficulties[4];
          default:
            return null;
        }
      },
      typeColor(type) {
        switch (type) {
          case '全タイプ':
            return 'brown--text darken-3';
          case 'キュート':
            return 'pink--text darken-3';
          case 'クール':
            return 'blue--text darken-3';
          case 'パッション':
            return 'orange--text darken-3';
          default:
            return null;
        }
      },
      sort(items, index, isDescending) {
        if (index === null) return items;

        return items.sort((a, b) => {
          let sortA = a[this.pagination.sortBy];
          let sortB = b[this.pagination.sortBy];
          if (this.pagination.sortBy === 'level') {
            sortA = this.difficulty(a, this.difficultySelected).level;
            sortB = this.difficulty(b, this.difficultySelected).level;
          }
          if (isDescending) {
            [sortA, sortB] = [sortB, sortA];
          }

          // Check if both are numbers
          if (!isNaN(sortA) && !isNaN(sortB)) {
            return sortA - sortB;
          }

          // Check if both cannot be evaluated
          if (sortA === null && sortB === null) {
            return 0;
          }

          [sortA, sortB] = [sortA, sortB]
            .map(s => (
              (s || '').toString().toLocaleLowerCase()
            ));

          if (sortA > sortB) return 1;
          if (sortA < sortB) return -1;

          return 0;
        });
      },
    },

    mounted() {
      this.$store.dispatch('fetchSongs');
    },
  };

</script>
<style lang="postcss">

</style>


Show all channels when learner has nothing else to see.

<template>

  <div>
    <YourClasses
      v-if="displayClasses"
      class="section"
      :classes="classes"
      data-test="classes"
      short
    />
    <ContinueLearning
      v-if="continueLearning"
      class="section"
      :fromClasses="continueLearningFromClasses"
      :data-test="continueLearningFromClasses ?
        'continueLearningFromClasses' :
        'continueLearningOnYourOwn'"
    />
    <AssignedLessonsCards
      v-if="hasActiveClassesLessons"
      class="section"
      :lessons="activeClassesLessons"
      displayClassName
      recent
      data-test="recentLessons"
    />
    <AssignedQuizzesCards
      v-if="hasActiveClassesQuizzes"
      class="section"
      :quizzes="activeClassesQuizzes"
      displayClassName
      recent
      data-test="recentQuizzes"
    />
    <ExploreChannels
      v-if="displayExploreChannels"
      :channels="channels"
      class="section"
      data-test="exploreChannels"
      :short="displayClasses ||
        continueLearning ||
        hasActiveClassesLessons ||
        hasActiveClassesQuizzes
      "
    />
  </div>

</template>

<script>

  import { computed } from 'kolibri.lib.vueCompositionApi';
  import { get } from '@vueuse/core';
  import useChannels from '../../composables/useChannels';
  import useDeviceSettings from '../../composables/useDeviceSettings';
  import useLearnerResources from '../../composables/useLearnerResources';
  import useUser from '../../composables/useUser';
  import AssignedLessonsCards from '../classes/AssignedLessonsCards';
  import AssignedQuizzesCards from '../classes/AssignedQuizzesCards';
  import YourClasses from '../YourClasses';
  import ContinueLearning from './ContinueLearning';
  import ExploreChannels from './ExploreChannels';

  /**
   * Home page contains useful suggestions for a learner, e.g. their
   * resources and quizzes in progress, classes, resources to explore, etc.
   * What sections are displayed depends on whether a learner
   * is signed in and also if they're a member of classes.
   */
  export default {
    name: 'HomePage',
    components: {
      AssignedLessonsCards,
      AssignedQuizzesCards,
      YourClasses,
      ContinueLearning,
      ExploreChannels,
    },
    setup() {
      const { isUserLoggedIn } = useUser();
      const { canAccessUnassignedContent } = useDeviceSettings();
      const { channels } = useChannels();
      const {
        classes,
        activeClassesLessons,
        activeClassesQuizzes,
        resumableClassesQuizzes,
        resumableClassesResources,
        resumableContentNodes,
        learnerFinishedAllClasses,
      } = useLearnerResources();

      const continueLearningFromClasses = computed(
        () =>
          (get(isUserLoggedIn) && get(resumableClassesQuizzes).length > 0) ||
          get(resumableClassesResources).length > 0
      );
      const continueLearningOnYourOwn = computed(
        () =>
          get(isUserLoggedIn) &&
          get(learnerFinishedAllClasses) &&
          get(canAccessUnassignedContent) &&
          get(resumableContentNodes).length > 0
      );

      const continueLearning = computed(
        () => get(continueLearningFromClasses) || get(continueLearningOnYourOwn)
      );

      const hasActiveClassesLessons = computed(
        () =>
          get(isUserLoggedIn) && get(activeClassesLessons) && get(activeClassesLessons).length > 0
      );
      const hasActiveClassesQuizzes = computed(
        () =>
          get(isUserLoggedIn) && get(activeClassesQuizzes) && get(activeClassesQuizzes).length > 0
      );
      const hasChannels = computed(() => {
        return get(channels) && get(channels).length > 0;
      });
      const displayExploreChannels = computed(() => {
        return (
          get(hasChannels) &&
          (!get(isUserLoggedIn) ||
            (get(learnerFinishedAllClasses) && get(canAccessUnassignedContent)))
        );
      });

      const displayClasses = computed(() => {
        return get(isUserLoggedIn) && (get(classes).length || !get(canAccessUnassignedContent));
      });

      return {
        isUserLoggedIn,
        channels,
        classes,
        activeClassesLessons,
        activeClassesQuizzes,
        hasActiveClassesLessons,
        hasActiveClassesQuizzes,
        continueLearningFromClasses,
        continueLearning,
        displayExploreChannels,
        displayClasses,
      };
    },
  };

</script>

<style lang="scss" scoped>

  .section:not(:first-child) {
    margin-top: 42px;
  }

</style>

<template>

  <div>
    <YourClasses
      v-if="displayClasses"
      class="section"
      :classes="classes"
      data-test="classes"
      short
    />
    <ContinueLearning
      v-if="continueLearningFromClasses || continueLearningOnYourOwn"
      class="section"
      :fromClasses="continueLearningFromClasses"
      :data-test="continueLearningFromClasses ?
        'continueLearningFromClasses' :
        'continueLearningOnYourOwn'"
    />
    <AssignedLessonsCards
      v-if="hasActiveClassesLessons"
      class="section"
      :lessons="activeClassesLessons"
      displayClassName
      recent
      data-test="recentLessons"
    />
    <AssignedQuizzesCards
      v-if="hasActiveClassesQuizzes"
      class="section"
      :quizzes="activeClassesQuizzes"
      displayClassName
      recent
      data-test="recentQuizzes"
    />
    <ExploreChannels
      v-if="displayExploreChannels"
      :channels="channels"
      class="section"
      data-test="exploreChannels"
      short
    />
  </div>

</template>

<script>

  import { computed } from 'kolibri.lib.vueCompositionApi';
  import { get } from '@vueuse/core';
  import useChannels from '../../composables/useChannels';
  import useDeviceSettings from '../../composables/useDeviceSettings';
  import useLearnerResources from '../../composables/useLearnerResources';
  import useUser from '../../composables/useUser';
  import AssignedLessonsCards from '../classes/AssignedLessonsCards';
  import AssignedQuizzesCards from '../classes/AssignedQuizzesCards';
  import YourClasses from '../YourClasses';
  import ContinueLearning from './ContinueLearning';
  import ExploreChannels from './ExploreChannels';

  /**
   * Home page contains useful suggestions for a learner, e.g. their
   * resources and quizzes in progress, classes, resources to explore, etc.
   * What sections are displayed depends on whether a learner
   * is signed in and also if they're a member of classes.
   */
  export default {
    name: 'HomePage',
    components: {
      AssignedLessonsCards,
      AssignedQuizzesCards,
      YourClasses,
      ContinueLearning,
      ExploreChannels,
    },
    setup() {
      const { isUserLoggedIn } = useUser();
      const { canAccessUnassignedContent } = useDeviceSettings();
      const { channels } = useChannels();
      const {
        classes,
        activeClassesLessons,
        activeClassesQuizzes,
        resumableClassesQuizzes,
        resumableClassesResources,
        resumableContentNodes,
        learnerFinishedAllClasses,
      } = useLearnerResources();

      const continueLearningFromClasses = computed(
        () =>
          (get(isUserLoggedIn) && get(resumableClassesQuizzes).length > 0) ||
          get(resumableClassesResources).length > 0
      );
      const continueLearningOnYourOwn = computed(
        () =>
          get(isUserLoggedIn) &&
          get(learnerFinishedAllClasses) &&
          get(canAccessUnassignedContent) &&
          get(resumableContentNodes).length > 0
      );
      const hasActiveClassesLessons = computed(
        () =>
          get(isUserLoggedIn) && get(activeClassesLessons) && get(activeClassesLessons).length > 0
      );
      const hasActiveClassesQuizzes = computed(
        () =>
          get(isUserLoggedIn) && get(activeClassesQuizzes) && get(activeClassesQuizzes).length > 0
      );
      const hasChannels = computed(() => {
        return get(channels) && get(channels).length > 0;
      });
      const displayExploreChannels = computed(() => {
        return (
          get(hasChannels) &&
          (!get(isUserLoggedIn) ||
            (get(learnerFinishedAllClasses) && get(canAccessUnassignedContent)))
        );
      });

      const displayClasses = computed(() => {
        return get(isUserLoggedIn) && (get(classes).length || !get(canAccessUnassignedContent));
      });

      return {
        isUserLoggedIn,
        channels,
        classes,
        activeClassesLessons,
        activeClassesQuizzes,
        hasActiveClassesLessons,
        hasActiveClassesQuizzes,
        continueLearningFromClasses,
        continueLearningOnYourOwn,
        displayExploreChannels,
        displayClasses,
      };
    },
  };

</script>

<style lang="scss" scoped>

  .section:not(:first-child) {
    margin-top: 42px;
  }

</style>


fix: tab-container component swipe bug on PC

<template>
  <div
    @touchstart="startDrag"
    @mousedown="startDrag"
    @touchmove="onDrag"
    @mousemove="onDrag"
    @mouseup="endDrag"
    @touchend="endDrag"
    class="mint-tab-container">
    <div
      ref="wrap"
      class="mint-tab-container-wrap">
      <slot></slot>
    </div>
  </div>
</template>

<style lang="css">
  @component-namespace mint {
    @component tab-container {
      overflow: hidden;
      position: relative;

      @descendent wrap {
        display: flex;
      }

      .swipe-transition {
        transition: transform 150ms ease-in-out;
      }
    }
  }
</style>

<script>
import { once } from 'mint-ui/src/utils/dom';
import arrayFindIndex from 'array-find-index';

/**
 * mt-tab-container
 * @desc 面板，搭配 tab-container-item 使用
 * @module components/tab-container
 *
 * @param {number|string} [value] - 当前激活的 tabId
 *
 * @example
 * <mt-tab-container v-model="selected">
 *   <mt-tab-container-item id="1"> 内容A </mt-tab-container-item>
 *   <mt-tab-container-item id="2"> 内容B </mt-tab-container-item>
 *   <mt-tab-container-item id="3"> 内容C </mt-tab-container-item>
 * </mt-tab-container>
 */
export default {
  name: 'mt-tab-container',

  props: {
    value: {},
    swipeable: Boolean
  },

  data() {
    return {
      start: { x: 0, y: 0 },
      swiping: false,
      activeItems: [],
      pageWidth: 0,
      currentActive: this.value
    };
  },

  watch: {
    value(val) {
      this.currentActive = val;
    },

    currentActive(val, oldValue) {
      this.$emit('input', val);
      if (!this.swipeable) return;
      const lastIndex = arrayFindIndex(this.$children,
        item => item.id === oldValue);
      this.swipeLeaveTransition(lastIndex);
    }
  },

  mounted() {
    if (!this.swipeable) return;

    this.wrap = this.$refs.wrap;
    this.pageWidth = this.wrap.clientWidth;
    this.limitWidth = this.pageWidth / 4;
  },

  methods: {
    swipeLeaveTransition(lastIndex = 0) {
      if (typeof this.index !== 'number') {
        this.index = arrayFindIndex(this.$children,
          item => item.id === this.currentActive);
        this.swipeMove(-lastIndex * this.pageWidth);
      }

      setTimeout(() => {
        this.wrap.classList.add('swipe-transition');
        this.swipeMove(-this.index * this.pageWidth);

        once(this.wrap, 'webkitTransitionEnd', _ => {
          this.wrap.classList.remove('swipe-transition');
          this.wrap.style.webkitTransform = '';
          this.swiping = false;
          this.index = null;
        });
      }, 0);
    },

    swipeMove(offset) {
      this.wrap.style.webkitTransform = `translate3d(${offset}px, 0, 0)`;
      this.swiping = true;
    },

    startDrag(evt) {
      if (!this.swipeable) return;
      evt = evt.changedTouches ? evt.changedTouches[0] : evt;
      this.dragging = true;
      this.start.x = evt.pageX;
      this.start.y = evt.pageY;
    },

    onDrag(evt) {
      if (!this.dragging) return;
      let swiping;
      const e = evt.changedTouches ? evt.changedTouches[0] : evt;
      const offsetTop = e.pageY - this.start.y;
      const offsetLeft = e.pageX - this.start.x;
      const y = Math.abs(offsetTop);
      const x = Math.abs(offsetLeft);

      swiping = !(x < 5 || (x >= 5 && y >= x * 1.73));
      if (!swiping) return;
      evt.preventDefault();

      const len = this.$children.length - 1;
      const index = arrayFindIndex(this.$children,
        item => item.id === this.currentActive);
      const currentPageOffset = index * this.pageWidth;
      const offset = offsetLeft - currentPageOffset;
      const absOffset = Math.abs(offset);

      if (absOffset > len * this.pageWidth ||
          (offset > 0 && offset < this.pageWidth)) {
        this.swiping = false;
        return;
      }

      this.offsetLeft = offsetLeft;
      this.index = index;
      this.swipeMove(offset);
    },

    endDrag() {
      if (!this.swiping) return;
      this.dragging = false;
      const direction = this.offsetLeft > 0 ? -1 : 1;
      const isChange = Math.abs(this.offsetLeft) > this.limitWidth;

      if (isChange) {
        this.index += direction;
        const child = this.$children[this.index];
        if (child) {
          this.currentActive = child.id;
          return;
        }
      }

      this.swipeLeaveTransition();
    }
  }
};
</script>

<template>
  <div
    @touchstart="startDrag"
    @mousedown="startDrag"
    @touchmove="onDrag"
    @mousemove="onDrag"
    @mouseleave="endDrag"
    @touchend="endDrag"
    class="mint-tab-container">
    <div
      ref="wrap"
      class="mint-tab-container-wrap">
      <slot></slot>
    </div>
  </div>
</template>

<style lang="css">
  @component-namespace mint {
    @component tab-container {
      overflow: hidden;
      position: relative;

      @descendent wrap {
        display: flex;
      }

      .swipe-transition {
        transition: transform 150ms ease-in-out;
      }
    }
  }
</style>

<script>
import { once } from 'mint-ui/src/utils/dom';
import arrayFindIndex from 'array-find-index';

/**
 * mt-tab-container
 * @desc 面板，搭配 tab-container-item 使用
 * @module components/tab-container
 *
 * @param {number|string} [value] - 当前激活的 tabId
 *
 * @example
 * <mt-tab-container v-model="selected">
 *   <mt-tab-container-item id="1"> 内容A </mt-tab-container-item>
 *   <mt-tab-container-item id="2"> 内容B </mt-tab-container-item>
 *   <mt-tab-container-item id="3"> 内容C </mt-tab-container-item>
 * </mt-tab-container>
 */
export default {
  name: 'mt-tab-container',

  props: {
    value: {},
    swipeable: Boolean
  },

  data() {
    return {
      start: { x: 0, y: 0 },
      swiping: false,
      activeItems: [],
      pageWidth: 0,
      currentActive: this.value
    };
  },

  watch: {
    value(val) {
      this.currentActive = val;
    },

    currentActive(val, oldValue) {
      this.$emit('input', val);
      if (!this.swipeable) return;
      const lastIndex = arrayFindIndex(this.$children,
        item => item.id === oldValue);
      this.swipeLeaveTransition(lastIndex);
    }
  },

  mounted() {
    if (!this.swipeable) return;

    this.wrap = this.$refs.wrap;
    this.pageWidth = this.wrap.clientWidth;
    this.limitWidth = this.pageWidth / 4;
  },

  methods: {
    swipeLeaveTransition(lastIndex = 0) {
      if (typeof this.index !== 'number') {
        this.index = arrayFindIndex(this.$children,
          item => item.id === this.currentActive);
        this.swipeMove(-lastIndex * this.pageWidth);
      }

      setTimeout(() => {
        this.wrap.classList.add('swipe-transition');
        this.swipeMove(-this.index * this.pageWidth);

        once(this.wrap, 'webkitTransitionEnd', _ => {
          this.wrap.classList.remove('swipe-transition');
          this.wrap.style.webkitTransform = '';
          this.swiping = false;
          this.index = null;
        });
      }, 0);
    },

    swipeMove(offset) {
      this.wrap.style.webkitTransform = `translate3d(${offset}px, 0, 0)`;
      this.swiping = true;
    },

    startDrag(evt) {
      if (!this.swipeable) return;
      evt = evt.changedTouches ? evt.changedTouches[0] : evt;
      this.dragging = true;
      this.start.x = evt.pageX;
      this.start.y = evt.pageY;
    },

    onDrag(evt) {
      if (!this.dragging) return;
      let swiping;
      const e = evt.changedTouches ? evt.changedTouches[0] : evt;
      const offsetTop = e.pageY - this.start.y;
      const offsetLeft = e.pageX - this.start.x;
      const y = Math.abs(offsetTop);
      const x = Math.abs(offsetLeft);

      swiping = !(x < 5 || (x >= 5 && y >= x * 1.73));
      if (!swiping) return;
      evt.preventDefault();

      const len = this.$children.length - 1;
      const index = arrayFindIndex(this.$children,
        item => item.id === this.currentActive);
      const currentPageOffset = index * this.pageWidth;
      const offset = offsetLeft - currentPageOffset;
      const absOffset = Math.abs(offset);

      if (absOffset > len * this.pageWidth ||
          (offset > 0 && offset < this.pageWidth)) {
        this.swiping = false;
        return;
      }

      this.offsetLeft = offsetLeft;
      this.index = index;
      this.swipeMove(offset);
    },

    endDrag() {
      if (!this.swiping) return;

      const direction = this.offsetLeft > 0 ? -1 : 1;
      const isChange = Math.abs(this.offsetLeft) > this.limitWidth;

      if (isChange) {
        this.index += direction;
        const child = this.$children[this.index];
        if (child) {
          this.currentActive = child.id;
          return;
        }
      }

      this.swipeLeaveTransition();
    }
  }
};
</script>


fix(h5): 固定 toast 大小

fix(h5): 固定 toast 大小

<template>
  <transition name="uni-fade">
    <uni-toast
      v-if="visible"
      :data-duration="duration"
    >

          {{ title }}

          {{ title }}

    </uni-toast>
  </transition>
</template>


不要な行を削除

不要な行を削除

<template>

      <textarea id="words" v-model="words" v-on:keyup.ctrl.enter="hantei_btn" placeholder="台詞を入力してください"></textarea>

      <button id="predict" v-on:click="hantei_btn" type="button" class="btn btn-success btn-block" v-bind:disabled="btn_disable">判定</button>

APIサーバの反応がありません。サービスはメンテナンス中です。

Words: {{ input_words }}

          Rank	Name	Score

          {{ ret.number }}	{{ ret.name }}	{{ ret.score }}

</template>


Stop the channel lsitlist box from submitting

<template>

                 {{page + 1}} / {{maxPages + 1}}

            <form class="u-form kiwi-channellist-search" @submit.prevent>
                <input v-model="search" placeholder="Search..." class="u-input" />
            </form>

                         {{channel.num_users}}
                        {{channel.channel}}

                    {{channel.topic}}

</template>

<template>

                 {{page + 1}} / {{maxPages + 1}}

            <form class="u-form kiwi-channellist-search">
                <input v-model="search" placeholder="Search..." class="u-input" />
            </form>

                         {{channel.num_users}}
                        {{channel.channel}}

                    {{channel.topic}}

</template>


fix #187

fix #187


Prevent refreshes

Prevent refreshes

<template>

        {{ error }}

    <form>

        Date
        <masked-input mask="99/99/9999"
                      v-model="eventUpdate.date">

        Registration Fee

$

          <masked-input mask="99.99"
                        v-model="eventUpdate.price">

        Registration Opens
        <masked-input mask="99/99/9999"
                      v-model="eventUpdate.registration_open">

        Registration Closes
        <masked-input mask="99/99/9999"
                      v-model="eventUpdate.registration_close">

          <button class="btn btn-danger"
                  @click.prevent="showDeleteConfirm()">Delete Event</button>

          <button class="btn btn-default"
                  @click.prevent="close()">Cancel</button>
          <button class="btn btn-primary"
                  type="submit"
                  @click.prevent="updateEvent()">Update Event</button>

      <confirm-delete v-if="showDeleteConfirmation"
                      :match-text="this.semesterAndYear"
                      :event-id="this.event.id"
                      @close="hideDeleteConfirm()"></confirm-delete>
    </form>

</template>

<template>

        {{ error }}

    <form>

        Date
        <masked-input mask="99/99/9999"
                      v-model="eventUpdate.date">

        Registration Fee

$

          <masked-input mask="99.99"
                        v-model="eventUpdate.price">

        Registration Opens
        <masked-input mask="99/99/9999"
                      v-model="eventUpdate.registration_open">

        Registration Closes
        <masked-input mask="99/99/9999"
                      v-model="eventUpdate.registration_close">

          <button class="btn btn-danger"
                  @click="showDeleteConfirm()">Delete Event</button>

          <button class="btn btn-default"
                  @click="close()">Cancel</button>
          <button class="btn btn-primary"
                  @click="updateEvent()">Update Event</button>

      <confirm-delete v-if="showDeleteConfirmation"
                      :match-text="this.semesterAndYear"
                      :event-id="this.event.id"
                      @close="hideDeleteConfirm()"></confirm-delete>
    </form>

</template>


Fix colors

Fix colors

<template>
  <f7-page>
    <f7-navbar title="Toggle" back-link="Back"></f7-navbar>
    <f7-block-title>Super Heroes</f7-block-title>
    <f7-list simple-list>
      <f7-list-item>
        Batman
        <f7-toggle checked></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Aquaman
        <f7-toggle checked color="blue"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Superman
        <f7-toggle checked color="red"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Hulk
        <f7-toggle color="green"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Spiderman (Disabled)
        <f7-toggle disabled></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Ironman (Disabled)
        <f7-toggle checked disabled></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Thor
        <f7-toggle checked color="orange"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Wonder Woman
        <f7-toggle color="pink"></f7-toggle>
      </f7-list-item>
    </f7-list>
  </f7-page>
</template>

<template>
  <f7-page>
    <f7-navbar title="Toggle" back-link="Back"></f7-navbar>
    <f7-block-title>Super Heroes</f7-block-title>
    <f7-list simple-list>
      <f7-list-item>
        Batman
        <f7-toggle checked></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Aquaman
        <f7-toggle checked color="cyan"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Superman
        <f7-toggle checked color="red"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Hulk
        <f7-toggle color="light-green"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Spiderman (Disabled)
        <f7-toggle disabled></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Ironman (Disabled)
        <f7-toggle checked disabled></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Thor
        <f7-toggle checked color="orange"></f7-toggle>
      </f7-list-item>
      <f7-list-item>
        Wonder Woman
        <f7-toggle color="pink"></f7-toggle>
      </f7-list-item>
    </f7-list>
  </f7-page>
</template>


Fix: Search method wasn't being called on hitting the enter key(for the search input field)

<template>

                  Logout

                  Register

                  Login

                Start Your Own Community

                  Search

            <router-view></router-view>

</template>


Get rootFolder from fileManager API on mount

<template>
  <div>
    <userInputs
      v-on:changeViewMode="changeViewMode"
    ></userInputs>
    <documents
      :view-mode="viewMode"
      :docs="docs"
    ></documents>
  </div>
</template>

<script>
import fileManager from 'src/logic/filemanager';
import documents from './Documents';
import userInputs from './UserInputs';

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      viewMode: 'listView',
      docs: ''
    };
  },
  watch: {
  },
  methods: {
    changeViewMode(viewMode) {
      this.viewMode = viewMode;
    },
  },
  mounted() {
    const startPromise = fileManager.start();

    startPromise
    .then((rootFolder) => {
      this.docs = rootFolder;
    })
    .catch((error) => {
      console.log(error);
    });
  }
};

</script>

<style scoped lang="scss">
@import 'src/assets/styles/variables.scss';

</style>

<template>
  <div>
    <userInputs
      v-on:changeViewMode="changeViewMode"
    ></userInputs>
    <documents
      :view-mode="viewMode"
      :docs="docs"
    ></documents>
  </div>
</template>

<script>
import fileManager from 'src/logic/filemanager';
import documents from './Documents';
import userInputs from './UserInputs';

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      viewMode: 'listView',
    };
  },
  watch: {
  },
  methods: {
    changeViewMode(viewMode) {
      this.viewMode = viewMode;
    },
    executeAction(action) {
      this.$emit('executeAction', action);
    }
  }
  // mounted() {
  //   const startPromise = fileManager.start();

  //   startPromise
  //   .then((rootFolder) => {
  //     console.log(rootFolder, 'rootFolder');
  //   })
  //   .catch((error) => {
  //     console.log(error);
  //   });
  // }
};

</script>

<style scoped lang="scss">
@import 'src/assets/styles/variables.scss';

</style>


Fix issue #107

Fix issue #107

<template>
  <input type="text" @input="onInput" data-input>
</template>

<script>
  import Flatpickr from 'flatpickr';
  // You have to import css yourself

  import {includedEvents, excludedEvents} from "./events";
  import {camelToKebab, cloneObject, arrayify} from "./util";

  // Keep a copy of all events for later use
  const allEvents = includedEvents.concat(excludedEvents);

  // Passing these properties in `set()` method will cause flatpickr to trigger some callbacks
  const configCallbacks = ['locale', 'showMonths'];

  export default {
    name: 'flat-pickr',
    props: {
      value: {
        default: null,
        required: true,
        validator(value) {
          return value === null || value instanceof Date || typeof value === 'string' || value instanceof String || value instanceof Array || typeof value === 'number'
        }
      },
      // https://chmln.github.io/flatpickr/options/
      config: {
        type: Object,
        default: () => ({
          wrap: false,
          defaultDate: null,
        })
      },
      events: {
        type: Array,
        default: () => includedEvents
      }
    },
    data() {
      return {
        /**
         * The flatpickr instance
         */
        fp: null,
      };
    },
    mounted() {
      // Return early if flatpickr is already loaded
      /* istanbul ignore if */
      if (this.fp) return;

      // Don't mutate original object on parent component
      let safeConfig = cloneObject(this.config);

      // Inject our methods into events array
      this.events.forEach((hook) => {
        safeConfig[hook] = arrayify(safeConfig[hook] || []).concat((...args) => {
          this.$emit(camelToKebab(hook), ...args)
        });
      });

      // Set initial date without emitting any event
      safeConfig.defaultDate = this.value || safeConfig.defaultDate;

      // Init flatpickr
      this.fp = new Flatpickr(this.getElem(), safeConfig);

      // Attach blur event
      this.fpInput().addEventListener('blur', this.onBlur)
    },
    methods: {
      /**
       * Get the HTML node where flatpickr to be attached
       * Bind on parent element if wrap is true
       */
      getElem() {
        return this.config.wrap ? this.$el.parentNode : this.$el
      },

      /**
       * Watch for value changed by date-picker itself and notify parent component
       *
       * @param event
       */
      onInput(event) {
        // Lets wait for DOM to be updated
        this.$nextTick(() => {
          this.$emit('input', event.target.value);
        });
      },

      /**
       * @return HTMLElement
       */
      fpInput() {
        return this.fp.altInput || this.fp.input;
      },

      /**
       * Blur event is required by many validation libraries
       *
       * @param event
       */
      onBlur(event) {
        this.$emit('blur', event.target.value);
      }
    },
    watch: {
      /**
       * Watch for any config changes and redraw date-picker
       *
       * @param newConfig Object
       */
      config: {
        deep: true,
        handler(newConfig) {
          let safeConfig = cloneObject(newConfig);
          // Workaround: Don't pass hooks to configs again otherwise
          // previously registered hooks will stop working
          // Notice: we are looping through all events
          // This also means that new callbacks can not passed once component has been initialized
          allEvents.forEach((hook) => {
            delete safeConfig[hook];
          });
          this.fp.set(safeConfig);

          // Workaround: Allow to change locale dynamically
          configCallbacks.forEach((name) => {
            if (typeof safeConfig[name] !== 'undefined') {
              this.fp.set(name, safeConfig[name])
            }
          });
        }
      },
      /**
       * Watch for changes from parent component and update DOM
       *
       * @param newValue
       */
      value(newValue) {
        // Prevent updates if v-model value is same as input's current value
        if (newValue === this.$el.value) return;
        // Make sure we have a flatpickr instance
        this.fp &&
        // Notify flatpickr instance that there is a change in value
        this.fp.setDate(newValue, true);
      }
    },
    /**
     * Free up memory
     */
    beforeDestroy() {
      /* istanbul ignore else */
      if (this.fp) {
        this.fpInput().removeEventListener('blur', this.onBlur);
        this.fp.destroy();
        this.fp = null;
      }
    },
  };
</script>

<template>
  <input type="text" @input="onInput" data-input>
</template>

<script>
  import Flatpickr from 'flatpickr';
  // You have to import css yourself

  import {includedEvents, excludedEvents} from "./events";
  import {camelToKebab, cloneObject, arrayify} from "./util";

  // Keep a copy of all events for later use
  const allEvents = includedEvents.concat(excludedEvents);

  // Passing these properties in `set()` method will cause flatpickr to trigger some callbacks
  const configCallbacks = ['locale', 'showMonths'];

  export default {
    name: 'flat-pickr',
    props: {
      value: {
        default: null,
        required: true,
        validator(value) {
          return value === null || value instanceof Date || typeof value === 'string' || value instanceof String || value instanceof Array || typeof value === 'number'
        }
      },
      // https://chmln.github.io/flatpickr/options/
      config: {
        type: Object,
        default: () => ({
          wrap: false,
          defaultDate: null,
        })
      },
      events: {
        type: Array,
        default: () => includedEvents
      }
    },
    data() {
      return {
        /**
         * The flatpickr instance
         */
        fp: null,
      };
    },
    mounted() {
      // Return early if flatpickr is already loaded
      /* istanbul ignore if */
      if (this.fp) return;

      // Don't mutate original object on parent component
      let safeConfig = cloneObject(this.config);

      // Inject our methods into events array
      this.events.forEach((hook) => {
        safeConfig[hook] = arrayify(safeConfig[hook] || []).concat((...args) => {
          this.$emit(camelToKebab(hook), ...args)
        });
      });

      // Set initial date without emitting any event
      safeConfig.defaultDate = this.value || safeConfig.defaultDate;

      // Init flatpickr
      this.fp = new Flatpickr(this.getElem(), safeConfig);

      // Attach blur event
      this.fpInput().addEventListener('blur', this.onBlur)
    },
    methods: {
      /**
       * Get the HTML node where flatpickr to be attached
       * Bind on parent element if wrap is true
       */
      getElem() {
        return this.config.wrap ? this.$el.parentNode : this.$el
      },

      /**
       * Watch for value changed by date-picker itself and notify parent component
       *
       * @param event
       */
      onInput(event) {
        this.$emit('input', event.target.value);
      },

      /**
       * @return HTMLElement
       */
      fpInput() {
        return this.fp.altInput || this.fp.input;
      },

      /**
       * Blur event is required by many validation libraries
       *
       * @param event
       */
      onBlur(event) {
        this.$emit('blur', event.target.value);
      }
    },
    watch: {
      /**
       * Watch for any config changes and redraw date-picker
       *
       * @param newConfig Object
       */
      config: {
        deep: true,
        handler(newConfig) {
          let safeConfig = cloneObject(newConfig);
          // Workaround: Don't pass hooks to configs again otherwise
          // previously registered hooks will stop working
          // Notice: we are looping through all events
          // This also means that new callbacks can not passed once component has been initialized
          allEvents.forEach((hook) => {
            delete safeConfig[hook];
          });
          this.fp.set(safeConfig);

          // Workaround: Allow to change locale dynamically
          configCallbacks.forEach((name) => {
            if (typeof safeConfig[name] !== 'undefined') {
              this.fp.set(name, safeConfig[name])
            }
          });
        }
      },
      /**
       * Watch for changes from parent component and update DOM
       *
       * @param newValue
       */
      value(newValue) {
        // Prevent updates if v-model value is same as input's current value
        if (newValue === this.$el.value) return;
        // Make sure we have a flatpickr instance
        this.fp &&
        // Notify flatpickr instance that there is a change in value
        this.fp.setDate(newValue, true);
      }
    },
    /**
     * Free up memory
     */
    beforeDestroy() {
      /* istanbul ignore else */
      if (this.fp) {
        this.fpInput().removeEventListener('blur', this.onBlur);
        this.fp.destroy();
        this.fp = null;
      }
    },
  };
</script>


fix cart price visibililty in offline mode

<template>

          {{ product.name | htmlDecode }}

          {{ product.sku }}

            {{ opt.label }}:

            {{ opt.label }}:

 0">
          {{ product.errors | formatProductMessages }}

 0 && Object.keys(product.errors).length === 0">
          {{ product.info | formatProductMessages }}

        <base-input-number
          :name="$t('Quantity')"
          :value="product.qty"
          @blur="updateQuantity"
          :min="1"
        />

          {{ product.priceInclTax * product.qty | price }}

          {{ product.originalPriceInclTax * product.qty | price }}

          {{ product.priceInclTax * product.qty | price }}

          {{ product.totals.row_total_incl_tax - product.totals.discount_amount | price }}

          {{ product.totals.row_total_incl_tax | price }}

          {{ product.totals.row_total_incl_tax | price }}

          {{ product.regular_price * product.qty | price }}

          <remove-button />

</template>

<template>

          {{ product.name | htmlDecode }}

          {{ product.sku }}

            {{ opt.label }}:

            {{ opt.label }}:

 0">
          {{ product.errors | formatProductMessages }}

 0 && Object.keys(product.errors).length === 0">
          {{ product.info | formatProductMessages }}

        <base-input-number
          :name="$t('Quantity')"
          :value="product.qty"
          @blur="updateQuantity"
          :min="1"
        />

          {{ product.priceInclTax * product.qty | price }}

          {{ product.originalPriceInclTax * product.qty | price }}

          {{ product.priceInclTax * product.qty | price }}

          {{ product.totals.row_total_incl_tax - product.totals.discount_amount | price }}

          {{ product.totals.row_total_incl_tax | price }}

          {{ product.totals.row_total_incl_tax | price }}

          <remove-button />

</template>


Replace SHOW MORE/LESS with faChevronDown/faChevronUp on Events card

<template>
  <uw-card v-if="!isReady || (shownEvents.length > 0 || futureCalCount > 0)"
           :loaded="isReady" :errored="isErrored" :mobile-only="mobileOnly"
  >
    <template #card-heading>

        Events

    </template>
    <template v-if="shownEvents.length > 0" #card-body>

        Showing events in the next 14 days.

      <uw-list-events :events="shownEvents" />
    </template>
    <template v-else #card-body>

 0">

          No events in the next 14 days.
          {{ futureCalCount }}
          {{ futureCalCount > 1 ? 'events' : 'event' }}
          from {{ futureCalLinks.length }} calendars in the next 30 days.

            {{ event.title }}

          No events in the next 14 days.
          {{ futureCalCount }}
          {{ futureCalCount > 1 ? 'events' : 'event' }}
          from

            {{ event.title }}

          in the next 30 days.

    </template>

    <template v-if="hiddenEvents.length > 0" #card-disclosure>

        <uw-list-events :events="hiddenEvents" />

 1" class="mt-3">

            See all events from:

              {{ event.title }}

            See all events from

              {{ calLinks[0].title }}

            calendar.

    </template>
    <template v-else-if="calLinks.length > 0" #card-disclosure>

 1">

          See all events from:

            {{ event.title }}

        See all events from
          {{ calLinks[0].title }}
         calendar.

    </template>
    <template v-if="hiddenEvents.length > 0" #card-footer>

        {{ hiddenEvents.length }} MORE

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

<template>
  <uw-card v-if="!isReady || (shownEvents.length > 0 || futureCalCount > 0)"
           :loaded="isReady" :errored="isErrored" :mobile-only="mobileOnly"
  >
    <template #card-heading>

        Events

    </template>
    <template v-if="shownEvents.length > 0" #card-body>

        Showing events in the next 14 days.

      <uw-list-events :events="shownEvents" />
    </template>
    <template v-else #card-body>

 0">

          No events in the next 14 days.
          {{ futureCalCount }}
          {{ futureCalCount > 1 ? 'events' : 'event' }}
          from {{ futureCalLinks.length }} calendars in the next 30 days.

            {{ event.title }}

          No events in the next 14 days.
          {{ futureCalCount }}
          {{ futureCalCount > 1 ? 'events' : 'event' }}
          from

            {{ event.title }}

          in the next 30 days.

    </template>

    <template v-if="hiddenEvents.length > 0" #card-disclosure>

        <uw-list-events :events="hiddenEvents" />

 1" class="mt-3">

            See all events from:

              {{ event.title }}

            See all events from

              {{ calLinks[0].title }}

            calendar.

    </template>
    <template v-else-if="calLinks.length > 0" #card-disclosure>

 1">

          See all events from:

            {{ event.title }}

        See all events from
          {{ calLinks[0].title }}
         calendar.

    </template>
    <template v-if="hiddenEvents.length > 0" #card-footer>

        SHOW ({{ hiddenEvents.length }}) MORE

        SHOW LESS

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


added markdown support to section headers

<template lang="pug">
  h2.headline.primary--text.mb-3
    markdown(:source="$t(value)")
</template>

<script>
  export default {
    name: 'section-head',

    props: {
      value: String
    }
  }
</script>

<template lang="pug">
  h2(v-html="$t(value)").headline.primary--text.mb-3
</template>

<script>
  export default {
    name: 'section-head',

    props: {
      value: String
    }
  }
</script>


docs: update incorrect link to example source

<template lang="pug">
  component(:is="component" :source="source")
</template>

<script>
  import { camel } from '@/util/helpers'
  import NotFound from '@/pages/general/404Page'

  export default {
    data: () => ({
      component: null
    }),

    computed: {
      source () {
        const path = this.$route.path.split('/').slice(2).join('/')
        return `https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetifyjs.com/src/${path}.vue`
      }
    },

    beforeRouteEnter (to, from, next) {
      let example = camel(to.params.example)
      example = `${example.substr(0, 1).toLowerCase()}${example.slice(1)}`

      return import(
        /* webpackChunkName: "examples" */
        /* webpackMode: "lazy-once" */
        `../../examples/${example}.vue`
      ).catch(e => NotFound).then(comp => {
        next(vm => { vm.component = comp.default })
      })
    }
  }
</script>

<template lang="pug">
  component(:is="component" :source="source")
</template>

<script>
  import { camel } from '@/util/helpers'
  import NotFound from '@/pages/general/404Page'

  export default {
    data: () => ({
      component: null
    }),

    computed: {
      source () {
        const path = this.$route.path.split('/').slice(2).join('/')
        return `https://github.com/vuetifyjs/vuetifyjs.com/blob/master/src/${path}.vue`
      }
    },

    beforeRouteEnter (to, from, next) {
      let example = camel(to.params.example)
      example = `${example.substr(0, 1).toLowerCase()}${example.slice(1)}`

      return import(
        /* webpackChunkName: "examples" */
        /* webpackMode: "lazy-once" */
        `../../examples/${example}.vue`
      ).catch(e => NotFound).then(comp => {
        next(vm => { vm.component = comp.default })
      })
    }
  }
</script>


Add toBuffer function

Add toBuffer function


Fix overflow for long health details

Fix overflow for long health details

fixes #960

<template>

        <span class="health-details__status" :class="`health-details__status--${health.status}`"
              v-text="health.status"/>

 0">

        <health-details :name="child.name" :health="child.value"/>

</template>


convert indent from tab to space

convert indent from tab to space

<template>

Parent Details

      Email *
      <input type="text" :class="`form-control ${this.emailErrorClass}`" id="parent-email" placeholder="Enter Email" @keydown="updateEmail" @keyup="updateEmail" :value="email" />

      First Name *
      <input type="text" class="form-control" id="parent-firstname" placeholder="Enter First Name" @keydown="updateFirstName" @keyup="updateFirstName" :value="firstName" />

      Last Name
      <input type="text" class="form-control" id="parent-lastname" placeholder="Enter Last Name" @keydown="updateLastName" @keyup="updateLastName" :value="lastName" />

</template>


update

update

<template>

{{ 'Add Video' | trans }}

        <video class="uk-responsive-width" :src="videoSrc" :width="width" :height="height" v-if="videoSrc"></video>

</template>


put v-if on entire component

put v-if on entire component


Add id to icon and list view buttons

Add id to icon and list view buttons

<template>

        <router-link to="/" class="vertical-align-child">

            SASHIMI NOTE

        </router-link>

        <input type="text" placeholder="Search...">

            <button class="navbar-buttons hover-grow"
                    v-on:click="execute('history back')"
            >
              arrow_back
            </button>

            <button class="navbar-buttons hover-grow"
                    v-on:click="execute('history forward')"
            >
              arrow_forward
            </button>

            Home

          <button class="navbar-buttons hover-grow">
            file_upload
          </button>
          <button class="navbar-buttons hover-grow"
                  v-on:click="execute('createFolder')"
          >
            create_new_folder
          </button>
          <button class="navbar-buttons hover-grow"
                  v-on:click="execute('createFile')"
          >
            note_add
          </button>
          <button class="navbar-buttons"
                  v-bind:class="{'hover-grow': buttonEffect}"
          >
            content_copy
          </button>
          <button class="navbar-buttons"
                  v-bind:class="{'hover-grow': buttonEffect}"
                    v-on:click="execute('download')"
          >
            file_download
          </button>
          <button class="navbar-buttons"
                  v-bind:class="{'hover-grow': buttonEffect}"
                  v-on:click="execute('delete')"
          >
            delete
          </button>

        <button id="button-icon-view" class="navbar-buttons hover-grow"
                v-on:click="setViewMode('iconView')"
        >Icon</button>|
        <button id="button-list-view" class="navbar-buttons hover-grow"
                v-on:click="setViewMode('listView')"
        >List</button>

</template>

<template>

        <router-link to="/" class="vertical-align-child">

            SASHIMI NOTE

        </router-link>

        <input type="text" placeholder="Search...">

            <button class="navbar-buttons hover-grow"
                    v-on:click="execute('history back')"
            >
              arrow_back
            </button>

            <button class="navbar-buttons hover-grow"
                    v-on:click="execute('history forward')"
            >
              arrow_forward
            </button>

            Home

          <button class="navbar-buttons hover-grow">
            file_upload
          </button>
          <button class="navbar-buttons hover-grow"
                  v-on:click="execute('createFolder')"
          >
            create_new_folder
          </button>
          <button class="navbar-buttons hover-grow"
                  v-on:click="execute('createFile')"
          >
            note_add
          </button>
          <button class="navbar-buttons"
                  v-bind:class="{'hover-grow': buttonEffect}"
          >
            content_copy
          </button>
          <button class="navbar-buttons"
                  v-bind:class="{'hover-grow': buttonEffect}"
                    v-on:click="execute('download')"
          >
            file_download
          </button>
          <button class="navbar-buttons"
                  v-bind:class="{'hover-grow': buttonEffect}"
                  v-on:click="execute('delete')"
          >
            delete
          </button>

        <button class="navbar-buttons hover-grow"
                v-on:click="setViewMode('iconView')"
        >Icon</button>|
        <button class="navbar-buttons hover-grow"
                v-on:click="setViewMode('listView')"
        >List</button>

</template>


fix(movable-view): 支持触摸中响应 disabled 变化

fix(movable-view): 支持触摸中响应 disabled 变化

<template>
  <uni-movable-view v-on="$listeners">
    <v-uni-resize-sensor @resize="setParent" />
    <slot />
  </uni-movable-view>
</template>
<script>
import touchtrack from 'uni-mixins/touchtrack'
import {
  Decline,
  Friction,
  STD
} from './utils'
import {
  initScrollBounce,
  disableScrollBounce
} from 'uni-platform/helpers/scroll'
var requesting = false

function _requestAnimationFrame (e) {
  if (!requesting) {
    requesting = true
    requestAnimationFrame(function () {
      e()
      requesting = false
    })
  }
}

function p (t, n) {
  if (t === n) {
    return 0
  }
  var i = t.offsetLeft
  return t.offsetParent ? (i += p(t.offsetParent, n)) : 0
}
function f (t, n) {
  if (t === n) {
    return 0
  }
  var i = t.offsetTop
  return t.offsetParent ? (i += f(t.offsetParent, n)) : 0
}
function v (a, b) {
  return +((1000 * a - 1000 * b) / 1000).toFixed(1)
}
function g (e, t, n) {
  var i = function (e) {
    if (e && e.id) {
      cancelAnimationFrame(e.id)
    }
    if (e) {
      e.cancelled = true
    }
  }
  var r = {
    id: 0,
    cancelled: false
  }
  function fn (n, i, r, o) {
    if (!n || !n.cancelled) {
      r(i)
      var a = e.done()
      if (!a) {
        if (!n.cancelled) {
          (n.id = requestAnimationFrame(fn.bind(null, n, i, r, o)))
        }
      }
      if (a && o) {
        o(i)
      }
    }
  }
  fn(r, e, t, n)
  return {
    cancel: i.bind(null, r),
    model: e
  }
}

export default {
  name: 'MovableView',
  mixins: [touchtrack],
  props: {
    direction: {
      type: String,
      default: 'none'
    },
    inertia: {
      type: [Boolean, String],
      default: false
    },
    outOfBounds: {
      type: [Boolean, String],
      default: false
    },
    x: {
      type: [Number, String],
      default: 0
    },
    y: {
      type: [Number, String],
      default: 0
    },
    damping: {
      type: [Number, String],
      default: 20
    },
    friction: {
      type: [Number, String],
      default: 2
    },
    disabled: {
      type: [Boolean, String],
      default: false
    },
    scale: {
      type: [Boolean, String],
      default: false
    },
    scaleMin: {
      type: [Number, String],
      default: 0.5
    },
    scaleMax: {
      type: [Number, String],
      default: 10
    },
    scaleValue: {
      type: [Number, String],
      default: 1
    },
    animation: {
      type: [Boolean, String],
      default: true
    }
  },
  data () {
    return {
      xSync: this._getPx(this.x),
      ySync: this._getPx(this.y),
      scaleValueSync: Number(this.scaleValue) || 1,
      width: 0,
      height: 0,
      minX: 0,
      minY: 0,
      maxX: 0,
      maxY: 0
    }
  },
  computed: {
    dampingNumber () {
      var val = Number(this.damping)
      return isNaN(val) ? 20 : val
    },
    frictionNumber () {
      var val = Number(this.friction)
      return isNaN(val) || val <= 0 ? 2 : val
    },
    scaleMinNumber () {
      var val = Number(this.scaleMin)
      return isNaN(val) ? 0.5 : val
    },
    scaleMaxNumber () {
      var val = Number(this.scaleMax)
      return isNaN(val) ? 10 : val
    },
    xMove () {
      return this.direction === 'all' || this.direction === 'horizontal'
    },
    yMove () {
      return this.direction === 'all' || this.direction === 'vertical'
    }
  },
  watch: {
    x (val) {
      this.xSync = this._getPx(val)
    },
    xSync (val) {
      this._setX(val)
    },
    y (val) {
      this.ySync = this._getPx(val)
    },
    ySync (val) {
      this._setY(val)
    },
    disabled () {
      this.__handleTouchStart()
    },
    scaleValue (val) {
      this.scaleValueSync = Number(val) || 0
    },
    scaleValueSync (val) {
      this._setScaleValue(val)
    },
    scaleMinNumber () {
      this._setScaleMinOrMax()
    },
    scaleMaxNumber () {
      this._setScaleMinOrMax()
    }
  },
  created: function () {
    this._offset = {
      x: 0,
      y: 0
    }
    this._scaleOffset = {
      x: 0,
      y: 0
    }
    this._translateX = 0
    this._translateY = 0
    this._scale = 1
    this._oldScale = 1

    this._STD = new STD(1, 9 * Math.pow(this.dampingNumber, 2) / 40, this.dampingNumber)
    this._friction = new Friction(1, this.frictionNumber)
    this._declineX = new Decline()
    this._declineY = new Decline()
    this.__touchInfo = {
      historyX: [0, 0],
      historyY: [0, 0],
      historyT: [0, 0]
    }
  },
  mounted: function () {
    this.touchtrack(this.$el, '_onTrack')
    this.setParent()
    this._friction.reconfigure(1, this.frictionNumber)
    this._STD.reconfigure(1, 9 * Math.pow(this.dampingNumber, 2) / 40, this.dampingNumber)
    this.$el.style.transformOrigin = 'center'
    initScrollBounce()
  },
  methods: {
    _getPx (val) {
      if (/\d+[ur]px$/i.test(val)) {
        return uni.upx2px(parseFloat(val))
      }
      return Number(val) || 0
    },
    _setX: function (val) {
      if (this.xMove) {
        if (val + this._scaleOffset.x === this._translateX) {
          return this._translateX
        } else {
          if (this._SFA) {
            this._SFA.cancel()
          }
          this._animationTo(val + this._scaleOffset.x, this.ySync + this._scaleOffset.y, this._scale)
        }
      }
      return val
    },
    _setY: function (val) {
      if (this.yMove) {
        if (val + this._scaleOffset.y === this._translateY) {
          return this._translateY
        } else {
          if (this._SFA) {
            this._SFA.cancel()
          }
          this._animationTo(this.xSync + this._scaleOffset.x, val + this._scaleOffset.y, this._scale)
        }
      }
      return val
    },
    _setScaleMinOrMax: function () {
      if (!this.scale) {
        return false
      }
      this._updateScale(this._scale, true)
      this._updateOldScale(this._scale)
    },
    _setScaleValue: function (scale) {
      if (!this.scale) {
        return false
      }
      scale = this._adjustScale(scale)
      this._updateScale(scale, true)
      this._updateOldScale(scale)
      return scale
    },
    __handleTouchStart: function () {
      if (!this._isScaling) {
        if (!this.disabled) {
          disableScrollBounce({
            disable: true
          })
          if (this._FA) {
            this._FA.cancel()
          }
          if (this._SFA) {
            this._SFA.cancel()
          }
          this.__touchInfo.historyX = [0, 0]
          this.__touchInfo.historyY = [0, 0]
          this.__touchInfo.historyT = [0, 0]
          if (this.xMove) {
            this.__baseX = this._translateX
          }
          if (this.yMove) {
            this.__baseY = this._translateY
          }
          this.$el.style.willChange = 'transform'
          this._checkCanMove = null
          this._firstMoveDirection = null
          this._isTouching = true
        }
      }
    },
    __handleTouchMove: function (event) {
      var self = this
      if (!this._isScaling && !this.disabled && this._isTouching) {
        let x = this._translateX
        let y = this._translateY
        if (this._firstMoveDirection === null) {
          this._firstMoveDirection = Math.abs(event.detail.dx / event.detail.dy) > 1 ? 'htouchmove' : 'vtouchmove'
        }
        if (this.xMove) {
          x = event.detail.dx + this.__baseX
          this.__touchInfo.historyX.shift()
          this.__touchInfo.historyX.push(x)
          if (!this.yMove && this._checkCanMove === null) {
            this._checkCanMove = Math.abs(event.detail.dx / event.detail.dy) < 1
          }
        }
        if (this.yMove) {
          y = event.detail.dy + this.__baseY
          this.__touchInfo.historyY.shift()
          this.__touchInfo.historyY.push(y)
          if (!this.xMove && this._checkCanMove === null) {
            this._checkCanMove = Math.abs(event.detail.dy / event.detail.dx) < 1
          }
        }
        this.__touchInfo.historyT.shift()
        this.__touchInfo.historyT.push(event.detail.timeStamp)

        if (!this._checkCanMove) {
          event.preventDefault()
          let source = 'touch'
          if (x < this.minX) {
            if (this.outOfBounds) {
              source = 'touch-out-of-bounds'
              x = this.minX - this._declineX.x(this.minX - x)
            } else {
              x = this.minX
            }
          } else if (x > this.maxX) {
            if (this.outOfBounds) {
              source = 'touch-out-of-bounds'
              x = this.maxX + this._declineX.x(x - this.maxX)
            } else {
              x = this.maxX
            }
          }
          if (y < this.minY) {
            if (this.outOfBounds) {
              source = 'touch-out-of-bounds'
              y = this.minY - this._declineY.x(this.minY - y)
            } else {
              y = this.minY
            }
          } else {
            if (y > this.maxY) {
              if (this.outOfBounds) {
                source = 'touch-out-of-bounds'
                y = this.maxY + this._declineY.x(y - this.maxY)
              } else {
                y = this.maxY
              }
            }
          }
          _requestAnimationFrame(function () {
            self._setTransform(x, y, self._scale, source)
          })
        }
      }
    },
    __handleTouchEnd: function () {
      var self = this
      if (!this._isScaling && !this.disabled && this._isTouching) {
        disableScrollBounce({
          disable: false
        })
        this.$el.style.willChange = 'auto'
        this._isTouching = false
        if (!this._checkCanMove && !this._revise('out-of-bounds') && this.inertia) {
          const xv = 1000 * (this.__touchInfo.historyX[1] - this.__touchInfo.historyX[0]) / (this.__touchInfo.historyT[1] - this.__touchInfo.historyT[0])
          const yv = 1000 * (this.__touchInfo.historyY[1] - this.__touchInfo.historyY[0]) / (this.__touchInfo.historyT[1] - this.__touchInfo.historyT[0])
          this._friction.setV(xv, yv)
          this._friction.setS(this._translateX, this._translateY)
          const x0 = this._friction.delta().x
          const y0 = this._friction.delta().y
          let x = x0 + this._translateX
          let y = y0 + this._translateY
          if (x < this.minX) {
            x = this.minX
            y = this._translateY + (this.minX - this._translateX) * y0 / x0
          } else {
            if (x > this.maxX) {
              x = this.maxX
              y = this._translateY + (this.maxX - this._translateX) * y0 / x0
            }
          }
          if (y < this.minY) {
            y = this.minY
            x = this._translateX + (this.minY - this._translateY) * x0 / y0
          } else {
            if (y > this.maxY) {
              y = this.maxY
              x = this._translateX + (this.maxY - this._translateY) * x0 / y0
            }
          }
          this._friction.setEnd(x, y)
          this._FA = g(this._friction, function () {
            var t = self._friction.s()
            var x = t.x
            var y = t.y
            self._setTransform(x, y, self._scale, 'friction')
          }, function () {
            self._FA.cancel()
          })
        }
      }
    },
    _onTrack: function (event) {
      switch (event.detail.state) {
        case 'start':
          this.__handleTouchStart()
          break
        case 'move':
          this.__handleTouchMove(event)
          break
        case 'end':
          this.__handleTouchEnd()
      }
    },
    _getLimitXY: function (x, y) {
      var outOfBounds = false
      if (x > this.maxX) {
        x = this.maxX
        outOfBounds = true
      } else {
        if (x < this.minX) {
          x = this.minX
          outOfBounds = true
        }
      }
      if (y > this.maxY) {
        y = this.maxY
        outOfBounds = true
      } else {
        if (y < this.minY) {
          y = this.minY
          outOfBounds = true
        }
      }
      return {
        x,
        y,
        outOfBounds
      }
    },
    setParent: function () {
      if (!this.$parent._isMounted) {
        return
      }
      if (this._FA) {
        this._FA.cancel()
      }
      if (this._SFA) {
        this._SFA.cancel()
      }
      var scale = this.scale ? this.scaleValueSync : 1
      this._updateOffset()
      this._updateWH(scale)
      this._updateBoundary()
      this._translateX = this.xSync + this._scaleOffset.x
      this._translateY = this.ySync + this._scaleOffset.y
      var limitXY = this._getLimitXY(this._translateX, this._translateY)
      var x = limitXY.x
      var y = limitXY.y
      this._setTransform(x, y, scale, '', true)
      this._updateOldScale(scale)
    },
    _updateOffset: function () {
      this._offset.x = p(this.$el, this.$parent.$el)
      this._offset.y = f(this.$el, this.$parent.$el)
    },
    _updateWH: function (scale) {
      scale = scale || this._scale
      scale = this._adjustScale(scale)
      var rect = this.$el.getBoundingClientRect()
      this.height = rect.height / this._scale
      this.width = rect.width / this._scale
      var height = this.height * scale
      var width = this.width * scale
      this._scaleOffset.x = (width - this.width) / 2
      this._scaleOffset.y = (height - this.height) / 2
    },
    _updateBoundary: function () {
      var x = 0 - this._offset.x + this._scaleOffset.x
      var width = this.$parent.width - this.width - this._offset.x - this._scaleOffset.x
      this.minX = Math.min(x, width)
      this.maxX = Math.max(x, width)
      var y = 0 - this._offset.y + this._scaleOffset.y
      var height = this.$parent.height - this.height - this._offset.y - this._scaleOffset.y
      this.minY = Math.min(y, height)
      this.maxY = Math.max(y, height)
    },
    _beginScale: function () {
      this._isScaling = true
    },
    _endScale: function () {
      this._isScaling = false
      this._updateOldScale(this._scale)
    },
    _setScale: function (scale) {
      if (this.scale) {
        scale = this._oldScale * scale
        this._beginScale()
        this._updateScale(scale)
      }
    },
    _updateScale: function (scale, animat) {
      var self = this
      if (this.scale) {
        scale = this._adjustScale(scale)
        this._updateWH(scale)
        this._updateBoundary()
        const limitXY = this._getLimitXY(this._translateX, this._translateY)
        const x = limitXY.x
        const y = limitXY.y
        if (animat) {
          this._animationTo(x, y, scale, '', true, true)
        } else {
          _requestAnimationFrame(function () {
            self._setTransform(x, y, scale, '', true, true)
          })
        }
      }
    },
    _updateOldScale: function (scale) {
      this._oldScale = scale
    },
    _adjustScale: function (scale) {
      scale = Math.max(0.5, this.scaleMinNumber, scale)
      scale = Math.min(10, this.scaleMaxNumber, scale)
      return scale
    },
    _animationTo: function (x, y, scale, source, r, o) {
      var self = this
      if (this._FA) {
        this._FA.cancel()
      }
      if (this._SFA) {
        this._SFA.cancel()
      }
      if (!this.xMove) {
        x = this._translateX
      }
      if (!this.yMove) {
        y = this._translateY
      }
      if (!this.scale) {
        scale = this._scale
      }
      var limitXY = this._getLimitXY(x, y)
      x = limitXY.x
      y = limitXY.y
      if (!this.animation) {
        this._setTransform(x, y, scale, source, r, o)
        return
      }
      this._STD._springX._solution = null
      this._STD._springY._solution = null
      this._STD._springScale._solution = null
      this._STD._springX._endPosition = this._translateX
      this._STD._springY._endPosition = this._translateY
      this._STD._springScale._endPosition = this._scale
      this._STD.setEnd(x, y, scale, 1)
      this._SFA = g(this._STD, function () {
        var data = self._STD.x()
        var x = data.x
        var y = data.y
        var scale = data.scale
        self._setTransform(x, y, scale, source, r, o)
      }, function () {
        self._SFA.cancel()
      })
    },
    _revise: function (source) {
      var limitXY = this._getLimitXY(this._translateX, this._translateY)
      var x = limitXY.x
      var y = limitXY.y
      var outOfBounds = limitXY.outOfBounds
      if (outOfBounds) {
        this._animationTo(x, y, this._scale, source)
      }
      return outOfBounds
    },
    _setTransform: function (x, y, scale, source = '', r, o) {
      if (!(x !== null && x.toString() !== 'NaN' && typeof x === 'number')) {
        x = this._translateX || 0
      }
      if (!(y !== null && y.toString() !== 'NaN' && typeof y === 'number')) {
        y = this._translateY || 0
      }
      x = Number(x.toFixed(1))
      y = Number(y.toFixed(1))
      scale = Number(scale.toFixed(1))
      if (!(this._translateX === x && this._translateY === y)) {
        if (!r) {
          this.$trigger('change', {}, {
            x: v(x, this._scaleOffset.x),
            y: v(y, this._scaleOffset.y),
            source: source
          })
        }
      }
      if (!this.scale) {
        scale = this._scale
      }
      scale = this._adjustScale(scale)
      scale = +scale.toFixed(3)
      if (o && scale !== this._scale) {
        this.$trigger('scale', {}, {
          x: x,
          y: y,
          scale: scale
        })
      }
      var transform = 'translateX(' + x + 'px) translateY(' + y + 'px) translateZ(0px) scale(' + scale + ')'
      this.$el.style.transform = transform
      this.$el.style.webkitTransform = transform
      this._translateX = x
      this._translateY = y
      this._scale = scale
    }
  }
}
</script>
<style>
uni-movable-view {
  display: inline-block;
  width: 10px;
  height: 10px;
  top: 0px;
  left: 0px;
  position: absolute;
  cursor: grab;
}

uni-movable-view[hidden] {
  display: none;
}
</style>

<template>
  <uni-movable-view v-on="$listeners">
    <v-uni-resize-sensor @resize="setParent" />
    <slot />
  </uni-movable-view>
</template>
<script>
import touchtrack from 'uni-mixins/touchtrack'
import {
  Decline,
  Friction,
  STD
} from './utils'
import {
  initScrollBounce,
  disableScrollBounce
} from 'uni-platform/helpers/scroll'
var requesting = false

function _requestAnimationFrame (e) {
  if (!requesting) {
    requesting = true
    requestAnimationFrame(function () {
      e()
      requesting = false
    })
  }
}

function p (t, n) {
  if (t === n) {
    return 0
  }
  var i = t.offsetLeft
  return t.offsetParent ? (i += p(t.offsetParent, n)) : 0
}
function f (t, n) {
  if (t === n) {
    return 0
  }
  var i = t.offsetTop
  return t.offsetParent ? (i += f(t.offsetParent, n)) : 0
}
function v (a, b) {
  return +((1000 * a - 1000 * b) / 1000).toFixed(1)
}
function g (e, t, n) {
  var i = function (e) {
    if (e && e.id) {
      cancelAnimationFrame(e.id)
    }
    if (e) {
      e.cancelled = true
    }
  }
  var r = {
    id: 0,
    cancelled: false
  }
  function fn (n, i, r, o) {
    if (!n || !n.cancelled) {
      r(i)
      var a = e.done()
      if (!a) {
        if (!n.cancelled) {
          (n.id = requestAnimationFrame(fn.bind(null, n, i, r, o)))
        }
      }
      if (a && o) {
        o(i)
      }
    }
  }
  fn(r, e, t, n)
  return {
    cancel: i.bind(null, r),
    model: e
  }
}

export default {
  name: 'MovableView',
  mixins: [touchtrack],
  props: {
    direction: {
      type: String,
      default: 'none'
    },
    inertia: {
      type: [Boolean, String],
      default: false
    },
    outOfBounds: {
      type: [Boolean, String],
      default: false
    },
    x: {
      type: [Number, String],
      default: 0
    },
    y: {
      type: [Number, String],
      default: 0
    },
    damping: {
      type: [Number, String],
      default: 20
    },
    friction: {
      type: [Number, String],
      default: 2
    },
    disabled: {
      type: [Boolean, String],
      default: false
    },
    scale: {
      type: [Boolean, String],
      default: false
    },
    scaleMin: {
      type: [Number, String],
      default: 0.5
    },
    scaleMax: {
      type: [Number, String],
      default: 10
    },
    scaleValue: {
      type: [Number, String],
      default: 1
    },
    animation: {
      type: [Boolean, String],
      default: true
    }
  },
  data () {
    return {
      xSync: this._getPx(this.x),
      ySync: this._getPx(this.y),
      scaleValueSync: Number(this.scaleValue) || 1,
      width: 0,
      height: 0,
      minX: 0,
      minY: 0,
      maxX: 0,
      maxY: 0
    }
  },
  computed: {
    dampingNumber () {
      var val = Number(this.damping)
      return isNaN(val) ? 20 : val
    },
    frictionNumber () {
      var val = Number(this.friction)
      return isNaN(val) || val <= 0 ? 2 : val
    },
    scaleMinNumber () {
      var val = Number(this.scaleMin)
      return isNaN(val) ? 0.5 : val
    },
    scaleMaxNumber () {
      var val = Number(this.scaleMax)
      return isNaN(val) ? 10 : val
    },
    xMove () {
      return this.direction === 'all' || this.direction === 'horizontal'
    },
    yMove () {
      return this.direction === 'all' || this.direction === 'vertical'
    }
  },
  watch: {
    x (val) {
      this.xSync = this._getPx(val)
    },
    xSync (val) {
      this._setX(val)
    },
    y (val) {
      this.ySync = this._getPx(val)
    },
    ySync (val) {
      this._setY(val)
    },
    scaleValue (val) {
      this.scaleValueSync = Number(val) || 0
    },
    scaleValueSync (val) {
      this._setScaleValue(val)
    },
    scaleMinNumber () {
      this._setScaleMinOrMax()
    },
    scaleMaxNumber () {
      this._setScaleMinOrMax()
    }
  },
  created: function () {
    this._offset = {
      x: 0,
      y: 0
    }
    this._scaleOffset = {
      x: 0,
      y: 0
    }
    this._translateX = 0
    this._translateY = 0
    this._scale = 1
    this._oldScale = 1

    this._STD = new STD(1, 9 * Math.pow(this.dampingNumber, 2) / 40, this.dampingNumber)
    this._friction = new Friction(1, this.frictionNumber)
    this._declineX = new Decline()
    this._declineY = new Decline()
    this.__touchInfo = {
      historyX: [0, 0],
      historyY: [0, 0],
      historyT: [0, 0]
    }
  },
  mounted: function () {
    this.touchtrack(this.$el, '_onTrack')
    this.setParent()
    this._friction.reconfigure(1, this.frictionNumber)
    this._STD.reconfigure(1, 9 * Math.pow(this.dampingNumber, 2) / 40, this.dampingNumber)
    this.$el.style.transformOrigin = 'center'
    initScrollBounce()
  },
  methods: {
    _getPx (val) {
      if (/\d+[ur]px$/i.test(val)) {
        return uni.upx2px(parseFloat(val))
      }
      return Number(val) || 0
    },
    _setX: function (val) {
      if (this.xMove) {
        if (val + this._scaleOffset.x === this._translateX) {
          return this._translateX
        } else {
          if (this._SFA) {
            this._SFA.cancel()
          }
          this._animationTo(val + this._scaleOffset.x, this.ySync + this._scaleOffset.y, this._scale)
        }
      }
      return val
    },
    _setY: function (val) {
      if (this.yMove) {
        if (val + this._scaleOffset.y === this._translateY) {
          return this._translateY
        } else {
          if (this._SFA) {
            this._SFA.cancel()
          }
          this._animationTo(this.xSync + this._scaleOffset.x, val + this._scaleOffset.y, this._scale)
        }
      }
      return val
    },
    _setScaleMinOrMax: function () {
      if (!this.scale) {
        return false
      }
      this._updateScale(this._scale, true)
      this._updateOldScale(this._scale)
    },
    _setScaleValue: function (scale) {
      if (!this.scale) {
        return false
      }
      scale = this._adjustScale(scale)
      this._updateScale(scale, true)
      this._updateOldScale(scale)
      return scale
    },
    __handleTouchStart: function () {
      if (!this._isScaling) {
        if (!this.disabled) {
          disableScrollBounce({
            disable: true
          })
          if (this._FA) {
            this._FA.cancel()
          }
          if (this._SFA) {
            this._SFA.cancel()
          }
          this.__touchInfo.historyX = [0, 0]
          this.__touchInfo.historyY = [0, 0]
          this.__touchInfo.historyT = [0, 0]
          if (this.xMove) {
            this.__baseX = this._translateX
          }
          if (this.yMove) {
            this.__baseY = this._translateY
          }
          this.$el.style.willChange = 'transform'
          this._checkCanMove = null
          this._firstMoveDirection = null
          this._isTouching = true
        }
      }
    },
    __handleTouchMove: function (event) {
      var self = this
      if (!this._isScaling && !this.disabled && this._isTouching) {
        let x = this._translateX
        let y = this._translateY
        if (this._firstMoveDirection === null) {
          this._firstMoveDirection = Math.abs(event.detail.dx / event.detail.dy) > 1 ? 'htouchmove' : 'vtouchmove'
        }
        if (this.xMove) {
          x = event.detail.dx + this.__baseX
          this.__touchInfo.historyX.shift()
          this.__touchInfo.historyX.push(x)
          if (!this.yMove && this._checkCanMove === null) {
            this._checkCanMove = Math.abs(event.detail.dx / event.detail.dy) < 1
          }
        }
        if (this.yMove) {
          y = event.detail.dy + this.__baseY
          this.__touchInfo.historyY.shift()
          this.__touchInfo.historyY.push(y)
          if (!this.xMove && this._checkCanMove === null) {
            this._checkCanMove = Math.abs(event.detail.dy / event.detail.dx) < 1
          }
        }
        this.__touchInfo.historyT.shift()
        this.__touchInfo.historyT.push(event.detail.timeStamp)

        if (!this._checkCanMove) {
          event.preventDefault()
          let source = 'touch'
          if (x < this.minX) {
            if (this.outOfBounds) {
              source = 'touch-out-of-bounds'
              x = this.minX - this._declineX.x(this.minX - x)
            } else {
              x = this.minX
            }
          } else if (x > this.maxX) {
            if (this.outOfBounds) {
              source = 'touch-out-of-bounds'
              x = this.maxX + this._declineX.x(x - this.maxX)
            } else {
              x = this.maxX
            }
          }
          if (y < this.minY) {
            if (this.outOfBounds) {
              source = 'touch-out-of-bounds'
              y = this.minY - this._declineY.x(this.minY - y)
            } else {
              y = this.minY
            }
          } else {
            if (y > this.maxY) {
              if (this.outOfBounds) {
                source = 'touch-out-of-bounds'
                y = this.maxY + this._declineY.x(y - this.maxY)
              } else {
                y = this.maxY
              }
            }
          }
          _requestAnimationFrame(function () {
            self._setTransform(x, y, self._scale, source)
          })
        }
      }
    },
    __handleTouchEnd: function () {
      var self = this
      if (!this._isScaling && !this.disabled && this._isTouching) {
        disableScrollBounce({
          disable: false
        })
        this.$el.style.willChange = 'auto'
        this._isTouching = false
        if (!this._checkCanMove && !this._revise('out-of-bounds') && this.inertia) {
          const xv = 1000 * (this.__touchInfo.historyX[1] - this.__touchInfo.historyX[0]) / (this.__touchInfo.historyT[1] - this.__touchInfo.historyT[0])
          const yv = 1000 * (this.__touchInfo.historyY[1] - this.__touchInfo.historyY[0]) / (this.__touchInfo.historyT[1] - this.__touchInfo.historyT[0])
          this._friction.setV(xv, yv)
          this._friction.setS(this._translateX, this._translateY)
          const x0 = this._friction.delta().x
          const y0 = this._friction.delta().y
          let x = x0 + this._translateX
          let y = y0 + this._translateY
          if (x < this.minX) {
            x = this.minX
            y = this._translateY + (this.minX - this._translateX) * y0 / x0
          } else {
            if (x > this.maxX) {
              x = this.maxX
              y = this._translateY + (this.maxX - this._translateX) * y0 / x0
            }
          }
          if (y < this.minY) {
            y = this.minY
            x = this._translateX + (this.minY - this._translateY) * x0 / y0
          } else {
            if (y > this.maxY) {
              y = this.maxY
              x = this._translateX + (this.maxY - this._translateY) * x0 / y0
            }
          }
          this._friction.setEnd(x, y)
          this._FA = g(this._friction, function () {
            var t = self._friction.s()
            var x = t.x
            var y = t.y
            self._setTransform(x, y, self._scale, 'friction')
          }, function () {
            self._FA.cancel()
          })
        }
      }
    },
    _onTrack: function (event) {
      switch (event.detail.state) {
        case 'start':
          this.__handleTouchStart()
          break
        case 'move':
          this.__handleTouchMove(event)
          break
        case 'end':
          this.__handleTouchEnd()
      }
    },
    _getLimitXY: function (x, y) {
      var outOfBounds = false
      if (x > this.maxX) {
        x = this.maxX
        outOfBounds = true
      } else {
        if (x < this.minX) {
          x = this.minX
          outOfBounds = true
        }
      }
      if (y > this.maxY) {
        y = this.maxY
        outOfBounds = true
      } else {
        if (y < this.minY) {
          y = this.minY
          outOfBounds = true
        }
      }
      return {
        x,
        y,
        outOfBounds
      }
    },
    setParent: function () {
      if (!this.$parent._isMounted) {
        return
      }
      if (this._FA) {
        this._FA.cancel()
      }
      if (this._SFA) {
        this._SFA.cancel()
      }
      var scale = this.scale ? this.scaleValueSync : 1
      this._updateOffset()
      this._updateWH(scale)
      this._updateBoundary()
      this._translateX = this.xSync + this._scaleOffset.x
      this._translateY = this.ySync + this._scaleOffset.y
      var limitXY = this._getLimitXY(this._translateX, this._translateY)
      var x = limitXY.x
      var y = limitXY.y
      this._setTransform(x, y, scale, '', true)
      this._updateOldScale(scale)
    },
    _updateOffset: function () {
      this._offset.x = p(this.$el, this.$parent.$el)
      this._offset.y = f(this.$el, this.$parent.$el)
    },
    _updateWH: function (scale) {
      scale = scale || this._scale
      scale = this._adjustScale(scale)
      var rect = this.$el.getBoundingClientRect()
      this.height = rect.height / this._scale
      this.width = rect.width / this._scale
      var height = this.height * scale
      var width = this.width * scale
      this._scaleOffset.x = (width - this.width) / 2
      this._scaleOffset.y = (height - this.height) / 2
    },
    _updateBoundary: function () {
      var x = 0 - this._offset.x + this._scaleOffset.x
      var width = this.$parent.width - this.width - this._offset.x - this._scaleOffset.x
      this.minX = Math.min(x, width)
      this.maxX = Math.max(x, width)
      var y = 0 - this._offset.y + this._scaleOffset.y
      var height = this.$parent.height - this.height - this._offset.y - this._scaleOffset.y
      this.minY = Math.min(y, height)
      this.maxY = Math.max(y, height)
    },
    _beginScale: function () {
      this._isScaling = true
    },
    _endScale: function () {
      this._isScaling = false
      this._updateOldScale(this._scale)
    },
    _setScale: function (scale) {
      if (this.scale) {
        scale = this._oldScale * scale
        this._beginScale()
        this._updateScale(scale)
      }
    },
    _updateScale: function (scale, animat) {
      var self = this
      if (this.scale) {
        scale = this._adjustScale(scale)
        this._updateWH(scale)
        this._updateBoundary()
        const limitXY = this._getLimitXY(this._translateX, this._translateY)
        const x = limitXY.x
        const y = limitXY.y
        if (animat) {
          this._animationTo(x, y, scale, '', true, true)
        } else {
          _requestAnimationFrame(function () {
            self._setTransform(x, y, scale, '', true, true)
          })
        }
      }
    },
    _updateOldScale: function (scale) {
      this._oldScale = scale
    },
    _adjustScale: function (scale) {
      scale = Math.max(0.5, this.scaleMinNumber, scale)
      scale = Math.min(10, this.scaleMaxNumber, scale)
      return scale
    },
    _animationTo: function (x, y, scale, source, r, o) {
      var self = this
      if (this._FA) {
        this._FA.cancel()
      }
      if (this._SFA) {
        this._SFA.cancel()
      }
      if (!this.xMove) {
        x = this._translateX
      }
      if (!this.yMove) {
        y = this._translateY
      }
      if (!this.scale) {
        scale = this._scale
      }
      var limitXY = this._getLimitXY(x, y)
      x = limitXY.x
      y = limitXY.y
      if (!this.animation) {
        this._setTransform(x, y, scale, source, r, o)
        return
      }
      this._STD._springX._solution = null
      this._STD._springY._solution = null
      this._STD._springScale._solution = null
      this._STD._springX._endPosition = this._translateX
      this._STD._springY._endPosition = this._translateY
      this._STD._springScale._endPosition = this._scale
      this._STD.setEnd(x, y, scale, 1)
      this._SFA = g(this._STD, function () {
        var data = self._STD.x()
        var x = data.x
        var y = data.y
        var scale = data.scale
        self._setTransform(x, y, scale, source, r, o)
      }, function () {
        self._SFA.cancel()
      })
    },
    _revise: function (source) {
      var limitXY = this._getLimitXY(this._translateX, this._translateY)
      var x = limitXY.x
      var y = limitXY.y
      var outOfBounds = limitXY.outOfBounds
      if (outOfBounds) {
        this._animationTo(x, y, this._scale, source)
      }
      return outOfBounds
    },
    _setTransform: function (x, y, scale, source = '', r, o) {
      if (!(x !== null && x.toString() !== 'NaN' && typeof x === 'number')) {
        x = this._translateX || 0
      }
      if (!(y !== null && y.toString() !== 'NaN' && typeof y === 'number')) {
        y = this._translateY || 0
      }
      x = Number(x.toFixed(1))
      y = Number(y.toFixed(1))
      scale = Number(scale.toFixed(1))
      if (!(this._translateX === x && this._translateY === y)) {
        if (!r) {
          this.$trigger('change', {}, {
            x: v(x, this._scaleOffset.x),
            y: v(y, this._scaleOffset.y),
            source: source
          })
        }
      }
      if (!this.scale) {
        scale = this._scale
      }
      scale = this._adjustScale(scale)
      scale = +scale.toFixed(3)
      if (o && scale !== this._scale) {
        this.$trigger('scale', {}, {
          x: x,
          y: y,
          scale: scale
        })
      }
      var transform = 'translateX(' + x + 'px) translateY(' + y + 'px) translateZ(0px) scale(' + scale + ')'
      this.$el.style.transform = transform
      this.$el.style.webkitTransform = transform
      this._translateX = x
      this._translateY = y
      this._scale = scale
    }
  }
}
</script>
<style>
uni-movable-view {
  display: inline-block;
  width: 10px;
  height: 10px;
  top: 0px;
  left: 0px;
  position: absolute;
  cursor: grab;
}

uni-movable-view[hidden] {
  display: none;
}
</style>


Updated California call script

Updated California call script

<template>

      Calling you now...

Introduce yourself, be polite, and say:

{{ callScript }}

      If lines are busy, we may call you in a few minutes.

Done calling? Do these things, too!

        Join an event near you

        <facebook-button @clicked="$trackEvent('call_script_facebook_button', 'click')">Share on Facebook</facebook-button>
        <twitter-button @clicked="$trackEvent('call_script_twitter_button', 'click')">Share on Twitter</twitter-button>
        <donate-button @clicked="$trackEvent('call_script_donate_button', 'click')">Donate</donate-button>

</template>


docs: fix dynamic tooltip text to show bottom instead of top (#2013)

<template>

    <md-avatar>

      <md-tooltip :md-active.sync="tooltipActive">Bottom</md-tooltip>
    </md-avatar>

    <md-button class="md-raised md-primary" @click="tooltipActive = !tooltipActive">Toggle Tooltip</md-button>

</template>

<template>

    <md-avatar>

      <md-tooltip :md-active.sync="tooltipActive">Top</md-tooltip>
    </md-avatar>

    <md-button class="md-raised md-primary" @click="tooltipActive = !tooltipActive">Toggle Tooltip</md-button>

</template>


Added navigation when user clicks on number.

<template>

 selectedStep}">
        {{number}}

{{name}}

</template>