(function (cache, modules) {
  function require(i) { return cache[i] || get(i); }
  function get(i) {
    var exports = {}, module = {exports: exports};
    modules[i].call(exports, window, require, module, exports);
    return (cache[i] = module.exports);
  }
  var main = require(0);
  return main.__esModule ? main.default : main;
}([],[function (global, require, module, exports) {
// admin.js
const EditPage = require(1)
const AllPages = require(7)
const AllArticles = require(9)
const EditArticle = require(11)
const AllStaff = require(14)
const EditStaff = require(15)
const Dialogue = require(8)

window.adminRoutes = {
  pages: [AllPages, EditPage],
  articles: [AllArticles, EditArticle],
  staff: [AllStaff, EditStaff],
}

let dialogueMount = document.createElement('div')
dialogueMount.id = 'dialogue_mount'
document.body.appendChild(dialogueMount)

m.mount(dialogueMount, Dialogue)

},function (global, require, module, exports) {
// site_editpage.js
const FileUpload = require(2)
const PageTree = require(3)

const api = require(4)
const Editor = require(6)

const AdminEditPage = {
  oninit: function(vnode) {
    this.loading = false
    this.showLoading = null
    this.data = {
      page: null,
    }
    this.pages = [{id: null, name: 'Frontpage'}]
    this.pages = this.pages.concat(PageTree.getFlatTree())

    this.newBanner = null
    this.newMedia = null
    this.editor = null

    this.fetchPage(vnode)
  },

  onbeforeupdate: function(vnode) {
    if (this.lastid !== m.route.param('id')) {
      this.fetchPage(vnode)
    }
  },

  fetchPage: function(vnode) {
    this.lastid = m.route.param('id')

    return this.requestPage(
      api.sendRequest({
        method: 'GET',
        url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
      })
    )
  },

  requestPage: function(data) {
    this.error = ''

    if (this.showLoading) {
      clearTimeout(this.showLoading)
    }

    if (this.data.page) {
      this.showLoading = setTimeout(() => {
        this.showLoading = null
        this.loading = true
        m.redraw()
      }, 150)
    } else {
      this.loading = true
    }

    data
    .then((result) => {
      this.data = result
      if (this.data.page.id) {
        document.title = 'Editing: ' + this.data.page.name + ' - Admin NFP Moe'
        this.editedPath = true
      } else {
        document.title = 'Create Page - Admin NFP Moe'
      }
    }, (err) => {
      this.error = err.message
    })
    .then(() => {
      if (!this.data.page) {
        this.data.page = {}
      }
      clearTimeout(this.showLoading)
      this.showLoading = null
      this.loading = false
      m.redraw()
    })
  },

  updateValue: function(name, e) {
    this.data.page[name] = e.currentTarget.value
    if (name === 'path') {
      this.editedPath = true
    } else if (name === 'name' && !this.editedPath) {
      this.data.page.path = this.data.page.name.toLowerCase().replace(/ /g, '-')
    }
  },

  updateParent: function(e) {
    this.data.page.parent_id = Number(e.currentTarget.value) || null
  },

  mediaUploaded: function(type, file) {
    if (type === 'banner') {
      this.newBanner = file
    } else {
      this.newMedia = file
    }
  },

  mediaRemoved: function(type) {
    this.data.page['remove_' + type] = true
    this.data.page[type + '_prefix'] = null
  },

  save: function(vnode, e) {
    e.preventDefault()
    if (!this.data.page.name) {
      this.error = 'Name is missing'
    } else if (!this.data.page.path) {
      this.error = 'Path is missing'
    } else {
      this.error = ''
    }
    if (this.error) return

    let formData = new FormData()
    if (this.newBanner) {
      formData.append('banner', this.newBanner.file)
    }
    if (this.newMedia) {
      formData.append('media', this.newMedia.file)
    }
    if (this.data.page.id) {
      formData.append('id', this.data.page.id)
    }

    formData.append('name', this.data.page.name)
    formData.append('parent_id', this.data.page.parent_id || null)
    formData.append('path', this.data.page.path)
    formData.append('remove_banner', this.data.page.remove_banner ? true : false)
    formData.append('remove_media', this.data.page.remove_media ? true : false)

    this.loading = true

    this.requestPage(
      this.editor.save()
      .then(body => {
        formData.append('content', JSON.stringify(body))

        return api.sendRequest({
          method: 'PUT',
          url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
          body: formData,
        })
      })
      .then(data => {
        if (!data.page.id) {
          throw new Error('Something went wrong with saving, try again later')
        } else if (this.lastid === 'add') {
          this.lastid = data.page.id.toString()
          m.route.set('/admin/pages/' + data.page.id)
        }
        return PageTree.refreshTree().then(() => {
          this.pages = [{id: null, name: 'Frontpage'}]
          this.pages = this.pages.concat(PageTree.getFlatTree())

          return data
        })
      })
    )
  },

  view: function(vnode) {
    let page = this.data.page
    let bannerImage = page && page.banner_alt_prefix
      ? page.banner_alt_prefix + '_large.avif'
      : null
    let mediaImage = page && page.media_alt_prefix
      ? page.media_alt_prefix + '_large.avif'
      : null

    return [
      m('div.admin', [
        !this.loading
          ? m(FileUpload, {
              class: 'banner',
              height: 150,
              onfile: this.mediaUploaded.bind(this, 'banner'),
              ondelete: this.mediaRemoved.bind(this, 'banner'),
              media: bannerImage,
            }, 'Click to upload banner image')
          : null,
        m('div.inside.vertical', [
          m('div.actions', [
            '« ',
            m(m.route.Link, { href: '/admin/pages' }, 'Pages'),
            page
              ? [
                  m('div.filler'),
                  'Actions:',
                  m(m.route.Link, { href: '/page/' + page.path }, 'View page'),
                ]
              : null,
          ]),
          m('h2.title', this.lastid === 'add' ? 'Create page' : 'Edit ' + (page && page.name || '(untitled)')),
          m('div.container', [
            m('div.error', {
              hidden: !this.error,
              onclick: function() { vnode.state.error = '' },
            }, this.error),
            this.loading
              ? m('div.loading-spinner')
              : null,
            page 
              ? [
                  m(FileUpload, {
                    class: 'cover',
                    useimg: true,
                    onfile: this.mediaUploaded.bind(this, 'media'),
                    ondelete: this.mediaRemoved.bind(this, 'media'),
                    media: mediaImage,
                  }, 'Click to upload page image'),
                  m('form', {
                    onsubmit: this.save.bind(this, vnode),
                  }, [
                    m('label', 'Parent'),
                    m('select', {
                      onchange: this.updateParent.bind(this),
                    }, this.pages.filter(item => !page || item.id !== page.id).map((item) => {
                        return m('option', {
                          value: item.id || 0,
                          selected: item.id === page.parent_id
                        }, item.name)
                      })),
                    m('div.input-row', [
                      m('div', [
                        m('label', 'Name'),
                        m('input', {
                          type: 'text',
                          value: page.name,
                          oninput: this.updateValue.bind(this, 'name'),
                        }),
                      ]),
                      m('div', [
                        m('label', 'Path'),
                        m('input', {
                          type: 'text',
                          value: page.path,
                          oninput: this.updateValue.bind(this, 'path'),
                        }),
                      ]),
                    ]),
                    m('label', 'Description'),
                    m('div.content', 
                      m(Editor, {
                        oncreate: (subnode) => {
                          this.editor = subnode.state.editor
                        },
                        contentdata: page.content,
                      })
                    ),
                    m('input', {
                      hidden: !page.name || !page.path,
                      type: 'submit',
                      value: 'Save',
                    }),
                  ])
                ]
              : null,
          ]),
        ]),
      ]),

      /*
      this.loading && !this.data.page
        ? m('div.admin-spinner.loading-spinner')
        : null,
      this.data.page
        ? m('div.admin-wrapper', [
          this.loading
            ? m('div.loading-spinner')
            : null,
          m('div.admin-actions', this.data.page.id
            ? [
              m('span', 'Actions:'),
              m(m.route.Link, { href: '/page/' + this.data.page.path }, 'View page'),
            ]
            : null),
          m('article.editarticle', [
            m('header', m('h1',
                (this.data.page.id ? 'Edit ' : 'Create Page ') + (this.data.page.name || '(untitled)')
              )
            ),
            m('div.error', {
              hidden: !this.error,
              onclick: () => { vnode.state.error = '' },
            }, this.error),
            m(FileUpload, {
              height: 300,
              onfile: this.mediaUploaded.bind(this, 'banner'),
              ondelete: this.mediaRemoved.bind(this, 'banner'),
              media: bannerImage,
            }),
            m(FileUpload, {
              class: 'cover',
              useimg: true,
              onfile: this.mediaUploaded.bind(this, 'media'),
              ondelete: this.mediaRemoved.bind(this, 'media'),
              media: mediaImage,
            }),
            m('form.editarticle.content', {
              onsubmit: this.save.bind(this, vnode),
            }, [
              m('label', 'Parent'),
              m('select', {
                onchange: this.updateParent.bind(this),
              }, this.pages.filter(item => !this.data.page || item.id !== this.data.page.id).map((item) => {
                  return m('option', {
                    value: item.id || 0,
                    selected: item.id === this.data.page.parent_id
                  }, item.name)
                })),
              m('div.input-row', [
                m('div.input-group', [
                  m('label', 'Name'),
                  m('input', {
                    type: 'text',
                    value: this.data.page.name,
                    oninput: this.updateValue.bind(this, 'name'),
                  }),
                ]),
                m('div.input-group', [
                  m('label', 'Path'),
                  m('input', {
                    type: 'text',
                    value: this.data.page.path,
                    oninput: this.updateValue.bind(this, 'path'),
                  }),
                ]),
              ]),
              m('label', 'Description'),
              m(Editor, {
                oncreate: (subnode) => {
                  this.editor = subnode.state.editor
                },
                contentdata: this.data.page.content,
              }),
              m('div', {
                hidden: !this.data.page.name || !this.data.page.path
              }, [
                m('input', {
                  type: 'submit',
                  value: 'Save',
                }),
              ]),
            ]),
          ]),
        ])
      : m('div.error', {
          hidden: !this.error,
          onclick: () => { this.fetchPage(vnode) },
        }, this.error),,*/
    ]
  },
}

module.exports = AdminEditPage

},function (global, require, module, exports) {
// fileupload.js

const FileUpload = {
  fileChanged: function(vnode, event) {
    if (!event.target.files[0]) return

    let preview = null
    if (event.target.files[0].type.startsWith('image')) {
      preview = URL.createObjectURL(event.target.files[0])
    }
    if (this.preview) {
      this.preview.clear()
    }

    let out = {
      file: event.target.files[0],
      preview: preview,
      clear: function() {
        URL.revokeObjectURL(preview)
      }
    }

    this.preview = out

    vnode.attrs.onfile(out)
  },

  fileRemoved: function(vnode) {
    if (this.preview) {
      this.preview.clear()
      this.preview = null
      vnode.attrs.onfile(null)
    }
    if (vnode.attrs.media) {
      vnode.attrs.ondelete(vnode.attrs.media)
    }
  },

  oninit: function(vnode) {
    this.loading = false
    this.preview = null
  },

  view: function(vnode) {
    let imageLink = this.preview && this.preview.preview || vnode.attrs.media

    return m('fileupload', {
      class: (vnode.attrs.class || '') + ' '
        + (!imageLink ? 'empty' : '') + ' '
        + (vnode.attrs.useimg ? 'useimg' : ''),
    }, [
      imageLink && vnode.attrs.useimg
        ? m('img', { src: imageLink })
        : null,
      imageLink && !vnode.attrs.useimg
        ? m('a', {
            href: imageLink,
            style: {
              'background-image': 'url("' + (imageLink) + '")',
              'height': vnode.attrs.height + 'px'
            },
          })
        : null
      ,
      !imageLink
        ? [
            m('div.noimage', {
              style: {
                'padding-top': vnode.attrs.useimg ? '56.25%' : null,
                'height': !vnode.attrs.useimg ? vnode.attrs.height + 'px' : null,
              },
            }),
            m('div.text', vnode.children)
          ]
        : null,
      m('input', {
        accept: 'image/*',
        type: 'file',
        onchange: this.fileChanged.bind(this, vnode),
      }),
      imageLink && vnode.attrs.ondelete
        ? m('button.remove', { onclick: this.fileRemoved.bind(this, vnode), title: 'Remove image' }, m.trust('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M144 400C144 408.8 136.8 416 128 416C119.2 416 112 408.8 112 400V176C112 167.2 119.2 160 128 160C136.8 160 144 167.2 144 176V400zM240 400C240 408.8 232.8 416 224 416C215.2 416 208 408.8 208 400V176C208 167.2 215.2 160 224 160C232.8 160 240 167.2 240 176V400zM336 400C336 408.8 328.8 416 320 416C311.2 416 304 408.8 304 400V176C304 167.2 311.2 160 320 160C328.8 160 336 167.2 336 176V400zM310.1 22.56L336.9 64H432C440.8 64 448 71.16 448 80C448 88.84 440.8 96 432 96H416V432C416 476.2 380.2 512 336 512H112C67.82 512 32 476.2 32 432V96H16C7.164 96 0 88.84 0 80C0 71.16 7.164 64 16 64H111.1L137 22.56C145.8 8.526 161.2 0 177.7 0H270.3C286.8 0 302.2 8.526 310.1 22.56V22.56zM148.9 64H299.1L283.8 39.52C280.9 34.84 275.8 32 270.3 32H177.7C172.2 32 167.1 34.84 164.2 39.52L148.9 64zM64 432C64 458.5 85.49 480 112 480H336C362.5 480 384 458.5 384 432V96H64V432z"/></svg>'))
        : null,
      /*this.loading
        ? m('div.loading-spinner')
        : null,*/
    ])
  },
}

module.exports = FileUpload

},function (global, require, module, exports) {
// ../page_tree.js
const api = require(4)

const Tree = window.__nfptree && window.__nfptree.tree || []
const TreeMap = new Map()

exports.Tree = Tree
exports.TreeMap = TreeMap

function parseLeaf(tree) {
  for (let branch of tree) {
    TreeMap.set(branch.path, branch)

    if (branch.children && branch.children.length) {
      parseLeaf(branch.children)
    }
  }
}

parseLeaf(Tree)

function processPageBranch(arr, branches, prefix) {
  branches.forEach((page) => {
    arr.push({ id: page.id, name: prefix + page.name })
    if (page.children && page.children.length) {
      processPageBranch(arr, page.children, page.name + ' -> ')
    }
  })
}

exports.getFlatTree = function() {
  let arr = []
  processPageBranch(arr, Tree, '')
  return arr
}

exports.refreshTree = function() {
  return api.sendRequest({
    method: 'GET',
    url: '/api/pagetree',
  })
  .then(pages => {
    Tree.splice(0, Tree.length)
    Tree.push.apply(Tree, pages.tree)
    TreeMap.clear()
    parseLeaf(Tree)
  })
}
},function (global, require, module, exports) {
// ../api.js
const Authentication = require(5)

exports.sendRequest = function(options, isPagination) {
  let token = Authentication.getToken()
  let pagination = isPagination

  if (token) {
    options.headers = options.headers || {}
    options.headers['Authorization'] = 'Bearer ' + token
  }

  options.extract = function(xhr) {
    if (xhr.responseText && xhr.responseText.slice(0, 9) === '<!doctype') {
      throw new Error('Expected JSON but got HTML (' + xhr.status + ': ' + this.url.split('?')[0] + ')')
    }
    let out = null
    if (pagination && xhr.status < 300) {
      let headers = {}

      xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
        var splitted = item.split(': ')
        headers[splitted[0]] = splitted[1]
      })

      out = {
        headers: headers || {},
        data: JSON.parse(xhr.responseText),
      }
    } else {
      if (xhr.responseText) {
        out = JSON.parse(xhr.responseText)
      } else {
        out = {}
      }
    }
    if (xhr.status >= 300) {
      throw out
    }
    return out
  }

  return m.request(options)
    .catch(function (error) {
      if (error.status === 403) {
        Authentication.clearToken()
        m.route.set('/login', { redirect: m.route.get() })
      }
      if (error.response && error.response.status) {
        return Promise.reject(error.response)
      }
      return Promise.reject(error)
    })
}

},function (global, require, module, exports) {
// ../authentication.js
const storageName = 'nfp_sites_logintoken'

const Authentication = {
  currentUser: null,
  isAdmin: false,
  loadingListeners: [],
  authListeners: [],

  updateToken: function(token) {
    if (!token) return Authentication.clearToken()
    localStorage.setItem(storageName, token)
    Authentication.currentUser = JSON.parse(atob(token.split('.')[1]))

    if (Authentication.authListeners.length) {
      Authentication.authListeners.forEach(function(x) { x(Authentication.currentUser) })
    }
  },

  clearToken: function() {
    Authentication.currentUser = null
    localStorage.removeItem(storageName)
    Authentication.isAdmin = false
  },

  addEvent: function(event) {
    Authentication.authListeners.push(event)
  },

  setAdmin: function(item) {
    Authentication.isAdmin = item
  },

  getToken: function() {
    return localStorage.getItem(storageName)
  },
}

Authentication.updateToken(localStorage.getItem(storageName))

window.Authentication = Authentication

module.exports = Authentication

},function (global, require, module, exports) {
// editor.js
const Editor = {
  oninit: function(vnode) {
    this.editor = null
    this.lastData = null
  },

  oncreate: function(vnode) {
    this.editor = new window.EditorJS({
      holder: vnode.dom,
      inlineToolbar: ['link', 'bold', 'inlineCode', 'italic'],
      tools: {
        inlineCode: {
          class: window.InlineCode, //<span class="inline-code"></span>
          shortcut: 'CMD+SHIFT+M',
        },
        header: window.Header,
        image: window.SimpleImage,
        quote: window.Quote,
        code: window.CodeTool,
        list: {
          class: window.List,
          inlineToolbar: true,
          config: {
            defaultStyle: 'unordered'
          }
        },
        delimiter: window.Delimiter,
        htmlraw: window.RawTool,
      },
      data: vnode.attrs.contentdata,
    })
    this.lastData = vnode.attrs.contentdata
  },

  onupdate: function(vnode) {
    if (this.lastData !== vnode.attrs.contentdata) {
      this.lastData = vnode.attrs.contentdata
      this.editor.render(this.lastData)
    }
  },
  
  view: function(vnode) {
    return m('div')
  }
}

module.exports = Editor

},function (global, require, module, exports) {
// site_pages.js
const PageTree = require(3)
const Dialogue = require(8)
const api = require(4)

const AdminPages = {
  oninit: function(vnode) {
    this.error = ''
    this.pages = []

    document.title = 'Pages - Admin NFP Moe'
    this.fetchPages(vnode)
  },

  fetchPages: function(vnode) {
    this.loading = true
    this.error = ''

    return api.sendRequest({
      method: 'GET',
      url: '/api/auth/pages',
    })
    .then((result) => {
      this.pages = result.tree
    }, (err) => {
      this.error = err.message
    })
    .then(() => {
      this.loading = false
      m.redraw()
    })
  },

  confirmRemovePage: function(vnode, page) {
    this.loading = true
    m.redraw()

    return api.sendRequest({
      method: 'DELETE',
      url: '/api/auth/pages/' + page.id,
    })
    .then(
      () => Promise.all([
        PageTree.refreshTree(),
        this.fetchPages(),
      ]),
      (err) => { this.error = err.message }
    )
    .then(() => {
      this.loading = false
      m.redraw()
    })
  },

  askConfirmRemovePage: function(vnode, page) {
    Dialogue.showDialogue(
      'Delete ' + page.name,
      'Are you sure you want to remove "' + page.name + '" (' + page.path + ')',
      'Remove',
      'alert',
      'Don\'t remove',
      '',
      page,
      this.confirmRemovePage.bind(this, vnode))
  },

  drawPage: function(vnode, page) {
    return [
      m('tr', [
        m('td.nopadding', page.banner_alt_prefix
          ? m('a', { href: page.banner_path, target: '_blank' }, m('img', { src: page.banner_alt_prefix + '_small.avif' }))
          : m.trust('&nbsp;') ),
        m('td.nopadding', page.media_alt_prefix
          ? m('a', { href: page.media_path, target: '_blank' }, m('img', { src: page.media_alt_prefix + '_small.avif' }))
          : m.trust('&nbsp;') ),
        m('td', [
          page.parent_id ? m('span.subpage', ' - ') : null,
          m(m.route.Link, { href: '/admin/pages/' + page.id }, page.name),
        ]),
        m('td', m(m.route.Link, { href: '/page/' + page.path }, '/page/' + page.path)),
        m('td.right', page.updated_at.replace('T', ' ').split('.')[0]),
        m('td.right', m('button', { onclick: this.askConfirmRemovePage.bind(this, vnode, page) }, 'Remove')),
      ]),
    ].concat(page.children ? page.children.map(AdminPages.drawPage.bind(this, vnode)) : [])
  },

  view: function(vnode) {
    return [
      m('div.admin', [
        m('div.inside.vertical', [
          m('div.actions', [
            m('div.filler'),
            m('span', 'Actions:'),
            m(m.route.Link, { href: '/admin/pages/add' }, 'Create new page'),
          ]),
          m('h2.title', 'All pages'),
          m('div.container', [
            m('div.error', {
              hidden: !this.error,
              onclick: function() { vnode.state.error = '' },
            }, this.error),
            this.loading
              ? m('div.loading-spinner')
              : m('table', [
                  m('thead', 
                    m('tr', [
                      m('th', 'Banner'),
                      m('th', 'Cover'),
                      m('th', 'Title'),
                      m('th', 'Path'),
                      m('th.right', 'Updated'),
                      m('th.right', 'Actions'),
                    ])
                  ),
                  m('tbody', this.pages.map(AdminPages.drawPage.bind(this, vnode))),
                ],
            ),
          ]),
        ]),
      ]),
    ]
  },
}

module.exports = AdminPages

},function (global, require, module, exports) {
// dialogue.js
const Dialogue = {
  showDialogueData: null,

  showDialogue: function(title, message, yes, yesclass, no, noclass, data, cb) {
    Dialogue.showDialogueData = {
      title: title,
      message: message,
      yes: yes,
      yesclass: yesclass || '',
      no: no,
      noclass: noclass || '',
      data: data,
      cb: cb,
    }
  },

  onclick: function(clickedYes) {
    if (clickedYes) {
      Dialogue.showDialogueData.cb(Dialogue.showDialogueData.data)
    }
    this.onclose()
    m.redraw()
  },

  onclose: function() {
    Dialogue.showDialogueData = null
  },

  view: function(vnode) {
    let data = Dialogue.showDialogueData
    return data
      ? m('div.floating-container.main', {
        onclick: this.onclose.bind(this),
      }, m('dialogue', { onclick: function(e) { e.stopPropagation() } }, [
            m('h2.title', data.title),
            m('p', data.message),
            m('div.buttons', [
              m('button', { class: data.yesclass , onclick: this.onclick.bind(this) }, data.yes),
              m('button', { class: data.noclass, onclick: this.onclose.bind(this) }, data.no),
            ]),
          ])
        )
      : null
  },
}

module.exports = Dialogue

},function (global, require, module, exports) {
// site_articles.js
const Dialogue = require(8)
const api = require(4)
const Paginator = require(10)

const ItemsPerPage = 20

const AdminArticles = {
  oninit: function(vnode) {
    this.error = ''
    this.loading = false
    this.showLoading = null
    this.data = {
      articles: [],
      total_articles: 0,
    }
    this.currentPage = Number(m.route.param('page')) || 1

    this.fetchArticles(vnode)
  },

  onbeforeupdate: function(vnode) {
    this.currentPage = Number(m.route.param('page')) || 1

    if (this.currentPage !== this.lastpage) {
      this.fetchArticles(vnode)
    }
  },

  fetchArticles: function(vnode) {
    this.error = ''
    this.lastpage = this.currentPage

    document.title = 'Articles Page ' + this.lastpage + ' - Admin NFP Moe'

    if (this.showLoading) {
      clearTimeout(this.showLoading)
    }

    if (this.data.articles.length) {
      this.showLoading = setTimeout(() => {
        this.showLoading = null
        this.loading = true
        m.redraw()
      }, 150)
    } else {
      this.loading = true
    }

    return api.sendRequest({
      method: 'GET',
      url: '/api/auth/articles?page=' + (this.lastpage || 1),
    })
    .then((result) => {
      this.data = result

      this.data.articles.forEach((article) => {
        article.hidden = new Date() < new Date(article.publish_at)
        article.page_path = article.page_path ? '/page/' + article.page_path : '/'
        article.page_name = article.page_name || 'Frontpage'
      })
    }, (err) => {
      this.error = err.message
    })
    .then(() => {
      clearTimeout(this.showLoading)
      this.showLoading = null
      this.loading = false
      m.redraw()
    })
  },

  confirmRemoveArticle: function(vnode, article) {
    this.loading = true
    m.redraw()

    return api.sendRequest({
      method: 'DELETE',
      url: '/api/auth/articles/' + article.id,
    })
    .then(
      () => this.fetchArticles(vnode),
      (err) => { this.error = err.message }
    )
    .then(() => {
      this.loading = false
      m.redraw()
    })
  },
  
  askConfirmRemovePage: function(vnode, article) {
    Dialogue.showDialogue(
      'Delete ' + article.name,
      'Are you sure you want to remove "' + article.name + '" (' + article.path + ')',
      'Remove',
      'alert',
      'Don\'t remove',
      '',
      article,
      this.confirmRemoveArticle.bind(this, vnode))
  },

  drawArticle: function(vnode, article) {
    return [
      m('tr', {
        class: article.hidden
          ? rowhidden
          : article.is_featured
            ? 'rowfeatured'
            : ''
      }, [
        m('td.nopadding', article.banner_alt_prefix
          ? m('a', { href: article.banner_path, target: '_blank' }, m('img', { src: article.banner_alt_prefix + '_small.avif' }))
          : m.trust('&nbsp;') ),
        m('td.nopadding', article.media_alt_prefix
          ? m('a', { href: article.media_path, target: '_blank' }, m('img', { src: article.media_alt_prefix + '_small.avif' }))
          : m.trust('&nbsp;') ),
        m('td', m(m.route.Link, { href: '/admin/articles/' + article.id }, article.name)),
        m('td', m(m.route.Link, { href: '/article/' + article.path }, 'View')),
        m('td', m(m.route.Link, { href: article.page_path }, article.page_name)),
        m('td.right', article.publish_at.replace('T', ' ').split('.')[0]),
        m('td.right', article.admin_name),
        m('td.right', m('button', { onclick: this.askConfirmRemovePage.bind(this, vnode, article) }, 'Remove')),
      ]),
    ]
  },

  view: function(vnode) {
    return [
      m('div.admin', [
        m('div.inside.vertical', [
          m('div.actions', [
            m('div.filler'),
            m('span', 'Actions:'),
            m(m.route.Link, { href: '/admin/articles/add' }, 'Create new article'),
          ]),
          m('h2.title', 'All articles'),
          m('div.container', [
            m('div.error', {
              hidden: !this.error,
              onclick: function() { vnode.state.error = '' },
            }, this.error),
            this.loading
              ? m('div.loading-spinner.full')
              : m('table', [
                  m('thead', 
                    m('tr', [
                      m('th', 'Banner'),
                      m('th', 'Cover'),
                      m('th', 'Title'),
                      m('th', 'Path'),
                      m('th', 'Page'),
                      m('th.right', 'Publish'),
                      m('th.right', 'By'),
                      m('th.right', 'Actions'),
                    ])
                  ),
                  m('tbody', this.data.articles.map((article) => this.drawArticle(vnode, article))),
                ],
            ),
            m(Paginator, {
              base: '/admin/articles',
              page: this.currentPage,
              perPage: ItemsPerPage,
              total: this.data.total_articles,
            }),
          ]),
        ]),
      ]),
    ]
  },
}

module.exports = AdminArticles

},function (global, require, module, exports) {
// ../paginator.js
const Paginator = {
  view: function(vnode) {
    let total = vnode.attrs.total
    let currentPage = vnode.attrs.page
    let perPage = vnode.attrs.perPage || 10
    let maxPage = total / perPage + 1

    if (total <= perPage) return null

    return m('paginator', [
      currentPage > 1
        ? [
            m(m.route.Link, {
              href: vnode.attrs.base,
            }, 'First'),
            m(m.route.Link, {
              href: vnode.attrs.base + (currentPage > 2
                ? '?page=' + (currentPage - 1)
                : ''
              ),
            }, 'Previous'),
          ]
        : [ m('div'), m('div')],
      m('div', 'Page ' + currentPage),
      currentPage < maxPage
        ? [
            m(m.route.Link, {
              href: vnode.attrs.base + '?page=' + (currentPage + 1),
            }, 'Next'),
            m(m.route.Link, {
              href: vnode.attrs.base + '?page=' + maxPage,
            }, 'Last')
          ]
        : [ m('div'), m('div')],
    ])
  },
}

module.exports = Paginator

},function (global, require, module, exports) {
// site_editarticle.js
require(12)
const FileUpload = require(2)
const PageTree = require(3)
const Fileinfo = require(13)
const api = require(4)
const Editor = require(6)
const Dialogue = require(8)

const EditArticle = {

  oninit: function(vnode) {
    this.loading = false
    this.showLoading = null
    this.data = {
      article: null,
      files: [],
      staff: [],
    }
    this.pages = [{id: null, name: 'Frontpage'}]
    this.pages = this.pages.concat(PageTree.getFlatTree())

    this.removeBanner = false
    this.removeMedia = false
    this.newBanner = null
    this.newMedia = null
    this.dateInstance = null
    this.editor = null

    this.fetchArticle(vnode)
  },

  onbeforeupdate: function(vnode) {
    if (this.lastid !== m.route.param('id')) {
      this.fetchArticle(vnode)
    }
  },

  fetchArticle: function(vnode) {
    this.lastid = m.route.param('id')

    return this.requestArticle(
      api.sendRequest({
        method: 'GET',
        url: '/api/auth/articles/' + (this.lastid === 'add' ? '0' : this.lastid),
      })
    )
  },

  requestArticle: function(data) {
    this.error = ''

    if (this.showLoading) {
      clearTimeout(this.showLoading)
    }

    if (this.data.article) {
      this.showLoading = setTimeout(() => {
        this.showLoading = null
        this.loading = true
        m.redraw()
      }, 150)
    } else {
      this.loading = true
    }

    data
    .then((result) => {
      this.data = result

      if (this.data.article.id) {
        this.data.article.publish_at = new Date(this.data.article.publish_at)
        document.title = 'Editing: ' + this.data.article.name + ' - Admin NFP Moe'
        this.editedPath = true
      } else {
        this.data.article.publish_at = new Date('3000-01-01')
        document.title = 'Create Article - Admin NFP Moe'
      }
    }, (err) => {
      this.error = err.message
    })
    .then(() => {
      clearTimeout(this.showLoading)
      this.showLoading = null
      this.loading = false
      m.redraw()
    })
  },

  updateValue: function(name, e) {
    if (name === 'is_featured') {
      this.data.article[name] = e.currentTarget.checked
    } else {
      this.data.article[name] = e.currentTarget.value
    }
    if (name === 'path') {
      this.editedPath = true
    } else if (name === 'name' && !this.editedPath) {
      this.data.article.path = this.data.article.name
        .normalize("NFD").replace(/[\u0300-\u036f]/g, '')
        .toLocaleLowerCase()
        .replace(/[^a-z0-9]/g, '-')
        .replace(/\-{2,}/g, '-')
        .replace(/^-+/, '')
        .replace(/-+$/, '')
    }
  },

  updateParent: function(e) {
    this.data.article.page_id = Number(e.currentTarget.value) || null
  },

  updateStaffer: function(e) {
    this.data.article.admin_id = Number(e.currentTarget.value)
  },

  mediaUploaded: function(type, file) {
    if (type === 'banner') {
      this.newBanner = file
      this.removeBanner = false
    } else {
      this.newMedia = file
      this.removeMedia = false
    }
  },

  mediaRemoved: function(type) {
    this.data.article[type + '_alt_prefix'] = null
    if (type === 'banner') {
      this.removeBanner = true
    } else {
      this.removeMedia = true
    }
  },

  save: function(vnode, e) {
    e.preventDefault()
    if (!this.data.article.name) {
      this.error = 'Name is missing'
    } else if (!this.data.article.path) {
      this.error = 'Path is missing'
    } else {
      this.error = ''
    }
    if (this.error) return

    let formData = new FormData()
    if (this.newBanner) {
      formData.append('banner', this.newBanner.file)
    }
    if (this.newMedia) {
      formData.append('media', this.newMedia.file)
    }
    if (this.data.article.id) {
      formData.append('id', this.data.article.id)
    }

    formData.append('admin_id', this.data.article.admin_id || this.data.staff[0].id)
    formData.append('name', this.data.article.name)
    formData.append('is_featured', this.data.article.is_featured || false)
    formData.append('path', this.data.article.path)
    formData.append('page_id', this.data.article.page_id || null)
    formData.append('publish_at', this.dateInstance.inputElem.value.replace(', ', 'T') + 'Z')
    formData.append('remove_banner', this.removeBanner ? true : false)
    formData.append('remove_media', this.removeMedia ? true : false)

    this.loading = true

    this.requestArticle(
      this.editor.save()
      .then(body => {
        formData.append('content', JSON.stringify(body))

        return api.sendRequest({
          method: 'PUT',
          url: '/api/auth/articles/' + (this.lastid === 'add' ? '0' : this.lastid),
          body: formData,
        })
      })
      .then(data => {
        if (!data.article.id) {
          throw new Error('Something went wrong with saving, try again later')
        } else if (this.lastid === 'add') {
          this.lastid = data.article.id.toString()
          m.route.set('/admin/articles/' + data.article.id)
        }
        return data
      })
    )
  },

  uploadFile: function(vnode, e) {
    if (!e.target.files[0]) return
    if (this.lastid === 'add') return

    let file = e.target.files[0]
    e.target.value = null

    let formData = new FormData()
    formData.append('file', file)

    return this.refreshFiles(vnode, api.sendRequest({
      method: 'POST',
      url: '/api/auth/articles/' + this.lastid + '/files',
      body: formData,
    }))
  },

  refreshFiles: function(vnode, prom) {
    this.loading = true
    m.redraw()

    prom.then(() => {
      return api.sendRequest({
        method: 'GET',
        url: '/api/auth/articles/' + this.lastid,
      })
    })
    .then((result) => {
      this.data.files = result.files
    }, (err) => {
      this.error = err.message
    })
    .then(() => {
      this.loading = false
      m.redraw()
    })
  },

  confirmRemoveFile: function(vnode, file) {
    return this.refreshFiles(vnode, api.sendRequest({
      method: 'DELETE',
      url: '/api/auth/articles/' + this.lastid + '/files/' + file.id,
    }))
  },

  askConfirmRemoveFile: function(vnode, file) {
    Dialogue.showDialogue(
      'Delete file',
      'Are you sure you want to remove "' + file.filename + '"',
      'Delete',
      'alert',
      'Don\'t delete',
      '',
      file,
      this.confirmRemoveFile.bind(this, vnode))
  },

  view: function(vnode) {
    let article = this.data.article
    const showPublish = article
      ? article.publish_at > new Date()
      : false
    const bannerImage = article && article.banner_alt_prefix
      ? article.banner_alt_prefix + '_large.avif'
      : null
    const mediaImage = article && article.media_alt_prefix
      ? article.media_alt_prefix + '_large.avif'
      : null

    return [
      m('div.admin', [
        !this.loading
          ? m(FileUpload, {
              class: 'banner',
              height: 150,
              onfile: this.mediaUploaded.bind(this, 'banner'),
              ondelete: this.mediaRemoved.bind(this, 'banner'),
              media: bannerImage,
            }, 'Click to upload banner image (only visible when featured)')
          : null,
        m('div.inside.vertical', [
          m('div.actions', [
            '« ',
            m(m.route.Link, { href: '/admin/articles' }, 'Articles'),
            article && article.id
              ? [
                  m('div.filler'),
                  m('span', 'Actions:'),
                  m(m.route.Link, { href: '/article/' + article.path }, 'View article'),
                ]
              : null,
          ]),
          m('h2.title', this.lastid === 'add' ? 'Create article' : 'Edit ' + (article && article.name || '(untitled)')),
          m('div.container', [
            m('div.error', {
              hidden: !this.error,
              onclick: function() { vnode.state.error = '' },
            }, this.error),
            this.loading
              ? m('div.loading-spinner')
              : null,
            article
              ? [
                  m(FileUpload, {
                    class: 'cover',
                    useimg: true,
                    onfile: this.mediaUploaded.bind(this, 'media'),
                    ondelete: this.mediaRemoved.bind(this, 'media'),
                    media: mediaImage,
                  }, 'Click to upload article image'),
                  m('form', {
                    onsubmit: this.save.bind(this, vnode),
                  }, [
                    m('label', 'Parent'),
                    m('select', {
                      onchange: this.updateParent.bind(this),
                    }, this.pages.map((item) => {
                        return m('option', {
                          value: item.id || 0,
                          selected: item.id === article.page_id
                        }, item.name)
                      })),
                    m('div.input-row', [
                      m('div.input-group', [
                        m('label', 'Name'),
                        m('input', {
                          type: 'text',
                          value: article.name,
                          oninput: this.updateValue.bind(this, 'name'),
                        }),
                      ]),
                      m('div.input-group', [
                        m('label', 'Path'),
                        m('input', {
                          type: 'text',
                          value: article.path,
                          oninput: this.updateValue.bind(this, 'path'),
                        }),
                      ]),
                    ]),
                    m('label', 'Description'),
                    m(Editor, {
                      oncreate: (subnode) => {
                        this.editor = subnode.state.editor
                      },
                      contentdata: article.content,
                    }),
                    m('div.input-row', [
                      m('div', [
                        m('label', 'Published at'),
                        m('input', {
                          type: 'text',
                          oncreate: (div) => {
                            if (!this.dateInstance) {
                              this.dateInstance = new dtsel.DTS(div.dom, {
                                dateFormat: 'yyyy-mm-dd',
                                timeFormat: 'HH:MM:SS',
                                showTime: true,
                              })
                              window.temp = this.dateInstance
                            }
                          },
                          value: article.publish_at.toISOString().replace('T', ', ').split('.')[0],
                        }),
                      ]),
                      m('div', [
                        m('label', 'Published by'),
                        m('select', {
                            onchange: this.updateStaffer.bind(this),
                          },
                          this.data.staff.map((item) => {
                            return m('option', {
                              value: item.id,
                              selected: item.id === article.admin_id
                            }, item.name)
                          })
                        ),
                      ]),
                      m('div.slim', [
                        m('label', 'Make featured'),
                        m('input', {
                          type: 'checkbox',
                          checked: article.is_featured,
                          oninput: this.updateValue.bind(this, 'is_featured'),
                        }),
                      ]),
                    ]),
                    m('div.actions', {
                      hidden: !article.name || !article.path
                    }, [
                      m('input', {
                        type: 'submit',
                        value: article.id ? 'Save' : 'Create',
                      }),
                      showPublish
                        ? m('button', {
                            onclick: () => {
                              this.dateInstance.inputElem.value = (new Date().toISOString()).replace('T', ', ').split('.')[0]
                            }
                          }, 'Publish now')
                        : null,
                    ]),
                  ]),
                  m('files', [
                    m('h5', 'Files'),
                    this.data.files.map((file) => {
                      return m(
                        Fileinfo,
                        { file: file },
                        m('div.remove',
                          m('button', { onclick: () => this.askConfirmRemoveFile(vnode, file) }, 'remove')
                        )
                      )
                    }),
                  ]),
                  article.id
                    ? m('div.actions', [
                        m('button.fileupload', [
                          'Add file',
                          m('input', {
                            accept: '*',
                            type: 'file',
                            onchange: this.uploadFile.bind(this, vnode),
                          }),
                        ])
                      ])
                    : null,
                ]
              : null,
          ]),
        ]),
      ]),
    ]
  },
}

module.exports = EditArticle

},function (global, require, module, exports) {
// dtsel.js
(function () {
  "use strict";

  var BODYTYPES = ["DAYS", "MONTHS", "YEARS"];

  /** @typedef {Object.<string, Function[]>} Handlers */
  /** @typedef {function(String, Function): null} AddHandler */
  /** @typedef {("DAYS"|"MONTHS"|"YEARS")} BodyType */
  /** @typedef {string|number} StringNum */
  /** @typedef {Object.<string, StringNum>} StringNumObj */

  /**
   * The local state
   * @typedef {Object} InstanceState
   * @property {Date} value
   * @property {Number} year
   * @property {Number} month
   * @property {Number} day
   * @property {Number} time
   * @property {Number} hours
   * @property {Number} minutes
   * @property {Number} seconds
   * @property {BodyType} bodyType
   * @property {Boolean} visible
   * @property {Number} cancelBlur
   */

  /** 
   * @typedef {Object} Config
   * @property {String} dateFormat
   * @property {String} timeFormat
   * @property {Boolean} showDate
   * @property {Boolean} showTime
   * @property {Boolean} showSeconds
   * @property {Number} paddingX
   * @property {Number} paddingY
   * @property {BodyType} defaultView
   * @property {"TOP"|"BOTTOM"} direction
   * @property {Array} months
   * @property {Array} monthsShort
   * @property {Array} weekdaysShort
   * @property {Array} timeDescr
  */

  /**
   * @class
   * @param {HTMLElement} elem 
   * @param {Config} config 
   */
  function DTS(elem, config) {
      var config = config || {};

      /** @type {Config} */
      var defaultConfig = {
          defaultView: BODYTYPES[0],
          dateFormat: "yyyy-mm-dd",
          timeFormat: "HH:MM:SS",
          showDate: true,
          showTime: false,
          showSeconds: true,
          paddingX: 5,
          paddingY: 5,
          direction: 'TOP',
          months: [
              "January", "February", "March", "April", "May", "June",
              "July", "August", "September", "October", "November", "December"
          ],
          monthsShort: [
              "Jan", "Feb", "Mar", "Apr", "May", "Jun",
              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
          ], 
          weekdaysShort: [
              "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"
          ],
          timeDescr: [
              "HH:", "MM:", "SS:"
          ]
      }

      if (!elem) {
          throw TypeError("input element or selector required for contructor");
      }
      if (Object.getPrototypeOf(elem) === String.prototype) {
          var _elem = document.querySelectorAll(elem);
          if (!_elem[0]){
              throw Error('"' + elem + '" not found.');
          }
          elem = _elem[0];
      }
      this.config = setDefaults(config, defaultConfig);
      this.dateFormat = this.config.dateFormat;
      this.timeFormat = this.config.timeFormat;
      this.dateFormatRegEx = new RegExp("yyyy|yy|mm|dd", "gi");
      this.timeFormatRegEx = new RegExp("hh|mm|ss|a", "gi");
      this.inputElem = elem;
      this.dtbox = null;
      this.setup();
  }
  DTS.prototype.setup = function () {
      var handler = this.inputElemHandler.bind(this);
      this.inputElem.addEventListener("focus", handler, false)
      this.inputElem.addEventListener("blur", handler, false);
  }
  DTS.prototype.inputElemHandler = function (e) {
      if (e.type == "focus") {
          if (!this.dtbox) {
              this.dtbox = new DTBox(e.target, this);
          }
          this.dtbox.visible = true;
      } else if (e.type == "blur" && this.dtbox && this.dtbox.visible) {
          var self = this;
          setTimeout(function () {
              if (self.dtbox.cancelBlur > 0) {
                  self.dtbox.cancelBlur -= 1;
               } else {
                  self.dtbox.visible = false;
                  self.inputElem.blur();
               }
          }, 100);
      }
  }
  /**
   * @class
   * @param {HTMLElement} elem 
   * @param {DTS} settings 
   */
  function DTBox(elem, settings) {
      /** @type {DTBox} */
      var self = this;

      /** @type {Handlers} */
      var handlers = {};

      /** @type {InstanceState} */
      var localState = {};

      /**
       * @param {String} key 
       * @param {*} default_val 
       */
      function getterSetter(key, default_val) {
          return {
              get: function () {
                  var val = localState[key];
                  return val === undefined ? default_val : val;
              },
              set: function (val) {
                  var prevState = self.state;
                  var _handlers = handlers[key] || [];
                  localState[key] = val;
                  for (var i = 0; i < _handlers.length; i++) {
                      _handlers[i].bind(self)(localState, prevState);
                  }
              },
          };
      };

      /** @type {AddHandler} */
      function addHandler(key, handlerFn) {
          if (!key || !handlerFn) {
              return false;
          }
          if (!handlers[key]) {
              handlers[key] = [];
          }
          handlers[key].push(handlerFn);
      }

      Object.defineProperties(this, {
          visible: getterSetter("visible", false),
          bodyType: getterSetter("bodyType", settings.config.defaultView),
          value: getterSetter("value"),
          year: getterSetter("year", 0),
          month: getterSetter("month", 0),
          day: getterSetter("day", 0),
          hours: getterSetter("hours", 0),
          minutes: getterSetter("minutes", 0),
          seconds: getterSetter("seconds", 0),
          cancelBlur: getterSetter("cancelBlur", 0),
          addHandler: {value: addHandler},
          month_long: {
              get: function () {
                  return self.settings.config.months[self.month];
              },
          },
          month_short: {
              get: function () {
                  return self.settings.config.monthsShort[self.month]
              },
          },
          state: {
              get: function () {
                  return Object.assign({}, localState);
              },
          },
          time: {
              get: function() {
                  var hours = self.hours * 60 * 60 * 1000;
                  var minutes = self.minutes * 60 * 1000;
                  var seconds = self.seconds * 1000;
                  return  hours + minutes + seconds;
              }
          },
      });
      this.el = {};
      this.settings = settings;
      this.elem = elem;
      this.setup();
  }
  DTBox.prototype.setup = function () {
      Object.defineProperties(this.el, {
          wrapper: { value: null, configurable: true },
          header: { value: null, configurable: true },
          body: { value: null, configurable: true },
          footer: { value: null, configurable: true }
      });
      this.setupWrapper();
      if (this.settings.config.showDate) {
          this.setupHeader();
          this.setupBody();
      }
      if (this.settings.config.showTime) {
          this.setupFooter();
      }

      var self = this;
      this.addHandler("visible", function (state, prevState) {
          if (state.visible && !prevState.visible){
              document.body.appendChild(this.el.wrapper);

              var parts = self.elem.value.split(/\s*,\s*/);
              var startDate = undefined;
              var startTime = 0;
              if (self.settings.config.showDate) {
                  startDate = parseDate(parts[0], self.settings);
              }
              if (self.settings.config.showTime) {
                  startTime = parseTime(parts[parts.length-1], self.settings);
                  startTime = startTime || 0;
              }
              if (!(startDate && startDate.getTime())) {
                  startDate = new Date();
                  startDate = new Date(
                      startDate.getFullYear(),
                      startDate.getMonth(),
                      startDate.getDate()
                  );
              }
              var value = new Date(startDate.getTime() + startTime);
              self.value = value;
              self.year = value.getFullYear();
              self.month = value.getMonth();
              self.day = value.getDate();
              self.hours = value.getHours();
              self.minutes = value.getMinutes();
              self.seconds = value.getSeconds();

              if (self.settings.config.showDate) {
                  self.setHeaderContent();
                  self.setBodyContent();
              }
              if (self.settings.config.showTime) {
                  self.setFooterContent();
              }
          } else if (!state.visible && prevState.visible) {
              document.body.removeChild(this.el.wrapper);
          }
      });
  }
  DTBox.prototype.setupWrapper = function () {
      if (!this.el.wrapper) {
          var el = document.createElement("div");
          el.classList.add("date-selector-wrapper");
          Object.defineProperty(this.el, "wrapper", { value: el });
      }
      var self = this;
      var htmlRoot = document.getElementsByTagName('html')[0];
      function setPosition(e){
          var minTopSpace = 300;
          var box = getOffset(self.elem);
          var config = self.settings.config;
          var paddingY = config.paddingY || 5;
          var paddingX = config.paddingX || 5;
          var top = box.top + self.elem.offsetHeight + paddingY;
          var left = box.left + paddingX;
          var bottom = htmlRoot.clientHeight - box.top + paddingY;

          self.el.wrapper.style.left = `${left}px`;
          if (box.top > minTopSpace && config.direction != 'BOTTOM') {
              self.el.wrapper.style.bottom = `${bottom}px`;
              self.el.wrapper.style.top = '';
          } else {
              self.el.wrapper.style.top = `${top}px`;
              self.el.wrapper.style.bottom = ''; 
          }
      }

      function handler(e) {
          self.cancelBlur += 1;
          setTimeout(function(){
              self.elem.focus();
          }, 50);
      }
      setPosition();
      this.setPosition = setPosition;
      this.el.wrapper.addEventListener("mousedown", handler, false);
      this.el.wrapper.addEventListener("touchstart", handler, false);
      window.addEventListener('resize', this.setPosition);
  }
  DTBox.prototype.setupHeader = function () {
      if (!this.el.header) {
          var row = document.createElement("div");
          var classes = ["cal-nav-prev", "cal-nav-current", "cal-nav-next"];
          row.classList.add("cal-header");
          for (var i = 0; i < 3; i++) {
              var cell = document.createElement("div");
              cell.classList.add("cal-nav", classes[i]);
              cell.onclick = this.onHeaderChange.bind(this);
              row.appendChild(cell);
          }
          row.children[0].innerHTML = "&lt;";
          row.children[2].innerHTML = "&gt;";
          Object.defineProperty(this.el, "header", { value: row });
          tryAppendChild(row, this.el.wrapper);
      }
      this.setHeaderContent();
  }
  DTBox.prototype.setHeaderContent = function () {
      var content = this.year;
      if ("DAYS" == this.bodyType) {
          content = this.month_long + " " + content;
      } else if ("YEARS" == this.bodyType) {
          var start = this.year + 10 - (this.year % 10);
          content = start - 10 + "-" + (start - 1);
      }
      this.el.header.children[1].innerText = content;
  }
  DTBox.prototype.setupBody = function () {
      if (!this.el.body) {
          var el = document.createElement("div");
          el.classList.add("cal-body");
          Object.defineProperty(this.el, "body", { value: el });
          tryAppendChild(el, this.el.wrapper);
      }
      var toAppend = null;
      function makeGrid(rows, cols, className, firstRowClass, clickHandler) {
          var grid = document.createElement("div");
          grid.classList.add(className);
          for (var i = 1; i < rows + 1; i++) {
              var row = document.createElement("div");
              row.classList.add("cal-row", "cal-row-" + i);
              if (i == 1 && firstRowClass) {
                  row.classList.add(firstRowClass);
              }
              for (var j = 1; j < cols + 1; j++) {
                  var col = document.createElement("div");
                  col.classList.add("cal-cell", "cal-col-" + j);
                  col.onclick = clickHandler;
                  row.appendChild(col);
              }
              grid.appendChild(row);
          }
          return grid;
      }
      if ("DAYS" == this.bodyType) {
          toAppend = this.el.body.calDays;
          if (!toAppend) {
              toAppend = makeGrid(7, 7, "cal-days", "cal-day-names", this.onDateSelected.bind(this));
              for (var i = 0; i < 7; i++) {
                  var cell = toAppend.children[0].children[i];
                  cell.innerText = this.settings.config.weekdaysShort[i];
                  cell.onclick = null;
              }
              this.el.body.calDays = toAppend;
          }
      } else if ("MONTHS" == this.bodyType) {
          toAppend = this.el.body.calMonths;
          if (!toAppend) {
              toAppend = makeGrid(3, 4, "cal-months", null, this.onMonthSelected.bind(this));
              for (var i = 0; i < 3; i++) {
                  for (var j = 0; j < 4; j++) {
                      var monthShort = this.settings.config.monthsShort[4 * i + j];
                      toAppend.children[i].children[j].innerText = monthShort;
                  }
              }
              this.el.body.calMonths = toAppend;
          }
      } else if ("YEARS" == this.bodyType) {
          toAppend = this.el.body.calYears;
          if (!toAppend) {
              toAppend = makeGrid(3, 4, "cal-years", null, this.onYearSelected.bind(this));
              this.el.body.calYears = toAppend;
          }
      }
      empty(this.el.body);
      tryAppendChild(toAppend, this.el.body);
      this.setBodyContent();
  }
  DTBox.prototype.setBodyContent = function () {
      var grid = this.el.body.children[0];
      var classes = ["cal-cell-prev", "cal-cell-next", "cal-value"];
      if ("DAYS" == this.bodyType) {
          var oneDayMilliSecs = 24 * 60 * 60 * 1000;
          var start = new Date(this.year, this.month, 1);
          var adjusted = new Date(start.getTime() - oneDayMilliSecs * start.getDay());

          grid.children[6].style.display = "";
          for (var i = 1; i < 7; i++) {
              for (var j = 0; j < 7; j++) {
                  var cell = grid.children[i].children[j];
                  var month = adjusted.getMonth();
                  var date = adjusted.getDate();
                  
                  cell.innerText = date;
                  cell.classList.remove(classes[0], classes[1], classes[2]);
                  if (month != this.month) {
                      if (i == 6 && j == 0) {
                          grid.children[6].style.display = "none";
                          break;
                      }
                      cell.classList.add(month < this.month ? classes[0] : classes[1]);
                  } else if (isEqualDate(adjusted, this.value)){
                      cell.classList.add(classes[2]);
                  }
                  adjusted = new Date(adjusted.getTime() + oneDayMilliSecs);
              }
          }
      } else if ("YEARS" == this.bodyType) {
          var year = this.year - (this.year % 10) - 1;
          for (i = 0; i < 3; i++) {
              for (j = 0; j < 4; j++) {
                  grid.children[i].children[j].innerText = year;
                  year += 1;
              }
          }
          grid.children[0].children[0].classList.add(classes[0]);
          grid.children[2].children[3].classList.add(classes[1]);
      }
  }

  /** @param {Event} e */
  DTBox.prototype.onTimeChange = function(e) {
      e.stopPropagation();
      if (e.type == 'mousedown') {
          this.cancelBlur += 1;
          return;
      }
      if (e.type == 'mouseup') {
          var self = this;
          setTimeout(function(){
              self.elem.focus();
          }, 50);
          return;
      }
      
      var el = e.target;
      this[el.name] = parseInt(el.value) || 0;
      this.setupFooter();
      this.setInputValue();
  }

  DTBox.prototype.setupFooter = function() {
      if (!this.el.footer) {
          var footer = document.createElement("div");
          var handler = this.onTimeChange.bind(this);
          var self = this;
          
          function makeRow(label, name, range, changeHandler) {
              var row = document.createElement("div");
              row.classList.add('cal-time');

              var labelCol = row.appendChild(document.createElement("div"));
              labelCol.classList.add('cal-time-label');
              labelCol.innerText = label;

              var valueCol = row.appendChild(document.createElement("div"));
              valueCol.classList.add('cal-time-value');
              valueCol.innerText = '00';

              var inputCol = row.appendChild(document.createElement("div"));
              var slider = inputCol.appendChild(document.createElement("input"));
              Object.assign(slider, {step:1, min:0, max:range, name:name, type:'range'});
              Object.defineProperty(footer, name, {value: slider});
              inputCol.classList.add('cal-time-slider');
              slider.onchange = changeHandler;
              slider.oninput = changeHandler;
              slider.onmousedown = changeHandler;
              slider.onmouseup = changeHandler;
              self[name] = self[name] || parseInt(slider.value) || 0;
              footer.appendChild(row)
          }
          makeRow(this.settings.config.timeDescr[0], 'hours', 23, handler);
          makeRow(this.settings.config.timeDescr[1], 'minutes', 59, handler);
          if (this.settings.config.showSeconds) {
              makeRow(this.settings.config.timeDescr[2], 'seconds', 59, handler);
          }

          footer.classList.add("cal-footer");
          Object.defineProperty(this.el, "footer", { value: footer });
          tryAppendChild(footer, this.el.wrapper);
      }
      this.setFooterContent();
  }

  DTBox.prototype.setFooterContent = function() {
      if (this.el.footer) {
          var footer = this.el.footer;
          footer.hours.value = this.hours;
          footer.children[0].children[1].innerText = padded(this.hours, 2);
          footer.minutes.value = this.minutes;
          footer.children[1].children[1].innerText = padded(this.minutes, 2);
          if (this.settings.config.showSeconds) {
              footer.seconds.value = this.seconds;
              footer.children[2].children[1].innerText = padded(this.seconds, 2);
          }
      }
  }

  DTBox.prototype.setInputValue = function() {
      var date = new Date(this.year, this.month, this.day);
      var strings = [];
      if (this.settings.config.showDate) {
          strings.push(renderDate(date, this.settings));
      }
      if (this.settings.config.showTime) {
          var joined = new Date(date.getTime() + this.time);
          strings.push(renderTime(joined, this.settings));
      }
      this.elem.value = strings.join(', ');
  }

  DTBox.prototype.onDateSelected = function (e) {
      var row = e.target.parentNode;
      var date = parseInt(e.target.innerText);
      if (!(row.nextSibling && row.nextSibling.nextSibling) && date < 8) {
          this.month += 1;
      } else if (!(row.previousSibling && row.previousSibling.previousSibling) && date > 7) {
          this.month -= 1;
      }
      this.day = parseInt(e.target.innerText);
      this.value = new Date(this.year, this.month, this.day);
      this.setInputValue();
      this.setHeaderContent();
      this.setBodyContent();
  }

  /** @param {Event} e */
  DTBox.prototype.onMonthSelected = function (e) {
      var col = 0;
      var row = 2;
      var cell = e.target;
      if (cell.parentNode.nextSibling){
          row = cell.parentNode.previousSibling ? 1: 0;
      }
      if (cell.previousSibling) {
          col = 3;
          if (cell.nextSibling) {
              col = cell.previousSibling.previousSibling ? 2 : 1;
          }
      }
      this.month = 4 * row + col;
      this.bodyType = "DAYS";
      this.setHeaderContent();
      this.setupBody();
  }

  /** @param {Event} e */
  DTBox.prototype.onYearSelected = function (e) {
      this.year = parseInt(e.target.innerText);
      this.bodyType = "MONTHS";
      this.setHeaderContent();
      this.setupBody();
  }

  /** @param {Event} e */
  DTBox.prototype.onHeaderChange = function (e) {
      var cell = e.target;
      if (cell.previousSibling && cell.nextSibling) {
          var idx = BODYTYPES.indexOf(this.bodyType);
          if (idx < 0 || !BODYTYPES[idx + 1]) {
              return;
          }
          this.bodyType = BODYTYPES[idx + 1];
          this.setupBody();
      } else {
          var sign = cell.previousSibling ? 1 : -1;
          switch (this.bodyType) {
              case "DAYS":
                  this.month += sign * 1;
                  break;
              case "MONTHS":
                  this.year += sign * 1;
                  break;
              case "YEARS":
                  this.year += sign * 10;
          }
          if (this.month > 11 || this.month < 0) {
              this.year += Math.floor(this.month / 11);
              this.month = this.month > 11 ? 0 : 11;
          }
      }
      this.setHeaderContent();
      this.setBodyContent();
  }


  /**
   * @param {HTMLElement} elem 
   * @returns {{left:number, top:number}}
   */
  function getOffset(elem) {
      var box = elem.getBoundingClientRect();
      var left = window.pageXOffset !== undefined ? window.pageXOffset : 
          (document.documentElement || document.body.parentNode || document.body).scrollLeft;
      var top = window.pageYOffset !== undefined ? window.pageYOffset : 
          (document.documentElement || document.body.parentNode || document.body).scrollTop;
      return { left: box.left + left, top: box.top + top };
  }
  function empty(e) {
      for (; e.children.length; ) e.removeChild(e.children[0]);
  }
  function tryAppendChild(newChild, refNode) {
      try {
          refNode.appendChild(newChild);
          return newChild;
      } catch (e) {
          console.trace(e);
      }
  }

  /** @class */
  function hookFuncs() {
      /** @type {Handlers} */
      this._funcs = {};
  }
  /**
   * @param {string} key 
   * @param {Function} func 
   */
  hookFuncs.prototype.add = function(key, func){
      if (!this._funcs[key]){
          this._funcs[key] = [];
      }
      this._funcs[key].push(func)
  }
  /**
   * @param {String} key 
   * @returns {Function[]} handlers
   */
  hookFuncs.prototype.get = function(key){
      return this._funcs[key] ? this._funcs[key] : [];
  }

  /**
   * @param {Array.<string>} arr 
   * @param {String} string 
   * @returns {Array.<string>} sorted string
   */
  function sortByStringIndex(arr, string) {
      return arr.sort(function(a, b){
          var h = string.indexOf(a);
          var l = string.indexOf(b);
          var rank = 0;
          if (h < l) {
              rank = -1;
          } else if (l < h) {
              rank = 1;
          } else if (a.length > b.length) {
              rank = -1;
          } else if (b.length > a.length) {
              rank = 1;
          }
          return rank;
      });
  }

  /**
   * Remove keys from array that are not in format
   * @param {string[]} keys 
   * @param {string} format 
   * @returns {string[]} new filtered array
   */
  function filterFormatKeys(keys, format) {
      var out = [];
      var formatIdx = 0;
      for (var i = 0; i<keys.length; i++) {
          var key = keys[i];
          if (format.slice(formatIdx).indexOf(key) > -1) {
              formatIdx += key.length;
              out.push(key);
          }
      }
      return out;
  }

  /**
   * @template {StringNumObj} FormatObj
   * @param {string} value 
   * @param {string} format 
   * @param {FormatObj} formatObj 
   * @param {function(Object.<string, hookFuncs>): null} setHooks 
   * @returns {FormatObj} formatObj
   */
  function parseData(value, format, formatObj, setHooks) {
      var hooks = {
          canSkip: new hookFuncs(),
          updateValue: new hookFuncs(),
      }
      var keys = sortByStringIndex(Object.keys(formatObj), format);
      var filterdKeys = filterFormatKeys(keys, format);
      var vstart = 0; // value start
      if (setHooks) {
          setHooks(hooks);
      }

      for (var i = 0; i < keys.length; i++) {
          var key = keys[i];
          var fstart = format.indexOf(key);
          var _vstart = vstart; // next value start
          var val = null;
          var canSkip = false;
          var funcs = hooks.canSkip.get(key);

          vstart = vstart || fstart;

          for (var j = 0; j < funcs.length; j++) {
              if (funcs[j](formatObj)){
                  canSkip = true;
                  break;
              }
          }
          if (fstart > -1 && !canSkip) {
              var sep = null;
              var stop = vstart + key.length;
              var fnext = -1;
              var nextKeyIdx = i + 1;
              _vstart += key.length; // set next value start if current key is found

              // get next format token used to determine separator
              while (fnext == -1 && nextKeyIdx < keys.length){
                  var nextKey = keys[nextKeyIdx];
                  nextKeyIdx += 1;
                  if (filterdKeys.indexOf(nextKey) === -1) {
                      continue;
                  }
                  fnext = nextKey ? format.indexOf(nextKey) : -1; // next format start
              }
              if (fnext > -1){
                  sep = format.slice(stop, fnext);
                  if (sep) {
                      var _stop = value.slice(vstart).indexOf(sep);
                      if (_stop && _stop > -1){
                          stop = _stop + vstart;
                          _vstart = stop + sep.length;
                      }
                  }
              }
              val = parseInt(value.slice(vstart, stop));

              var funcs = hooks.updateValue.get(key);
              for (var k = 0; k < funcs.length; k++) {
                  val = funcs[k](val, formatObj, vstart, stop);
              }
          }
          formatObj[key] = { index: vstart, value: val };
          vstart = _vstart; // set next value start
      }
      return formatObj;
  }

  /**
   * @param {String} value 
   * @param {DTS} settings 
   * @returns {Date} date object
   */
  function parseDate(value, settings) {
      /** @type {{yyyy:number=, yy:number=, mm:number=, dd:number=}} */
      var formatObj = {yyyy:null, yy:null, mm:null, dd:null};
      var format = ((settings.dateFormat) || '').toLowerCase();
      if (!format) {
          throw new TypeError('dateFormat not found (' + settings.dateFormat + ')');
      }
      var formatObj = parseData(value, format, formatObj, function(hooks){
          hooks.canSkip.add("yy", function(data){
              return data["yyyy"].value;
          });
          hooks.updateValue.add("yy", function(val){
              return 100 * Math.floor(new Date().getFullYear() / 100) + val;
          });
      });
      var year = formatObj["yyyy"].value || formatObj["yy"].value;
      var month = formatObj["mm"].value - 1;
      var date = formatObj["dd"].value;
      var result = new Date(year, month, date);
      return result;
  }

  /**
   * @param {String} value 
   * @param {DTS} settings 
   * @returns {Number} time in milliseconds <= (24 * 60 * 60 * 1000) - 1
   */
  function parseTime(value, settings) {
      var format = ((settings.timeFormat) || '').toLowerCase();
      if (!format) {
          throw new TypeError('timeFormat not found (' + settings.timeFormat + ')');
      }

      /** @type {{hh:number=, mm:number=, ss:number=, a:string=}} */
      var formatObj = {hh:null, mm:null, ss:null, a:null};
      var formatObj = parseData(value, format, formatObj, function(hooks){
          hooks.updateValue.add("a", function(val, data, start, stop){
              return value.slice(start, start + 2);
          });
      });
      var hours = formatObj["hh"].value;
      var minutes = formatObj["mm"].value;
      var seconds = formatObj["ss"].value;
      var am_pm = formatObj["a"].value;
      var am_pm_lower = am_pm ? am_pm.toLowerCase() : am_pm;
      if (am_pm && ["am", "pm"].indexOf(am_pm_lower) > -1){
          if (am_pm_lower == 'am' && hours == 12){
              hours = 0;
          } else if (am_pm_lower == 'pm') {
              hours += 12;
          }
      }
      var time = hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000;
      return time;
  }

  /**
   * @param {Date} value 
   * @param {DTS} settings 
   * @returns {String} date string
   */
  function renderDate(value, settings) {
      var format = settings.dateFormat.toLowerCase();
      var date = value.getDate();
      var month = value.getMonth() + 1;
      var year = value.getFullYear();
      var yearShort = year % 100;
      var formatObj = {
          dd: date < 10 ? "0" + date : date,
          mm: month < 10 ? "0" + month : month,
          yyyy: year,
          yy: yearShort < 10 ? "0" + yearShort : yearShort
      };
      var str = format.replace(settings.dateFormatRegEx, function (found) {
          return formatObj[found];
      });
      return str;
  }

  /**
   * @param {Date} value 
   * @param {DTS} settings 
   * @returns {String} date string
   */
  function renderTime(value, settings) {
      var Format = settings.timeFormat;
      var format = Format.toLowerCase();
      var hours = value.getHours();
      var minutes = value.getMinutes();
      var seconds = value.getSeconds();
      var am_pm = null;
      var hh_am_pm = null;
      if (format.indexOf('a') > -1) {
          am_pm = hours >= 12 ? 'pm' : 'am';
          am_pm = Format.indexOf('A') > -1 ? am_pm.toUpperCase() : am_pm;
          hh_am_pm = hours == 0 ? '12' : (hours > 12 ? hours%12 : hours);
      }
      var formatObj = {
          hh: am_pm ? hh_am_pm : (hours < 10 ? "0" + hours : hours),
          mm: minutes < 10 ? "0" + minutes : minutes,
          ss: seconds < 10 ? "0" + seconds : seconds,
          a: am_pm,
      };
      var str = format.replace(settings.timeFormatRegEx, function (found) {
          return formatObj[found];
      });
      return str;
  }

  /**
   * checks if two dates are equal
   * @param {Date} date1 
   * @param {Date} date2 
   * @returns {Boolean} true or false
   */
  function isEqualDate(date1, date2) {
      if (!(date1 && date2)) return false;
      return (date1.getFullYear() == date2.getFullYear() && 
              date1.getMonth() == date2.getMonth() && 
              date1.getDate() == date2.getDate());
  }

  /**
   * @param {Number} val 
   * @param {Number} pad 
   * @param {*} default_val 
   * @returns {String} padded string
   */
  function padded(val, pad, default_val) {
      var default_val = default_val || 0;
      var valStr = '' + (parseInt(val) || default_val);
      var diff = Math.max(pad, valStr.length) - valStr.length;
      return ('' + default_val).repeat(diff) + valStr;
  }

  /**
   * @template X
   * @template Y
   * @param {X} obj 
   * @param {Y} objDefaults 
   * @returns {X|Y} merged object
   */
  function setDefaults(obj, objDefaults) {
      var keys = Object.keys(objDefaults);
      for (var i=0; i<keys.length; i++) {
          var key = keys[i];
          if (!Object.prototype.hasOwnProperty.call(obj, key)) {
              obj[key] = objDefaults[key];
          }
      }
      return obj;
  }


  window.dtsel = Object.create({},{
      DTS: { value: DTS },
      DTObj: { value: DTBox },
      fn: {
          value: Object.defineProperties({}, {
              empty: { value: empty },
              appendAfter: {
                  value: function (newElem, refNode) {
                      refNode.parentNode.insertBefore(newElem, refNode.nextSibling);
                  },
              },
              getOffset: { value: getOffset },
              parseDate: { value: parseDate },
              renderDate: { value: renderDate },
              parseTime: {value: parseTime},
              renderTime: {value: renderTime},
              setDefaults: {value: setDefaults},
          }),
      },
  });
})();

},function (global, require, module, exports) {
// ../fileinfo.js
const Fileinfo = {
  getPrefix: function(vnode) {
    if (!vnode.attrs.file.filename.endsWith('.torrent')) {
      return vnode.attrs.file.filename.split('.').slice(-1)
    }
    if (vnode.attrs.file.filename.indexOf('720 ') >= 0) {
      return '720p'
    }
    if (vnode.attrs.file.filename.indexOf('1080 ') >= 0) {
      return '1080p'
    }
    if (vnode.attrs.file.filename.indexOf('480 ') >= 0) {
      return '480p'
    }
    if (vnode.attrs.file.filename.toLowerCase().indexOf('flac') >= 0) {
      return 'FLAC'
    }
    if (vnode.attrs.file.filename.toLowerCase().indexOf('mp3') >= 0) {
      return 'MP3'
    }
    if (vnode.attrs.file.filename.toLowerCase().indexOf('psd') >= 0) {
      return 'PSD'
    }
    if (vnode.attrs.file.filename.toLowerCase().indexOf('.zip') >= 0) {
      return 'ZIP'
    }
    return 'Other'
  },

  getTitle: function(vnode) {
    if (vnode.attrs.file.meta.torrent) {
      return vnode.attrs.file.meta.torrent.name
    }
    return vnode.attrs.file.filename
  },

  getDownloadName: function(vnode) {
    if (vnode.attrs.file.meta.torrent) {
      return 'Torrent'
    }
    return 'Download'
  },

  getSize: function(orgSize) {
    var size = orgSize
    var i = -1
    var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']
    do {
      size = size / 1024
      i++
    } while (size > 1024)

    return Math.max(size, 0.1).toFixed(1) + byteUnits[i]
  },

  view: function(vnode) {
    return m('fileinfo', { class: vnode.attrs.slim ? 'slim' : ''}, [
      m('p', [
        m('span', this.getPrefix(vnode) + ':'),
        m('a', {
          target: '_blank',
          rel: 'noopener',
          href: vnode.attrs.file.url,
        }, this.getDownloadName(vnode)),
        vnode.attrs.file.magnet
          ? m('a', {
              href: vnode.attrs.file.magnet,
            }, 'Magnet')
          : null,
        this.getTitle(vnode),
      ]),
      vnode.attrs.file.meta.torrent
          && !vnode.attrs.slim
          && vnode.attrs.file.meta.torrent.files.length > 1
          && (!vnode.attrs.trim || vnode.attrs.file.meta.torrent.files.length <= 4)
        ? m('ul', vnode.attrs.file.meta.torrent.files.map(function(file) {
            return m('li', [
              file.name + ' ',
              m('span.meta', '(' + Fileinfo.getSize(file.size) + ')'),
            ])
          }))
        : null,
      vnode.attrs.trim
          && vnode.attrs.file.meta.torrent
          && vnode.attrs.file.meta.torrent.files.length > 4
        ? m('div.trimmed', '...' + vnode.attrs.file.meta.torrent.files.length + ' files...')
        : null,
      vnode.children,
    ])
  },
}

module.exports = Fileinfo

},function (global, require, module, exports) {
// stafflist.js
const Dialogue = require(8)
const Pages = require(10)

const AdminStaffList = {
  /*
  oninit: function(vnode) {
    this.error = ''
    this.lastpage = m.route.param('page') || '1'
    this.staff = []
    this.removeStaff = null

    this.fetchStaffs(vnode)
  },

  fetchStaffs: function(vnode) {
    this.loading = true

    document.title = 'Staff members - Admin NFP Moe'

    return Staff.getAllStaff()
    .then(function(result) {
      vnode.state.staff = result
    })
    .catch(function(err) {
      vnode.state.error = err.message
    })
    .then(function() {
      vnode.state.loading = false
      m.redraw()
    })
  },

  confirmRemoveStaff: function(vnode) {
    let removingStaff = this.removeStaff
    this.removeStaff = null
    this.loading = true
    Staff.removeStaff(removingStaff.id)
      .then(this.oninit.bind(this, vnode))
      .catch(function(err) {
        vnode.state.error = err.message
        vnode.state.loading = false
        m.redraw()
      })
  },

  getLevel: function(level) {
    if (level === 100) {
      return 'Admin'
    }
    return 'Manager'
  },

  view: function(vnode) {
    return [
      m('div.admin-wrapper', [
        m('div.admin-actions', [
            m('span', 'Actions:'),
            m(m.route.Link, { href: '/admin/staff/add' }, 'Create new staff'),
          ]),
        m('article.editarticle', [
          m('header', m('h1', 'All staff')),
          m('div.error', {
            hidden: !this.error,
            onclick: function() { vnode.state.error = '' },
          }, this.error),
          (this.loading
            ? m('div.loading-spinner.full')
            : m('table', [
                m('thead', 
                  m('tr', [
                    m('th', 'Fullname'),
                    m('th', 'Email'),
                    m('th', 'Level'),
                    m('th.right', 'Updated'),
                    m('th.right', 'Actions'),
                  ])
                ),
                m('tbody', this.staff.map(function(item) {
                  return m('tr', [
                    m('td', m(m.route.Link, { href: '/admin/staff/' + item.id }, item.name)),
                    m('td', item.email),
                    m('td.right', AdminStaffList.getLevel(item.rank)),
                    m('td.right', (item.updated_at || '---').replace('T', ' ').split('.')[0]),
                    m('td.right', m('button', { onclick: function() { vnode.state.removeStaff = item } }, 'Remove')),
                  ])
                })),
              ])
          ),
          m(Pages, {
            base: '/admin/staff',
            links: this.links,
          }),
        ]),
      ]),
      m(Dialogue, {
        hidden: vnode.state.removeStaff === null,
        title: 'Delete ' + (vnode.state.removeStaff ? vnode.state.removeStaff.name : ''),
        message: 'Are you sure you want to remove "' + (vnode.state.removeStaff ? vnode.state.removeStaff.name : '') + '" (' + (vnode.state.removeStaff ? vnode.state.removeStaff.email : '') + ')',
        yes: 'Remove',
        yesclass: 'alert',
        no: 'Cancel',
        noclass: 'cancel',
        onyes: this.confirmRemoveStaff.bind(this, vnode),
        onno: function() { vnode.state.removeStaff = null },
      }),
    ]
  },*/
}

module.exports = AdminStaffList

},function (global, require, module, exports) {
// editstaff.js
const EditStaff = {
  /*
  oninit: function(vnode) {
    this.fetchStaff(vnode)
  },

  onupdate: function(vnode) {
    if (this.lastid !== m.route.param('id')) {
      this.fetchStaff(vnode)
    }
  },

  fetchStaff: function(vnode) {
    this.lastid = m.route.param('id')
    this.loading = this.lastid !== 'add'
    this.creating = this.lastid === 'add'
    this.error = ''
    this.staff = {
      name: '',
      email: '',
      password: '',
      rank: 10,
    }

    if (this.lastid !== 'add') {
      Staff.getStaff(this.lastid)
      .then(function(result) {
        vnode.state.editedPath = true
        vnode.state.staff = result
        document.title = 'Editing: ' + result.name + ' - Admin NFP Moe'
      })
      .catch(function(err) {
        vnode.state.error = err.message
      })
      .then(function() {
        vnode.state.loading = false
        m.redraw()
      })
    } else {
      document.title = 'Creating Staff Member - Admin NFP Moe'
    }
  },

  updateValue: function(key, e) {
    this.staff[key] = e.currentTarget.value
  },

  save: function(vnode, e) {
    e.preventDefault()
    if (!this.staff.name) {
      this.error = 'Fullname is missing'
    } else if (!this.staff.email) {
      this.error = 'Email is missing'
    } else {
      this.error = ''
    }
    if (this.error) return

    this.staff.description = vnode.state.froala && vnode.state.froala.html.get() || this.staff.description

    this.loading = true

    let promise

    if (this.staff.id) {
      promise = Staff.updateStaff(this.staff.id, {
        name: this.staff.name,
        email: this.staff.email,
        rank: this.staff.rank,
        password: this.staff.password,
      })
    } else {
      promise = Staff.createStaff({
        name: this.staff.name,
        email: this.staff.email,
        rank: this.staff.rank,
        password: this.staff.password,
      })
    }

    promise.then(function(res) {
      m.route.set('/admin/staff')
    })
    .catch(function(err) {
      vnode.state.error = err.message
    })
    .then(function() {
      vnode.state.loading = false
      m.redraw()
    })
  },

  updateLevel: function(e) {
    this.staff.rank = Number(e.currentTarget.value)
  },

  view: function(vnode) {
    const ranks = [[10, 'Manager'], [100, 'Admin']]
    return (
      this.loading ?
        m('div.loading-spinner')
      : m('div.admin-wrapper', [
          m('div.admin-actions', this.staff.id
            ? [
              m('span', 'Actions:'),
              m(m.route.Link, { href: '/admin/staff' }, 'Staff list'),
            ]
            : null),
          m('article.editstaff', [
            m('header', m('h1', this.creating ? 'Create Staff' : 'Edit ' + (this.staff.name || '(untitled)'))),
            m('div.error', {
              hidden: !this.error,
              onclick: function() { vnode.state.error = '' },
            }, this.error),
            m('form.editstaff.content', {
              onsubmit: this.save.bind(this, vnode),
            }, [
              m('label', 'Level'),
              m('select', {
                onchange: this.updateLevel.bind(this),
              }, ranks.map(function(rank) { return m('option', { value: rank[0], selected: rank[0] === vnode.state.staff.rank }, rank[1]) })),
              m('label', 'Fullname'),
              m('input', {
                type: 'text',
                value: this.staff.name,
                oninput: this.updateValue.bind(this, 'name'),
              }),
              m('label', 'Email'),
              m('input', {
                type: 'text',
                value: this.staff.email,
                oninput: this.updateValue.bind(this, 'email'),
              }),
              m('label', 'Password (optional)'),
              m('input', {
                type: 'text',
                value: this.staff.password,
                oninput: this.updateValue.bind(this, 'password'),
              }),
              m('input', {
                type: 'submit',
                value: 'Save',
              }),
            ]),
          ]),
        ])
    )
  },*/
}

module.exports = EditStaff

}]));