orchid-0.0.7: viewer/js/app.js
/* ------------------------------------------------------------------------- */
$(document).ready(
function start ()
{
setupLoadingIcon()
installCollapsers()
installOptions()
installEnters()
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)
$('#rename-btn').click(rename)
$('#delete-btn').click(_delete)
$('#search-btn').click(search)
$('#search-prev-btn').click(searchPrev)
$('#search-next-btn').click(searchNext)
}
/* ------------------------------------------------------------------------- */
// Current application state.
Wiki = {
// Static information.
prefix : 'data/'
, previewURI : 'data/preview'
, searchURI : 'data/search'
, loginURI : 'login'
, logoutURI : 'logout'
, loginfoURI : 'loginfo'
, signupURI : 'signup'
, viewerURI : '#'
// State information.
, title : ''
, ext : 'html'
, rev : ''
, anchor : ''
, prevTitle : ''
, prevExt : ''
, prevRev : ''
, prevAnchor : ''
, author : ''
, user : ''
, search : {
results : []
, max : 20
, index : 0
}
}
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')
panelClose($('#edit-btn'), $('#editor'))
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 = $('a[name=' + ref + ']')[0]
target.scrollIntoView()
// setTimeout(
// function ()
// {
// focusElement(target.childeNodes[0])
// }, 0)
}
// function focusElement (target)
// {
// alert(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()
, email : $('#su-email').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-results').html(historyTable(res))
$('#history-status').removeClass('error')
$('#history-status').html('')
},
'',
function (e)
{
$('#history-results').html('')
$('#history-status').addClass('error')
$('#history-status').html('no history available: <span>' + e.responseText + '</span>')
}
)
}
function historyTable (res)
{
function row (c, i)
{
var modified = c.match( /(^|[\n])date=([^\n]*)/ )[2]
var author = c.match( /(^|[\n])author=([^\n]*)/ )[2]
var email = c.match( /(^|[\n])email=([^\n]*)/ )[2]
var description = c.match( /(^|[\n])description=([^\n]*)/ )[2]
var revid = c.match( /(^|[\n])id=([^\n]*)/ )[2]
// 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="$uri">$description</a></td>'
, '</tr>'
].join('').interpolate({
'uri' : makeURI(Wiki.viewerURI, undefined, undefined, description)
, 'klass' : klass
, 'modified' : modified
, 'author' : author
, 'description' : description
})
}
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 rename ()
{
$._delete(
makeURI(undefined, undefined, '', $('#change.full').val())
, $('#rename').val()
, function (res)
{
$('#edit-status').removeClass('error')
$('#edit-status').html('rename successful')
document.location.hash = $('#rename').val()
}
)
}
function _delete ()
{
$._delete(
makeURI(undefined, undefined, '', $('#change.full').val())
, ''
, function (res)
{
$('#edit-status').removeClass('error')
$('#edit-status').html('delete successful')
document.location.hash = ''
}
)
}
/* ---- search bar --------------------------------------------------------- */
function search ()
{
if ($('#search-btn').hasClass('disabled'))
return;
$('#search-status').removeClass('error')
$('#search-status').html('searching...')
$.post(
Wiki.searchURI
, { patterns : $('#search-terms').val()
, wholewords : 'False'
, matchall : 'False'
, ignorecase : $('#search-case:checked').length == 0 ? 'True' : 'False'
}
, function (res)
{
var rs = Wiki.search.results = res.asHTML().split("\n\n")
Wiki.search.index = 0
if (!rs || rs.length == 0 || (rs.length == 1 && !rs[0])) {
$('#search-status').addClass('error')
$('#search-status').html('no search results')
searchPrevReset()
searchNextReset()
$('#search-results').html('')
} else {
$('#search-status').removeClass('error')
$('#search-status').html('search successful (' + rs.length + ' results)')
$('#search-results').html(searchTable())
}
}
, ''
, function (e)
{
$('#search-status').addClass('error')
$('#search-status').html('search failed: <span>' + e.responseText + '</span>')
}
)
}
function searchPrevReset ()
{
$('#search-prev-btn').addClass('disabled')
$('#search-prev-btn').html('previous results')
}
function searchNextReset ()
{
$('#search-next-btn').addClass('disabled')
$('#search-next-btn').html('next results')
}
function searchNext ()
{
if ($('#search-next-btn').hasClass('disabled'))
return;
Wiki.search.index += Wiki.search.max
$('#search-results').html(searchTable())
}
function searchPrev ()
{
if ($('#search-prev-btn').hasClass('disabled'))
return;
Wiki.search.index -= Wiki.search.max
$('#search-results').html(searchTable())
}
function searchTable ()
{
function row (c, i)
{
var resource = c.match( /(^|[\n])resource=([^\n]*)/ )[2]
var linenumber = c.match( /(^|[\n])linenumber=([^\n]*)/ )[2]
var line = c.match( /(^|[\n])line=([^\n]*)/ )[2]
return [
'<tr>'
, '<td><a href="$uri">$resource</a></td>'
, '<td>$linenumber</td>'
, '<td>$line</td>'
, '</tr>'
].join('').interpolate({
'uri' : makeURI(Wiki.viewerURI, resource, undefined, '')
, 'resource' : resource
, 'linenumber' : linenumber
, 'line' : line
})
}
function col (c)
{
return '<td>' + c + '</td>'
}
var rs = Wiki.search.results
ix = Wiki.search.index
max = Wiki.search.max
prevIx = Math.max(0, ix - max)
prevMax = Math.max(0, ix)
nextIx = Math.min(rs.length - 1, ix + max)
nextMax = Math.min(rs.length - 1, ix + max + max)
if (prevMax > 0) {
$('#search-prev-btn').removeClass('disabled')
$('#search-prev-btn').html('previous results <span>(' + prevIx + '-' + prevMax + ')</span>')
} else
searchPrevReset()
if (nextIx < rs.length - 1) {
$('#search-next-btn').removeClass('disabled')
$('#search-next-btn').html('next results <span>(' + nextIx + '-' + nextMax + ')</span>')
} else
searchNextReset()
return '' +
'<table cellpadding="0" cellspacing="0"><tr>'
+ '<th id="resource">resource</th>'
+ '<th id="linenumber">line</th>'
+ '<th id="line">match</th>'
+ '</tr>'
+ rs.slice(ix, ix + max).map(row).join('')
+ '</tbody></table>'
}