code:
import { basename, auth } from './utils'
import { get, paramsString, DEFAULT_TIMEOUT } from '../_utils/ajax'

export async function getRelationship (instanceName, accessToken, accountId) {
  let url = `${basename(instanceName)}/api/v1/accounts/relationships?${paramsString({ id: accountId })}`
  let res = await get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
  return res[0]
}


code:
import { auth, basename } from './utils'
import { post } from '../_utils/ajax'

export async function report (instanceName, accessToken, accountId, statusIds, comment, forward) {
  let url = `${basename(instanceName)}/api/v1/reports`
  return post(url, {
    account_id: accountId,
    status_ids: statusIds,
    comment,
    forward
  }, auth(accessToken))
}

repo_name: RayoGundead/pinny

license: mit


code:
import { post, WRITE_TIMEOUT } from '../_utils/ajax'
import { auth, basename } from './utils'

export async function approveFollowRequest (instanceName, accessToken, accountId) {
  let url = `${basename(instanceName)}/api/v1/follow_requests/${accountId}/authorize`
  return post(url, null, auth(accessToken), { timeout: WRITE_TIMEOUT })
}

export async function rejectFollowRequest (instanceName, accessToken, accountId) {
  let url = `${basename(instanceName)}/api/v1/follow_requests/${accountId}/reject`
  return post(url, null, auth(accessToken), { timeout: WRITE_TIMEOUT })
}


code:
import { get, paramsString, DEFAULT_TIMEOUT } from '../_utils/ajax'
import { auth, basename } from './utils'

export function search (instanceName, accessToken, query, resolve = true, limit = 40, signal = null) {
  let url = `${basename(instanceName)}/api/v1/search?` + paramsString({
    q: query,
    resolve,
    limit
  })
  return get(url, auth(accessToken), {
    timeout: DEFAULT_TIMEOUT,
    signal
  })
}


code:
import { auth, basename } from './utils'
import { post, WRITE_TIMEOUT } from '../_utils/ajax'

export function setShowReblogs (instanceName, accessToken, accountId, showReblogs) {
  let url = `${basename(instanceName)}/api/v1/accounts/${accountId}/follow`
  return post(url, { reblogs: !!showReblogs }, auth(accessToken), { timeout: WRITE_TIMEOUT })
}


code:
import { auth, basename } from './utils'
import { DEFAULT_TIMEOUT, get, post, WRITE_TIMEOUT } from '../_utils/ajax'

export async function postStatus (instanceName, accessToken, text, inReplyToId, mediaIds,
  sensitive, spoilerText, visibility, poll) {
  let url = `${basename(instanceName)}/api/v1/statuses`

  let body = {
    status: text,
    in_reply_to_id: inReplyToId,
    media_ids: mediaIds,
    sensitive: sensitive,
    spoiler_text: spoilerText,
    visibility: visibility,
    poll: poll
  }

  for (let key of Object.keys(body)) {
    let value = body[key]
    // remove any unnecessary fields, except 'status' which must at least be an empty string
    if (key !== 'status' && (!value || (Array.isArray(value) && !value.length))) {
      delete body[key]
    }
  }

  return post(url, body, auth(accessToken), { timeout: WRITE_TIMEOUT })
}

export async function getStatusContext (instanceName, accessToken, statusId) {
  let url = `${basename(instanceName)}/api/v1/statuses/${statusId}/context`
  return get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
}

export async function getStatus (instanceName, accessToken, statusId) {
  let url = `${basename(instanceName)}/api/v1/statuses/${statusId}`
  return get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
}


code:
import { get, paramsString, DEFAULT_TIMEOUT } from '../_utils/ajax'
import { auth, basename } from './utils'

function getTimelineUrlPath (timeline) {
  switch (timeline) {
    case 'local':
    case 'federated':
      return 'timelines/public'
    case 'home':
      return 'timelines/home'
    case 'notifications':
    case 'notifications/mentions':
      return 'notifications'
    case 'favorites':
      return 'favourites'
    case 'direct':
      return 'conversations'
  }
  if (timeline.startsWith('tag/')) {
    return 'timelines/tag'
  } else if (timeline.startsWith('account/')) {
    return 'accounts'
  } else if (timeline.startsWith('list/')) {
    return 'timelines/list'
  }
}

export async function getTimeline (instanceName, accessToken, timeline, maxId, since, limit) {
  let timelineUrlName = getTimelineUrlPath(timeline)
  let url = `${basename(instanceName)}/api/v1/${timelineUrlName}`

  if (timeline.startsWith('tag/')) {
    url += '/' + timeline.split('/')[1]
  } else if (timeline.startsWith('account/')) {
    url += '/' + timeline.split('/')[1] + '/statuses'
  } else if (timeline.startsWith('list/')) {
    url += '/' + timeline.split('/')[1]
  }

  let params = {}
  if (since) {
    params.since = since
  }

  if (maxId) {
    params.max_id = maxId
  }

  if (limit) {
    params.limit = limit
  }

  if (timeline === 'local') {
    params.local = true
  }

  if (timeline.startsWith('account/')) {
    if (timeline.endsWith('media')) {
      params.only_media = true
    } else {
      params.exclude_replies = !timeline.endsWith('/with_replies')
    }
  }

  if (timeline === 'notifications/mentions') {
    params.exclude_types = ['follow', 'favourite', 'reblog', 'poll']
  }

  url += '?' + paramsString(params)

  const items = await get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })

  if (timeline === 'direct') {
    return items.map(item => item.last_status)
  }
  return items
}


code:
import { WRITE_TIMEOUT, patch } from '../_utils/ajax'
import { auth, basename } from './utils'

export async function updateCredentials (instanceName, accessToken, accountData) {
  let url = `${basename(instanceName)}/api/v1/accounts/update_credentials`
  return patch(url, accountData, auth(accessToken), { timeout: WRITE_TIMEOUT })
}


code:
import { get, DEFAULT_TIMEOUT } from '../_utils/ajax'
import { auth, basename } from './utils'

export function getVerifyCredentials (instanceName, accessToken) {
  let url = `${basename(instanceName)}/api/v1/accounts/verify_credentials`
  return get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
}

export function getAccount (instanceName, accessToken, accountId) {
  let url = `${basename(instanceName)}/api/v1/accounts/${accountId}`
  return get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
}


code:
function targetIsLocalhost (instanceName) {
  return instanceName.startsWith('localhost:') || instanceName.startsWith('127.0.0.1:')
}

export function basename (instanceName) {
  if (targetIsLocalhost(instanceName)) {
    return `http://${instanceName}`
  }
  return `https://${instanceName}`
}

export function auth (accessToken) {
  return {
    'Authorization': `Bearer ${accessToken}`
  }
}


code:
  {#if loading}
    <LoadingPage />
  {:elseif accounts && accounts.length}

      {#each accounts as account}
        <AccountSearchResult
          {account}
          actions={accountActions}
          on:click="onClickAction(event)"
        />
      {/each}

  {/if}


code:
<div class="autoplay-wrapper  {$largeInlineMedia ? '' : 'autoplay-video-fixed-size'}"
     style="width: {width}px; height: {height}px;"
>
    <video
    class="autoplay-video {$largeInlineMedia ? '' : 'autoplay-video-fixed-size'}"
    aria-label={ariaLabel || ''}
    style="{focusStyle} background-image: url({poster}); "
    {poster}
    {width}
    {height}
    {src}
    autoplay
    muted
    loop
    webkit-playsinline
    playsinline
    />
</div>
<style>
  .autoplay-wrapper {
    position: relative;
    margin: 0;
    padding: 0;
  }

  .autoplay-video {
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
    display: flex;
  }

  .autoplay-video-fixed-size {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
  }
</style>
<script>
  import { store } from '../_store/store'
  import { coordsToPercent } from '../_utils/coordsToPercent'

  export default {

    data: () => ({
      focus: void 0
    }),
    store: () => store,
    computed: {
      focusStyle: ({ focus }) => {
        // Here we do a pure css version instead of using
        // https://github.com/jonom/jquery-focuspoint#1-calculate-your-images-focus-point

        if (!focus) return 'background-position: center;'
        return `object-position: ${coordsToPercent(focus.x)}% ${100 - coordsToPercent(focus.y)}%;`
      }
    }
  }
</script>


code:
{#if error}
<SvgIcon className={computedClass} style={svgStyle} href="#fa-user" />
{:elseif $autoplayGifs}
  <LazyImage
    className={computedClass}
    ariaHidden="true"
    forceSize=true
    alt=""
    src={account.avatar}
    {width}
    {height}
    on:imgLoad="set({loaded: true})"
    on:imgLoadError="set({error: true})" />
{:else}
  <NonAutoplayImg
    className={computedClass}
    ariaHidden="true"
    alt=""
    src={account.avatar}
    staticSrc={account.avatar_static}
    {width}
    {height}
    {isLink}
    on:imgLoad="set({loaded: true})"
    on:imgLoadError="set({error: true})"
  />
{/if}
<style>
  :global(.avatar) {
    border-radius: 4px;
    background: var(--loading-bg);
  }

  :global(.avatar.loaded) {
    background: none;
  }

  :global(svg.avatar) {
    fill: var(--deemphasized-text-color);
  }
</style>
<script>
  import { store } from '../_store/store'
  import NonAutoplayImg from './NonAutoplayImg.html'
  import { classname } from '../_utils/classname'
  import LazyImage from './LazyImage.html'
  import SvgIcon from './SvgIcon.html'

  export default {
    data: () => ({
      className: void 0,
      loaded: false,
      error: void 0,
      isLink: false,
      size: 'medium'
    }),
    store: () => store,
    computed: {
      computedClass: ({ className, loaded }) => (classname(
        'avatar',
        className,
        loaded && 'loaded'
      )),
      width: ({ size, $isMobileSize }) => {
        switch (size) {
          case 'extra-small':
            return 24
          case 'small':
            return 48
          case 'big':
            return $isMobileSize ? 80 : 100
          case 'medium':
          default:
            return 64
        }
      },
      height: ({ width }) => width,
      svgStyle: ({ width, height }) => `width: ${width}px; height: ${height}px;`
    },
    components: {
      NonAutoplayImg,
      LazyImage,
      SvgIcon
    }
  }
</script>


code:
  {#if icon}
  <SvgIcon className="dynamic-page-banner-svg" href={icon} />
  {/if}

{title}

  <button type="button"
          class="dynamic-page-go-back"
          aria-label="Go back"
          on:click|preventDefault="onGoBack()">Back</button>

<Shortcut key="Backspace" on:pressed="onGoBack()"/>


code:
  <slot></slot>{#if showIcon}
    <SvgIcon className="external-link-svg" href="#fa-external-link" />
  {/if}


code:
<div class="free-text">
  <slot></slot>
</div>
<style>
  :global(.free-text) {
    margin: 20px;
  }
  :global(.free-text p) {
    margin: 30px 0;
  }
  :global(.free-text) {
    overflow-y: auto; /* fixes weird iOS Safari bug where scrolling gets stuck */
  }
</style>


code:
<!-- toggled in template.html based on whether the user is logged in or not -->
<div class="hidden-from-ssr">
  <slot></slot>
</div>
<style>
  .hidden-from-ssr {
    opacity: 0;
  }
</style>


code:
<button type="button"
        title={label}
        aria-label={label}
        aria-pressed={pressable ? !!pressed : void 0}
        aria-hidden={ariaHidden}
        class={computedClass}
        {disabled}
        ref:node
>
  <SvgIcon className="icon-button-svg {svgClassName || ''}" ref:svg {href} />
</button>
<style>
  .icon-button {
    padding: 6px 10px;
    background: none;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  :global(.icon-button-svg) {
    width: 24px;
    height: 24px;
    fill: var(--action-button-fill-color);
    pointer-events: none; /* hack for Edge */
  }

  :global(.icon-button.big-icon .icon-button-svg) {
    width: 32px;
    height: 32px;
  }

  /*
   * regular styles
   */

  :global(.icon-button:hover .icon-button-svg) {
    fill: var(--action-button-fill-color-hover);
  }

  :global(.icon-button.not-pressable:active .icon-button-svg,
  .icon-button.same-pressed:active .icon-button-svg) {
    fill: var(--action-button-fill-color-active);
  }

  :global(.icon-button.pressed.not-same-pressed .icon-button-svg) {
    fill: var(--action-button-fill-color-pressed);
  }

  :global(.icon-button.pressed.not-same-pressed:hover .icon-button-svg) {
    fill: var(--action-button-fill-color-pressed-hover);
  }

  :global(.icon-button.pressed.not-same-pressed:active .icon-button-svg) {
    fill: var(--action-button-fill-color-pressed-active);
  }

  /*
   * muted
   */

  :global(.icon-button.muted-style .icon-button-svg) {
    fill: var(--action-button-deemphasized-fill-color);
  }

  :global(.icon-button.muted-style:hover .icon-button-svg) {
    fill: var(--action-button-deemphasized-fill-color-hover);
  }

  :global(.icon-button.muted-style.not-pressable:active .icon-button-svg,
  .icon-button.muted-style.same-pressed:active .icon-button-svg) {
    fill: var(--action-button-deemphasized-fill-color-active);
  }

  :global(.icon-button.muted-style.pressed.not-same-pressed .icon-button-svg) {
    fill: var(--action-button-deemphasized-fill-color-pressed);
  }

  :global(.icon-button.muted-style.pressed.not-same-pressed:hover .icon-button-svg) {
    fill: var(--action-button-deemphasized-fill-color-pressed-hover);
  }

  :global(.icon-button.muted-style.pressed.not-same-pressed:active .icon-button-svg) {
    fill: var(--action-button-deemphasized-fill-color-pressed-active);
  }

</style>
<script>
  import { classname } from '../_utils/classname'
  import { store } from '../_store/store'
  import SvgIcon from './SvgIcon.html'

  export default {
    oncreate () {
      let { clickListener, elementId } = this.get()
      if (clickListener) {
        this.onClick = this.onClick.bind(this)
        this.refs.node.addEventListener('click', this.onClick)
      }
      if (elementId) {
        this.refs.node.setAttribute('id', elementId)
      }
    },
    ondestroy () {
      let { clickListener } = this.get()
      if (clickListener) {
        this.refs.node.removeEventListener('click', this.onClick)
      }
    },
    data: () => ({
      big: false,
      muted: false,
      disabled: false,
      svgClassName: void 0,
      elementId: void 0,
      pressable: false,
      pressed: false,
      className: void 0,
      sameColorWhenPressed: false,
      ariaHidden: false,
      clickListener: true
    }),
    store: () => store,
    computed: {
      computedClass: ({ pressable, pressed, big, muted, sameColorWhenPressed, className }) => {
        return classname(
          'icon-button',
          !pressable && 'not-pressable',
          pressed && 'pressed',
          big && 'big-icon',
          muted && 'muted-style',
          sameColorWhenPressed ? 'same-pressed' : 'not-same-pressed',
          className
        )
      }
    },
    methods: {
      animate (animation) {
        this.refs.svg.animate(animation)
      },
      onClick (e) {
        this.fire('click', e)
      }
    },
    components: {
      SvgIcon
    }
  }
</script>


code:
<HiddenFromSSR>

      Pinafore is <ExternalLink href="https://github.com/nolanlawson/pinafore">open-source software</ExternalLink>
      created by <ExternalLink href="https://nolanlawson.com">Nolan Lawson</ExternalLink> and distributed under the
      <ExternalLink href="https://github.com/nolanlawson/pinafore/blob/master/LICENSE">AGPL License</ExternalLink>.
      Here is the privacy policy.

</HiddenFromSSR>


code:
<div class="lazy-image {fillFixSize ? 'lazy-image-fixed-size': ''}" style={computedStyle} >
  <img
    class="{fillFixSize ? 'fixed-size-img': ''}"
    aria-hidden={ariaHidden}
    {alt}
    {title}
    {width}
    {height}
    src={displaySrc}
    style={focusStyle}
    ref:node
  />
</div>
<style>
  .lazy-image {
    margin: 0;
    padding: 0;
    overflow: hidden;
    display: flex;
  }

  .fixed-size-img {
    width: 100%;
    height: 100%;
  }
  .lazy-image-fixed-size {
    position: absolute;
    width: 100%;
    height: 100%;
  }

</style>
<script>
  import { decodeImage } from '../_utils/decodeImage'
  import { coordsToPercent } from '../_utils/coordsToPercent'

  export default {
    async oncreate () {
      try {
        await decodeImage(this.refs.node)
      } catch (e) {
        this.set({ error: true })
      }
    },
    data: () => ({
      error: false,
      forceSize: false,
      fallback: void 0,
      focus: void 0,
      background: '',
      width: void 0,
      height: void 0,
      ariaHidden: false,
      alt: '',
      title: ''
    }),
    computed: {
      computedStyle: ({ background }) => {
        return [
          background && `background: ${background};`
        ].filter(Boolean).join('')
      },
      focusStyle: ({ focus }) => {
        // Here we do a pure css version instead of using
        // https://github.com/jonom/jquery-focuspoint#1-calculate-your-images-focus-point

        if (!focus) return 'background-position: center;'
        return `object-position: ${coordsToPercent(focus.x)}% ${100 - coordsToPercent(focus.y)}%;`
      },
      fillFixSize: ({ forceSize, $largeInlineMedia }) => !$largeInlineMedia && !forceSize,
      displaySrc: ({ error, src, fallback }) => ((error && fallback) || src)
    }
  }
</script>


code:
{#if revealed}
  <svelte:component this={pageComponent} {params} />
{/if}
<script>
  import { doubleRAF } from '../_utils/doubleRAF'
  // On the very first page load, avoid doing a "reveal" because
  // it leads to a flash between when the SSR is shown, the two frame we hide it,
  // and then when we show it again.
  //
  // We only really need LazyPage behavior when the user is clicking around
  // after the page has loaded, to improve input responsiveness.
  let firstTime = true

  export default {
    oncreate () {
      firstTime = false
      // Yes, triple raf. This is to ensure the NavItem animation plays before we
      // start rendering the new page.
      doubleRAF(() => requestAnimationFrame(() => this.set({ revealed: true })))
    },
    data: () => ({
      revealed: !process.browser || firstTime
    })
  }
</script>


code:
  {#if shown}

      <LoadingSpinner maskStyle="true"/>

  {/if}


code:
<div class="loading-page {shown ? '' : 'hidden'}">
  <LoadingSpinner />
</div>
<style>
  .loading-page {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 150px;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 50;
    transition: opacity 0.25s linear;
  }
</style>
<script>
  import LoadingSpinner from './LoadingSpinner.html'

  const SPINNER_DELAY = 700

  export default {
    oncreate () {
      setTimeout(() => {
        this.set({ shown: true })
      }, SPINNER_DELAY)
    },
    data: () => ({
      shown: false
    }),
    components: {
      LoadingSpinner
    }
  }
</script>


code:
<SvgIcon className="loading-spinner-icon spin {maskStyle ? 'mask-style' : ''}"
         style="width: {size}px; height: {size}px;"
         href="#fa-spinner"
         ariaLabel="Loading"
/>
<style>
  :global(.loading-spinner-icon) {
    fill: var(--svg-fill);
  }

  :global(.loading-spinner-icon.mask-style) {
    fill: var(--mask-svg-fill);
  }
</style>
<script>
  import SvgIcon from './SvgIcon.html'

  export default {
    data: () => ({
      maskStyle: false,
      size: 64
    }),
    components: {
      SvgIcon
    }
  }
</script>


code:
    {#each $navPages as navPage (navPage.href)}

        <NavItem
          {page}
          name={navPage.name}
          href={navPage.href}
          svg={navPage.svg}
          label={navPage.label}
        />

    {/each}

{#await importNavShortcuts}

{:then component}
<svelte:component this={component}/>
{:catch error}

{/await}


code:
    <NavItemIcon
      {showBadge}
      {badgeNumber}
      {svg}
    />
    {label}


code:
{#if showBadge}

    <SvgIcon className="nav-link-svg" href={svg} />

      {badgeNumberToShow}

{:else}
  <SvgIcon className="nav-link-svg" href={svg} />
{/if}


code:
<Shortcut key="g t" on:pressed="goto('/federated')"/>
<Shortcut key="g f" on:pressed="goto('/favorites')"/>
<Shortcut key="g l" on:pressed="goto('/local')"/>
<Shortcut key="g h" on:pressed="goto('/')"/>
<Shortcut key="g n" on:pressed="goto('/notifications')"/>
<Shortcut key="g c" on:pressed="goto('/community')"/>
<Shortcut key="g d" on:pressed="goto('/direct')"/>
<Shortcut key="s" on:pressed="goto('/search')"/>
<Shortcut key="h|?" on:pressed="showShortcutHelpDialog()"/>
<Shortcut key="c" on:pressed="showComposeDialog()"/>
{#each $navPages as navPage, i}
  <Shortcut key={(i + 1).toString()} on:pressed="goto(navPage.href)" />
{/each}

<script>
  import Shortcut from './shortcut/Shortcut'
  import { goto } from '../../../__sapper__/client'
  import { importShowShortcutHelpDialog, importShowComposeDialog } from './dialog/asyncDialogs'
  import { store } from '../_store/store'

  export default {
    store: () => store,
    components: {
      Shortcut
    },
    methods: {
      goto,
      async showShortcutHelpDialog () {
        let showShortcutHelpDialog = await importShowShortcutHelpDialog()
        showShortcutHelpDialog()
      },
      async showComposeDialog () {
        let showComposeDialog = await importShowComposeDialog()
        showComposeDialog()
      }
    }
  }
</script>


code:
<div class="non-autoplay-gifv  {$largeInlineMedia ? '' : 'non-autoplay-gifv-fixed-size'}"
     style="width: {width}px; height: {height}px;"
     on:mouseover="onMouseOver(event)"
     ref:node
>
  {#if playing}
    <AutoplayVideo
      ariaLabel={label}
      {poster}
      {src}
      {width}
      {height}
      {focus}
      />
  {:else}
    <LazyImage
      alt={label || ''}
      title={label || ''}
      src={staticSrc}
      fallback={oneTransparentPixel}
      {width}
      {height}
      background="var(--loading-bg)"
      {focus}
    />
  {/if}
  <PlayVideoIcon className={playing ? 'hidden' : ''}/>
</div>
<style>
  .non-autoplay-gifv {
    cursor: zoom-in;
    display: flex;
    position: relative;
    margin: 0;
    padding: 0;
  }

  .non-autoplay-gifv-fixed-size {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
  }

  :global(.non-autoplay-gifv .play-video-icon) {
    transition: opacity 0.2s linear;
  }
</style>
<script>
  import { mouseover } from '../_utils/events'
  import PlayVideoIcon from './PlayVideoIcon.html'
  import { ONE_TRANSPARENT_PIXEL } from '../_static/media'
  import LazyImage from './LazyImage.html'
  import AutoplayVideo from './AutoplayVideo.html'

  export default {
    methods: {
      onMouseOver (mouseOver) {
        this.set({ playing: mouseOver })
      }
    },
    events: {
      mouseover
    },
    data: () => ({
      oneTransparentPixel: ONE_TRANSPARENT_PIXEL,
      focus: void 0
    }),
    components: {
      PlayVideoIcon,
      LazyImage,
      AutoplayVideo
    }
  }
</script>


code:
<img
  class={computedClass}
  aria-hidden={ariaHidden}
  {alt}
  {title}
  {width}
  {height}
  src={displaySrc}
  on:mouseover="onMouseOver(event)"
  ref:node
/>
<style>
  .non-autoplay-zoom-in {
    cursor: zoom-in;
  }
  .non-autoplay-zoom-in.is-link {
    cursor: pointer;
  }
</style>
<script>
  import { mouseover } from '../_utils/events'
  import { decodeImage } from '../_utils/decodeImage'
  import { classname } from '../_utils/classname'
  import { ONE_TRANSPARENT_PIXEL } from '../_static/media'

  export default {
    async oncreate () {
      let { currentSrc } = this.get()
      try {
        let image = new Image()
        image.src = currentSrc
        await decodeImage(image)
        this.set({ loaded: true })
        this.fire('imgLoad')
      } catch (e) {
        this.fire('imgLoadError', e)
      }
    },
    methods: {
      onMouseOver (mouseOver) {
        this.set({ mouseOver })
      }
    },
    events: {
      mouseover
    },
    data: () => ({
      alt: '',
      title: '',
      mouseOver: false,
      loaded: false,
      ariaHidden: false
    }),
    computed: {
      computedClass: ({ className, src, staticSrc, isLink }) => (classname(
        className,
        src !== staticSrc && 'non-autoplay-zoom-in',
        isLink && 'is-link'
      )),
      currentSrc: ({ mouseOver, src, staticSrc }) => mouseOver ? src : staticSrc,
      // using a transparent pixel as placeholder ensures broken images don't have wrong sizes
      displaySrc: ({ loaded, currentSrc }) => loaded ? currentSrc : ONE_TRANSPARENT_PIXEL
    }
  }
</script>


code:
<HiddenFromSSR>
  <FreeTextLayout>

        <SvgIcon className="not-logged-in-home-svg" href="#pinafore-logo" />

Pinafore

Pinafore is a web client for <ExternalLink href="https://joinmastodon.org">Mastodon</ExternalLink>, designed for speed and simplicity.

Read the <ExternalLink href="https://nolanlawson.com/2018/04/09/introducing-pinafore-for-mastodon/">introductory blog post</ExternalLink>, or get started by logging in to an instance:

Add instance

  </FreeTextLayout>
</HiddenFromSSR>


code:
<TabSet
  label="Filters"
  currentTabName={filter}
  {tabs}
  className="notification-filters"
/>
<script>
  import TabSet from './TabSet.html'

  export default {
    data: () => ({
      tabs: [
        {
          name: '',
          label: 'All',
          href: `/notifications`
        },
        {
          name: 'mentions',
          label: 'Mentions',
          href: `/notifications/mentions`
        }
      ]
    }),
    components: {
      TabSet
    }
  }
</script>


code:
<div class="play-video-icon {className || ''}">
  <SvgIcon className="play-video-icon-svg" href="#fa-play-circle" />
</div>
<style>
  .play-video-icon {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 40;
    pointer-events: none;
  }
  :global(.play-video-icon-svg) {
    width: 72px;
    height: 72px;
    fill: var(--mask-svg-fill);
    border-radius: 100%;
    background: var(--mask-opaque-bg);
  }
</style>
<script>
  import SvgIcon from './SvgIcon.html'

  export default {
    data: () => ({
      className: void 0
    }),
    components: {
      SvgIcon
    }
  }
</script>


code:
  <select on:change aria-label={label}>
    {#each options as option (option.value)}
      <option value="{option.value}" selected="{option.value === defaultValue ? 'selected' : ''}">
        {option.label}
      </option>
    {/each}
  </select>

    <SvgIcon href="#fa-angle-down" className="select-dropdown-icon"/>


code:
{@html `

Global

c to compose a new toot

s to search

1 - 6 to switch columns

g + h to go home

g + n to go to notifications

g + l to go to the local timeline

g + t to go to the federated timeline

g + c to go to the community page

g + d to go to the conversations page

h or ? to toggle the help dialog

Backspace to go back, close dialogs

Timeline

j or ↓ to activate the next toot

k or ↑ to activate the previous toot

. to show more and scroll to top

o to open

f to favorite

b to boost

r to reply

m to mention the author

p to open the author's profile

l to open the card's link in a new tab

x to show or hide text behind content warning

y to show or hide sensitive media

Media

← - → to go to next or previous

`}


code:
<svg
  class={className}
  {style}
  aria-hidden={!ariaLabel}
  aria-label={ariaLabel}
  ref:svg>
  <use xlink:href="{inline ? '' : '/icons.svg'}{href}" />
</svg>
<script>
  import { animate } from '../_utils/animate'
  import { store } from '../_store/store'

  export default {
    data: () => ({
      className: '',
      style: '',
      ariaLabel: ''
    }),
    store: () => store,
    computed: {
      inline: ({ href }) => {
        // filled in during build
        return process.env.INLINE_SVGS.includes(href)
      }
    },
    methods: {
      animate (animation) {
        let { reduceMotion } = this.store.get()
        if (!animation || reduceMotion) {
          return
        }
        let svg = this.refs.svg
        animate(svg, animation)
      }
    }
  }
</script>


code:
    {#each tabs as tab (tab.name)}

        {tab.label}

    {/each}


code:
  {#if hidePage}
    <LoadingPage />
  {/if}
  <ComposeBox realm="home" hidden={hidePage}/>

    {#if !hidePage && hideTimeline}
      <LoadingPage />
    {/if}

      <LazyTimeline timeline="home" />


code:
  {#if hidePage}
    <LoadingPage />
  {/if}

    <slot></slot>

    {#if !hidePage && hideTimeline}
      <LoadingPage />
    {/if}

      <LazyTimeline {timeline} />


code:
<svelte:head>
  <title>{notificationsIndicator}{instanceIndicator} · {name}</title>
</svelte:head>
<script>
  import { store } from '../_store/store'

  export default {
    data: () => ({
      settingsPage: false
    }),
    store: () => store,
    computed: {
      instanceIndicator: ({ $isUserLoggedIn, $currentInstance, settingsPage }) => (
        // If the user is not logged in, or if they're on a settings page (which
        // is more general than instance-specific), of if this is server-rendered, then
        // show "Pinafore". Otherwise show the instance name.
        `${($isUserLoggedIn && !settingsPage && $currentInstance) ? $currentInstance : 'Pinafore'}`
      ),
      notificationsIndicator: ({ $hasNotifications, $numberOfNotifications }) => (
        $hasNotifications ? `(${$numberOfNotifications}) ` : ''
      )
    }
  }
</script>


code:
  {text}

  {tooltipText}


code:
  {#if label}

    <slot></slot>

  {:else}

    <slot></slot>

  {/if}


code:
    <SvgIcon className="page-list-item-svg" href={icon} />

      {label}

    {#if pinnable}
      <IconButton pressable="true"
                  pressed={$pinnedPage === href}
                  label={$pinnedPage === href ? 'Unpin timeline' : 'Pin timeline'}
                  href="#fa-thumb-tack"
                  on:click="onPinClick(event)" />
    {/if}


code:
  <Avatar account={verifyCredentials} size="small"/>

  <AccountDisplayName account={verifyCredentials} />

  {'@' + verifyCredentials.acct}


code:
  <ComposeAutosuggestionList
    items={autosuggestSearchResults}
    on:click="onClick(event)"
    type={autosuggestType}
    selected={autosuggestSelected}
    {realm}
  />

    {assertiveAriaText}


code:
  {#each items as item, i (item.shortcode ? `emoji-${item.shortcode}` : `account-${item.id}`)}
  <li id="{i === selected ? `compose-autosuggest-active-item-${realm}` : ''}"
      class="compose-autosuggest-list-item {i === selected ? 'selected' : ''}"
      role="option"
      aria-selected="{i === selected}"
      aria-label="{ariaLabels[i]}"
      on:click="onClick(event, item)"
  >

        {#if type === 'account'}

          <Avatar
            size="small"
            account={item}
          />

            <AccountDisplayName account={item} />

            {'@' + item.acct}

        {:else}

              {':' + item.shortcode + ':'}

        {/if}

  {/each}


code:
{#if realm === 'home'}

Compose status

{/if}
<ComposeFileDrop {realm} >

    <ComposeAuthor {realm} {dialogId} />
    {#if contentWarningShown}

        <ComposeContentWarning {realm} {contentWarning} />

    {/if}
    <ComposeInput {realm} {text} {autoFocus} on:postAction="doPostStatus()" />
    <ComposeLengthGauge {length} {overLimit} />
    <ComposeAutosuggest {realm} {text} />
    {#if poll && poll.options && poll.options.length}

        <ComposePoll {realm} {poll} />

    {/if}
    <ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} {poll} />
    <ComposeLengthIndicator {length} {overLimit} />
    <ComposeMedia {realm} {media} />

</ComposeFileDrop>
<ComposeStickyButton {showSticky}
                     {overLimit}
                     {hideAndFadeIn}
                     on:postAction="doPostStatus()" />
{#if !hideBottomBorder}

{/if}


code:
  <button class="primary compose-box-button"
          {disabled}
          aria-label={sticky ? 'Compose' : 'Toot!'}
          on:click>

      Toot!

      <SvgIcon className="compose-box-button-svg {$postingStatus ? 'spin' : 'hidden'}"
               href="#fa-spinner" />

      <SvgIcon className="compose-box-button-svg"
               href="#fa-pencil" />

  </button>


code:
<input class="content-warning-input"
       type="text"
       placeholder="Content warning"
       aria-label="Content warning"
       bind:value=rawText
/>
<style>
  .content-warning-input {
    font-size: 1.2em;
    margin: 10px 0 0 5px;
    padding: 10px;
    border: 1px solid var(--input-border);
    width: calc(100% - 5px);
  }
</style>
<script>
  import { store } from '../../_store/store'
  import debounce from 'lodash-es/debounce'
  import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
  import { observe } from 'svelte-extras'

  export default {
    oncreate () {
      this.setupSyncFromStore()
      this.setupSyncToStore()
    },
    store: () => store,
    data: () => ({
      rawText: ''
    }),
    methods: {
      observe,
      setupSyncFromStore () {
        this.observe('contentWarning', contentWarning => {
          this.set({ rawText: contentWarning })
        })
      },
      setupSyncToStore () {
        const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)

        this.observe('rawText', rawText => {
          let { realm } = this.get()
          this.store.setComposeData(realm, {
            contentWarning: rawText
          })
          saveText()
        }, { init: false })
      }
    }
  }
</script>


code:
<file-drop class="file-drop file-drop-realm-{realm}" accept={mediaAccept} ref:fileDrop >

      Drop to upload
      Invalid file type

  <slot></slot>
</file-drop>


code:
<textarea
  id="the-compose-box-input-{realm}"
  class="compose-box-input compose-box-input-realm-{realm}"
  placeholder="What's on your mind?"
  aria-describedby="compose-box-input-description-{realm}"
  aria-owns="compose-autosuggest-list-{realm}"
  aria-expanded={autosuggestShownForThisInput}
  aria-autocomplete="both"
  aria-activedescendant="{autosuggestShownForThisInput ? `compose-autosuggest-active-item-${realm}` : ''}"
  ref:textarea
  bind:value=rawText
  on:blur="onBlur()"
  on:focus="onFocus()"
  on:selectionChange="onSelectionChange(event)"
  on:keydown="onKeydown(event)"
></textarea>
<label for="the-compose-box-input-{realm}" class="sr-only">
  What's on your mind?
</label>
<span id="compose-box-input-description-{realm}" class="sr-only">
  When autocomplete results are available, press up or down arrows and enter to select.
</span>
<style>
  .compose-box-input {
    grid-area: input;
    margin: 10px 0 0 5px;
    padding: 10px;
    border: 1px solid var(--input-border);
    min-height: 75px;
    resize: none;
    overflow: hidden;
    word-wrap: break-word;
    /* Text must be at least 16px or else iOS Safari zooms in */
    font-size: 1.2em;
    /* Hack to make Edge stretch the element all the way to the right.
     * Also desktop Safari makes the gauge stretch too far to the right without it.
     */
    width: calc(100% - 5px);
  }

  .compose-box-input-realm-dialog {
    max-height: 40vh;
    overflow-y: auto;
  }

  @media (max-width: 767px) {
    .compose-box-input-realm-dialog {
      max-height: 30vh;
    }
  }

  @media (max-width: 479px) {
    .compose-box-input-realm-dialog {
      max-height: 20vh;
    }
  }
</style>
<script>
  import { store } from '../../_store/store'
  import { autosize } from '../../_thirdparty/autosize/autosize'
  import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
  import debounce from 'lodash-es/debounce'
  import { mark, stop } from '../../_utils/marks'
  import { selectionChange } from '../../_utils/events'
  import {
    clickSelectedAutosuggestionUsername,
    clickSelectedAutosuggestionEmoji
  } from '../../_actions/autosuggest'
  import { observe } from 'svelte-extras'
  import { get } from '../../_utils/lodash-lite'

  export default {
    oncreate () {
      this.setupSyncFromStore()
      this.setupSyncToStore()
      this.setupAutosize()
    },
    ondestroy () {
      this.teardownAutosize()
    },
    methods: {
      observe,
      setupSyncFromStore () {
        let textarea = this.refs.textarea
        let firstTime = true
        this.observe('text', text => {
          let { rawText } = this.get()
          if (rawText !== text) {
            this.set({ rawText: text })
            // this next autosize is required to resize after
            // the user clicks the "toot" button
            mark('autosize.update()')
            autosize.update(textarea)
            stop('autosize.update()')
          }
          if (firstTime) {
            firstTime = false
            let { autoFocus } = this.get()
            if (autoFocus) {
              requestAnimationFrame(() => textarea.focus({ preventScroll: true }))
            }
          }
        })
      },
      setupSyncToStore () {
        const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)

        this.observe('rawText', rawText => {
          mark('observe rawText')
          let { realm } = this.get()
          this.store.setComposeData(realm, { text: rawText })
          saveStore()
          stop('observe rawText')
        }, { init: false })
      },
      setupAutosize () {
        let textarea = this.refs.textarea
        requestAnimationFrame(() => {
          mark('autosize()')
          autosize(textarea)
          stop('autosize()')
        })
      },
      teardownAutosize () {
        mark('autosize.destroy()')
        autosize.destroy(this.refs.textarea)
        stop('autosize.destroy()')
      },
      onBlur () {
        requestAnimationFrame(() => {
          this.store.setForCurrentAutosuggest({ composeFocused: false })
        })
      },
      onFocus () {
        scheduleIdleTask(() => {
          let { realm } = this.get()
          this.store.set({ currentComposeRealm: realm })
          this.store.setForCurrentAutosuggest({ composeFocused: true })
        })
      },
      onSelectionChange (selectionStart) {
        scheduleIdleTask(() => {
          this.store.setForCurrentAutosuggest({ composeSelectionStart: selectionStart })
        })
      },
      onKeydown (e) {
        let { keyCode } = e
        // ctrl or cmd (on macs) was pressed; ctrl-enter means post a toot
        const ctrlPressed = e.getModifierState('Control') || e.getModifierState('Meta')
        switch (keyCode) {
          case 9: // tab
            this.clickSelectedAutosuggestion(e)
            break
          case 13: // enter
            const autosuggestionClicked = this.clickSelectedAutosuggestion(e)
            if (!autosuggestionClicked && ctrlPressed) {
              this.fire('postAction')
            }
            break
          case 38: // up
            this.incrementAutosuggestSelected(-1, e)
            break
          case 40: // down
            this.incrementAutosuggestSelected(1, e)
            break
          case 27: // escape
            this.clearAutosuggestions(e)
            break
          default:
        }
      },
      clickSelectedAutosuggestion (event) {
        let {
          autosuggestShown,
          autosuggestType
        } = this.store.get()
        if (!autosuggestShown) {
          return false
        }
        event.preventDefault()
        event.stopPropagation()

        const clickAutosuggestedItem = async () => {
          let { realm } = this.get()
          /* autosuggestSelecting prevents a flash of searched content */
          this.store.setForCurrentAutosuggest({ autosuggestSelecting: true })
          if (autosuggestType === 'account') {
            await clickSelectedAutosuggestionUsername(realm)
          } else { // emoji
            await clickSelectedAutosuggestionEmoji(realm)
          }
          this.store.setForCurrentAutosuggest({ autosuggestSelecting: false })
        }

        /* no await */ clickAutosuggestedItem()
        return true
      },
      incrementAutosuggestSelected (increment, event) {
        let {
          autosuggestShown,
          autosuggestSelected,
          autosuggestSearchResults
        } = this.store.get()
        if (!autosuggestShown) {
          return
        }
        autosuggestSelected += increment
        if (autosuggestSelected >= 0) {
          autosuggestSelected = autosuggestSelected % autosuggestSearchResults.length
        } else {
          autosuggestSelected = autosuggestSearchResults.length + autosuggestSelected
        }
        this.store.setForCurrentAutosuggest({ autosuggestSelected })
        event.preventDefault()
        event.stopPropagation()
      },
      clearAutosuggestions (event) {
        let { autosuggestShown } = this.store.get()
        if (!autosuggestShown) {
          return
        }
        this.store.setForCurrentAutosuggest({
          autosuggestSearchResults: [],
          autosuggestSelected: 0
        })
        event.preventDefault()
        event.stopPropagation()
      }
    },
    store: () => store,
    data: () => ({
      rawText: ''
    }),
    computed: {
      /* eslint-disable camelcase */
      composeFocused: ({ $autosuggestData_composeFocused, $currentInstance, realm }) => (
        get($autosuggestData_composeFocused, [$currentInstance, realm], false)
      ),
      /* eslint-enable camelcase */
      autosuggestShownForThisInput: ({ realm, $autosuggestShown, composeFocused }) => (
        !!($autosuggestShown && composeFocused)
      )
    },
    events: {
      selectionChange
    }
  }
</script>


code:
<span class="compose-box-length {overLimit ? 'over-char-limit' : ''}"
      aria-label={lengthLabel}>
      {lengthToDisplayDeferred}
    </span>
<style>
  .compose-box-length {
    grid-area: length;
    justify-self: right;
    color: var(--length-indicator-color);
    font-size: 1.3em;
    align-self: center;
  }

  .compose-box-length.over-char-limit {
    color: var(--warning-color);
  }
</style>
<script>
  import { mark, stop } from '../../_utils/marks'
  import { store } from '../../_store/store'
  import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
  import { observe } from 'svelte-extras'

  export default {
    oncreate () {
      let { lengthToDisplay } = this.get()
      this.set({ lengthToDisplayDeferred: lengthToDisplay })
      // perf improvement for keyboard input latency
      this.observe('lengthToDisplay', () => {
        scheduleIdleTask(() => {
          mark('set lengthToDisplayDeferred')
          let { lengthToDisplay } = this.get()
          this.set({ lengthToDisplayDeferred: lengthToDisplay })
          stop('set lengthToDisplayDeferred')
        })
      }, { init: false })
    },
    data: () => ({
      lengthToDisplayDeferred: 0
    }),
    store: () => store,
    computed: {
      lengthToDisplay: ({ length, $maxStatusChars }) => $maxStatusChars - length,
      lengthLabel: ({ overLimit, lengthToDisplayDeferred }) => {
        if (overLimit) {
          return `${lengthToDisplayDeferred} characters over limit`
        } else {
          return `${lengthToDisplayDeferred} characters remaining`
        }
      }
    },
    methods: {
      observe
    }
  }
</script>


code:
{#if media.length}
  <ul class="compose-media-container"
      aria-label="Media uploads"
      style="grid-template-columns: repeat({media.length}, 1fr);"
  >
    {#each media as mediaItem, index}
      <ComposeMediaItem {realm} {mediaItem} {index} {media} />
    {/each}
  </ul>
{/if}
<style>
  .compose-media-container {
    grid-area: media;
    list-style: none;
    display: grid;
    grid-column-gap: 5px;
    align-items: center;
    justify-content: center;
    margin: 10px 0 0 0;
    background: var(--form-bg);
    padding: 5px;
    border-radius: 4px;
  }
</style>
<script>
  import { store } from '../../_store/store'
  import ComposeMediaItem from './ComposeMediaItem.html'

  export default {
    store: () => store,
    components: {
      ComposeMediaItem
    }
  }
</script>


code:
    <button class="compose-media-delete-button"
            aria-label="Delete {shortName}"
            on:click="onDeleteMedia()" >
      <SvgIcon className="compose-media-delete-button-svg" href="#fa-times" />
    </button>

    <textarea id="compose-media-input-{uuid}"
           class="compose-media-alt-input"
           placeholder="Describe for the visually impaired"
           ref:textarea
           bind:value=rawText
    ></textarea>

      Describe {shortName} for the visually impaired


code:
    {#each poll.options as option, i}
      <input id="poll-option-{realm}-{i}"
             type="text"
             maxlength="25"
             on:change="onChange(i)"
             placeholder="Choice {i + 1}"

      >
      <IconButton
        label="Remove choice {i + 1}"
        href="#fa-times"
        muted={true}
        on:click="onDeleteClick(i)"
      />
    {/each}

    <input type="checkbox"
           id="poll-option-multiple-{realm}"
           on:change="onMultipleChange()"
    >

      Multiple choice

    <Select className="poll-expiry-select"
            options={pollExpiryOptions}
            defaultValue={pollExpiryDefaultValue}
            on:change="onExpiryChange(event)"
            label="Poll duration"
    />

  <IconButton
    className="add-poll-choice-button"
    label="Add choice"
    href="#fa-plus"
    muted={true}
    disabled={poll.options.length === 4}
    on:click="onAddClick()"
  />
  {#each poll.options as option, i}

      Choice {i + 1}

  {/each}


code: <ComposeButton {overLimit} {sticky} on:click="onClickButton()" />


code:
    <IconButton
      className="compose-toolbar-button"
      label="Insert emoji"
      href="#fa-smile"
      on:click="onEmojiClick()"
    />
    <IconButton
      className="compose-toolbar-button"
      svgClassName={$uploadingMedia ? 'spin' : ''}
      label="Add media"
      href={$uploadingMedia ? '#fa-spinner' : '#fa-camera'}
      on:click="onMediaClick()"
      disabled={$uploadingMedia || (media.length === 4)}
    />
    <IconButton
      className="compose-toolbar-button"
      label="{poll && poll.options && poll.options.length ? 'Remove poll' : 'Add poll'}"
      href="#fa-bar-chart"
      on:click="onPollClick()"
      pressable="true"
      pressed={poll && poll.options && poll.options.length}
    />
    <IconButton
      className="compose-toolbar-button"
      label="Adjust privacy (currently {postPrivacy.label})"
      href={postPrivacy.icon}
      on:click="onPostPrivacyClick()"
    />
    <IconButton
      className="compose-toolbar-button"
      label={contentWarningShown ? 'Remove content warning' : 'Add content warning'}
      href="#fa-exclamation-triangle"
      on:click="onContentWarningClick()"
      pressable="true"
      pressed={contentWarningShown}
    />

  <input ref:input
         on:change="onFileChange(event)"
         style="display: none;"
         type="file"
         accept={mediaAccept} >


code:
const getDefault = mod => mod.default

export const importShowAccountProfileOptionsDialog = () => import(
  /* webpackChunkName: 'showAccountProfileOptionsDialog' */ './creators/showAccountProfileOptionsDialog'
  ).then(getDefault)

export const importShowComposeDialog = () => import(
  /* webpackChunkName: 'showComposeDialog' */ './creators/showComposeDialog'
  ).then(getDefault)

export const importShowTextConfirmationDialog = () => import(
  /* webpackChunkName: 'showTextConfirmationDialog' */ './creators/showTextConfirmationDialog'
  ).then(getDefault)

export const importShowEmojiDialog = () => import(
  /* webpackChunkName: 'showEmojiDialog' */ './creators/showEmojiDialog'
  ).then(getDefault)

export const importShowPostPrivacyDialog = () => import(
  /* webpackChunkName: 'showPostPrivacyDialog' */ './creators/showPostPrivacyDialog'
  ).then(getDefault)

export const importShowStatusOptionsDialog = () => import(
  /* webpackChunkName: 'showStatusOptionsDialog' */ './creators/showStatusOptionsDialog'
  ).then(getDefault)

export const importShowCopyDialog = () => import(
  /* webpackChunkName: 'showCopyDialog' */ './creators/showCopyDialog'
  ).then(getDefault)

export const importShowShortcutHelpDialog = () => import(
  /* webpackChunkName: 'showShortcutHelpDialog' */ './creators/showShortcutHelpDialog'
  ).then(getDefault)

export const importShowMediaDialog = () => import(
  /* webpackChunkName: 'showMediaDialog' */ './creators/showMediaDialog'
  ).then(getDefault)

export const importShowMuteDialog = () => import(
  /* webpackChunkName: 'showMuteDialog' */ './creators/showMuteDialog'
  ).then(getDefault)

export const importShowReportDialog = () => import(
  /* webpackChunkName: 'showReportDialog' */ './creators/showReportDialog'
  ).then(getDefault)


code:
<ModalDialog
  {id}
  {label}
  {title}
  shrinkWidthToFit={true}
  background="var(--main-bg)"
>
  <GenericDialogList selectable={false} {items} on:click="onClick(event)"/>
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../../_store/store'
import GenericDialogList from './GenericDialogList.html'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { oncreate } from '../helpers/onCreateDialog'
import { setAccountBlocked } from '../../../_actions/block'
import { setAccountFollowed } from '../../../_actions/follow'
import { setShowReblogs } from '../../../_actions/setShowReblogs'
import { setDomainBlocked } from '../../../_actions/setDomainBlocked'
import { copyText } from '../../../_actions/copyText'
import { composeNewStatusMentioning } from '../../../_actions/mention'
import { toggleMute } from '../../../_actions/toggleMute'
import { reportStatusOrAccount } from '../../../_actions/report'

export default {
  oncreate,
  store: () => store,
  computed: {
    //
    // begin copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html)
    //
    verifyCredentialsId: ({ verifyCredentials }) => verifyCredentials.id,
    following: ({ relationship }) => relationship && relationship.following,
    followRequested: ({ relationship }) => relationship && relationship.requested,
    accountId: ({ account }) => account && account.id,
    acct: ({ account }) => account.acct,
    muting: ({ relationship }) => relationship && relationship.muting,
    blocking: ({ relationship }) => relationship && relationship.blocking,
    followLabel: ({ following, followRequested, account, acct }) => {
      if (typeof following === 'undefined' || !account) {
        return ''
      }
      return (following || followRequested)
        ? `Unfollow @${acct}`
        : `Follow @${acct}`
    },
    followIcon: ({ following, followRequested }) => (
      following ? '#fa-user-times' : followRequested ? '#fa-hourglass' : '#fa-user-plus'
    ),
    blockLabel: ({ blocking, acct }) => (
      blocking ? `Unblock @${acct}` : `Block @${acct}`
    ),
    blockIcon: ({ blocking }) => blocking ? '#fa-unlock' : '#fa-ban',
    muteLabel: ({ muting, acct }) => (
      muting ? `Unmute @${acct}` : `Mute @${acct}`
    ),
    muteIcon: ({ muting }) => muting ? '#fa-volume-up' : '#fa-volume-off',
    isUser: ({ accountId, verifyCredentialsId }) => accountId === verifyCredentialsId,
    //
    // end copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html)
    //
    showingReblogs: ({ relationship }) => relationship ? relationship.showing_reblogs : true,
    showReblogsLabel: ({ showingReblogs, acct }) => (
      showingReblogs
        ? `Hide boosts from @${acct}`
        : `Show boosts from @${acct}`
    ),
    domain: ({ acct }) => acct.split('@')[1],
    blockingDomain: ({ relationship }) => relationship && relationship.domain_blocking,
    blockDomainLabel: ({ blockingDomain, domain }) => (
      blockingDomain
        ? `Unhide ${domain}`
        : `Hide ${domain}`
    ),
    reportLabel: ({ acct }) => `Report @${acct}`,
    items: ({
      blockLabel, blocking, blockIcon, muteLabel, muteIcon,
      followLabel, followIcon, following, followRequested,
      accountId, verifyCredentialsId, acct, isUser, showReblogsLabel,
      domain, blockDomainLabel, reportLabel
    }) => ([
      !isUser && {
        key: 'mention',
        label: `Mention @${acct}`,
        icon: '#fa-comments'
      },
      !isUser && !blocking && {
        key: 'follow',
        label: followLabel,
        icon: followIcon
      },
      !isUser && {
        key: 'block',
        label: blockLabel,
        icon: blockIcon
      },
      !isUser && !blocking && {
        key: 'mute',
        label: muteLabel,
        icon: muteIcon
      },
      !isUser && following && {
        key: 'showReblogs',
        label: showReblogsLabel,
        icon: '#fa-retweet'
      },
      !isUser && domain && {
        key: 'blockDomain',
        label: blockDomainLabel,
        icon: '#fa-ban'
      },
      !isUser && {
        key: 'report',
        label: reportLabel,
        icon: '#fa-flag'
      },
      {
        key: 'copy',
        label: 'Copy link to account',
        icon: '#fa-link'
      }
    ].filter(Boolean))
  },
  methods: {
    show,
    close,
    onClick (item) {
      switch (item.key) {
        case 'mention':
          return this.onMentionClicked()
        case 'follow':
          return this.onFollowClicked()
        case 'block':
          return this.onBlockClicked()
        case 'mute':
          return this.onMuteClicked()
        case 'showReblogs':
          return this.onShowReblogsClicked()
        case 'blockDomain':
          return this.onBlockDomainClicked()
        case 'copy':
          return this.onCopyClicked()
        case 'report':
          return this.onReport()
      }
    },
    async onMentionClicked () {
      let { account } = this.get()
      this.close()
      await composeNewStatusMentioning(account)
    },
    async onFollowClicked () {
      let { accountId, following } = this.get()
      this.close()
      await setAccountFollowed(accountId, !following, true)
    },
    async onBlockClicked () {
      let { accountId, blocking } = this.get()
      this.close()
      await setAccountBlocked(accountId, !blocking, true)
    },
    async onMuteClicked () {
      let { account, muting } = this.get()
      this.close()
      await toggleMute(account, !muting)
    },
    async onShowReblogsClicked () {
      let { accountId, showingReblogs } = this.get()
      this.close()
      await setShowReblogs(accountId, !showingReblogs, true)
    },
    async onBlockDomainClicked () {
      let { accountId, domain, blockingDomain } = this.get()
      this.close()
      await setDomainBlocked(accountId, domain, !blockingDomain, true)
    },
    async onCopyClicked () {
      let { account } = this.get()
      let { url } = account
      this.close()
      await copyText(url)
    },
    async onReport () {
      let { account } = this.get()
      this.close()
      await reportStatusOrAccount({ account })
    }
  },
  components: {
    ModalDialog,
    GenericDialogList
  }
}
</script>


code:
<ModalDialog
  {id}
  {label}
  {title}
  background="var(--main-bg)"
>
  <ComposeBox realm="dialog" autoFocus={true} dialogId={id} />
</ModalDialog>
<script>
  import ModalDialog from './ModalDialog.html'
  import ComposeBox from '../../compose/ComposeBox.html'
  import { on } from '../../../_utils/eventBus'
  import { show } from '../helpers/showDialog'
  import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
  import { close } from '../helpers/closeDialog'

  export default {
    oncreate () {
      on('postedStatus', this, this.onPostedStatus)
      onCreateDialog.call(this)
    },
    data: () => ({
      title: void 0
    }),
    methods: {
      show,
      close,
      onPostedStatus (realm) {
        if (realm !== 'dialog') {
          return
        }
        this.close()
      }
    },
    components: {
      ModalDialog,
      ComposeBox
    }
  }
</script>


code:
<ModalDialog
  {id}
  {label}
  {title}
  background="var(--main-bg)"
>
  <form class="confirmation-dialog-form">
    {#if component}
      <svelte:component this={component} {...componentOpts} />
    {:else}

{text}

    {/if}

      <button type="button" on:click="onPositive()">
        {positiveText}
      </button>
      <button type="button" on:click="onNegative()">
        {negativeText}
      </button>

  </form>
</ModalDialog>


code:
<ModalDialog
  {id}
  {label}
  {title}
  shrinkWidthToFit={true}
  background="var(--main-bg)"
  on:show="onShow()"
>
  <form class="copy-dialog-form">
    <input value={text}
           ref:input
    >
    <button type="button" on:click="onClick()">
      Copy
    </button>
  </form>
</ModalDialog>
<style>
  .copy-dialog-form {
    display: grid;
    grid-template-rows: min-content min-content;
    grid-template-columns: 1fr;
    grid-gap: 10px;
    padding: 10px 20px;
    width: 400px;
    max-width: calc(100% - 40px);
  }
</style>
<script>
  import ModalDialog from './ModalDialog.html'
  import { show } from '../helpers/showDialog'
  import { close } from '../helpers/closeDialog'
  import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
  import { toast } from '../../toast/toast'
  import { doubleRAF } from '../../../_utils/doubleRAF'

  function copyFromInput (input) {
    // workarounds for iOS, via https://stackoverflow.com/a/34046084
    let range = document.createRange()
    range.selectNodeContents(input)
    let selection = window.getSelection()
    selection.removeAllRanges()
    selection.addRange(range)
    input.setSelectionRange(0, 9999999)
    document.execCommand('copy')
  }

  export default {
    oncreate () {
      onCreateDialog.call(this)
    },
    methods: {
      show,
      close,
      onClick () {
        let { input } = this.refs
        copyFromInput(input)
        toast.say('Copied to clipboard')
        this.close()
      },
      onShow () {
        let { text } = this.get()
        let { input } = this.refs
        // double raf is to work around a11y-dialog trying to set the input
        doubleRAF(() => {
          input.focus()
          input.setSelectionRange(0, text.length)
        })
      }
    },
    data: () => ({
      text: ''
    }),
    components: {
      ModalDialog
    }
  }
</script>


code:
<ModalDialog
  {id}
  {label}
  {title}
  shrinkWidthToFit={true}
  background="var(--main-bg)"
  on:show="onShow()"
>

    {#if loaded}
      <emoji-mart props-json={emojiMartPropsJson} ></emoji-mart>
    {:elseif error}

Failed to load emoji picker: {error}

    {:else}

        <LoadingSpinner />

    {/if}

</ModalDialog>


code:
<ModalDialog
  {id}
  {label}
  {title}
  {className}
  shrinkWidthToFit={true}
  background="var(--main-bg)"
>
  <form class="confirmation-dialog-form">
    <slot></slot>
    <div class="confirmation-dialog-form-flex">
      <button type="button" on:click="onPositive()">
        {positiveText || 'OK'}
      </button>
      <button type="button" on:click="onNegative()">
        {negativeText || 'Cancel'}
      </button>
    </div>
  </form>
</ModalDialog>
<style>
  .confirmation-dialog-form-flex {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-gap: 10px;
    padding: 10px 20px;
  }
  .confirmation-dialog-form-flex button {
    min-width: 125px;
  }
</style>
<script>
  import ModalDialog from './ModalDialog.html'
  import { show } from '../helpers/showDialog'
  import { close } from '../helpers/closeDialog'
  import { on } from '../../../_utils/eventBus'
  import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'

  export default {
    oncreate () {
      on('destroyDialog', this, this.onDestroyDialog)
      onCreateDialog.call(this)
    },
    data: () => ({
      positiveText: void 0,
      negativeText: void 0,
      className: ''
    }),
    methods: {
      show,
      close,
      onDestroyDialog (thisId) {
        let {
          id,
          positiveResult
        } = this.get()
        if (thisId !== id) {
          return
        }
        if (positiveResult) {
          this.fire('positive')
        } else {
          this.fire('negative')
        }
      },
      onPositive () {
        this.set({ positiveResult: true })
        this.close()
      },
      onNegative () {
        this.close()
      }
    },
    components: {
      ModalDialog
    }
  }
</script>


code:
  {#each items as item (item.key)}

    <button
        class="generic-dialog-list-button"
        on:applyFocusStylesToParent="noop()"
        on:click="fire('click', item)">
      <SvgIcon className="generic-dialog-list-item-svg" href={item.icon} />

        {item.label}

      {#if selectable}
        <SvgIcon className="generic-dialog-list-item-svg {item.selected ? '' : 'hidden'}"
                 href="#fa-check" />
      {/if}
    </button>

  {/each}


code:
<ModalDialog
  {id}
  {label}
  background="var(--muted-modal-bg)"
  muted="true"
  className="media-modal-dialog"
  on:show="onShow()"
>

      {#each mediaItems as media (media.id)}

                {#if canPinchZoom && pinchZoomMode}
                  <PinchZoomable className='media-pinch-zoom' >
                    <MediaInDialog {media} />
                  </PinchZoomable>
                {:else}
                  <MediaInDialog {media} />
                {/if}

      {/each}

      {#if canPinchZoom}
        <IconButton
          className="media-control-button media-control-button-dummy-spacer"
          href="#fa-search"
          label=""
          ariaHidden={true}
        />
      {/if}
      {#if dots.length > 1}

            <IconButton
              className="media-control-button"
              disabled={scrolledItem === 0}
              label="Show previous media"
              href="#fa-angle-left"
              on:click="prev()"
            />
            {#each dots as dot, i (dot.i)}
              <IconButton
                className="media-control-button"
                pressable={true}
                label="Show {nth(i)} media"
                pressed={i === scrolledItem}
                href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
                sameColorWhenPressed={true}
                on:click="onClick(i)"
              />
            {/each}
            <IconButton
              className="media-control-button"
              disabled={scrolledItem === length - 1}
              label="Show next media"
              href="#fa-angle-right"
              on:click="next()"
            />

      {/if}
      {#if canPinchZoom}
        <IconButton
          className="media-control-button"
          pressable={true}
          pressed={pinchZoomMode}
          label={pinchZoomMode ? 'Disable pinch-zoom mode' : 'Enable pinch-zoom mode'}
          href="#fa-search"
          on:click="togglePinchZoomMode()"
        />
      {/if}

</ModalDialog>

<Shortcut scope='modal-{id}' key="ArrowLeft" on:pressed="prev()" />
<Shortcut scope='modal-{id}' key="ArrowRight" on:pressed="next()" />


code:
{#if type === 'video'}
  <video
    class="media-fit"
    aria-label={description}
    src={url}
    {poster}
    controls
    {intrinsicsize}
    ref:video
  />
{:elseif type === 'gifv'}
  <video
    class="media-fit"
    aria-label={description}
    src={url}
    {poster}
    autoplay
    muted
    loop
    webkit-playsinline
    playsinline
    {intrinsicsize}
  />
{:else}
  <img
    class="media-fit"
    alt={description}
    title={description}
    src={url}
    {intrinsicsize}
  />
{/if}
<style>
  .media-fit {
    object-fit: contain;
    width: 100%;
    height: 100%;
  }
</style>
<script>
  export default {
    computed: {
      type: ({ media }) => media.type,
      url: ({ media }) => media.url,
      description: ({ media }) => media.description || '',
      poster: ({ media }) => media.preview_url,
      static_url: ({ media }) => media.static_url,
      intrinsicsize: ({ media }) => {
        if (media.meta && media.meta.original && media.meta.original.width && media.meta.original.height) {
          let { width, height } = media.meta.original
          return `${width} x ${height}`
        }
        return '' // pleroma does not give us original width/height
      }
    },
    ondestroy () {
      if (this.refs.video && !this.refs.video.paused) {
        this.refs.video.pause()
      }
    }
  }
</script>


code:
      {#if title}

{title}

      {/if}

        <button class="close-dialog-button"
                data-a11y-dialog-hide aria-label="Close dialog">
          <SvgIcon className="close-dialog-button-svg" href="#fa-times" />
        </button>

    <slot></slot>

<Shortcut scope="modal-{id}" key="Backspace" on:pressed="closeDialog(id)"/>


code:
<GenericConfirmationDialog
  {id}
  {label}
  {title}
  {positiveText}
  on:positive="doMute()"
>

      Mute @{account.acct} ?

      <input type="checkbox"
             id="mute-notifications"
             name="mute-notifications"
             bind:checked="muteNotifications">
      Mute notifications as well

</GenericConfirmationDialog>


code:
<div class="pinch-zoom {className ? className : ''}" >
  <pinch-zoom class="pinch-zoom-inner" ref:node>
    <slot></slot>
  </pinch-zoom>
  <IconButton
    className="pinch-zoom-button pinch-zoom-button-zoom-out"
    muted={true}
    label="Zoom out"
    href="#fa-search-minus"
    on:click="zoomOut()"
  />
  <IconButton
    className="pinch-zoom-button pinch-zoom-button-zoom-in"
    muted={true}
    label="Zoom in"
    href="#fa-search-plus"
    on:click="zoomIn()"
  />
</div>
<style>
  .pinch-zoom {
    position: relative;
  }
  .pinch-zoom-inner {
    width: 100%;
    height: 100%;
  }

  :global(.icon-button.pinch-zoom-button) {
    position: absolute;
    z-index: 110;
    bottom: 10px;
    background: var(--mask-opaque-bg);
  }

  :global(.pinch-zoom-button-zoom-in) {
    right: 10px;
  }

  :global(.pinch-zoom-button-zoom-out) {
    left: 10px;
  }

  @media (max-width: 767px) {
    :global(.pinch-zoom-button-zoom-in) {
      right: 5px;
    }

    :global(.pinch-zoom-button-zoom-out) {
      left: 5px;
    }
  }

  @media (max-width: 320px) {
    :global(.icon-button.pinch-zoom-button) {
      padding-left: 5px;
      padding-right: 5px;
    }
  }
</style>
<script>
  import IconButton from '../../IconButton.html'
  import 'pinch-zoom-element'

  const ZOOM_INCREMENT = 0.1

  export default {
    components: {
      IconButton
    },
    methods: {
      zoomIn () {
        this.zoomBy(ZOOM_INCREMENT)
      },
      zoomOut () {
        this.zoomBy(-ZOOM_INCREMENT)
      },
      zoomBy (increment) {
        let { node } = this.refs
        let scale = node.scale || 1
        node.scaleTo(scale + increment, {
          originX: '50%',
          originY: '50%'
        })
      }
    }
  }
</script>


code:
<ModalDialog
  {id}
  {label}
  {title}
  shrinkWidthToFit={true}
  background="var(--main-bg)"
>
  <GenericDialogList selectable={true} {items} on:click="onClick(event)" />
</ModalDialog>
<script>
  import ModalDialog from './ModalDialog.html'
  import { store } from '../../../_store/store'
  import { POST_PRIVACY_OPTIONS } from '../../../_static/statuses'
  import { setPostPrivacy } from '../../../_actions/postPrivacy'
  import GenericDialogList from './GenericDialogList.html'
  import { show } from '../helpers/showDialog'
  import { close } from '../helpers/closeDialog'
  import { oncreate } from '../helpers/onCreateDialog'

  export default {
    oncreate,
    components: {
      ModalDialog,
      GenericDialogList
    },
    store: () => store,
    data: () => ({
      postPrivacyOptions: POST_PRIVACY_OPTIONS
    }),
    methods: {
      show,
      close,
      onClick (item) {
        let { realm } = this.get()
        setPostPrivacy(realm, item.key)
        this.close()
      }
    },
    computed: {
      composeData: ({ $currentComposeData, realm }) => $currentComposeData[realm] || {},
      postPrivacy: ({ postPrivacyKey }) => {
        return POST_PRIVACY_OPTIONS.find(_ => _.key === postPrivacyKey)
      },
      postPrivacyKey: ({ composeData, $currentVerifyCredentials }) => {
        return composeData.postPrivacy || $currentVerifyCredentials.source.privacy
      },
      items: ({ postPrivacy, postPrivacyOptions }) => {
        return postPrivacyOptions.map(option => ({
          key: option.key,
          label: option.label,
          icon: option.icon,
          selected: postPrivacy.key === option.key
        }))
      }
    }
  }
</script>


code:
<GenericConfirmationDialog
  {id}
  {label}
  {title}
  className="report-dialog-contents"
  {positiveText}
  on:positive="doReport()">

        {#if loading}

            <LoadingSpinner />

        {:else}

            {#each displayStatuses as status (status.id)}

                <input type="checkbox"
                       id="status-report-{status.id}"
                       name="status-report-{status.id}"
                       checked={status.report}
                       on:change="onChange(status.id, event)"
                >

                  {status.text}

            {/each}

        {/if}

You are reporting @{account.acct} to the moderators of {$currentInstance}.

        Additional comments
        <textarea bind:value="comments"
                  placeholder="Additional comments"
                  aria-labelledby="comments-label"
                  maxlength="1000"></textarea>
        {#if remoteInstance}

Forward to the moderators of {remoteInstance} as well?

          <input type="checkbox"
                 id="report-forward"
                 name="report-forward"
                 bind:checked="forward">

            Forward to {remoteInstance}

        {/if}

</GenericConfirmationDialog>


code:
<ModalDialog
  {id}
  {label}
  background="var(--muted-modal-bg)"
  muted="true"
  className="shortcut-help-modal-dialog">

Hotkeys

  <ShortcutHelpInfo inDialog={true} />

  <Shortcut scope='modal-{id}' key='h|?' on:pressed='close()'/>
</ModalDialog>


code:
<ModalDialog
  {id}
  {label}
  {title}
  shrinkWidthToFit={true}
  background="var(--main-bg)"
>
  <GenericDialogList selectable={false} {items} on:click="onClick(event)"/>
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../../_store/store'
import GenericDialogList from './GenericDialogList.html'
import { setAccountFollowed } from '../../../_actions/follow'
import { doDeleteStatus } from '../../../_actions/delete'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { oncreate } from '../helpers/onCreateDialog'
import { setAccountBlocked } from '../../../_actions/block'
import { setStatusPinnedOrUnpinned } from '../../../_actions/pin'
import { setConversationMuted } from '../../../_actions/muteConversation'
import { copyText } from '../../../_actions/copyText'
import { deleteAndRedraft } from '../../../_actions/deleteAndRedraft'
import { shareStatus } from '../../../_actions/share'
import { toggleMute } from '../../../_actions/toggleMute'
import { reportStatusOrAccount } from '../../../_actions/report'

export default {
  oncreate,
  data: () => ({
    supportsWebShare: process.browser && typeof navigator.share === 'function'
  }),
  computed: {
    relationship: ({ $currentAccountRelationship }) => $currentAccountRelationship,
    account: ({ $currentAccountProfile }) => $currentAccountProfile,
    verifyCredentials: ({ $currentVerifyCredentials }) => $currentVerifyCredentials,
    statusId: ({ status }) => status.id,
    pinned: ({ statusId, $currentStatusModifications, status }) => {
      if ($currentStatusModifications && statusId in $currentStatusModifications.pins) {
        return $currentStatusModifications.pins[statusId]
      }
      return status.pinned
    },
    //
    // begin copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html)
    //
    verifyCredentialsId: ({ verifyCredentials }) => verifyCredentials.id,
    following: ({ relationship }) => relationship && relationship.following,
    followRequested: ({ relationship }) => relationship && relationship.requested,
    accountId: ({ account }) => account && account.id,
    acct: ({ account }) => account.acct,
    muting: ({ relationship }) => relationship.muting,
    blocking: ({ relationship }) => relationship.blocking,
    followLabel: ({ following, followRequested, account, acct }) => {
      if (typeof following === 'undefined' || !account) {
        return ''
      }
      return (following || followRequested)
        ? `Unfollow @${acct}`
        : `Follow @${acct}`
    },
    followIcon: ({ following, followRequested }) => (
      following ? '#fa-user-times' : followRequested ? '#fa-hourglass' : '#fa-user-plus'
    ),
    blockLabel: ({ blocking, acct }) => (
      blocking ? `Unblock @${acct}` : `Block @${acct}`
    ),
    blockIcon: ({ blocking }) => blocking ? '#fa-unlock' : '#fa-ban',
    muteLabel: ({ muting, acct }) => (
      muting ? `Unmute @${acct}` : `Mute @${acct}`
    ),
    muteIcon: ({ muting }) => muting ? '#fa-volume-up' : '#fa-volume-off',
    isUser: ({ accountId, verifyCredentialsId }) => accountId === verifyCredentialsId,
    //
    // end copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html)
    //
    pinLabel: ({ pinned, isUser }) => isUser ? (pinned ? 'Unpin from profile' : 'Pin to profile') : '',
    visibility: ({ status }) => status.visibility,
    mentions: ({ status }) => status.mentions || [],
    mentionsUser: ({ mentions, verifyCredentialsId }) => !!mentions.find(_ => _.id === verifyCredentialsId),
    mutingConversation: ({ status }) => !!status.muted,
    muteConversationLabel: ({ mutingConversation }) => mutingConversation ? `Unmute conversation` : `Mute conversation`,
    muteConversationIcon: ({ mutingConversation }) => mutingConversation ? '#fa-volume-up' : '#fa-volume-off',
    isPublicOrUnlisted: ({ visibility }) => visibility === 'public' || visibility === 'unlisted',
    items: ({
      blockLabel, blocking, blockIcon, muteLabel, muteIcon, followLabel, followIcon,
      following, followRequested, pinLabel, isUser, visibility, mentionsUser, mutingConversation,
      muteConversationLabel, muteConversationIcon, supportsWebShare, isPublicOrUnlisted
    }) => ([
      isUser && {
        key: 'delete',
        label: 'Delete',
        icon: '#fa-trash'
      },
      isPublicOrUnlisted && isUser && {
        key: 'pin',
        label: pinLabel,
        icon: '#fa-thumb-tack'
      },
      !isUser && !blocking && {
        key: 'follow',
        label: followLabel,
        icon: followIcon
      },
      !isUser && {
        key: 'block',
        label: blockLabel,
        icon: blockIcon
      },
      !isUser && !blocking && {
        key: 'mute',
        label: muteLabel,
        icon: muteIcon
      },
      (isUser || mentionsUser) && {
        key: 'muteConversation',
        label: muteConversationLabel,
        icon: muteConversationIcon
      },
      isUser && {
        key: 'redraft',
        label: 'Delete and redraft',
        icon: '#fa-pencil'
      },
      !isUser && {
        key: 'report',
        label: 'Report toot',
        icon: '#fa-flag'
      },
      isPublicOrUnlisted && supportsWebShare && {
        key: 'share',
        label: 'Share toot',
        icon: '#fa-share-square-o'
      },
      isPublicOrUnlisted && {
        key: 'copy',
        label: 'Copy link to toot',
        icon: '#fa-link'
      }
    ].filter(Boolean))
  },
  components: {
    ModalDialog,
    GenericDialogList
  },
  store: () => store,
  methods: {
    show,
    close,
    onClick (item) {
      switch (item.key) {
        case 'delete':
          return this.onDeleteClicked()
        case 'pin':
          return this.onPinClicked()
        case 'follow':
          return this.onFollowClicked()
        case 'block':
          return this.onBlockClicked()
        case 'mute':
          return this.onMuteClicked()
        case 'copy':
          return this.onCopyClicked()
        case 'muteConversation':
          return this.onMuteConversationClicked()
        case 'redraft':
          return this.onRedraft()
        case 'share':
          return this.onShare()
        case 'report':
          return this.onReport()
      }
    },
    async onDeleteClicked () {
      let { statusId } = this.get()
      this.close()
      await doDeleteStatus(statusId)
    },
    async onPinClicked () {
      let { statusId, pinned } = this.get()
      this.close()
      await setStatusPinnedOrUnpinned(statusId, !pinned, true)
    },
    async onFollowClicked () {
      let { accountId, following } = this.get()
      this.close()
      await setAccountFollowed(accountId, !following, true)
    },
    async onBlockClicked () {
      let { accountId, blocking } = this.get()
      this.close()
      await setAccountBlocked(accountId, !blocking, true)
    },
    async onMuteClicked () {
      let { account, muting } = this.get()
      this.close()
      await toggleMute(account, !muting)
    },
    async onMuteConversationClicked () {
      let { statusId, mutingConversation } = this.get()
      this.close()
      await setConversationMuted(statusId, !mutingConversation, true)
    },
    async onCopyClicked () {
      let { status } = this.get()
      let { url } = status
      this.close()
      await copyText(url)
    },
    async onRedraft () {
      let { status } = this.get()
      this.close()
      await deleteAndRedraft(status)
    },
    async onShare () {
      let { status } = this.get()
      this.close()
      await shareStatus(status)
    },
    async onReport () {
      let { status, account } = this.get()
      this.close()
      await reportStatusOrAccount(({ status, account }))
    }
  }
}
</script>


code:
<GenericConfirmationDialog
  {id}
  {label}
  {title}
  {positiveText}
  {negativeText}
  on:positive
  on:negative>
  <p>{text}</p>
</GenericConfirmationDialog>
<style>
  p {
    font-size: 1.3em;
    padding: 40px 20px;
  }
</style>
<script>
  import GenericConfirmationDialog from './GenericConfirmationDialog.html'
  import { show } from '../helpers/showDialog'
  import { close } from '../helpers/closeDialog'
  import { oncreate } from '../helpers/onCreateDialog'

  export default {
    oncreate,
    data: () => ({
      title: void 0,
      positiveText: void 0,
      negativeText: void 0
    }),
    methods: {
      show,
      close
    },
    components: {
      GenericConfirmationDialog
    }
  }
</script>


code:
import AccountProfileOptionsDialog from '../components/AccountProfileOptionsDialog.html'
import { showDialog } from './showDialog'

export default function showAccountProfileOptionsDialog (account, relationship, verifyCredentials) {
  return showDialog(AccountProfileOptionsDialog, {
    label: 'Profile options dialog',
    title: '',
    account: account,
    relationship: relationship,
    verifyCredentials: verifyCredentials
  })
}


code:
import ComposeDialog from '../components/ComposeDialog.html'
import { showDialog } from './showDialog'

export default function showComposeDialog () {
  return showDialog(ComposeDialog, { label: 'Compose dialog' })
}


code:
import CopyDialog from '../components/CopyDialog.html'
import { showDialog } from './showDialog'

export default function showCopyDialog (text) {
  return showDialog(CopyDialog, {
    label: 'Copy dialog',
    title: 'Copy link',
    text
  })
}


code:
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'

export function showDialog (Dialog, data) {
  let dialog = new Dialog({
    target: createDialogElement(),
    data: Object.assign({
      id: createDialogId()
    }, data)
  })
  dialog.show()
  return dialog
}


code:
import EmojiDialog from '../components/EmojiDialog.html'
import { showDialog } from './showDialog'

export default function showEmojiDialog (realm) {
  return showDialog(EmojiDialog, {
    label: 'Emoji dialog',
    title: 'Emoji',
    realm
  })
}


code:
import MediaDialog from '../components/MediaDialog.html'
import { showDialog } from './showDialog'

export default function showMediaDialog (mediaItems, scrolledItem) {
  return showDialog(MediaDialog, {
    label: 'Media dialog',
    mediaItems,
    scrolledItem
  })
}


code:
import MuteDialog from '../components/MuteDialog.html'
import { showDialog } from './showDialog'

export default function showMuteDialog (account) {
  return showDialog(MuteDialog, {
    label: 'Mute dialog',
    account
  })
}


code:
import PostPrivacyDialog from '../components/PostPrivacyDialog.html'
import { showDialog } from './showDialog'

export default function showPostPrivacyDialog (realm) {
  return showDialog(PostPrivacyDialog, {
    label: 'Post privacy dialog',
    title: 'Post privacy',
    realm: realm
  })
}


code:
import ReportDialog from '../components/ReportDialog.html'
import { showDialog } from './showDialog'

export default function showReportDialog ({ account, status }) {
  return showDialog(ReportDialog, {
    label: 'Report dialog',
    title: `Report @${account.acct}`,
    account,
    status
  })
}


code:
import ShortcutHelpDialog from '../components/ShortcutHelpDialog.html'
import { showDialog } from './showDialog'

export default function showShortcutHelpDialog (options) {
  return showDialog(ShortcutHelpDialog, Object.assign({
    label: 'shortcut help dialog'
  }, options))
}


code:
import StatusOptionsDialog from '../components/StatusOptionsDialog.html'
import { showDialog } from './showDialog'

export default function showStatusOptionsDialog (status) {
  return showDialog(StatusOptionsDialog, {
    label: 'Status options dialog',
    title: '',
    status: status
  })
}


code:
import TextConfirmationDialog from '../components/TextConfirmationDialog.html'
import { showDialog } from './showDialog'

export default function showTextConfirmationDialog (options) {
  return showDialog(TextConfirmationDialog, Object.assign({
    label: 'Confirmation dialog'
  }, options))
}


code:
import { emit } from '../../../_utils/eventBus'

export function close () {
  let { id } = this.get()
  emit('closeDialog', id)
}


code:
export function createDialogElement () {
  let div = document.createElement('div')
  div.setAttribute('class', 'modal-dialog')
  div.setAttribute('aria-hidden', 'true')
  document.body.appendChild(div)
  return div
}


code:
let count = -1

export function createDialogId () {
  return ++count
}


code:
import { on } from '../../../_utils/eventBus'

function onDestroy (thisId) {
  let { id } = this.get()
  if (id !== thisId) {
    return
  }
  this.destroy()
}

export function oncreate () {
  on('destroyDialog', this, onDestroy)
}


code:
import { emit } from '../../../_utils/eventBus'

export function show () {
  let { id } = this.get()
  emit('showDialog', id)
}


code:
<div class="the-list" on:initialized>
  {#each safeItems as item, i (item)}
    <ListLazyItem
      {component}
      index={i}
      {length}
      {makeProps}
      key={item}
      on:initialized="itemInitialized()"
    />
  {/each}
</div>
<style>
  .the-list {
    position: relative;
  }
</style>
<script>
  import ListLazyItem from './ListLazyItem.html'
  import { listStore } from './listStore'
  import { getScrollContainer } from '../../_utils/scrollContainer'
  import { getMainTopMargin } from '../../_utils/getMainTopMargin'

  export default {
    oncreate () {
      let { realm } = this.get()
      this.store.setCurrentRealm(realm)
    },
    ondestroy () {
      this.store.setCurrentRealm(null)
    },
    methods: {
      itemInitialized () {
        let { initializedCount, length } = this.get()
        initializedCount++
        this.set({ initializedCount })
        if (initializedCount === length) {
          this.initialize()
        }
      },
      initialize () {
        let { scrollToItem } = this.get()
        if (scrollToItem) {
          let element = document.getElementById(`list-item-${scrollToItem}`)
          requestAnimationFrame(() => {
            console.log('scrolling element into view')
            // TODO: this is hacky
            element.scrollIntoView(true)
            getScrollContainer().scrollTop -= (getMainTopMargin() + 10) // 10 is the status-article padding top
            this.fire('initialized')
          })
        } else {
          this.fire('initialized')
        }
      }
    },
    data: () => ({
      initializedCount: 0
    }),
    computed: {
      safeItems: ({ items }) => items || [],
      length: ({ safeItems }) => safeItems.length
    },
    components: {
      ListLazyItem
    },
    store: () => listStore
  }
</script>


code:
<div class="list-item"
     id={`list-item-${key}`} >
  <svelte:component this={component}
              virtualProps={props}
              virtualIndex={index}
              virtualLength={length}
              virtualKey={key}
  />


code:
{#if props}
  <ListItem
    {component}
    {props}
    {key}
    {index}
    {length}
    on:initialized
  />
{/if}
<script>
  import ListItem from './ListItem.html'
  import { mark, stop } from '../../_utils/marks'

  export default {
    async oncreate () {
      let { makeProps, key } = this.get()
      if (makeProps) {
        let props = await makeProps(key)
        mark('ListLazyItem set props')
        this.set({ props: props })
        this.fire('initialized')
        stop('ListLazyItem set props')
      }
    },
    data: () => ({
      props: void 0
    }),
    components: {
      ListItem
    }
  }
</script>


code:
import { RealmStore } from '../../_utils/RealmStore'

class ListStore extends RealmStore {
  constructor (state) {
    super(state, /* maxSize */ 10)
  }
}

const listStore = new ListStore()

listStore.computeForRealm('intersectionStates', {})

if (process.browser && process.env.NODE_ENV !== 'production') {
  window.listStore = listStore
}

export { listStore }


code: {@html massagedAccountName }


code:
Profile for {accountName}

{#if moved}
  <AccountProfileMovedBanner {account} />
{/if}

      <AccountProfileHeader {account} {relationship} {verifyCredentials} />
      <AccountProfileFollow {account} {relationship} {verifyCredentials} />
      <AccountProfileNote {account} />
      <AccountProfileMeta {account} />
      <AccountProfileDetails {account} {relationship} {verifyCredentials} />

  <AccountProfileFilters account={$currentAccountProfile} {filter} />


code:
Stats and more options

      Toots

      {numStatusesDisplay}

      Follows

      {numFollowingDisplay}

      Followers

      {numFollowersDisplay}

  {#if account && verifyCredentials && account.id !== verifyCredentials.id}

      <IconButton
        label="More options"
        href="#fa-bars"
        muted="true"
        on:click="onMoreOptionsClick()"
      />

  {/if}


code:
<TabSet
  label="Filters"
  currentTabName={filter}
  {tabs}
  className="account-profile-filters"
/>
<style>
  :global(.account-profile-filters) {
    background: var(--account-profile-bg);
  }
</style>
<script>
  import TabSet from '../TabSet.html'

  export default {
    computed: {
      tabs: ({ account }) => (
        [
          {
            name: '',
            label: 'Toots',
            href: `/accounts/${account.id}`
          },
          {
            name: 'with_replies',
            label: 'Toots and replies',
            href: `/accounts/${account.id}/with_replies`
          },
          {
            name: 'media',
            label: 'Media',
            href: `/accounts/${account.id}/media`
          }
        ]
      )
    },
    components: {
      TabSet
    }
  }
</script>


code:
<div class="account-profile-follow {shown ? 'shown' : ''}">
  <IconButton
    label={followLabel}
    href={followIcon}
    pressable="true"
    pressed={following}
    big="true"
    on:click="onFollowButtonClick(event)"
    ref:icon
  />
</div>
<style>
  .account-profile-follow {
    grid-area: follow;
    align-self: flex-start;
    display: none;
  }
  .account-profile-follow.shown {
    display: block;
  }
</style>
<script>
  import IconButton from '../IconButton.html'
  import { FOLLOW_BUTTON_ANIMATION } from '../../_static/animations'
  import { store } from '../../_store/store'
  import { setAccountFollowed } from '../../_actions/follow'
  import { setAccountBlocked } from '../../_actions/block'

  export default {
    methods: {
      async onFollowButtonClick (e) {
        e.preventDefault()
        e.stopPropagation()
        let {
          account,
          accountId,
          following,
          followRequested,
          blocking
        } = this.get()
        if (blocking) { // unblock
          await setAccountBlocked(accountId, false)
        } else { // follow/unfollow
          let newFollowingValue = !(following || followRequested)
          if (!account.locked) { // be optimistic, show the user that it succeeded
            this.set({ overrideFollowing: newFollowingValue })
          }
          if (newFollowingValue) {
            this.refs.icon.animate(FOLLOW_BUTTON_ANIMATION)
          }
          await setAccountFollowed(accountId, newFollowingValue)
        }
      }
    },
    store: () => store,
    data: () => ({
      overrideFollowing: void 0
    }),
    computed: {
      accountId: ({ account }) => account.id,
      following: ({ relationship, overrideFollowing }) => {
        if (typeof overrideFollowing === 'boolean') {
          return overrideFollowing
        }
        return relationship && relationship.following
      },
      blocking: ({ relationship }) => relationship && relationship.blocking,
      followRequested: ({ relationship }) => {
        return relationship && relationship.requested
      },
      followLabel: ({ blocking, following, followRequested }) => {
        if (blocking) {
          return 'Unblock'
        } else if (following) {
          return 'Unfollow'
        } else if (followRequested) {
          return 'Unfollow (follow requested)'
        } else {
          return 'Follow'
        }
      },
      followIcon: ({ blocking, following, followRequested }) => {
        if (blocking) {
          return '#fa-unlock'
        } else if (following) {
          return '#fa-user-times'
        } else if (followRequested) {
          return '#fa-hourglass'
        } else {
          return '#fa-user-plus'
        }
      },
      shown: ({ verifyCredentials, relationship }) => {
        return verifyCredentials && relationship && verifyCredentials.id !== relationship.id
      }
    },
    components: {
      IconButton
    }
  }
</script>


code:
Name and following

  <Avatar {account} size="big" />

  <ExternalLink
    className="account-profile-name-link"
    href={account.url}
    showIcon="true"
    normalIconColor="true"
    ariaLabel="{accessibleName} (opens in new window)">
    <AccountDisplayName {account} />
  </ExternalLink>

{#if label}

{/if}

  {'@' + account.acct}

  {#if relationship && relationship.blocking}
    Blocked
  {/if}
  {#if relationship && relationship.domain_blocking}
    Domain hidden
  {/if}
  {#if relationship && relationship.muting}
    Muted
  {/if}
  {#if relationship && relationship.followed_by}
    Follows you
  {/if}


code:
{#if massagedFields.length}

Fields

    {#each massagedFields as field, i}

        {field.name}

        {@html field.value}

        {#if field.verified}
          <SvgIcon className="account-profile-meta-verified-svg" href="#fa-check" />
        {/if}

    {/each}

{/if}


code:
  <Avatar className="from-avatar" size="extra-small" {account} />

    <SvgIcon className="moved-svg" href="#fa-suitcase" />
    {accessibleName} has moved:

    <Avatar account={moved} />

  {movedAccessibleName}
  @{moved.acct}


code:
Description

  {@html massagedNote}


code:
{#if $isUserLoggedIn}
<TimelinePage {timeline} >
  <DynamicPageBanner title="" ariaTitle="Profile page for {accountName}"/>
  {#if $currentAccountProfile && $currentVerifyCredentials}
  <AccountProfile account={$currentAccountProfile}
                  relationship={$currentAccountRelationship}
                  verifyCredentials={$currentVerifyCredentials}
                  {filter}
  />
  {/if}
  {#if !filter}
    <PinnedStatuses {accountId} />
  {/if}
</TimelinePage>
{:else}
<HiddenFromSSR>
  <FreeTextLayout>

Profile

A user timeline will appear here when logged in.

  </FreeTextLayout>
</HiddenFromSSR>
{/if}


code:
<SearchResult href="/accounts/{account.id}">

      <Avatar {account} size="small" />

      <AccountDisplayName {account} />

      {'@' + account.acct}

    {#if actions && actions.length}

        {#each actions as action}
          <IconButton
            label={action.label}
            on:click="onButtonClick(event, action, account.id)"
            href={action.icon}
            big="true"
          />
        {/each}

    {/if}

</SearchResult>


code:
<SearchResult href="/tags/{hashtag}">
  {'#' + hashtag}
</SearchResult>
<style>
</style>
<script>
  import SearchResult from './SearchResult.html'
  export default {
    components: {
      SearchResult
    }
  }
</script>


code:
<form class="search-input-form" on:submit="onSubmit(event)">

    <input type="search"
           class="search-input"
           placeholder="Search"
           aria-label="Search input"
           required
           bind:value="$queryInSearch">

  <button type="submit" class="primary search-button" aria-label="Search" disabled={$searchLoading}>
    <SvgIcon className="search-button-svg" href="#fa-search" />
  </button>
</form>
{#if $searchLoading}

    <LoadingPage />

{:elseif $searchResults && $searchResultsForQuery === $queryInSearch}

    <SearchResults />

{/if}


code:
<li class="search-result">
  <a {href} class="search-result-anchor" rel="prefetch">
    <slot></slot>
  </a>
</li>
<style>
  .search-result {
    box-sizing: border-box;
    border-bottom: 1px solid var(--main-border);
    display: flex;
  }
  .search-result:last-child {
    border-bottom: none;
  }
  .search-result-anchor {
    padding: 20px;
    flex: 1;
    background: var(--settings-list-item-bg);
  }
  .search-result-anchor, .search-result-anchor:visited {
    color: var(--body-text-color);
  }
  .search-result-anchor:hover {
    background: var(--settings-list-item-bg-hover);
    text-decoration: none;
  }
  .search-result-anchor:active {
    background: var(--settings-list-item-bg-active);
  }

  @media (max-width: 767px) {
    .search-result-anchor {
      padding: 15px 10px;
    }
  }
</style>


code:
<ul class="search-results">
  {#each $searchResults.hashtags as hashtag}
    <HashtagSearchResult {hashtag} />
  {/each}
  {#each $searchResults.accounts as account}
    <AccountSearchResult {account} />
  {/each}
  {#each $searchResults.statuses as status, index}
  <StatusSearchResult {status} {index} length={$searchResults.statuses.length}/>
  {/each}
</ul>
<style>
  .search-results {
    list-style: none;
    box-sizing: border-box;
    border: 1px solid var(--main-border);
    border-radius: 2px;
  }
</style>
<script>
  import { store } from '../../_store/store'
  import AccountSearchResult from './AccountSearchResult.html'
  import HashtagSearchResult from './HashtagSearchResult.html'
  import StatusSearchResult from './StatusSearchResult.html'

  export default {
    store: () => store,
    components: {
      AccountSearchResult,
      HashtagSearchResult,
      StatusSearchResult
    }
  }
</script>


code:
<SearchResult href="/statuses/{status.id}">
  <Status {index} {length}
          timelineType="search" timelineValue="search"
          {status} />
</SearchResult>
<style>
</style>
<script>
  import SearchResult from './SearchResult.html'
  import Status from '../status/Status.html'

  export default {
    components: {
      SearchResult,
      Status
    }
  }
</script>


code:
<SettingsNav {page} {label}/>

<div class="settings">
  <FreeTextLayout>
    <slot></slot>
  </FreeTextLayout>
</div>
<style>
  .settings {
    margin: 20px;
  }
  :global(.settings .free-text h1) {
    margin-bottom: 30px;
  }
  :global(.settings .free-text h2) {
    margin: 20px 0 10px;
  }
  @media (max-width: 767px) {
    .settings {
      margin: 20px 10px;
    }
  }
</style>
<script>
  import SettingsNav from './SettingsNav.html'
  import FreeTextLayout from '../FreeTextLayout'

  export default {
    components: {
      FreeTextLayout,
      SettingsNav
    }
  }
</script>


code:
  {#if label}

    <slot></slot>

  {:else}

    <slot></slot>

  {/if}


code:
<a {href} on:click="fire('click', event)" rel="prefetch" aria-label={ariaLabel || label} class="settings-list-button {className ? className : ''}">
  <span>
    {label}
  </span>
</a>
<style>
  .settings-list-button {
    display: flex;
    flex-grow: 1;
  }
  .settings-list-button span {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
</style>
<script>
  export default {
    data: () => ({
      ariaLabel: void 0,
      className: void 0
    })
  }
</script>


code:
<li class="settings-list-item">
  <slot></slot>
</li>
<style>
  .settings-list-item {
    display: flex;
    flex-direction: row;
  }

  .settings-list-item > :global(*) {
    font-size: 1.3em;
    display: flex;
    padding: 20px 40px;
    border: 1px solid var(--settings-list-item-border);
    background: var(--settings-list-item-bg);
    color: var(--settings-list-item-text);
    text-decoration: none;
  }
  .settings-list-item > :global(*:hover) {
    text-decoration: none;
    background: var(--settings-list-item-bg-hover);
    color: var(--settings-list-item-text-hover);
  }
  .settings-list-item > :global(*:active) {
    background: var(--settings-list-item-bg-active);
  }
  .settings-list-item > :global(*:not(:last-child)) {
    border-right: none;
  }

  @media (max-width: 767px) {
    .settings-list-item > :global(*) {
      padding: 20px 10px;
    }
  }
</style>
<script>
  export default {
    data: () => ({
      icon: void 0,
      ariaLabel: void 0
    })
  }
</script>


code:
    {#each navItems as navItem}

      <SettingsNavItem {page} name={navItem.name} href={navItem.href} label={navItem.label} />

    {/each}

      <SettingsNavItem {page} name={page} href="/{page}" {label} />


code:
<a class="settings-nav-item {className}"
   aria-label={ariaLabel}
   rel="prefetch"
   {href} >
  {label}
</a>
<style>
  a.settings-nav-item {
    font-size: 1.2em;
  }

  a.settings-nav-item.selected {
    font-weight: 600;
  }
</style>
<script>
  export default {
    computed: {
      className: ({ page, name }) => page === name ? 'selected' : '',
      ariaLabel: ({ page, name, label }) => page === name ? `${label} (current page)` : label
    }
  }
</script>


code:
  <form aria-label={label} ref:form>
    {#each options as option, i (option.key) }
      {#if i > 0}

      {/if}
      <input type="checkbox"
             id="instance-option-{option.key}"
             name="{option.key}"
             on:change="onChange(event)"
      >

        {option.label}

    {/each}
  </form>


code:
<GenericInstanceSettings
  {instanceName}
  {options}
  label="Home timeline filter settings"
/>
<script>
  import GenericInstanceSettings from './GenericInstanceSettings.html'
  import { HOME_REBLOGS, HOME_REPLIES } from '../../../_static/instanceSettings'

  export default {
    data: () => ({
      options: [
        {
          key: HOME_REBLOGS,
          label: 'Show boosts',
          defaultValue: true
        },
        {
          key: HOME_REPLIES,
          label: 'Show replies',
          defaultValue: true
        }
      ]
    }),
    components: {
      GenericInstanceSettings
    }
  }
</script>