Corrigindo pequeno problema na petição inicial

<template>

Confirmação de Recebimento de Petição Inicial

          Erro! {{errormsg}}

          Atenção! {{warningmsg}}

    <template v-if="!editando">

            Petição: {{protocolo}}

            Processo: {{numeroFormatado}}

            Data de Entrada:

    </template>

    <template>

          <vue-clip ref="clip" :options="vueclipOptions" :on-added-file="addedFileProxy" :on-complete="completeProxy">
            <template slot="clip-uploader-action">

                  Arraste os arquivos que compoem a petição inicial e solte eles nesta área, ou clique aqui para selecioná-los.
Use o nome dos PDFs para ordenar as peças, por exemplo: 01-Termo.pdf, 02-Identidade.pdf, etc.

Cada PDF está limitado a 5MB.

            </template>

            <template slot="clip-uploader-body" scope="props">

                  Arquivos inválidos! Não foi possível aceitar os seguintes arquivos:

{{ifile.name}} ({{ifile.errorMessage}})

            </template>
          </vue-clip>

É necessário fornecer os PDFs para prosseguir

 0">

Arquivos

          <button type="button" @click="adicionarArquivo()" class="btn btn-info btn-sm mt-2 float-right">Adicionar Arquivo
          </button>

 0">

                Tipo
                Descrição
                Arquivo
                Status

                {{localizar(f.tipo, tipospeca)}}

                  <select class="form-control mr-sm-2" v-model="f.tipo" :disabled="f.protocolado" @change="selecionarTipo(f, f.tipo)" :name="'tipopeca[' + index +']'" :class="{ 'is-invalid': errors.has('tipopeca[' + index +']') }" v-validate="'required'">
                    <option v-for="tipo in tipospeca" :value="tipo.id">{{tipo.nome}}</option>
                    <option disabled hidden selected value=""> [Selecionar]</option>
                  </select>

                  {{f.nome}}

                    <input type="text" class="form-control" style="min-width: 16em;" placeholder="Descrição" v-model="f.nome" @input="alterarArquivo(f)" :disabled="f.bloq || f.protocolado">

                    {{f.nome}}

                  <span v-show="f.file.progress && f.file.progress < 100">{{f.file.progress.toFixed(1)}}%
                  {{f.status}}
                  {{f.errormsg}}
                  {{f.$error}} {{f.$errorParam}}

É necessário informar o teor da petição

Dados Básicos

            <my-select name="orgao" label="Órgão" v-model="orgao" :list="orgaos" @change="selecionarOrgao" :edit="editando" v-validate="'required'" :error="errors.first('orgao')"></my-select>

            <my-select :disabled="localidades.length == 0" name="localidade" label="Localidade" v-model="localidade" :list="localidades" @change="selecionarLocalidade" :edit="editando" v-validate="'required'" :error="errors.first('localidade')"></my-select>

            <my-select :disabled="especialidades.length == 0" name="especialidade" label="Especialidade" v-model="especialidade" :list="especialidades" @change="selecionarEspecialidade" :edit="editando" v-validate="'required'" :error="errors.first('especialidade')"></my-select>

            <my-select :disabled="classes.length == 0" name="classe" label="Classe" v-model="classe" :list="classes" @change="selecionarClasse" :edit="editando" v-validate="'required'" :error="errors.first('classe')"></my-select>

            <my-input :disabled="!classe" name="valorcausa" label="Valor da Causa (R$)" v-model="valorcausa" :edit="editando" placeholder="0,00" mask="money" v-validate="valordacausaobrigatorio ? 'required|min:5' : ''" :error="errors.first('valorcausa')"></my-input>

            CDA

{{cda}}

            <input v-if="editando" type="text" class="form-control" :class="{ 'is-invalid': errors.has('cda') }" v-model="cda" name="cda" placeholder="" />
            Se houver mais de uma, separar com vírgulas.

            Processo Administrativo

{{pa}}

            <input v-if="editando" type="text" class="form-control" :class="{ 'is-invalid': errors.has('pa') }" v-model="pa" name="pa" placeholder="" />
            Se houver mais de um, separar com vírgulas.

             <input :disabled="!editando" type="checkbox" class="form-check-input" v-model="nivelsigilo"> Segredo de Justiça

             <input :disabled="!editando" type="checkbox" class="form-check-input" v-model="justicagratuita"> Justiça Gratuita

             <input :disabled="!editando" type="checkbox" class="form-check-input" v-model="tutelaantecipada"> Tutela Liminar/Antecipada

             <input :disabled="!editando" type="checkbox" class="form-check-input" v-model="prioridadeidoso"> Prioridade de Idoso

Partes

          <button type="button" @click="adicionarParte()" class="btn btn-info btn-sm mt-2">Adicionar Parte</button>

                Polo
                Tipo
                Documento
                Nome

                {{localizar(p.polo, polos)}}

                  <select class="form-control mr-sm-2" v-model="p.polo" @change="selecionarPolo(p, p.polo)">
                    <option disabled hidden selected value="">[Selecionar]</option>
                    <option v-for="polo in polos" :value="polo.id">{{poloNome(polo.nome)}}</option>
                  </select>

                {{localizar(p.tipopessoa, tipospessoa)}}

                  <select class="form-control mr-sm-2" v-model="p.tipopessoa" @change="selecionarTipoPessoa(p, p.tipopessoa)">
                    <option disabled hidden selected value="">[Selecionar]</option>
                    <option v-for="tipopessoa in tipospessoa" :value="tipopessoa.id">{{tipopessoa.nome}}</option>
                  </select>

                {{p.documento}}

                  <my-input v-if="p.tipopessoa == '1'" :disabled="!orgao" :name="'documento[' + index +']'" v-model="p.documento" :edit="editando" placeholder="CPF" mask="999.999.999-99" @change="alterouCpf(p)" v-validate="'required|cpf'" :error="errors.first('documento[' + index +']')"></my-input>

                  <input type="text" :disabled="!orgao" class="form-control mr-sm-2" :class="{ 'is-invalid': errors.has('documento[' + index +']') }" v-model="p.documento" :name="'documento[' + index +']'" placeholder="CNPJ" v-if="p.tipopessoa == '2'" v-validate="'required|cnpj'" v-mask="'99.999.999/9999-99'" @change="alterouCnpj(p)" />

                  <select :disabled="!orgao" class="form-control mr-sm-2" :class="{ 'is-invalid': errors.has('documento[' + index +']') }" v-model="p.documento" :name="'documento[' + index +']'" placeholder="Entidade" v-if="p.tipopessoa == '3'" v-validate="'required'" @change="alterouEntidade(p)">
                    <option disabled selected hidden :value="undefined">[Selecionar]</option>
                    <option v-for="l in entidadesFiltradas" :value="l.documento">{{l.nome}}</option>
                  </select>

                  <input type="text" :disabled="!orgao" class="form-control mr-sm-2" :class="{ 'is-invalid': errors.has('documento[' + index +']') }" v-model="p.documento" :name="'documento[' + index +']'" placeholder="OAB" v-if="p.tipopessoa == '4'" v-validate="'required|oab'" v-mask="'AA999999'" @change="alterouOab(p)" v-on:blur="fixOab(p)"/>

                {{p.nome}}

                  <my-input :disabled="!orgao" :name="'nome[' + index +']'" v-model="p.nome" :edit="editando" placeholder="Nome Completo" v-validate="'required'" :error="errors.first('nome[' + index +']')"></my-input>

          <button type="button " @click="peticionar()" id="prosseguir" :disabled="arquivos.length==0 || faltaTeorDaPeticao || !isAllValid() || errors.any()" class="btn btn-primary float-right d-print-none">Protocolar</button>

        <template v-if="!editando">

            <button type="button " @click="limpar()" id="voltar" class="btn btn-secondary d-print-none">Enviar Outra Petição</button>

                <input type="checkbox" v-model="manterCampos">
                Manter preenchimento anterior

            <button type="button" @click="imprimir()" id="imprimir" class="btn btn-info float-right ml-3">Imprimir</button>

        </template>

    </template>

</template>


devop: 🔧 get correct rewards balance

devop: 🔧 get correct rewards balance

<template>

          Rewards - RETH2

{{ rethBalance }}

${{ rethBalanceFiat }}

      You have not earned any rewards yet. Please wait 24 hours after staking to
      start earning rewards.

      <mew-button
        title="Redeem to ETH"
        btn-style="transparent"
        btn-size="small"
        class="mew-body"
        @click.native="routeToSwap"
      />
      <mew-button
        title="Compound"
        :btn-style="compoundRewards ? 'transparent' : 'background'"
        btn-size="small"
        class="mew-body"
        @click.native="scrollToInput"
      />

</template>

<template>

          Rewards - RETH2

{{ rethBalance }}

${{ rethUsdBalance }}

      You have not earned any rewards yet. Please wait 24 hours after staking to
      start earning rewards.

      <mew-button
        title="Redeem to ETH"
        btn-style="transparent"
        btn-size="small"
        class="mew-body"
        @click.native="routeToSwap"
      />
      <mew-button
        title="Compound"
        :btn-style="compoundRewards ? 'transparent' : 'background'"
        btn-size="small"
        class="mew-body"
        @click.native="scrollToInput"
      />

</template>


Amend stats help text

Amend stats help text

<template>

            Total ÐApps

{{ statDappCount.toLocaleString() }}

            Daily active users
            <Help text="Unique source addresses in transactions to ÐApp contracts (DAU)"/>

{{ statDappDau | abbreviateNumber(2) || 0 }}

            24 hr transactions
            <Help text="Number of transactions to ÐApp contracts"/>

{{ statDappTx24Hr | abbreviateNumber(2) || 0 }}

            24 hr volume (ETH)
            <Help text="Transaction volume to ÐApp contracts"/>

{{ statDappVol24Hr | abbreviateNumber(2) || 0 }}

Smart contracts

{{ statDappContractCount | abbreviateNumber(2) || 0 }}

ÐApps by Category

      <StatsBarChart/>

</template>

<template>

            Total ÐApps

{{ statDappCount.toLocaleString() }}

            Daily active users
            <Help text="Daily Active Users (unique source addresses in transactions to ÐApp contracts)"/>

{{ statDappDau | abbreviateNumber(2) || 0 }}

            24 hr transactions
            <Help text="Number of transactions to ÐApp contracts"/>

{{ statDappTx24Hr | abbreviateNumber(2) || 0 }}

            24 hr volume (ETH)
            <Help text="Transaction volume to ÐApp contracts"/>

{{ statDappVol24Hr | abbreviateNumber(2) || 0 }}

Smart contracts

{{ statDappContractCount | abbreviateNumber(2) || 0 }}

ÐApps by Category

      <StatsBarChart/>

</template>


remove inspect from uploader as it can't be parallelized

<template>

Inspect data: {{_id}}

Selecting more than one mode won't affect much the performance, running time will roughly be the same.

                <select id="select-mode" class="ui dropdown" multiple="">

                    <option selected>mapping</option>

                </select>

mapping

Analyzes data so the inspection results can be converted into an ElasticSearch mapping (used during indexing step)

                <select id="select-data_provider" class="ui dropdown">
                    <option value="src">Data Collection</option>

                </select>

Data Collection

Data is fetched from collection (stored data)

                Cancel

                OK

</template>

<template>

Inspect data: {{_id}}

Selecting more than one mode won't affect much the performance, running time will roughly be the same.

                <select id="select-mode" class="ui dropdown" multiple="">

                    <option selected>mapping</option>

                </select>

mapping

Analyzes data so the inspection results can be converted into an ElasticSearch mapping (used during indexing step)

                <select id="select-data_provider" class="ui dropdown">
                    <option value="src">Data Collection</option>
                    <option value="uploader">Uploader</option>
                </select>

Data Collection

Data is fetched from collection (stored data)

Uploader

Data is fetched directly from the parser, before storage into a collection

                Cancel

                OK

</template>


devop: 🔧 check if tokens are undefined

devop: 🔧 check if tokens are undefined

<template>

    <v-container class="pl-4 pr-4 d-flex align-center pt-8">
      <v-row align="center" no-gutters>
        <v-col class="d-md-none" cols="2" md="4">
          <the-default-mobile-navigation class="ml-n2" />
        </v-col>
        <v-col cols="8" md="8" class="d-flex align-center">
          <v-img
            :class="$vuetify.breakpoint.smAndDown ? 'mx-auto' : ''"
            class="cursor--pointer mr-md-14"
            src="@/assets/images/icons/logo-mew.svg"
            max-height="36"
            max-width="130"
            @click="$router.push({ name: ROUTES_HOME.HOME.NAME })"
          />

            <router-link
              class="white--text text-decoration--none menu-item"
              :to="{ name: ROUTES_HOME.HOW_IT_WORKS.NAME }"
            >
              {{ $t('header.what-is-mew') }}
            </router-link>

              <mew-menu
                top-arrow
                activator-text-color="white--text"
                :list-obj="menuObj"
                @goToPage="routeTo"
              />

              {{ $t('header.buy-eth') }}

        </v-col>
        <v-col cols="2" md="4" class="d-flex justify-end">
          <mew-tools class="ml-auto" />
        </v-col>
      </v-row>
    </v-container>

</template>


refactor - unused elements

refactor - unused elements

<template>
  <div class="image" v-on="$listeners">
    <img
      v-show="showPlaceholder"
      src="/assets/placeholder.svg"
      :alt="alt"
      key="placeholder"
      ref="images"
      itemprop="image"
      class="image__thumb image__thumb--placeholder">
    <img
      v-if="!lowerQualityImageError || isOnline"
      v-show="showLowerQuality"
      :src="image.loading"
      :alt="alt"
      @load="imageLoaded('lower', true)"
      @error="imageLoaded('lower', false)"
      key="lowerQualityImage"
      ref="images"
      itemprop="image"
      class="image__thumb">
    <img
      v-if="!highQualityImageError || isOnline"
      v-show="showHighQuality"
      :src="image.src"
      :alt="alt"
      @load="imageLoaded('high', true)"
      @error="imageLoaded('high', false)"
      key="highQualityImage"
      ref="images"
      itemprop="image"
      class="image__thumb">
  </div>
</template>

<script>
import { onlineHelper } from '@vue-storefront/core/helpers'

export default {
  props: {
    image: {
      type: Object,
      default: () => ({
        src: '',
        loading: ''
      })
    },
    alt: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      lowerQualityImage: false,
      lowerQualityImageError: false,
      highQualityImage: false,
      highQualityImageError: false
    }
  },
  computed: {
    showPlaceholder () {
      return !this.showLowerQuality && !this.showHighQuality
    },
    showLowerQuality () {
      return this.lowerQualityImage && !this.showHighQuality
    },
    showHighQuality () {
      return this.highQualityImage
    },
    isOnline (value) {
      return onlineHelper.isOnline
    }
  },
  methods: {
    imageLoaded (type, success = true) {
      this[`${type}QualityImage`] = success
      this[`${type}QualityImageError`] = !success
    }
  }
}
</script>

<style lang="scss" scoped>
  .image{
    position: relative;
    width: 100%;
    height: 0;
    padding-bottom: calc(740% / (600 / 100));
    overflow: hidden;
    mix-blend-mode: multiply;
    &__thumb{
      max-width: 100%;
      height: auto;
      position: absolute;
      top: 50%;
      left: 50%;
      width: auto;
      height: 100%;
      transform: translate3d(-50%, -50%, 0);
      &--placeholder{
        width: auto;
        height: auto;
      }
    }
  }
</style>

<template>
  <div class="image" v-on="$listeners">
    <img
      v-show="showPlaceholder"
      src="/assets/placeholder.svg"
      :alt="alt"
      key="placeholder"
      ref="images"
      itemprop="image"
      class="image__thumb image__thumb--placeholder">
    <img
      v-if="!lowerQualityImageError || isOnline"
      v-show="showLowerQuality"
      :src="image.loading"
      :alt="alt"
      @load="imageLoaded('lower', true)"
      @error="imageLoaded('lower', false)"
      key="lowerQualityImage"
      ref="images"
      itemprop="image"
      class="image__thumb">
    <img
      v-if="!highQualityImageError || isOnline"
      v-show="showHighQuality"
      :src="image.src"
      :alt="alt"
      @load="imageLoaded('high', true)"
      @error="imageLoaded('high', false)"
      key="highQualityImage"
      ref="images"
      itemprop="image"
      class="image__thumb">
  </div>
</template>

<script>
import { onlineHelper } from '@vue-storefront/core/helpers'
import { isOnline } from '@vue-storefront/core/lib/search';
export default {
  props: {
    image: {
      type: Object,
      default: () => ({
        src: '',
        loading: ''
      })
    },
    alt: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      lowerQualityImage: false,
      lowerQualityImageError: false,
      highQualityImage: false,
      highQualityImageError: false
    }
  },
  watch: {
    isOnline (online){}
  },
  computed: {
    showPlaceholder () {
      return !this.showLowerQuality && !this.showHighQuality
    },
    showLowerQuality () {
      return this.lowerQualityImage && !this.showHighQuality
    },
    showHighQuality () {
      return this.highQualityImage
    },
    isOnline (value) {
      return onlineHelper.isOnline
    }
  },
  methods: {
    imageLoaded (type,success = true) {
      this[`${type}QualityImage`] = success
      this[`${type}QualityImageError`] = !success
    }
  }
}
</script>

<style lang="scss" scoped>
  .image{
    position: relative;
    width: 100%;
    height: 0;
    padding-bottom: calc(740% / (600 / 100));
    overflow: hidden;
    mix-blend-mode: multiply;
    &__thumb{
      max-width: 100%;
      height: auto;
      position: absolute;
      top: 50%;
      left: 50%;
      width: auto;
      height: 100%;
      transform: translate3d(-50%, -50%, 0);
      &--placeholder{
        width: auto;
        height: auto;
      }
    }
  }
</style>


Added local view for gallery elements.

Added local view for gallery elements.

<template>

        <spinner :loading="loading" text='Loading...' v-if="loading"/>

                <span v-if="otherArgs == null && results.length <= 1">{{results.length}} result (0.59 seconds)
                = 2">About {{results.length}} results (0.59 seconds)
                Home >  {{ otherArgs }} > About {{results.length}} results (0.59 seconds)
                No result (0.59 seconds)

                <md-card v-for="(result, index) in newResults" :key="index" class="btn--animated">

                    <md-card-media-cover md-solid @click.native.prevent="navigate(result)" >
                        <md-card-media md-ratio="4:3" >

                        </md-card-media>
                        <md-card-area class="utility-gridbg">
                            <md-card-header class="utility-show_hide">

                                    {{ result.label }}

                                {{ reduceDescription(result.description) }}
                            </md-card-header>
                        </md-card-area>
                    </md-card-media-cover>
                </md-card>

            <pagination v-if="results.length > basePage" :cpage="currentPage" :tpages="totalPages" />

</template>


bullet and ordered lists

bullet and ordered lists

<script lang="coffee">
import Records from '@/shared/services/records'
import Session from '@/shared/services/session'
import AppConfig from '@/shared/services/app_config'
import FileUploader from '@/shared/services/file_uploader'
import FilesList from './files_list.vue'
import EventBus from '@/shared/services/event_bus'
import I18n from '@/i18n'
import { convertToMd } from '@/shared/services/format_converter'

# import { Editor, EditorContent, EditorMenuBar } from 'tiptap'

import Blockquote from '@tiptap/extension-blockquote'
import Bold from '@tiptap/extension-bold'
import BulletList from '@tiptap/extension-bullet-list'
import CodeBlock from '@tiptap/extension-code-block'
import Code from '@tiptap/extension-code'
import CharacterCount from '@tiptap/extension-character-count'
import Document from '@tiptap/extension-document'
import Dropcursor from '@tiptap/extension-dropcursor'
import GapCursor from '@tiptap/extension-gapcursor'
import HardBreak from '@tiptap/extension-hard-break'
import Heading from '@tiptap/extension-heading'
import Highlight from '@tiptap/extension-highlight'
import History from '@tiptap/extension-history'
import HorizontalRule from '@tiptap/extension-horizontal-rule'
import Image from '@tiptap/extension-image'
import Italic from '@tiptap/extension-italic'
import Link from '@tiptap/extension-link'
import ListItem from '@tiptap/extension-list-item'
import Mention from '@tiptap/extension-mention'
import OrderedList from '@tiptap/extension-ordered-list'
import Paragraph from '@tiptap/extension-paragraph'
import Placeholder from '@tiptap/extension-placeholder'
import Strike from '@tiptap/extension-strike'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import Table from '@tiptap/extension-table'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import Text from '@tiptap/extension-text'
import Typography from '@tiptap/extension-typography'
import Underline from '@tiptap/extension-underline'

import { Editor, EditorContent } from '@tiptap/vue-2'

# import Collaboration from '@/shared/tiptap_extentions/collaboration.js'

# import ForeColor from '@/shared/tiptap_extentions/fore_color'
# import BackColor from '@/shared/tiptap_extentions/back_color'
# import FormatClear from '@/shared/tiptap_extentions/format_clear'
# import Align from '@/shared/tiptap_extentions/align'
# import Alignment from '@/shared/tiptap_extentions/alignment'
# import Paragraph from '@/shared/tiptap_extentions/paragraph'
# import Heading from '@/shared/tiptap_extentions/heading'
# import Iframe from './iframe'
# import TodoItem from './todo_item'

# import { insertText } from 'tiptap-commands'
# import Image from '@/shared/tiptap_extentions/image.js'

# import {getEmbedLink} from '@/shared/helpers/embed_link.coffee'

# import { CommonMentioning, HtmlMentioning, MentionPluginConfig } from './mentioning.coffee'
# import SuggestionList from './suggestion_list'
# import Attaching from './attaching.coffee'
import {compact, uniq, throttle, difference, reject, uniqBy} from 'lodash'
import TextHighlightBtn from './text_highlight_btn'
import TextAlignBtn from './text_align_btn'

# import io from 'socket.io-client'

export default
  # mixins: [CommonMentioning, HtmlMentioning, Attaching]
  props:
    model: Object
    field: String
    label: String
    placeholder: String
    maxLength: Number
    shouldReset: Boolean
    autofocus: Boolean

  components: {
    EditorContent
    TextAlignBtn
    TextHighlightBtn
  }
    # EditorMenuBar: EditorMenuBar
    # FilesList: FilesList
    # SuggestionList: SuggestionList
    # TextHighlightBtn: TextHighlightBtn
    # TextAlignBtn: TextAlignBtn

  data: ->
    loading: true
    socket: null
    count: 0
    editor: null
    expanded: true
    closeEmojiMenu: false
    linkUrl: ""
    iframeUrl: ""
    linkDialogIsOpen: false
    iframeDialogIsOpen: false
    fetchedUrls: []

  computed:
    format: ->
      @model["#{@field}Format"]

  mounted: ->
    # @expanded = Session.user().experiences['html-editor.expanded']
    @editor = new Editor
      extensions: [
        Document,
        Paragraph
        Text
        Highlight
        Blockquote
        CodeBlock
        Bold
        Italic
        Strike
        Underline
        Heading
        Highlight.configure(multicolor: true)
        History
        HorizontalRule
        Typography
        BulletList
        OrderedList
        ListItem
        Table
        TableHeader
        TableRow
        TableCell
        TextAlign
      ]
      content: @model[@field]
    # @onInit({doc: null, version: 0})
    # @socket = io(@tiptapAddress())
    #   .on('init', (data) => @onInit(data))
    #   .on('update', (data) =>
    #     @nditor.extensions.options.collaboration.update(data)
    #     @editor.extensions.options.collaboration.updateCursors(data)
    #   )
    #   .on('getCount', (count) => @setCount(count))
    #   .on('cursorupdate', (data) =>
    #     this.editor.extensions.options.collaboration.updateCursors(data)
    #   )

  watch:
    'shouldReset': 'reset'

  methods:
    # onInit: ({doc, version}) ->
    #   @loading = false
    #   @editor.destroy() if @editor
    #
    #   @editor = new Editor
    #     editorProps:
    #       scrollThreshold: 100
    #       scrollMargin: 100
    #     extensions: [
    #       Link(),
    #       new Mention(MentionPluginConfig.bind(@)()),
    #       new Blockquote(),
    #       new BulletList(),
    #       new CodeBlock(),
    #       new HardBreak(),
    #       new Image({attachFile: @attachFile, attachImageFile: @attachImageFile}),
    #       new Heading({ levels: [1, 2, 3] }),
    #       new HorizontalRule(),
    #       new ListItem(),
    #       new OrderedList(),
    #       new TodoItem(),
    #       new TodoList(),
    #       new Table(),
    #       new TableHeader(),
    #       new TableCell(),
    #       new TableRow(),
    #       new Bold(),
    #       new Code(),
    #       new Italic(),
    #       new Strike(),
    #       new Underline(),
    #       new History(),
    #       new Iframe(),
    #       new ForeColor(),
    #       new BackColor(),
    #       new FormatClear(),
    #       new Align(),
    #       new Alignment(),
    #       new Paragraph(),
    #       new Placeholder({
    #         emptyClass: 'is-empty',
    #         emptyNodeText: @placeholder,
    #         showOnlyWhenEditable: true,
    #       }),
    #       # new Collaboration({
    #       #   socket: @socket,
    #       #   user: Session.user()
    #       #   version: version
    #       #   debounce: 250
    #       # })
    #     ]
    #     content: @model[@field] # doc
    #     onUpdate: @updateModel
    #     autoFocus: @autofocus
      #
      # @editor.setContent(@model[@field]) if version == 0
      #
      # setTimeout =>
      #   if @$refs.editor && @$refs.editor.$el
      #     @$refs.editor.$el.children[0].setAttribute("role", "textbox")
      #     @$refs.editor.$el.children[0].setAttribute("aria-label", @placeholder) if @placeholder

    setCount: (count) ->
      @count = count

    tiptapAddress: ->
      if @model.isNew()
        compact([AppConfig.theme.channels_uri, 'tiptap', @model.constructor.singular, 'new', @model.groupId, @model.discussionId, @model.parentId, Session.user().secretToken]).join('/')
      else
        [AppConfig.theme.channels_uri, 'tiptap', @model.constructor.singular, @model.id, (@model.secretToken || Session.user().secretToken)].join('/')

    selectedText: ->
      { selection, state } = @editor
      { from, to } = selection
      state.doc.textBetween(from, to, ' ')

    reset: ->
      @editor.clearContent()
      @resetFiles()

    convertToMd: ->
      if confirm I18n.t('formatting.markdown_confirm')
        convertToMd(@model, @field)
        Records.users.saveExperience('html-editor.uses-markdown')

    toggleExpanded: ->
      if !@expanded
        @expanded = true
        Records.users.saveExperience('html-editor.expanded')
      else
        @expanded = false
        Records.users.removeExperience('html-editor.expanded')

    setLinkUrl: (command) ->
      if @linkUrl
        @linkUrl = "http://".concat(@linkUrl) unless @linkUrl.includes("://")
        command({ href: @linkUrl })
        @fetchLinkPreviews([@linkUrl])
        @linkUrl = null
      @linkDialogIsOpen = false
      @editor.focus()

    setIframeUrl: (command) ->
      command({ src: getEmbedLink(@iframeUrl) })
      @iframeUrl = null
      @iframeDialogIsOpen = false
      @editor.focus()

    emojiPicked: (shortcode, unicode) ->
      { view } = this.editor
      insertText(unicode)(view.state, view.dispatch, view)
      @closeEmojiMenu = false
      @editor.focus()

    updateModel: ->
      @model[@field] = @editor.getHTML()
      @updateFiles()
      @scrapeLinkPreviews() if @model.isNew()

    removeLinkPreview: (url) ->
      @model.linkPreviews = reject(@model.linkPreviews, (p) -> p.url == url)

    scrapeLinkPreviews: throttle ->
      parser = new DOMParser()
      doc = parser.parseFromString(@model[@field], 'text/html')
      @fetchLinkPreviews difference((Array.from(doc.querySelectorAll('a')).map (el) => el.href), @fetchedUrls)
    ,
      500

    fetchLinkPreviews: (urls) ->
      if urls.length
        Records.remote.post('link_previews', {urls: urls}).then (data) =>
          @model.linkPreviews = uniqBy(@model.linkPreviews.concat(data.previews), 'url')
      @fetchedUrls = uniq @fetchedUrls.concat(urls)

  beforeDestroy: ->
    # @editor.destroy() if @editor
    # @socket.close() if @socket

</script>

<template lang="pug">
div
  //- template(v-if="!editor || loading")
  //-   | Connecting to socket server …
  .editor.mb-3(v-if="editor")
    editor-content.html-editor__textarea(ref="editor" :editor='editor').lmo-markdown-wrapper
    .menubar
      div
        v-layout(align-center v-if="editor.isActive('table')")
          v-btn(icon @click="editor.chain().deleteTable().focus().run()" :title="$t('formatting.remove_table')")
            v-icon mdi-table-remove
          v-btn(icon @click="editor.chain().addColumnBefore().focus().run()" :title="$t('formatting.add_column_before')")
            v-icon mdi-table-column-plus-before
          v-btn(icon @click="editor.chain().addColumnAfter().focus().run()" :title="$t('formatting.add_column_after')")
            v-icon mdi-table-column-plus-after
          v-btn(icon @click="editor.chain().deleteColumn().focus().run()" :title="$t('formatting.remove_column')")
            v-icon mdi-table-column-remove
          v-btn(icon @click="editor.chain().addRowBefore().focus().run()" :title="$t('formatting.add_row_before')")
            v-icon mdi-table-row-plus-before
          v-btn(icon @click="editor.chain().addRowAfter().focus().run()" :title="$t('formatting.add_row_after')")
            v-icon mdi-table-row-plus-after
          v-btn(icon @click="editor.chain().deleteRow().focus().run()" :title="$t('formatting.remove_row')")
            v-icon mdi-table-row-remove
          v-btn(icon @click="editor.chain().mergeOrSplit().focus().run()" :title="$t('formatting.merge_selected')")
            v-icon mdi-table-merge-cells

        v-layout.py-2.justify-space-between.flex-wrap(align-center)
          section.d-flex.flex-wrap(:aria-label="$t('formatting.formatting_tools')")
            //- attach
            //- v-btn(icon @click='$refs.filesField.click()' :title="$t('formatting.attach')")
            //-   v-icon mdi-paperclip

            //- v-btn(icon @click='$refs.imageFilesField.click()' :title="$t('formatting.insert_image')")
            //-   v-icon mdi-image

            //- link
            //- v-menu(:close-on-content-click="!selectedText()" v-model="linkDialogIsOpen" min-width="320px")
            //-   template(v-slot:activator="{on, attrs}")
            //-     v-btn(icon v-on="on" v-bind="attrs" :title="$t('formatting.link')")
            //-       v-icon mdi-link
            //-   v-card
            //-     template(v-if="selectedText()")
            //-       v-card-title.title(v-t="'text_editor.insert_link'")
            //-       v-card-text
            //-         v-text-field(type="url" label="https://www.example.com" v-model="linkUrl" autofocus ref="focus" v-on:keyup.enter="setLinkUrl(commands.link)")
            //-       v-card-actions
            //-         v-spacer
            //-         v-btn(color="primary" @click="setLinkUrl(commands.link)" v-t="'common.action.apply'")
            //-     template(v-else)
            //-       v-card-title(v-t="'text_editor.select_text_to_link'")

            //- emoji
            //- v-menu(:close-on-content-click="false" v-model="closeEmojiMenu")
            //-   template(v-slot:activator="{on, attrs}")
            //-     v-btn.emoji-picker__toggle(v-on="on" v-bind="attrs" icon  :title="$t('formatting.insert_emoji')")
            //-       v-icon mdi-emoticon-outline
            //-   emoji-picker(:insert="emojiPicked")

            //- headings menu
            //- v-menu
            //-   template(v-slot:activator="{ on, attrs }")
            //-     v-btn.drop-down-button(icon v-on="on" v-bind="attrs" :title="$t('formatting.heading_size')")
            //-       v-icon mdi-format-size
            //-       v-icon.menu-down-arrow mdi-menu-down
            //-   v-list(dense)
            //-     template(v-for="i in 3")
            //-       v-list-item(@click='commands.heading({ level: i })' :class="{ 'v-list-item--active': isActive.heading({level: i}) }")
            //-         v-list-item-icon
            //-           v-icon {{'mdi-format-header-'+i}}
            //-         v-list-item-title(v-t="'formatting.heading'+i")
            //-     v-list-item(@click='commands.paragraph()' :class="{ 'v-list-item--active': isActive.paragraph() }")
            //-       v-list-item-icon
            //-         v-icon mdi-format-pilcrow
            //-       v-list-item-title(v-t="'formatting.paragraph'")
            //-
            template(v-if="expanded")
              v-btn(icon @click='editor.chain().focus().setParagraph().run()' :outlined="editor.isActive('paragraph')" :title="$t('formatting.paragraph')")
                v-icon mdi-format-pilcrow
              template(v-for="i in 3")
                v-btn(icon @click='editor.chain().focus().toggleHeading({ level: i }).run()' :outlined="editor.isActive('heading', { level: i })" :title="$t('formatting.heading'+i)")
                  v-icon {{'mdi-format-header-'+i}}

            //- bold

            v-btn(icon v-if="expanded" @click='editor.chain().toggleBold().focus().run()' :outlined="editor.isActive('bold')" :title="$t('formatting.bold')")
              v-icon mdi-format-bold

            //- italic
            v-btn(icon v-if="expanded" @click='editor.chain().toggleItalic().focus().run()' :outlined="editor.isActive('italic')" :title="$t('formatting.italicize')")
              v-icon mdi-format-italic
            //-
            //- //- strikethrough
            v-btn(icon v-if="expanded" @click='editor.chain().toggleStrike().focus().run()' :outlined="editor.isActive('strike')"  :title="$t('formatting.strikethrough')")
              v-icon mdi-format-strikethrough
            //- //- underline
            v-btn(icon v-if="expanded" @click='editor.chain().toggleUnderline().focus().run()' :outlined="editor.isActive('underline')",  :title="$t('formatting.underline')")
              v-icon mdi-format-underline
            //-
            text-highlight-btn(v-if="expanded" :editor="editor")
            text-align-btn(v-if="expanded" :editor="editor")

            //- list menu (always a menu)
            v-menu(v-if="expanded")
              template(v-slot:activator="{ on, attrs }")
                v-btn.drop-down-button(
                    icon
                    v-on="on"
                    v-bind="attrs"
                    :title="$t('formatting.lists')")
                  v-icon mdi-format-list-bulleted
                  v-icon.menu-down-arrow mdi-menu-down
              v-list(dense)
                //- v-list-item(@click='commands.todo_list')
                //-   v-list-item-icon
                //-     v-icon mdi-format-list-checks
                //-   v-list-item-title(v-t="'formatting.check_list'")
                v-list-item(@click='editor.chain().toggleBulletList().focus().run()')
                  v-list-item-icon
                    v-icon mdi-format-list-bulleted
                  v-list-item-title(v-t="'formatting.bullet_list'")
                v-list-item(@click='editor.chain().toggleOrderedList().focus().run()')
                  v-list-item-icon
                    v-icon mdi-format-list-numbered
                  v-list-item-title(v-t="'formatting.number_list'")

            //- extra text marks
            template(v-if="expanded")
              //- strikethrough
              //- v-menu(:close-on-content-click="false" v-model="iframeDialogIsOpen" min-width="320px")
              //-   template(v-slot:activator="{on}")
              //-     v-btn(icon v-on="on" :title="$t('formatting.embed')")
              //-       v-icon mdi-youtube
              //-   v-card
              //-     v-card-title.title(v-t="'text_editor.insert_embedded_url'")
              //-     v-card-text
              //-       v-text-field(type="url" label="e.g. https://www.youtube.com/watch?v=Zlzuqsunpxc" v-model="iframeUrl" ref="focus" autofocus v-on:keyup.enter="setIframeUrl(commands.iframe)")
              //-     v-card-actions
              //-       v-spacer
              //-       v-btn(color="primary" @click="setIframeUrl(commands.iframe)" v-t="'common.action.apply'")
              //- blockquote
              v-btn(icon @click='editor.chain().toggleBlockquote().focus().run()' :outlined="editor.isActive('blockquote')"  :title="$t('formatting.blockquote')")
                v-icon mdi-format-quote-close
              //- //- code block
              v-btn(small icon @click='editor.chain().toggleCodeBlock().focus().run()' :outlined="editor.isActive('codeBlock')"  :title="$t('formatting.code_block')")
                v-icon mdi-code-braces
              //- embded
              v-btn(icon @click='editor.chain().setHorizontalRule().focus().run()' :title="$t('formatting.divider')")
                v-icon mdi-minus
              //- table
              v-btn(icon @click='editor.chain().insertTable({rows: 3, cols: 3, withHeaderRow: false }).focus().run()' :title="$t('formatting.add_table')")
                v-icon mdi-table
              //- markdown (save experience)
              //- v-btn(icon @click="convertToMd" :title="$t('formatting.edit_markdown')")
              //-   v-icon mdi-language-markdown-outline
            //-
            //- v-btn.html-editor__expand(v-if="!expanded" icon @click="toggleExpanded" :title="$t('formatting.expand')")
            //-   v-icon mdi-chevron-right
            //-
            //- v-btn.html-editor__expand(v-if="expanded" icon @click="toggleExpanded" :title="$t('formatting.collapse')")
            //-   v-icon mdi-chevron-left
          //- //- save button?
          //- v-spacer
          //- slot(v-if="!expanded" name="actions")
    //- div.d-flex(v-if="expanded" name="actions")
    //-   v-spacer
    //-   slot(name="actions")
    //-
    //- v-alert(v-if="maxLength && model[field] && model[field].length > maxLength" color='error')
    //-   span( v-t="'poll_common.too_long'")

  link-previews(:model="model" :remove="removeLinkPreview")
  //- suggestion-list(:query="query" :loading="fetchingMentions" :mentionable="mentionable" :positionStyles="suggestionListStyles" :navigatedUserIndex="navigatedUserIndex" @select-user="selectUser")
  //- files-list(:files="files" v-on:removeFile="removeFile")

  //- form(style="display: block" @change="fileSelected")
  //-   input(ref="filesField" type="file" name="files" multiple=true)
</template>
<style lang="sass">

// .count
//   display: flex
//   align-items: center
//   font-weight: bold
//   color: rgba(#000, 0.5)
//   color: #27b127
//   margin-bottom: 1rem
//   text-transform: uppercase
//   font-size: 0.7rem
//   line-height: 1
//   &:before
//     content: ''
//     display: inline-flex
//     background-color: #27b127
//     width: 0.4rem
//     height: 0.4rem
//     border-radius: 50%
//     margin-right: 0.3rem

.cursor
  color: #222
  text-align: center
  border-radius: 6px 6px 6px 0px
  padding: 5px
  margin-left: -4.5px
  position: absolute
  z-index: 1
  bottom: 5px
  left: -50%
  opacity: 0.85
  white-space: nowrap
  -webkit-touch-callout: none
  -webkit-user-select: none
  -khtml-user-select: none
  -moz-user-select: none
  -ms-user-select: none
  user-select: none
  &.me
    display: none
    &::after
      display: none
      border-color: inherit
  &.inactive
    opacity: 0.5
    &::after
      opacity: inherit
      border-color: inherit
  &::after
    content: ""
    position: absolute
    top: 100%
    left: 0%
    border-width: 5px
    border-style: solid
    border-color: inherit
    color: transparent

.ProseMirror-widget
  position: absolute
  width: 0.1px
  /*border-style: solid;*/

.bv-row
  padding-top: 20px

.ProseMirror [contenteditable="false"]
  white-space: normal

.ProseMirror [contenteditable="true"]
  white-space: pre-wrap

.ProseMirror
  outline: none

progress
  width: 50%!important
  -webkit-appearance: none
  appearance: none
  background-color: #fff
  border: 1px solid #ccc
progress::-webkit-progress-bar
  background-color: #fff
  border: 1px solid #ccc
progress::-webkit-progress-value
  background-color: lightblue
  border: 0
  transition: width 120ms ease-out, opacity 60ms 60ms ease-in
progress::-moz-progress-bar
  background-color: lightblue
  border: 0
  transition: width 120ms ease-out, opacity 60ms 60ms ease-in

.menubar
  position: sticky
  bottom: 0

.menubar
  .drop-down-button
    width: 40px !important
  .menu-down-arrow
    margin-left: -10px
  .v-btn--icon
    width: 32px
    height: 32px

  .v-btn.v-btn--icon
    min-width: 0
    margin-left: 0
    margin-right: 0
    max-width: 32px
    .v-icon
      font-size: 16px

.html-editor__textarea .ProseMirror
  border-bottom: 1px solid #999
  padding: 4px 0px
  margin: 4px 0px
  outline: none
  overflow-y: scroll
  overflow: visible

.html-editor__textarea .ProseMirror:focus
  border-bottom: 1px solid var(--v-primary-base)

// .lmo-textarea img
//   display: block

ul[data-type="todo_list"]
  padding-left: 0

li[data-type="todo_item"]
  display: flex
  flex-direction: row

.todo-checkbox
  border: 1px solid #999
  height: 1.3em
  width: 1.3em
  box-sizing: border-box
  margin-right: 8px
  margin-top: 0px
  user-select: none
  border-radius: 0.2em
  background-color: transparent
  &:hover
    border: 1px solid var(--v-primary-base)
    // background: #eee

.lmo-textarea .todo-checkbox
  cursor: pointer

.todo-content
  flex: 1
  > p:last-of-type
    margin-bottom: 0
  > ul[data-type="todo_list"]
    margin: .5rem 0
  p
    margin: 0

li[data-done="true"]
  > .todo-content
    > p
      text-decoration: line-through
  > .todo-checkbox::before
    position: relative
    top: -6px
    color: var(--v-accent-base)
    font-size: 1.5rem
    content: "✓"

li[data-done="false"]
  text-decoration: none

.lmo-textarea p.is-empty:first-child::before
  content: attr(data-empty-text)
  float: left
  color: #aaa
  pointer-events: none
  height: 0

.lmo-textarea p.is-empty
  font-size: 16px
  padding-bottom: 16px

input[type="file"]
  display: none
</style>

<script lang="coffee">
import Records from '@/shared/services/records'
import Session from '@/shared/services/session'
import AppConfig from '@/shared/services/app_config'
import FileUploader from '@/shared/services/file_uploader'
import FilesList from './files_list.vue'
import EventBus from '@/shared/services/event_bus'
import I18n from '@/i18n'
import { convertToMd } from '@/shared/services/format_converter'

# import { Editor, EditorContent, EditorMenuBar } from 'tiptap'

import Blockquote from '@tiptap/extension-blockquote'
import Bold from '@tiptap/extension-bold'
import BulletList from '@tiptap/extension-bullet-list'
import CodeBlock from '@tiptap/extension-code-block'
import Code from '@tiptap/extension-code'
import CharacterCount from '@tiptap/extension-character-count'
import Document from '@tiptap/extension-document'
import Dropcursor from '@tiptap/extension-dropcursor'
import GapCursor from '@tiptap/extension-gapcursor'
import HardBreak from '@tiptap/extension-hard-break'
import Heading from '@tiptap/extension-heading'
import Highlight from '@tiptap/extension-highlight'
import History from '@tiptap/extension-history'
import HorizontalRule from '@tiptap/extension-horizontal-rule'
import Image from '@tiptap/extension-image'
import Italic from '@tiptap/extension-italic'
import Link from '@tiptap/extension-link'
import ListItem from '@tiptap/extension-list-item'
import Mention from '@tiptap/extension-mention'
import OrderedList from '@tiptap/extension-ordered-list'
import Paragraph from '@tiptap/extension-paragraph'
import Placeholder from '@tiptap/extension-placeholder'
import Strike from '@tiptap/extension-strike'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import Table from '@tiptap/extension-table'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import Text from '@tiptap/extension-text'
import Typography from '@tiptap/extension-typography'
import Underline from '@tiptap/extension-underline'

import { Editor, EditorContent } from '@tiptap/vue-2'

# import Collaboration from '@/shared/tiptap_extentions/collaboration.js'

# import ForeColor from '@/shared/tiptap_extentions/fore_color'
# import BackColor from '@/shared/tiptap_extentions/back_color'
# import FormatClear from '@/shared/tiptap_extentions/format_clear'
# import Align from '@/shared/tiptap_extentions/align'
# import Alignment from '@/shared/tiptap_extentions/alignment'
# import Paragraph from '@/shared/tiptap_extentions/paragraph'
# import Heading from '@/shared/tiptap_extentions/heading'
# import Iframe from './iframe'
# import TodoItem from './todo_item'

# import { insertText } from 'tiptap-commands'
# import Image from '@/shared/tiptap_extentions/image.js'

# import {getEmbedLink} from '@/shared/helpers/embed_link.coffee'

# import { CommonMentioning, HtmlMentioning, MentionPluginConfig } from './mentioning.coffee'
# import SuggestionList from './suggestion_list'
# import Attaching from './attaching.coffee'
import {compact, uniq, throttle, difference, reject, uniqBy} from 'lodash'
import TextHighlightBtn from './text_highlight_btn'
import TextAlignBtn from './text_align_btn'

# import io from 'socket.io-client'

export default
  # mixins: [CommonMentioning, HtmlMentioning, Attaching]
  props:
    model: Object
    field: String
    label: String
    placeholder: String
    maxLength: Number
    shouldReset: Boolean
    autofocus: Boolean

  components: {
    EditorContent
    TextAlignBtn
    TextHighlightBtn
  }
    # EditorMenuBar: EditorMenuBar
    # FilesList: FilesList
    # SuggestionList: SuggestionList
    # TextHighlightBtn: TextHighlightBtn
    # TextAlignBtn: TextAlignBtn

  data: ->
    loading: true
    socket: null
    count: 0
    editor: null
    expanded: true
    closeEmojiMenu: false
    linkUrl: ""
    iframeUrl: ""
    linkDialogIsOpen: false
    iframeDialogIsOpen: false
    fetchedUrls: []

  computed:
    format: ->
      @model["#{@field}Format"]

  mounted: ->
    # @expanded = Session.user().experiences['html-editor.expanded']
    @editor = new Editor
      extensions: [
        Document,
        Paragraph
        Text
        Highlight
        Blockquote
        CodeBlock
        Bold
        Italic
        Strike
        Underline
        Heading
        Highlight.configure(multicolor: true)
        History
        HorizontalRule
        Typography
        Table
        TableHeader
        TableRow
        TableCell
        TextAlign
      ]
      content: @model[@field]
    # @onInit({doc: null, version: 0})
    # @socket = io(@tiptapAddress())
    #   .on('init', (data) => @onInit(data))
    #   .on('update', (data) =>
    #     @nditor.extensions.options.collaboration.update(data)
    #     @editor.extensions.options.collaboration.updateCursors(data)
    #   )
    #   .on('getCount', (count) => @setCount(count))
    #   .on('cursorupdate', (data) =>
    #     this.editor.extensions.options.collaboration.updateCursors(data)
    #   )

  watch:
    'shouldReset': 'reset'

  methods:
    # onInit: ({doc, version}) ->
    #   @loading = false
    #   @editor.destroy() if @editor
    #
    #   @editor = new Editor
    #     editorProps:
    #       scrollThreshold: 100
    #       scrollMargin: 100
    #     extensions: [
    #       Link(),
    #       new Mention(MentionPluginConfig.bind(@)()),
    #       new Blockquote(),
    #       new BulletList(),
    #       new CodeBlock(),
    #       new HardBreak(),
    #       new Image({attachFile: @attachFile, attachImageFile: @attachImageFile}),
    #       new Heading({ levels: [1, 2, 3] }),
    #       new HorizontalRule(),
    #       new ListItem(),
    #       new OrderedList(),
    #       new TodoItem(),
    #       new TodoList(),
    #       new Table(),
    #       new TableHeader(),
    #       new TableCell(),
    #       new TableRow(),
    #       new Bold(),
    #       new Code(),
    #       new Italic(),
    #       new Strike(),
    #       new Underline(),
    #       new History(),
    #       new Iframe(),
    #       new ForeColor(),
    #       new BackColor(),
    #       new FormatClear(),
    #       new Align(),
    #       new Alignment(),
    #       new Paragraph(),
    #       new Placeholder({
    #         emptyClass: 'is-empty',
    #         emptyNodeText: @placeholder,
    #         showOnlyWhenEditable: true,
    #       }),
    #       # new Collaboration({
    #       #   socket: @socket,
    #       #   user: Session.user()
    #       #   version: version
    #       #   debounce: 250
    #       # })
    #     ]
    #     content: @model[@field] # doc
    #     onUpdate: @updateModel
    #     autoFocus: @autofocus
      #
      # @editor.setContent(@model[@field]) if version == 0
      #
      # setTimeout =>
      #   if @$refs.editor && @$refs.editor.$el
      #     @$refs.editor.$el.children[0].setAttribute("role", "textbox")
      #     @$refs.editor.$el.children[0].setAttribute("aria-label", @placeholder) if @placeholder

    setCount: (count) ->
      @count = count

    tiptapAddress: ->
      if @model.isNew()
        compact([AppConfig.theme.channels_uri, 'tiptap', @model.constructor.singular, 'new', @model.groupId, @model.discussionId, @model.parentId, Session.user().secretToken]).join('/')
      else
        [AppConfig.theme.channels_uri, 'tiptap', @model.constructor.singular, @model.id, (@model.secretToken || Session.user().secretToken)].join('/')

    selectedText: ->
      { selection, state } = @editor
      { from, to } = selection
      state.doc.textBetween(from, to, ' ')

    reset: ->
      @editor.clearContent()
      @resetFiles()

    convertToMd: ->
      if confirm I18n.t('formatting.markdown_confirm')
        convertToMd(@model, @field)
        Records.users.saveExperience('html-editor.uses-markdown')

    toggleExpanded: ->
      if !@expanded
        @expanded = true
        Records.users.saveExperience('html-editor.expanded')
      else
        @expanded = false
        Records.users.removeExperience('html-editor.expanded')

    setLinkUrl: (command) ->
      if @linkUrl
        @linkUrl = "http://".concat(@linkUrl) unless @linkUrl.includes("://")
        command({ href: @linkUrl })
        @fetchLinkPreviews([@linkUrl])
        @linkUrl = null
      @linkDialogIsOpen = false
      @editor.focus()

    setIframeUrl: (command) ->
      command({ src: getEmbedLink(@iframeUrl) })
      @iframeUrl = null
      @iframeDialogIsOpen = false
      @editor.focus()

    emojiPicked: (shortcode, unicode) ->
      { view } = this.editor
      insertText(unicode)(view.state, view.dispatch, view)
      @closeEmojiMenu = false
      @editor.focus()

    updateModel: ->
      @model[@field] = @editor.getHTML()
      @updateFiles()
      @scrapeLinkPreviews() if @model.isNew()

    removeLinkPreview: (url) ->
      @model.linkPreviews = reject(@model.linkPreviews, (p) -> p.url == url)

    scrapeLinkPreviews: throttle ->
      parser = new DOMParser()
      doc = parser.parseFromString(@model[@field], 'text/html')
      @fetchLinkPreviews difference((Array.from(doc.querySelectorAll('a')).map (el) => el.href), @fetchedUrls)
    ,
      500

    fetchLinkPreviews: (urls) ->
      if urls.length
        Records.remote.post('link_previews', {urls: urls}).then (data) =>
          @model.linkPreviews = uniqBy(@model.linkPreviews.concat(data.previews), 'url')
      @fetchedUrls = uniq @fetchedUrls.concat(urls)

  beforeDestroy: ->
    # @editor.destroy() if @editor
    # @socket.close() if @socket

</script>

<template lang="pug">
div
  //- template(v-if="!editor || loading")
  //-   | Connecting to socket server …
  .editor.mb-3(v-if="editor")
    editor-content.html-editor__textarea(ref="editor" :editor='editor').lmo-markdown-wrapper
    .menubar
      div
        v-layout(align-center v-if="editor.isActive('table')")
          v-btn(icon @click="editor.chain().deleteTable().focus().run()" :title="$t('formatting.remove_table')")
            v-icon mdi-table-remove
          v-btn(icon @click="editor.chain().addColumnBefore().focus().run()" :title="$t('formatting.add_column_before')")
            v-icon mdi-table-column-plus-before
          v-btn(icon @click="editor.chain().addColumnAfter().focus().run()" :title="$t('formatting.add_column_after')")
            v-icon mdi-table-column-plus-after
          v-btn(icon @click="editor.chain().deleteColumn().focus().run()" :title="$t('formatting.remove_column')")
            v-icon mdi-table-column-remove
          v-btn(icon @click="editor.chain().addRowBefore().focus().run()" :title="$t('formatting.add_row_before')")
            v-icon mdi-table-row-plus-before
          v-btn(icon @click="editor.chain().addRowAfter().focus().run()" :title="$t('formatting.add_row_after')")
            v-icon mdi-table-row-plus-after
          v-btn(icon @click="editor.chain().deleteRow().focus().run()" :title="$t('formatting.remove_row')")
            v-icon mdi-table-row-remove
          v-btn(icon @click="editor.chain().mergeOrSplit().focus().run()" :title="$t('formatting.merge_selected')")
            v-icon mdi-table-merge-cells

        v-layout.py-2.justify-space-between.flex-wrap(align-center)
          section.d-flex.flex-wrap(:aria-label="$t('formatting.formatting_tools')")
            //- attach
            //- v-btn(icon @click='$refs.filesField.click()' :title="$t('formatting.attach')")
            //-   v-icon mdi-paperclip

            //- v-btn(icon @click='$refs.imageFilesField.click()' :title="$t('formatting.insert_image')")
            //-   v-icon mdi-image

            //- link
            //- v-menu(:close-on-content-click="!selectedText()" v-model="linkDialogIsOpen" min-width="320px")
            //-   template(v-slot:activator="{on, attrs}")
            //-     v-btn(icon v-on="on" v-bind="attrs" :title="$t('formatting.link')")
            //-       v-icon mdi-link
            //-   v-card
            //-     template(v-if="selectedText()")
            //-       v-card-title.title(v-t="'text_editor.insert_link'")
            //-       v-card-text
            //-         v-text-field(type="url" label="https://www.example.com" v-model="linkUrl" autofocus ref="focus" v-on:keyup.enter="setLinkUrl(commands.link)")
            //-       v-card-actions
            //-         v-spacer
            //-         v-btn(color="primary" @click="setLinkUrl(commands.link)" v-t="'common.action.apply'")
            //-     template(v-else)
            //-       v-card-title(v-t="'text_editor.select_text_to_link'")

            //- emoji
            //- v-menu(:close-on-content-click="false" v-model="closeEmojiMenu")
            //-   template(v-slot:activator="{on, attrs}")
            //-     v-btn.emoji-picker__toggle(v-on="on" v-bind="attrs" icon  :title="$t('formatting.insert_emoji')")
            //-       v-icon mdi-emoticon-outline
            //-   emoji-picker(:insert="emojiPicked")

            //- headings menu
            //- v-menu
            //-   template(v-slot:activator="{ on, attrs }")
            //-     v-btn.drop-down-button(icon v-on="on" v-bind="attrs" :title="$t('formatting.heading_size')")
            //-       v-icon mdi-format-size
            //-       v-icon.menu-down-arrow mdi-menu-down
            //-   v-list(dense)
            //-     template(v-for="i in 3")
            //-       v-list-item(@click='commands.heading({ level: i })' :class="{ 'v-list-item--active': isActive.heading({level: i}) }")
            //-         v-list-item-icon
            //-           v-icon {{'mdi-format-header-'+i}}
            //-         v-list-item-title(v-t="'formatting.heading'+i")
            //-     v-list-item(@click='commands.paragraph()' :class="{ 'v-list-item--active': isActive.paragraph() }")
            //-       v-list-item-icon
            //-         v-icon mdi-format-pilcrow
            //-       v-list-item-title(v-t="'formatting.paragraph'")
            //-
            template(v-if="expanded")
              v-btn(icon @click='editor.chain().focus().setParagraph().run()' :outlined="editor.isActive('paragraph')" :title="$t('formatting.paragraph')")
                v-icon mdi-format-pilcrow
              template(v-for="i in 3")
                v-btn(icon @click='editor.chain().focus().toggleHeading({ level: i }).run()' :outlined="editor.isActive('heading', { level: i })" :title="$t('formatting.heading'+i)")
                  v-icon {{'mdi-format-header-'+i}}

            //- bold

            v-btn(icon v-if="expanded" @click='editor.chain().toggleBold().focus().run()' :outlined="editor.isActive('bold')" :title="$t('formatting.bold')")
              v-icon mdi-format-bold

            //- italic
            v-btn(icon v-if="expanded" @click='editor.chain().toggleItalic().focus().run()' :outlined="editor.isActive('italic')" :title="$t('formatting.italicize')")
              v-icon mdi-format-italic
            //-
            //- //- strikethrough
            v-btn(icon v-if="expanded" @click='editor.chain().toggleStrike().focus().run()' :outlined="editor.isActive('strike')"  :title="$t('formatting.strikethrough')")
              v-icon mdi-format-strikethrough
            //- //- underline
            v-btn(icon v-if="expanded" @click='editor.chain().toggleUnderline().focus().run()' :outlined="editor.isActive('underline')",  :title="$t('formatting.underline')")
              v-icon mdi-format-underline
            //-
            text-highlight-btn(v-if="expanded" :editor="editor")
            text-align-btn(v-if="expanded" :editor="editor")

            //- list menu (always a menu)
            //- v-menu(v-if="expanded")
            //-   template(v-slot:activator="{ on, attrs }")
            //-     v-btn.drop-down-button(icon v-on="on" v-bind="attrs" :title="$t('formatting.lists')")
            //-       v-icon mdi-format-list-bulleted
            //-       v-icon.menu-down-arrow mdi-menu-down
            //-   v-list(dense)
            //-     v-list-item(@click='commands.todo_list')
            //-       v-list-item-icon
            //-         v-icon mdi-format-list-checks
            //-       v-list-item-title(v-t="'formatting.check_list'")
            //-     v-list-item(@click='commands.bullet_list')
            //-       v-list-item-icon
            //-         v-icon mdi-format-list-bulleted
            //-       v-list-item-title(v-t="'formatting.bullet_list'")
            //-     v-list-item(@click='commands.ordered_list')
            //-       v-list-item-icon
            //-         v-icon mdi-format-list-numbered
            //-       v-list-item-title(v-t="'formatting.number_list'")

            //- extra text marks
            template(v-if="expanded")
              //- strikethrough
              //- v-menu(:close-on-content-click="false" v-model="iframeDialogIsOpen" min-width="320px")
              //-   template(v-slot:activator="{on}")
              //-     v-btn(icon v-on="on" :title="$t('formatting.embed')")
              //-       v-icon mdi-youtube
              //-   v-card
              //-     v-card-title.title(v-t="'text_editor.insert_embedded_url'")
              //-     v-card-text
              //-       v-text-field(type="url" label="e.g. https://www.youtube.com/watch?v=Zlzuqsunpxc" v-model="iframeUrl" ref="focus" autofocus v-on:keyup.enter="setIframeUrl(commands.iframe)")
              //-     v-card-actions
              //-       v-spacer
              //-       v-btn(color="primary" @click="setIframeUrl(commands.iframe)" v-t="'common.action.apply'")
              //- blockquote
              v-btn(icon @click='editor.chain().toggleBlockquote().focus().run()' :outlined="editor.isActive('blockquote')"  :title="$t('formatting.blockquote')")
                v-icon mdi-format-quote-close
              //- //- code block
              v-btn(small icon @click='editor.chain().toggleCodeBlock().focus().run()' :outlined="editor.isActive('codeBlock')"  :title="$t('formatting.code_block')")
                v-icon mdi-code-braces
              //- embded
              v-btn(icon @click='editor.chain().setHorizontalRule().focus().run()' :title="$t('formatting.divider')")
                v-icon mdi-minus
              //- table
              v-btn(icon @click='editor.chain().insertTable({rows: 3, cols: 3, withHeaderRow: false }).focus().run()' :title="$t('formatting.add_table')")
                v-icon mdi-table
              //- markdown (save experience)
              //- v-btn(icon @click="convertToMd" :title="$t('formatting.edit_markdown')")
              //-   v-icon mdi-language-markdown-outline
            //-
            //- v-btn.html-editor__expand(v-if="!expanded" icon @click="toggleExpanded" :title="$t('formatting.expand')")
            //-   v-icon mdi-chevron-right
            //-
            //- v-btn.html-editor__expand(v-if="expanded" icon @click="toggleExpanded" :title="$t('formatting.collapse')")
            //-   v-icon mdi-chevron-left
          //- //- save button?
          //- v-spacer
          //- slot(v-if="!expanded" name="actions")
    //- div.d-flex(v-if="expanded" name="actions")
    //-   v-spacer
    //-   slot(name="actions")
    //-
    //- v-alert(v-if="maxLength && model[field] && model[field].length > maxLength" color='error')
    //-   span( v-t="'poll_common.too_long'")

  link-previews(:model="model" :remove="removeLinkPreview")
  //- suggestion-list(:query="query" :loading="fetchingMentions" :mentionable="mentionable" :positionStyles="suggestionListStyles" :navigatedUserIndex="navigatedUserIndex" @select-user="selectUser")
  //- files-list(:files="files" v-on:removeFile="removeFile")

  //- form(style="display: block" @change="fileSelected")
  //-   input(ref="filesField" type="file" name="files" multiple=true)
</template>
<style lang="sass">

// .count
//   display: flex
//   align-items: center
//   font-weight: bold
//   color: rgba(#000, 0.5)
//   color: #27b127
//   margin-bottom: 1rem
//   text-transform: uppercase
//   font-size: 0.7rem
//   line-height: 1
//   &:before
//     content: ''
//     display: inline-flex
//     background-color: #27b127
//     width: 0.4rem
//     height: 0.4rem
//     border-radius: 50%
//     margin-right: 0.3rem

.cursor
  color: #222
  text-align: center
  border-radius: 6px 6px 6px 0px
  padding: 5px
  margin-left: -4.5px
  position: absolute
  z-index: 1
  bottom: 5px
  left: -50%
  opacity: 0.85
  white-space: nowrap
  -webkit-touch-callout: none
  -webkit-user-select: none
  -khtml-user-select: none
  -moz-user-select: none
  -ms-user-select: none
  user-select: none
  &.me
    display: none
    &::after
      display: none
      border-color: inherit
  &.inactive
    opacity: 0.5
    &::after
      opacity: inherit
      border-color: inherit
  &::after
    content: ""
    position: absolute
    top: 100%
    left: 0%
    border-width: 5px
    border-style: solid
    border-color: inherit
    color: transparent

.ProseMirror-widget
  position: absolute
  width: 0.1px
  /*border-style: solid;*/

.bv-row
  padding-top: 20px

.ProseMirror [contenteditable="false"]
  white-space: normal

.ProseMirror [contenteditable="true"]
  white-space: pre-wrap

.ProseMirror
  outline: none

progress
  width: 50%!important
  -webkit-appearance: none
  appearance: none
  background-color: #fff
  border: 1px solid #ccc
progress::-webkit-progress-bar
  background-color: #fff
  border: 1px solid #ccc
progress::-webkit-progress-value
  background-color: lightblue
  border: 0
  transition: width 120ms ease-out, opacity 60ms 60ms ease-in
progress::-moz-progress-bar
  background-color: lightblue
  border: 0
  transition: width 120ms ease-out, opacity 60ms 60ms ease-in

.menubar
  position: sticky
  bottom: 0

.menubar
  .drop-down-button
    width: 40px !important
  .menu-down-arrow
    margin-left: -10px
  .v-btn--icon
    width: 32px
    height: 32px

  .v-btn.v-btn--icon
    min-width: 0
    margin-left: 0
    margin-right: 0
    max-width: 32px
    .v-icon
      font-size: 16px

.html-editor__textarea .ProseMirror
  border-bottom: 1px solid #999
  padding: 4px 0px
  margin: 4px 0px
  outline: none
  overflow-y: scroll
  overflow: visible

.html-editor__textarea .ProseMirror:focus
  border-bottom: 1px solid var(--v-primary-base)

// .lmo-textarea img
//   display: block

ul[data-type="todo_list"]
  padding-left: 0

li[data-type="todo_item"]
  display: flex
  flex-direction: row

.todo-checkbox
  border: 1px solid #999
  height: 1.3em
  width: 1.3em
  box-sizing: border-box
  margin-right: 8px
  margin-top: 0px
  user-select: none
  border-radius: 0.2em
  background-color: transparent
  &:hover
    border: 1px solid var(--v-primary-base)
    // background: #eee

.lmo-textarea .todo-checkbox
  cursor: pointer

.todo-content
  flex: 1
  > p:last-of-type
    margin-bottom: 0
  > ul[data-type="todo_list"]
    margin: .5rem 0
  p
    margin: 0

li[data-done="true"]
  > .todo-content
    > p
      text-decoration: line-through
  > .todo-checkbox::before
    position: relative
    top: -6px
    color: var(--v-accent-base)
    font-size: 1.5rem
    content: "✓"

li[data-done="false"]
  text-decoration: none

.lmo-textarea p.is-empty:first-child::before
  content: attr(data-empty-text)
  float: left
  color: #aaa
  pointer-events: none
  height: 0

.lmo-textarea p.is-empty
  font-size: 16px
  padding-bottom: 16px

input[type="file"]
  display: none
</style>


quick fix for flash-of-error issue (https://trello.com/c/t0lnait9/207-fix-flash-of-the-error-page-on-navigation)

<template>

  <core-base class="learn-page">

          <svg role="presentation" fill="#000000" height="40" viewbox="0 0 24 24" width="40" xmlns="http://www.w3.org/2000/svg">
            <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path>
            <path d="M0 0h24v24H0z" fill="none"></path>
          </svg>
          Learn

          <svg role="presentation" fill="#000000" height="40" viewbox="0 0 24 24" width="40" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"></path>
            <path d="M0 0h24v24H0z" fill="none"></path>
          </svg>
          Explore

        <explore-page v-if='showExplorePage'></explore-page>
        <content-page v-if='showContentPage'></content-page>
        <learn-page v-if='showLearnPage'></learn-page>
        <scratchpad-page v-if='showScratchpadPage'></scratchpad-page>
        <error-page v-if='error'></error-page>

      <router-view></router-view>

  </core-base>

</template>

<template>

  <core-base class="learn-page">

          <svg role="presentation" fill="#000000" height="40" viewbox="0 0 24 24" width="40" xmlns="http://www.w3.org/2000/svg">
            <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path>
            <path d="M0 0h24v24H0z" fill="none"></path>
          </svg>
          Learn

          <svg role="presentation" fill="#000000" height="40" viewbox="0 0 24 24" width="40" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"></path>
            <path d="M0 0h24v24H0z" fill="none"></path>
          </svg>
          Explore

        <explore-page v-if='showExplorePage'></explore-page>
        <content-page v-if='showContentPage'></content-page>
        <learn-page v-if='showLearnPage'></learn-page>
        <scratchpad-page v-if='showScratchpadPage'></scratchpad-page>

        <error-page></error-page>

      <router-view></router-view>

  </core-base>

</template>


add resize cursor to history console

add resize cursor to history console

<template>

        <icon iname="history" /> {{ $t('history.title') }}

        {{ $t('history.' + (lastAction.status === 'pending' ? 'current_action' : 'last_action')) }}

      <query-header v-if="lastAction" :action="lastAction" class="w-auto ml-2 xs-hide" />

            <query-header
              role="tab" v-b-toggle="action.messages.length ? 'messages-collapse-' + i : false"
              :action="action" show-time show-error
            />

            <message-list-group :messages="action.messages" flush />

</template>


Apply new styles to Quiz Plan List Page

Apply new styles to Quiz Plan List Page

Coach may now Open, Close and Toggle visiblity for quizzes from the
Plan > Quizzes view.

<template>

  <CoreBase
    :immersivePage="false"
    :authorized="userIsAuthorized"
    authorizedRole="adminOrCoach"
    :showSubNav="true"
  >
    <TopNavbar slot="sub-nav" />

    <KPageContainer>
      <PlanHeader />

        <KSelect
          v-model="statusSelected"
          :label="coreString('showAction')"
          :options="statusOptions"
          :inline="true"
        />
        <KRouterLink
          :primary="true"
          appearance="raised-button"
          :to="newExamRoute"
          :text="coachString('newQuizAction')"
        />

      <CoreTable>

            {{ coachString('titleLabel') }}
            {{ coachString('recipientsLabel') }}

        <transition-group slot="tbody" tag="tbody" name="list">

              <KLabeledIcon icon="quiz">
                <KRouterLink
                  :to="$router.getRoute('QuizSummaryPage', { quizId: exam.id })"
                  appearance="basic-link"
                  :text="exam.title"
                />
              </KLabeledIcon>

              <Recipients
                :groupNames="getGroupNames(exam.groups)"
                :hasAssignments="exam.assignments.length > 0"
              />

              <KButton
                v-if="!exam.active && !exam.archive"
                :text="coachString('openQuizLabel')"
                appearance="flat-button"
                @click="showOpenConfirmationModal = true; modalQuizId=exam.id"
              />

              <KButton
                v-if="exam.active && !exam.archive"
                :text="coachString('closeQuizLabel')"
                appearance="flat-button"
                @click="showCloseConfirmationModal = true; modalQuizId=exam.id;"
              />

              <KSwitch
                v-if="exam.archive"
                name="toggle-quiz-visibility"
                :checked="exam.active"
                :value="exam.active"
                :label="coachString('reportVisibleLabel')"
                style="margin: 0.5rem;"
                @change="handleToggleVisibility(exam.id, exam.active)"
              />

        </transition-group>
      </CoreTable>

        {{ $tr('noExams') }}

        {{ $tr('noActiveExams') }}

        {{ $tr('noInactiveExams') }}

      <QuizStatusModal
        v-if="showOpenConfirmationModal"
        :modalHeader="coachString('openQuizLabel')"
        :modalDetail="coachString('openQuizModalDetail')"
        @cancel="showOpenConfirmationModal = false"
        @submit="handleOpenQuiz(modalQuizId)"
      />
      <QuizStatusModal
        v-if="showCloseConfirmationModal"
        :modalHeader="coachString('closeQuizLabel')"
        :modalDetail="coachString('closeQuizModalDetail')"
        @cancel="showCloseConfirmationModal = false"
        @submit="handleCloseQuiz(modalQuizId)"
      />
    </KPageContainer>

  </CoreBase>

</template>

<template>

  <CoreBase
    :immersivePage="false"
    :authorized="userIsAuthorized"
    authorizedRole="adminOrCoach"
    :showSubNav="true"
  >
    <TopNavbar slot="sub-nav" />

    <KPageContainer>
      <PlanHeader />

        <KSelect
          v-model="statusSelected"
          :label="coreString('showAction')"
          :options="statusOptions"
          :inline="true"
        />
        <KRouterLink
          :primary="true"
          appearance="raised-button"
          :to="newExamRoute"
          :text="coachString('newQuizAction')"
        />

      <CoreTable>

            {{ coachString('titleLabel') }}
            {{ coachString('recipientsLabel') }}

              {{ coachString('statusLabel') }}

        <transition-group slot="tbody" tag="tbody" name="list">

              <KLabeledIcon icon="quiz">
                <KRouterLink
                  :to="$router.getRoute('QuizSummaryPage', { quizId: exam.id })"
                  appearance="basic-link"
                  :text="exam.title"
                />
              </KLabeledIcon>

              <Recipients
                :groupNames="getGroupNames(exam.groups)"
                :hasAssignments="exam.assignments.length > 0"
              />

              <QuizActive :active="exam.active" />

        </transition-group>
      </CoreTable>

        {{ $tr('noExams') }}

        {{ $tr('noActiveExams') }}

        {{ $tr('noInactiveExams') }}

    </KPageContainer>

  </CoreBase>

</template>


update sw page to show sw download

update sw page to show sw download

<template>

  <GeneratorMenu/>

{{ $t('serviceworker.title') }}

        <form @submit.prevent="download" @keydown.enter.prevent="download">

              <input type="radio" value="1" v-model="serviceworker$"> {{ $t('serviceworker.titles.offline_page') }}

            {{ $t('serviceworker.descriptions.offline_page') }}

              <input type="radio" value="2" v-model="serviceworker$"> {{ $t('serviceworker.titles.offline_copy') }}

            {{ $t('serviceworker.descriptions.offline_copy') }}

              <input type="radio" value="3" v-model="serviceworker$"> {{ $t('serviceworker.titles.offline_copy_backup') }}

            {{ $t('serviceworker.descriptions.offline_copy_backup') }}

              <input type="radio" value="4" v-model="serviceworker$"> {{ $t('serviceworker.titles.cache_first') }}

            {{ $t('serviceworker.descriptions.cache_first') }}

              <input type="radio" value="5" disabled="" v-model="serviceworker$"> {{ $t('serviceworker.titles.advanced') }}

            {{ $t('serviceworker.descriptions.advanced') }}

            <button @click="$ga.event('Download', 'Serviceworker', 'Download Archive', { 'page': `/download/serviceworker` })" class="pwa-button pwa-button--simple isEnabled">
              {{ $t('serviceworker.download') }}
              {{ $t('serviceworker.building') }}
                <Loading :active="true" class="u-display-inline_block u-margin-left-sm" />

            </button>

 {{ $t(error) }}

        </form>

{{ $t('serviceworker.download_link') }}
          GitHub.

        <CodeViewer :size="viewerSize" :code="webPreview" :title="$t('serviceworker.code_preview_web')">
          <nuxt-link :to="$i18n.path('publish')" class="pwa-button pwa-button--simple pwa-button--brand pwa-button--header" @click="$ga.event('item', 'click', 'generator-nextStep-trigger')">
            {{ $t("serviceworker.next_step") }}
          </nuxt-link>
        </CodeViewer>
        <CodeViewer :size="viewerSize" :code="serviceworkerPreview" :title="$t('serviceworker.code_preview_serviceworker')"></CodeViewer>

    <nuxt-link :to="$i18n.path('publish')" class="pwa-button" @click="$ga.event('item', 'click', 'generator-nextStep-trigger')">
      {{ $t("serviceworker.next_step") }}
    </nuxt-link>

  <StartOver />
  <TwoWays/>

</template>

<template>

  <GeneratorMenu/>

{{ $t('serviceworker.title') }}

        <form @submit.prevent="download" @keydown.enter.prevent="download">

              <input type="radio" value="1" v-model="serviceworker$"> {{ $t('serviceworker.titles.offline_page') }}

            {{ $t('serviceworker.descriptions.offline_page') }}

              <input type="radio" value="2" v-model="serviceworker$"> {{ $t('serviceworker.titles.offline_copy') }}

            {{ $t('serviceworker.descriptions.offline_copy') }}

              <input type="radio" value="3" v-model="serviceworker$"> {{ $t('serviceworker.titles.offline_copy_backup') }}

            {{ $t('serviceworker.descriptions.offline_copy_backup') }}

              <input type="radio" value="4" v-model="serviceworker$"> {{ $t('serviceworker.titles.cache_first') }}

            {{ $t('serviceworker.descriptions.cache_first') }}

              <input type="radio" value="5" disabled="" v-model="serviceworker$"> {{ $t('serviceworker.titles.advanced') }}

            {{ $t('serviceworker.descriptions.advanced') }}

            <button @click="$ga.event('Download', 'Web', 'Download Archive', { 'page': `/download/web` })" class="pwa-button pwa-button--simple isEnabled">
              {{ $t('serviceworker.download') }}
              {{ $t('serviceworker.building') }}
                <Loading :active="true" class="u-display-inline_block u-margin-left-sm" />

            </button>

 {{ $t(error) }}

        </form>

{{ $t('serviceworker.download_link') }}
          GitHub.

        <CodeViewer :size="viewerSize" :code="webPreview" :title="$t('serviceworker.code_preview_web')">
          <nuxt-link :to="$i18n.path('publish')" class="pwa-button pwa-button--simple pwa-button--brand pwa-button--header" @click="$ga.event('item', 'click', 'generator-nextStep-trigger')">
            {{ $t("serviceworker.next_step") }}
          </nuxt-link>
        </CodeViewer>
        <CodeViewer :size="viewerSize" :code="serviceworkerPreview" :title="$t('serviceworker.code_preview_serviceworker')"></CodeViewer>

    <nuxt-link :to="$i18n.path('publish')" class="pwa-button" @click="$ga.event('item', 'click', 'generator-nextStep-trigger')">
      {{ $t("serviceworker.next_step") }}
    </nuxt-link>

  <StartOver />
  <TwoWays/>

</template>


Wording

Wording

<template>

      {{ greeting }}

Most Played

Recently Played

            Your most-recent songs in this session will be displayed here.

            Start listening!

Recently Added

            <album-item v-for="album in recentlyAdded.albums" :album="album"></album-item>

Top Artists

          <artist-item v-for="artist in topArtists" :artist="artist"></artist-item>

Top Albums

          <album-item v-for="album in topAlbums" :album="album"></album-item>

      <to-top-button :showing="showBackToTop"></to-top-button>

</template>

<template>

      {{ greeting }}

Most Played Songs

Recently Played

            Your most-recent songs in this session will be displayed here.

            Start listening!

Recently Added

            <album-item v-for="album in recentlyAdded.albums" :album="album"></album-item>

Top Artists

          <artist-item v-for="artist in topArtists" :artist="artist"></artist-item>

Top Albums

          <album-item v-for="album in topAlbums" :album="album"></album-item>

      <to-top-button :showing="showBackToTop"></to-top-button>

</template>


dont fetch sheet if no sheets exist. fixes #105

<template>
  <div id="app">
    <site-nav></site-nav>
    <notification
       v-for="item in notifications"
       :msg="item.msg"
       :type="item.type"
       @dismiss="dismissNotification(item.id)"></notification>
    <router-view class="router-view"></router-view>
  </div>
</template>

<script>
const { mapState, mapActions, mapMutations } = require('vuex')
const values = require('lodash/values')

const SiteNav = require('../components/site-nav.vue')
const Notification = require('../components/notification.vue')

module.exports = {
  name: 'Layout',
  computed: mapState({
    sheets: (state) => state.db.sheets,
    notifications: (state) => values(state.ui.notifications)
  }),
  methods: {
    ...mapActions([
      'getSheetList',
      'getSheet'
    ]),
    ...mapMutations([
      'dismissNotification'
    ]),
    getActiveSheet () {
      const route = this.$route.params.sheetName
      const activeSheetName = determineActiveSheet(route, this.sheets)
      if (activeSheetName) this.getSheet({ name: activeSheetName })
    }
  },
  async created () {
    await this.getSheetList()
    this.getActiveSheet()
  },
  watch: {
    '$route': 'getActiveSheet'
  },
  components: {
    'site-nav': SiteNav,
    'notification': Notification
  }
}

function determineActiveSheet (route, sheets) {
  if (route) {
    return route
  } else if (sheets.length > 0) {
    return sheets[0].name
  }
}
</script>

<style src="../../../node_modules/bulma/css/bulma.css"></style>
<style>
html,
body,
#app {
  height: 100%;
}
#app {
  display: flex;
  flex-direction: column;
}
.router-view {
  flex: 1;
}
</style>

<template>
  <div id="app">
    <site-nav></site-nav>
    <notification
       v-for="item in notifications"
       :msg="item.msg"
       :type="item.type"
       @dismiss="dismissNotification(item.id)"></notification>
    <router-view class="router-view"></router-view>
  </div>
</template>

<script>
const { mapState, mapActions, mapMutations } = require('vuex')
const values = require('lodash/values')

const SiteNav = require('../components/site-nav.vue')
const Notification = require('../components/notification.vue')

module.exports = {
  name: 'Layout',
  computed: mapState({
    sheets: (state) => state.db.sheets,
    notifications: (state) => values(state.ui.notifications)
  }),
  methods: {
    ...mapActions([
      'getSheetList',
      'getSheet'
    ]),
    ...mapMutations([
      'dismissNotification'
    ]),
    getActiveSheet () {
      const route = this.$route.params.sheetName
      const activeSheetName = determineActiveSheet(route, this.sheets)
      this.getSheet({ name: activeSheetName })
    }
  },
  async created () {
    await this.getSheetList()
    this.getActiveSheet()
  },
  watch: {
    '$route': 'getActiveSheet'
  },
  components: {
    'site-nav': SiteNav,
    'notification': Notification
  }
}

function determineActiveSheet (route, sheets) {
  if (route) {
    return route
  } else if (sheets.length > 0) {
    return sheets[0].name
  } else {
    return ''
  }
}
</script>

<style src="../../../node_modules/bulma/css/bulma.css"></style>
<style>
html,
body,
#app {
  height: 100%;
}
#app {
  display: flex;
  flex-direction: column;
}
.router-view {
  flex: 1;
}
</style>


remove v-if

remove v-if

<template>

        <Dropdown @is-content-visible="showPrivateQueries = !showPrivateQueries">
          <template v-slot:trigger>

              <Caret :rotate="showPrivateQueries" />

              {{ labelYourQueries }} ({{ privateQueries.length }})

          </template>

            <transition-group name="fade">

                <router-link
                  :to="`/datos/${$route.params.id}/q/${id}`"
                  class="gobierto-data-summary-queries-container-name"
                >
                  {{ name }}
                </router-link>

                  <PrivateIcon
                    :is-closed="privacy_status === 'closed'"
                    class="icons-your-queries"
                  />

            </transition-group>

        </Dropdown>

        <Dropdown @is-content-visible="showFavQueries = !showFavQueries">
          <template v-slot:trigger>

              <Caret :rotate="showFavQueries" />

              {{ labelFavs }} ({{ 0 }})

          </template>

        </Dropdown>

        <Dropdown @is-content-visible="showPublicQueries = !showPublicQueries">
          <template v-slot:trigger>

              <Caret :rotate="showPublicQueries" />

              {{ labelAll }} ({{ publicQueries.length }})

          </template>

              <router-link
                :to="`/datos/${$route.params.id}/q/${id}`"
                class="gobierto-data-summary-queries-container-name"
              >
                {{ name }}
              </router-link>

        </Dropdown>

          <transition name="fade">
            <textarea ref="querySnippet" />
          </transition>

</template>


update webhook layout

update webhook layout

<template>

         {{ $t('message.webhookPageTitle') }}

          <WebhookPicker :selected="curHook ? curHook.uuid : ''" :project="projectName" @item:selected="(item) => handleSelect(item)"/>

        <button
          type="button"
          class="btn btn-primary btn-full"
          :class="{'btn-primary': this.rootStore.webhooks.loaded.get(projectName) && this.rootStore.webhooks.webhooksForProject(projectName).length == 0 && !this.curHook}"
          @click="handleAddNew">
             {{ $t('message.webhookCreateBtn') }}
        </button>

                <WebhookTitle :webhook="this.curHook"/>

{{ $t('message.webhookDeleteBtn') }}

                    {{ $t('message.cancel') }}
                    <btn
                            :disabled="!(dirty || curHook.new)"
                            type="cta"
                            style="margin-left: 5px;font-weight: 800;"
                            @click="handleSave"
                    >{{ $t('message.webhookSaveBtn') }}</btn>

          <Tabs data-tabkey="webhook-header" style="height: 200px;" :key="curHook.new ? curHook.uuid : ''">
            <Tab :index="0" title="General">

                    {{ $t('message.webhookPostUrlLabel') }}

                      {{$t('message.webhookPostUrlHelp')}}

                    <CopyBox style="max-width: 800px" v-if="!curHook.new" :content="postUrl()"/>
                    {{$t('message.webhookPostUrlPlaceholder')}}

                    {{ $t('message.webhookAuthLabel') }}

 {{$t('message.webhookGenerateSecretCheckboxHelp')}}

<input type="checkbox" v-model="curHook.useAuth" @click="confirmAuthToggle" class="form-control" id="wh-authtoggle">{{ $t('message.webhookGenerateSecurityLabel') }}

                      <button class="btn btn-sm btn-default" @click="setRegenerate">Regenerate</button>
                       {{$t('message.webhookRegenClicked')}}

                      {{$t('message.webhookSecretMessageHelp')}}
                      <CopyBox style="max-width: 800px" :content="hkSecret"/>

{{ $t('message.webhookNameLabel') }}<input v-model="curHook.name" class="form-control">

{{ $t('message.webhookUserLabel') }}
                    <input v-model="curHook.user" class="form-control" v-if="curHook.new">
                    {{curHook.user}}

                      {{$t('message.webhookUserHelp')}}

{{ $t('message.webhookRolesLabel') }}<input v-model="curHook.roles" class="form-control">

                      {{$t('message.webhookRolesHelp')}}

<input type="checkbox" v-model="curHook.enabled" class="form-control" id="wh-enabled">{{ $t('message.webhookEnabledLabel') }}

            </Tab>
            <Tab :index="1" title="Handler Configuration">

                      <button class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" :class="{'btn-default':!curHook.eventPlugin, 'btn-muted':curHook.eventPlugin}"
                        aria-expanded="false" >

                        <plugin-info
                          :detail="curHook.eventPlugin"
                          :show-description="false"
                          v-if="curHook.eventPlugin" />

                          {{$t('message.webhookPluginLabel')}}

                      </button>

                            <plugin-info :detail="plugin" />

                    {{curHook.eventPlugin.description}}

                    <plugin-config
                      @change="input"
                      :mode="'edit'"
                      :serviceName="'WebhookEvent'"
                      v-model="selectedPlugin"
                      :provider="curHook.eventPlugin.name"
                      :key="curHook.name"
                      :show-title="false"
                      :show-description="false"
                      :validation="validation"/>

                  <component v-else
                    :is="customConfigComponent"
                    :webhook="curHook"
                    :pluginConfig="curHook.config"
                    :errors="errors"
                    @change="input"></component>

            </Tab>
          </Tabs>

</template>

<template>

     {{ $t('message.webhookPageTitle') }}

       {{ $t('message.webhookCreateBtn') }}

      <WebhookPicker :selected="curHook ? curHook.uuid : ''" :project="projectName" @item:selected="(item) => handleSelect(item)"/>

                <WebhookTitle :webhook="this.curHook"/>

{{ $t('message.webhookDeleteBtn') }}

                    {{ $t('message.cancel') }}
                    <btn
                            :disabled="!(dirty || curHook.new)"
                            type="cta"
                            style="margin-left: 5px;font-weight: 800;"
                            @click="handleSave"
                    >{{ $t('message.webhookSaveBtn') }}</btn>

          <Tabs data-tabkey="webhook-header" style="height: 200px;" :key="curHook.new ? curHook.uuid : ''">
            <Tab :index="0" title="General">

                    {{ $t('message.webhookPostUrlLabel') }}

                      {{$t('message.webhookPostUrlHelp')}}

                    <CopyBox style="max-width: 800px" v-if="!curHook.new" :content="postUrl()"/>
                    {{$t('message.webhookPostUrlPlaceholder')}}

                    {{ $t('message.webhookAuthLabel') }}

 {{$t('message.webhookGenerateSecretCheckboxHelp')}}

<input type="checkbox" v-model="curHook.useAuth" @click="confirmAuthToggle" class="form-control" id="wh-authtoggle">{{ $t('message.webhookGenerateSecurityLabel') }}

                      <button class="btn btn-sm btn-default" @click="setRegenerate">Regenerate</button>
                       {{$t('message.webhookRegenClicked')}}

                      {{$t('message.webhookSecretMessageHelp')}}
                      <CopyBox style="max-width: 800px" :content="hkSecret"/>

{{ $t('message.webhookNameLabel') }}<input v-model="curHook.name" class="form-control">

{{ $t('message.webhookUserLabel') }}
                    <input v-model="curHook.user" class="form-control" v-if="curHook.new">
                    {{curHook.user}}

                      {{$t('message.webhookUserHelp')}}

{{ $t('message.webhookRolesLabel') }}<input v-model="curHook.roles" class="form-control">

                      {{$t('message.webhookRolesHelp')}}

<input type="checkbox" v-model="curHook.enabled" class="form-control" id="wh-enabled">{{ $t('message.webhookEnabledLabel') }}

            </Tab>
            <Tab :index="1" title="Handler Configuration">

                      <button class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" :class="{'btn-default':!curHook.eventPlugin, 'btn-muted':curHook.eventPlugin}"
                        aria-expanded="false" >

                        <plugin-info
                          :detail="curHook.eventPlugin"
                          :show-description="false"
                          v-if="curHook.eventPlugin" />

                          {{$t('message.webhookPluginLabel')}}

                      </button>

                            <plugin-info :detail="plugin" />

                    {{curHook.eventPlugin.description}}

                    <plugin-config
                      @change="input"
                      :mode="'edit'"
                      :serviceName="'WebhookEvent'"
                      v-model="selectedPlugin"
                      :provider="curHook.eventPlugin.name"
                      :key="curHook.name"
                      :show-title="false"
                      :show-description="false"
                      :validation="validation"/>

                  <component v-else
                    :is="customConfigComponent"
                    :webhook="curHook"
                    :pluginConfig="curHook.config"
                    :errors="errors"
                    @change="input"></component>

            </Tab>
          </Tabs>

</template>


MeetingCheckIn: never finished the blade to Vue conversion

<template>

Quorum Requirements

              Current Member Count:
              {{ currentMembers }}

              Voting Member Count:
              {{ votingMembers }}

              Quorum Required:
              {{ quorum }}

Current Attendance

                  Proxies Registered:

              {{ proxies }}

                  Attendees in the room:

              {{ attendees }}

              Proxies Represented:
              {{ representedProxies }}

Total Checked-in

= quorum ? 'bg-success' : 'bg-danger'"
          >

{{ checkInCount }}

Member Check-in

      Use the search bellow to find a member to check in.

      You can search via Name, Username, Email or Post Code.

      Search via Username or Email will give the smallest set of results.

      <member-select-two
        ref="mst"
        v-model="userId"
        :name="null"
        :current-only="true"
        />

      <button type="button" class="btn btn-primary btn-block" :disabled="buttonDisable" @click="checkInUser"> Check-in</button>
    </form>

</template>

<template>

Quorum Requirements

              Current Member Count:
              {{ currentMembers }}

              Voting Member Count:
              {{ votingMembers }}

              Quorum Required:
              {{ quorum }}

Current Attendance

                  Proxies Registered:

              {{ proxies }}

                  Attendees in the room:

              {{ attendees }}

              Proxies Represented:
              {{ representedProxies }}

Total Checked-in

= $meeting->getQuorum()) bg-success @else bg-danger @endif">

{{ checkInCount }}

Member Check-in

      Use the search bellow to find a member to check in.

      You can search via Name, Username, Email or Post Code.

      Search via Username or Email will give the smallest set of results.

      <member-select-two
        ref="mst"
        v-model="userId"
        :name="null"
        :current-only="true"
        />

      <button type="button" class="btn btn-primary btn-block" :disabled="buttonDisable" @click="checkInUser"> Check-in</button>
    </form>

</template>


6927: updated button for forwarder

6927: updated button for forwarder

Signed-off-by: Nikola <ef0921d7af2a67b1e5ab56530351d29e92ff52e1@nextcloud.com>

<template>

		<RoomSelector v-if="!showForwardedConfirmation"
			:container="container"
			:show-postable-only="true"
			:dialog-title="dialogTitle"
			:dialog-subtitle="dialogSubtitle"
			@select="setSelectedConversationToken"
			@close="handleClose" />

		<Modal v-else
			@close="handleClose">

				<EmptyContent icon="icon-checkmark" class="forwarded-confirmation__emptycontent">
					<template #desc>
						{{ t('spreed', 'The message has been forwarded to {selectedConversationName}', { selectedConversationName }) }}
					</template>
				</EmptyContent>

					<Button type="tertiary" @click="handleClose">
						{{ t('spreed', 'Dismiss') }}
					</Button>
					<Button type="primary" @click="openConversation">
						{{ t('spreed', 'Go to conversation') }}
					</Button>

		</Modal>

</template>

<template>

		<RoomSelector v-if="!showForwardedConfirmation"
			:container="container"
			:show-postable-only="true"
			:dialog-title="dialogTitle"
			:dialog-subtitle="dialogSubtitle"
			@select="setSelectedConversationToken"
			@close="handleClose" />

		<Modal v-else
			@close="handleClose">
			<EmptyContent icon="icon-checkmark" class="forwarded-confirmation__emptycontent">
				<template #desc>
					{{ t('spreed', 'The message has been forwarded to {selectedConversationName}', { selectedConversationName }) }}
				</template>
			</EmptyContent>

				<button @click="handleClose">
					{{ t('spreed', 'Dismiss') }}
				</button>
				<button class="primary" @click="openConversation">
					{{ t('spreed', 'Go to conversation') }}
				</button>

		</Modal>

</template>


feat(form wizard): simple vertical wizard

<template>

= index, 'current': currentStep === index}" :style="{ height: 100/steps.length + '%' }" v-for="(step, index) of steps">

        {{step.label}} Vertical

</template>

<template>

= index, 'current': currentStep === index}" :style="{ width: 100/steps.length + '%' }" v-for="(step, index) of steps">

        {{step.label}} Vertical

</template>