/* appjet:version 0.1 */

/* Space Wiki, http://space.appjet.net/
* made in 2009 by Vezquex, http://vezquex.com/
* Creative Commons Attribution 3.0 Unported, http://creativecommons.org/licenses/by/3.0/
*/

import('lib-dispatch-plus')
import('lib-general')
import('lib-markdown')
import('lib-files')
import('lib-jquery')
//import('lib-export')

if(!storage.pages)
{
  storage.pages = new StorableCollection()
  storage.pages.add({title: 'a', info: 'blah blah'})
}

let fullView = true
let at = request.path
let htmlTitle = ['Wiki']
let perPage = 32
let content = DIV()
let navLinks = [
  ['/', 'Contents'],
  ['/JGate', 'About'],
  ['/Help', 'Help']
]

function printPage(){
  page.setFavicon('/static/space-favicon.png')
  let nav = UL({'class': 'nav', id: 'menu'})
  navLinks.forEach(function(i){
      if(i[0] == request.path)
          nav.push(LI(SPAN({'class': 'tab'}, i[1])))
      else
          nav.push(LI(A({href: i[0]}, i[1])))
  })

  print(
      DIV({id: 'top', 'class':'bar'},
          DIV({'class':'center'},
              nav,
              FORM({method: 'get', action: '/main/goto'},
                  INPUT({name: 'to'}),
                  INPUT({type:'submit', value: 'Go'})
              )
          )
      ),
      DIV({'class':'content'},
          content
      ),
      DIV({id: 'footer'},
          DIV({'class':'center'},
              P('Wiki application by ', link('http://vezquex.com/', 'Vezquex'), '. ')
          )
      )
  )
}

function get_main()
{
  let start = parseInt(request.params.start)
  if(isNaN(start)) start = 0
  let pages = storage.pages.filter({parent: undefined}).sortBy('-hits')
  let pageUL = UL({'class': 'list'})
  pages.skip(start).limit(perPage).forEach(function(o){
      pageUL.push(
          LI(
              link('/'+(o.title), o.title)
          )
      )
  })

  content.push(
      pageUL
  )

  // Pagination
  let size = pages.size()
  let pageLinks = UL({'class':'nav page-links'})
  if(start > 0)
  {
      let newStart = start - perPage
      if(newStart <= 0) newStart = '/'
      else newStart = '?start='+ newStart
      pageLinks.push(LI(link(newStart, '◄ Previous')))
  }
  if(size > perPage)
      pageLinks.push(LI(SPAN((start/perPage + 1)+' / '+Math.ceil(size/perPage))))
  if(size > start + perPage)
      pageLinks.push(LI(link('?start='+ (start + perPage), 'Next ►')))
  content.push(pageLinks)
}

function get_(title)
{
  title = title || (request.pathTail.substr(1))
  let doc = findPage(title)
  if(doc)
      view(doc)
  else
      get_edit_(title, doc)
}

function view(doc)
{
  if(!doc)
  {
      doc = getStorable(request.pathTail.substr(1))
      if(doc.title)
          response.redirect('/'+doc.title)
  }

  if(doc)
  {
      doc.hits = (doc.hits >= 0) ? doc.hits + 1 : 1
      let h1 = ''
      if(doc.title){
          h1 = H1({id: 'title'}, doc.title)
          htmlTitle.unshift(doc.title)
      }
      let context = doc.parent ? P(link('/' + doc.parent, '^ Context')) : ''

      var dt = doc.lastEdit.toString().replace(/-/g, "/");

      dt = dt.replace(/T/, " ");
      dt = dt.replace(/Z/, "");
      content.push(
          DIV({id: 'edit', 'class':'side'},
              P(link('/edit/'+(doc.title || doc.id), '¶ Edit')),
              //P(doc.id),
              //P(link('/delete/'+doc.id, 'X Delete')),
              P(doc.hits, ' views'),
              P(timeHover(new Date(dt)) || new Date(0))
          ),
          h1,
          DIV({'class':'info'}, format(doc.info || ''), context),
          reply(doc.id),
          DIV({'class':'replies'}, viewReplies(doc.id, true))
      )
  }
  else
  {
      dispatchPlus.f404()
  }
}

function get_edit_(title, doc)
{
  if(!title) title = request.pathTail.substr(1)
  htmlTitle.unshift(title, '(editing)')
  let doc = doc || findPage(title) || {}
  content.push(H1({id: 'title'}, link('/'+title, title)))
  content.push(B("Please do not save test edits. If you want to experiment, please use the ", link("http://space-wiki.jgate.de", "sandbox.")));
  content.push(
      FORM({method:'post', action:'/edit/'+title, 'class':'form'},
          TEXTAREA({name: 'info'}, html(doc.info||'')),
          INPUT({type: 'hidden', value: request.params["apikey"], name: "apikey"}),
          INPUT({type: 'submit', value: 'Save'})
      )
  )
}

function post_edit_()
{
   var apikey = unescape(request.param("apikey"));
       if (apikey == storage.apikey)
       {
               let title = request.pathTail.substr(1)
               let doc = findPage(title)
               if(!doc)
               {
                       doc = storage.pages.add({
                          title: title,
                          hits: 0
                      })
                  }
                  doc.info = request.params.info
                  doc.lastEdit = new Date()
                  response.redirect('/'+title)
       }
       else
       {
           printp("You are not authorized to edit. If you feel that this is an error, contact your administrator.");
           response.setStatusCode(403);
   }
}


function reply(id, path)
{
  return FORM({method:'post', action:'/reply/'+id+'/at/'+(path||at), 'class':'form reply', id: 'reply'},
      TEXTAREA({name: 'info'}),
      INPUT({type: 'submit', value: 'Reply'})
  )
}

function get_reply_()
{
  content.push(
      P('Write your comment.'),
      reply(request.fullPathSegments[2], request.fullPathSegments[4])
  )
}

function post_reply_()
{
   var apikey = unescape(request.param("apikey"));
       if (apikey == storage.apikey)
       {
               let text = request.params.info
               let id = request.fullPathSegments[2]
               if(text) let reply = storage.pages.add({info: text, parent: id, lastEdit: new Date()})
               response.redirect(request.fullPathSegments[4]+'#'+reply.id)
       }
       else
       {
           printp("You are not authorized to reply. If you feel that this is an error, contact your administrator.");
           response.setStatusCode(403);
       }
}

function viewReplies(id, top)
{
  let element = DIV()
  let replies = storage.pages.filter({parent: id}).sort()
  let size

  replies.forEach(function(reply){
      element.push(DIV({id: reply.id},
          format(reply.info),
          P({'class':'reply-info'},
              link('/'+reply.id, timeHover(reply.lastEdit)),
              ' - ', A({href: '/reply/'+reply.id+'/at/'+at, id: 'r'+reply.id, 'class':'sub-reply'}, 'Reply')
          ),
          viewReplies(reply.id)
      ))
  })
  return element
}


function get_delete_()
{
   var apikey = unescape(request.param("apikey"));

   if (apikey == storage.apikey)
   {
    let id = request.pathTail.substr(1)
    if(page)
    {
      removeById(id, storage.pages)
      response.redirect('/')
    }
   }
   else
   {
      printp("You are not authorized to delete an article. If you feel that this is an error, contact your administrator.");
      response.setStatusCode(403);
   }
}

function findPage(title)
{
  let p = storage.pages.filter({title: title}).first()
  if(!p) try{ p = getStorable(title) } catch(e){}
  return p
}


function format(text)
{
  //wikilinks
  text = text.replace(/\[\[(.*?)\]\]/g, function(match, m1){ return '['+m1+'](/'+m1+')' })

  return markdown(text)
}

function get_main_goto()
{
  response.redirect('/'+(request.params.to))
}

dispatchPlus()
if(request.params.plain) print(content)
else printPage()

//enableStorageExport();
//showExportAdminPanel();
dispatchPlus.f404 = function()
{
  response.setStatusCode(404)
  content.push("Document not found: ", request.fullPath)
  htmlTitle.unshift('404')
}

page.setTitle(htmlTitle.join(' - '))


/* appjet:client */
$(document).ready(function(){
  $('.sub-reply').click(reply)
  recolor()
  $('.info img').toggle(
      function () { $(this).css({'max-width':'none', 'max-height':'none'}) },
      function () { $(this).css({'max-width':'100%', 'max-height':'8em'}) }
  )
})

function reply(){
      var id = $(this).attr('id').substr(1)
      var theForm
      if($('#f' + id).length)
      {
          $('#f' + id).toggle()
      }
      else
      {
          $(this).after(
              $('#reply').clone()
                  .attr({
                      action: '/reply/'+ id + '/at/' + (window.location.pathname),
                      id: 'f'+id
                  })
          )
      }

      $('#f'+id).find('textarea').focus()

      return false
  }

function recolor()
{
      time = new Date()
      frac = (time.getHours() * 3600 + time.getMinutes() * 60 + time.getSeconds() ) / 86400
      $('#top').css({'background-color': shade(frac, .7, .93)})
}

function shade(hue, saturation, brightness)
{
      var c = hslToRgb(hue, saturation, brightness)
      c.forEach(function(el, i, arr){ arr[i] = Math.floor(el) })
      return 'rgb('+c[0]+','+c[1]+','+c[2]+')'
}

/* appjet:css */

* { padding: 0; margin: 0; }
body { font: .90em 'Verdana', sans-serif; color: #000; background: #f8f8f8; }
.center, .content > *, #appjetfooter { margin: 0 auto; max-width: 800px; padding: 0 1em; position: relative; }
.content { background: #fff; padding: 0 0 2em; border-bottom: 1px #ddd solid;  overflow: auto; }
h1, h2 { font-family: 'Georgia', 'Times New Roman', serif; font-weight: normal; border-bottom: 1px #bbb solid; }
h2 { margin-top: 12px; margin-bottom: 6px;}
h3 { margin-top: 12px; margin-bottom: 6px; }
h4 { margin-top: 12px; margin-bottom: 6px; }
pre { border: 1px #bbb solid; background: rgb(250, 250, 250); padding: 5px; margin-top: 12px; margin-bottom: 12px;}
td, th { border: 1px solid rgb(204, 204, 204); padding: 2px; }
p { padding: .5em 0; }
img {border: 0; max-height: 40em;}
a { text-decoration: none; }
.info a:hover, #footer a:hover, #appjetfooter a:hover { text-decoration: underline; }
a img {padding-left: 4px; }

.nav { display: block; margin: 0 0 0 -.75em; }
.nav li { display: inline; }
.nav a, .nav span { padding: .5em .75em .75em; }
.nav a { color: #000; }
.nav a:hover { background: rgba(255,255,255,.5); color: #000; }

.tab { background: #fff; border: 1px #888 solid; border-bottom-color: transparent; }
.bar { border-bottom: 1px #888 solid; }

#top form { position: absolute; top: 0; right: 0; padding: .25em;}
#top input { font-size: 1em; padding: .125em; }
#menu { padding: .75em 0; }

.side { float: right; background: #fff; padding-left: 1em; clear: right; width: 11em; overflow: hidden; }
#edit a { display: block; color: #080; font-weight: bold; padding: .5em 2em .5em 0; border-bottom: 5px #dec solid; }
#edit a:hover { color: #000; border-color: #0c0; }
.page-links { margin-top: 1em; }

.list { list-style: none; width: 17em; padding: 1em 0 0; }
.list a { display: block; padding: .25em 0; border-bottom: 1px #ccc dotted; }
.list a:hover { background: #F0FFE5; }

.side dl { font-size: 90%; }
.side dt { font-style: italic; color: #888; padding: .75em 0 0; }
.side dd { padding-left: .5em; }

#title { font-size: 3em; margin: 0 0 .25em; border: 0; }

.info { border-bottom: 3px #ccc solid; padding-bottom: 2em; margin-bottom: 2em; }
.info h1, .info h2 { margin: .5em 0 .25em 0; }
.info h1 { font-size: 2em; }
.info ul, .info ol { margin-left: 1em; }
.info blockquote { background: #eed; margin-left: 2em; padding: 0 1em; }
.info li { margin-left: .5em; }

.form form {padding: .5em;}
.form label {display: block; padding: .25em 0; font-weight: bold; }
.form input[type="text"] {width: 10em; margin: 0 0 .5em .5em; }
.form textarea { height: 22em; border: #def solid; border-width: 1.5em 2px 2px; padding: .25em; width: 100%; }
.form input[type="submit"] {
  display: block;
  margin: 0 0 0 48%;
  width: 50%;
  position: relative;
  top: -3px;
  padding: .25em;
  background: #e8f0ff;
  border: 2px #def solid;
  border-top-color: #fff;
}

.reply textarea { height: 6em; }
.replies div div div { margin-left: 1em; margin-top: 1em; }
.replies div div { margin-bottom: 1em; }
.sub-reply {}
.reply-info {font-size: .8em; padding: 0; }

#footer { padding: 2em 0 0; font-size: 80%; }

#appjetfooter { border: 0!important; margin-bottom: 4em; }