$ = require('jquery')
Tag = require('tf')
Router = require('diso.router')$ = require('jquery')
Tag = require('tf')
Router = require('diso.router')PageMap = require('./PageMap')body tag attribute used to communicate the page’s name between the server and the client
PAGE_ATTR = 'data-page'Used by the client to sync the initial serverside render with the clientside page, perform navigation between and within pages via HTML5 history api
class ClientContainer
constructor : (args)->
map = args.map
pages = args.pages @_page_map = new PageMap(
map : map
pages : pages
) @_router = new Router()
@_router.route(page_map.routes)Basicaly, traverse the dom and instantiate views for their associated dom nodes
sync : (args)->
init_data = args.init_data
callback = args.callback
page_name = $('body').attr(PAGE_ATTR)
unless page_name
error = new Error("HTML body is missing #{PAGE_ATTR} attribute")
return callback(error, null)
unless (page_name of @_pages)
error = new Error("No page named #{page_name}")
return callback(error)
route = @_router.match(url : document.location)
Page = @_pages[page_name]
@_page = Page.create(
data : @_initial_data.view_data
id_map : @_initial_data.id_map
url : document.location
route : route
)
sync : (id_map)->
ids = Object.keys(id_map)
temp_ids = Object.keys(@_subviews)
if (ids.length != temp_ids.length)
throw "Mismatch between subview id lengths"
for i in [0..(ids.length - 1)]
id = ids[i]
map = id_map[id]
temp_id = temp_ids[i]
view = @_subviews[temp_id]
if view
view.setId(id)
view.setContainer()
view.sync(map)
page = new @(options)
page_id = Object.keys(id_map)[0]
page.setId(page_id)
page.setContainer()
map = id_map[page_id]
page.sync(map)
page.run()
pageInitialize the html5 history api. Using the browser api rather than adapter and falling back to full page loads if it isn’t support http://diveintohtml5.info/history.html
_initializeHistory : ()->
if @_supportsHistory()
$(window).on('popstate', @_onPopState)returns true if the user’s browser supports HTML5 history http://caniuse.com/#search=history
_supportsHistory : ()->
!!(window.history?.pushState)called when user presses back button
TODO: handle this correctly / integrate with router
_onPopState : ()=>
console.log("HANDLE POP STATE YO:" + document.location)add url to the history. this will change the location bar to url
_pushHistory : (url)->
window.history.pushState(null, null, url)
pushPage : ()->
popPage : ()->
navigate : (args)=>
unless @_supportsHistory()TODO : find way to pass the JWT-token via get param or header
window.location = url
return
if ('url' of args)
url = args.url
route = @_router.match(url : url)
else if ('route' of args)
route = args.route
url = @_router.format(route)
else
error = new Error("Must pass url or route arg to navigate")
return @_onError(error)
page = @_page_map.page(
route : route
url : url
store : @_store
)
unless page
error = new Error("No page for #{route.name}")
return @_onError(error)
page.load((error)=>
console.log("LOADED PAGE!")
@_changePage(page)
@_pushHistory(url)
)
setBody : (body)->
if @body
@removeSubview(@body)
@body = body
@addSubview(@body)
swapBody : (new_body)->
@body.removeBehaviors()
$body = $("##{@body.id}")
$body.replaceWith(new_body.html())
@setBody(new_body)
new_body.run()
class ServerContainer
scripts : []
styles : []called on the server to build a map of all the view ids for the client to use for sync.
idMap : ()->
_idMap = (view)->
map = {}
for id, subview of view.subviews()
map[view.id] = _idMap(subview)
map
map = {}
map[@page.id] = _idMap(@page)
map
meta : (data)->
metadata = {
name : {
description : data.description || @description
viewport : @viewport || 'width=device-width, initial-scale=1'
}
itemprop : {
name : data.title
description : data.description
image : data.image
url : data.url
}
property : {
"og:title" : data.title
"og:description" : data.description
"og:image" : data.image
"og:type" : data.type || 'website'
"og:url" : data.url
"og:site_name" : data.site_name || data.title
}
}
html = Tag.meta(charset : 'utf-8') + "\n"
for attr, m of metadata
for name, content of m
opts = {
content : content
}
opts[attr] = name
html += Tag.meta(opts) + "\n"
html
head : ()->
@page.head()
headers : ()->
@page.headers()
status : ()->
@page.status()render this container as html
html : ()->TODO
"""
<!DOCTYPE html>
<html>
<head>
#{@meta(@page.meta())}
<title>#{@page.meta().title || @site_name}</title>
#{(Tag.stylesheet(href: href) for href in @styles).join("\n")}
#{(Tag.script(src: src) for src in @scripts).join("\n")}
#{@head()}
</head>
<body #{PAGE_ATTR}="#{@page.constructor.name}">
#{@page.html()}
</body>
</html>
"""render this container as text
text : ()->
@page.text()render this container as json
json : ()->
@page.json()
module.exports = {
Server : ServerContainer
Client : ClientContainer
}