packages feed

orchid-0.0.6: viewer/js/app.js

/* ------------------------------------------------------------------------- */

$(document).ready( 
  function start ()
  {
    setupLoadingIcon()
    installCollapsers()
    installOptions()
    installSmartFields()
    installHandlers()
    index()
    setupBrowserHistory()
    updateAuth()
  }
)

function index ()
{
  // When no document is requested using the resource hash, open the index
  // document by default.
  if (!document.location.hash)
    document.location.hash = 'Index'
}

function setupBrowserHistory ()
{
  // History changes are reflected using the parseURI/update functions.
  $.history.init(
    function historyChange (hash)
    {
      parseURI()
      update()
    }
  )
}

function setupLoadingIcon ()
{
  $("#loading").ajaxStart(
    function ()
    {
       $(this).show();
    }
  );
  $("#loading").ajaxStop(
    function ()
    {
       $(this).hide();
    }
  ); 
}

function installHandlers ()
{
  $('#history-btn').click(showHistory)
  $('#edit-btn').click(showEditor)
  $('#login-btn').click(login)
  $('#logout-btn').click(logout)
  $('#create-account-btn').click(signup)
  $('#save-btn').click(save)
  $('#preview-btn').click(preview)
  $('#delete-btn').click(_delete)
}

/* ------------------------------------------------------------------------- */

// Current application state.

Wiki = {

  // Static information.
    prefix     : 'data/'
  , previewURI : 'data/preview'
  , loginURI   : 'login'
  , logoutURI  : 'logout'
  , loginfoURI : 'loginfo'
  , signupURI  : 'signup'
  , viewerURI  : '#'

  // State information.
  , title      : ''
  , ext        : 'html'
  , rev        : ''
  , anchor     : ''

  , prevTitle  : ''
  , prevExt    : ''
  , prevRev    : ''
  , prevAnchor : ''

  , author     : ''
  , user       : ''
}

function parseURI ()
{
  var hash = window.location.hash
  var parse = hash.match(/^#([^?#]*)\??([^#]*)#?(.*)/)

  var ext = parse[1].match(/\.([^.]*)$/)
  var tit = parse[1].match(/(.*)\.([^.]*)$/)

  Wiki.prevTitle  = Wiki.title
  Wiki.prevExt    = Wiki.ext
  Wiki.prevRev    = Wiki.rev
  Wiki.prevAnchor = Wiki.anchor

  Wiki.title  = tit ? tit[1] : parse[1]
  Wiki.ext    = ext ? ext[1] : 'html'
  Wiki.rev    = parse[2]
  Wiki.anchor = parse[3]
}

function update ()
{
  var t = 0

  // Only refresh when one of these change.
  if (Wiki.title != Wiki.prevTitle ||
      Wiki.ext   != Wiki.prevExt   ||
      Wiki.rev   != Wiki.prevRev) {

    // Update information bar.
    $('#document').html(Wiki.title)
    $('#revision').html(Wiki.rev || '<i>head revision</i>')
    $('#user').html(Wiki.user)

    show()
    showHistory()
    updateAuth()

    // Anchor focus delay when refreshing.
    t = 1000
  }

  if (Wiki.anchor)
    setTimeout(
      function ()
      {
        followAnchor(Wiki.anchor)
      }, t
    )
}

function makeURI (prefix, title, ext, rev, anchor)
{
  var p = prefix === undefined ? Wiki.prefix : prefix
  var t = title  === undefined ? Wiki.title  : title
  var e = ext    === undefined ? Wiki.ext    : ext
  var r = rev    === undefined ? Wiki.rev    : rev
  var a = anchor === undefined ? Wiki.anchor : anchor
  return p + t
    + (e ? '.' + e : '')
    + (r ? '?' + r : '')
    + (a ? '#' + a : '')
}

function makeURIExt (ext)
{
  return makeURI(undefined, undefined, ext)
}

/* ---- showing documents -------------------------------------------------- */

function viewerHTML (txt)
{
  if (Wiki.ext == 'html') {
    $('#plain-viewer').hide()
    $('#plain-viewer').html('')
    $('#rich-viewer').show()
    $('#rich-viewer').html(txt)
  } else {
    $('#rich-viewer').hide()
    $('#rich-viewer').html('')
    $('#plain-viewer').show()
    $('#plain-viewer').html(txt)
  }
}

function setExternalLinks ()
{
  $('#txt-btn').   attr('href', makeURI(Wiki.viewerURI, undefined, 'txt'))
  $('#html-btn').  attr('href', makeURI(Wiki.viewerURI, undefined, 'html'))
  $('#latex-btn'). attr('href', makeURI(Wiki.viewerURI, undefined, 'tex'))
  $('#adt-btn').   attr('href', makeURI(Wiki.viewerURI, undefined, 'hs'))
  $('#pdf-btn').   attr('href', makeURIExt('pdf'))
  $('#print-btn'). attr('href', makeURIExt('html'))
  $('#src-btn').   attr('href', makeURIExt('txt'))
}

function show ()
{
  $.get(
      makeURIExt(Wiki.ext)
    , ''

    // Showing existing document:
    , function (res)
      {
        viewerHTML(res)
        $('#history-btn').removeClass('disabled')
        processLinks()
        processImages()
        setExternalLinks()
      }
    , ''

    // `Showing' non-existing document:
    , function ()
      {
        viewerHTML('new document, click <strong>edit</strong> to start editing')
        $('#history-btn').addClass('disabled')
        showEditor() //todo
      }
  )
}

function processLinks ()
{
  $(".Internal").each(
    function ()
    {
      var href = this.getAttribute('href')
      if (!href.match(/:/))
        this.setAttribute('href', '#' + href)
    }
  )

  $(".Reference").each(
    function ()
    {
      $(this).attr('href', makeURI(Wiki.viewerURI, undefined, undefined, undefined, $(this).attr('href').substring(1)))
    }
  )
}

function processImages ()
{
  $("img").each(
    function ()
    {
      var src = this.getAttribute('src')
      this.setAttribute('src', src.replace(/^_/, 'data/_'))
    }
  )
}

function followAnchor (ref)
{
  var target = $('*[name=' + ref + ']')[0]
  target.scrollIntoView()

  setTimeout(
    function ()
    {
      focusElement(target)
    }, 0)
}

function focusElement (target)
{
  var div = document.createElement('div')
  document.body.appendChild(div)
  var bcr = target.getBoundingClientRect()
  div.style.left   = bcr.left
  div.style.top    = bcr.top
  div.style.width  = target.clientWidth
  div.style.height = target.clientHeight
  $(div).addClass('find')
  $(div).fadeOut('slow')
}

/* ---- authentication bar ------------------------------------------------- */

function login ()
{
  if ($('#login-btn').hasClass('disabled'))
    return;

  $.post(
    Wiki.loginURI
  , { username : $('#username').val()
    , password : $('#password').val()
    } 
  , function ()
    {
      $('#login-status').removeClass('error')
      $('#login-status').html('login successful')
      panelClose($('#auth-btn'), $('#auth'))
      updateAuth()
    }
  , ''
  , function (e)
    {
      $('#login-status').addClass('error')
      $('#login-status').html('login failed: <span>' + e.responseText + '</span>')
    }
  )
}

function logout ()
{
  if ($('#logout-btn').hasClass('disabled'))
    return;

  $.post(
    Wiki.logoutURI
  , function ()
    {
      $('#login-status').removeClass('error')
      $('#login-status').html('logout successful')
      $('#auth').fadeOut()
      updateAuth()
    }
  )
}

function updateAuth ()
{
  $.get(
      Wiki.loginfoURI
    , ''
    , function (res)
      {
        var m = res.match(/username=(.*?\n)/)
        Wiki.user = (m && m[1]) || ''
        $('#user').html(Wiki.user)
        var bla = $('#user-ui').val(Wiki.user)
        fieldUpdate.call(bla)
      }
    , ''
    , function (res)
      {
        Wiki.user = ''
        $('#user').html('<i>not logged in</i>')
        fieldUpdate.call($('#user-ui').val(''))
      }
  )
}

function signup ()
{
  if ($('#signup-btn').hasClass('disabled'))
    return;

  $.post(
    Wiki.signupURI
  , { username : $('#su-username').val()
    , password : $('#su-password').val()
    } 
  , function ()
    {
      $('#signup-status').removeClass('error')
      $('#signup-status').html('signup successful')
      panelClose($('#signup-btn'), $('#signup'))
    }
  , ''
  , function (e)
    {
      $('#signup-status').addClass('error')
      $('#signup-status').html('signup failed: <span>' + e.responseText + '</span>')
    }
  )
}

/* ---- document history --------------------------------------------------- */

function showHistory (title)
{
  // Refuse history updates when panel is closed.
  if (!$('#history').hasClass('open'))
    return

  $.get(makeURIExt('his'), '',
    function (res)
    {
      $('#history').html(historyTable(res))
    }
  )
}

function historyTable (res)
{
  function row (c, i)
  {
    var lines    = c.split('\n')
      , modified = lines.shift()
      , author   = lines.shift()
      , name     = lines.shift()

    // Side note.
    var klass =
      name == Wiki.rev ||
      (i == 0 && !Wiki.rev) 
      ? 'class="current" ' : ''

    return [
      , '<tr $klass>'
      , '<td>$modified</td>'
      , '<td>$author</td>'
      , '<td><a href="' + makeURI(Wiki.viewerURI, undefined, undefined, i == 0 ? '' : name) + '">$name</a></td>'
      , '</tr>'
      ].join('').interpolate({
          'klass'    : klass
        , 'modified' : modified
        , 'author'   : author
        , 'name'     : name
      })
  }

  function col (c)
  {
    return '<td>' + c + '</td>'
  }

  return '' + 
    '<table cellpadding="0" cellspacing="0"><tr>' +
    '<th id="modified">modified</th>' +
    '<th id="author">author</th>' +
    '<th id="changename">change</th>' +
    '</tr>' +
    res.asHTML().split("\n\n").map(row).join('')
    + '</tbody></table>'
}

/* ---- document editor ---------------------------------------------------- */

function showEditor (title)
{
  $.get(makeURIExt('txt'), '',

    // Editing an existing document:
    function (res)
    {
      $('#editField').val(res)
    },
    '',

    // `Editing' a new document:
    function (res)
    {
      $('#editField').val('<new document>')
    }
  )
}

function save (title)
{
  if (!Wiki.user)
    return;

  $.put(
    makeURI(undefined, undefined, 'txt', $('#change.full').val())
  , $('#editField').val()
  , function ()
    {
      $('#edit-status').removeClass('error')
      $('#edit-status').html('save successful')
      panelClose($('#edit-btn'), $('#editor'))
      show()
      showHistory()
    }
  , ''
  , function (e)
    {
      $('#edit-status').addClass('error')
      $('#edit-status').html('save failed: <span>' + e.responseText + '</span>')
    }
  )
}

function preview ()
{
  $.put(
    Wiki.previewURI + '.' + Wiki.ext
  , $('#editField').val()
  , function (res)
    {
      viewerHTML(res)
      processLinks()
      processImages()
      $('#edit-status').removeClass('error')
      $('#edit-status').html('preview successful')
    }
  )
}

function _delete ()
{
  $._delete(
    makeURI(undefined, undefined, '', $('#change.full').val())
  , ''
  , function (res)
    {
      $('#edit-status').removeClass('error')
      $('#edit-status').html('delete successful')
      // todo: close edit & history.
    }
  )
}