Skip to content

Instantly share code, notes, and snippets.

@Tolia
Created June 30, 2015 13:27
Show Gist options
  • Save Tolia/99453da0ccecf16d3cc0 to your computer and use it in GitHub Desktop.
Save Tolia/99453da0ccecf16d3cc0 to your computer and use it in GitHub Desktop.
angular
.module('turbo.pages', [])
.config(["$locationProvider", ($locationProvider) ->
$locationProvider.html5Mode
enabled: true
requireBase: false
])
.provider('$turboPageTemplate', ->
provider =
config:
heders:
'Accept': 'text/html'
$get: [
"$http"
"$q"
"$cacheFactory"
"$documentParser"
"$timeout"
($http, $q, $cacheFactory, $documentParser, $timeout) ->
$cachePage = $cacheFactory "routTemplate", capacity: 20
api =
getState: (pathname) ->
$cachePage.get(pathname) || {pathname: pathname}
updateSate: (pathname, params) ->
state = @getState pathname
if params
state = angular.extend state, params
$cachePage.put pathname, state
state
updateScroll: (pathname, scrollTop) ->
scrollTop = window.scrollTop || window.scrollY unless scrollTop?
@updateSate pathname,
$scrollTop: scrollTop
get: (pathname) ->
deferred = $q.defer()
state = @getState pathname
if !state || !(state.$template && state.$cache)
httpConfig =
method: 'GET'
url: pathname
cache: false
headers: provider.config.heders
parseResponse = (data) ->
parser = $documentParser data
window.parser = parser
page = parser.find(".turbo-page")
unless page
deferred.reject 'no turbo'
return
cacheTemplate = parser.find("[data-turbo-page-cache]")?.getAttribute('data-turbo-page-cache')
state = @updateSate pathname,
$template: page.innerHTML
$cache: cacheTemplate
$title: parser.title()
deferred.resolve state
$http(httpConfig)
.success parseResponse.bind(@)
.error parseResponse.bind(@)
else
deferred.resolve state
deferred.promise
]
)
.service('$linkParser', [ ->
getLink = (url) ->
link = document.createElement('a')
link.href = url
link
(url = "") ->
link = getLink(url)
parser =
protocol: link.protocol # "http:"
hostname: link.hostname # "example.com"
port: link.port # "3000"
pathname: link.pathname # "/pathname/"
search: link.search # "?search=test"
hash: link.hash # "#hash"
fullpath: link.pathname + link.hash
])
.service('$documentParser', [ ->
createDocumentUsingParser = (html) ->
(new DOMParser).parseFromString html, 'text/html'
createDocumentUsingDOM = (html) ->
doc = document.implementation.createHTMLDocument ''
if doc.documentElement
doc.documentElement.innerHTML = html
else
console.error html
doc
createDocumentUsingWrite = (html) ->
doc = document.implementation.createHTMLDocument ''
doc.open 'replace'
doc.write html
doc.close()
doc
parser = do ->
try
if window.DOMParser
testDoc = createDocumentUsingParser '<html><body><p>test'
createDocumentUsingParser
catch e
testDoc = createDocumentUsingDOM '<html><body><p>test'
createDocumentUsingDOM
finally
unless testDoc?.body?.childNodes.length is 1
return createDocumentUsingWrite
class domBulder
constructor: (text) ->
@doc = parser text
@body = @doc.body
title: ->
@doc.title
find: (selector) ->
@doc.querySelector(selector)
(text) ->
new domBulder text
])
.directive('turboPage', [
"$compile"
"$location"
"$turboPageTemplate"
"$rootScope"
"$linkParser"
'$timeout'
($compile, $location, $turboPageTemplate, $rootScope, $linkParser, $timeout) ->
linkTemplate = ($element, state) ->
$template = state.$template
$element.html $template
$compile $element.contents()
class CheckPagePaths
constructor: (newUrl, oldUrl) ->
@newest = $linkParser newUrl
@last = $linkParser oldUrl
validate: ->
@newest.fullpath != @last.fullpath
directive =
restrict: "AC"
priority: 999
transclude: true
replace: true
scope: true
template: "<div ng-class=\"className\" ng-transclude></div>"
link: (scope, $element) ->
dropOldScope = ->
scope.$$childTail?.$destroy()
setSatus = (status) ->
scope.className = status + "_loading_router"
load = (path,oldPath) ->
setSatus "start"
$turboPageTemplate.updateScroll(oldPath)
$turboPageTemplate.get(path)
.then (state) ->
dropOldScope()
link = linkTemplate $element, state
document.title = state.$title if state.$title
link scope.$new()
$rootScope.$broadcast '$turbolinksUpdate'
timerId = $timeout ->
y = state.$scrollTop || 0
window.scrollTo 0, y
setSatus "done"
, (error) ->
window.location.reload()
update = (e, newUrl, oldUrl) ->
check = new CheckPagePaths newUrl, oldUrl
if check.validate()
load(check.newest.fullpath, check.last.fullpath)
$rootScope.$on '$turbolinksReload', (event, href) =>
load href
.then ->
window.scrollTo 0, 0
$rootScope.$on '$locationChangeStart', update
])
.directive('turboReload', [
'$rootScope'
'$location'
($rootScope, $location) ->
directive =
restrict: "A"
link: (scope, $element) ->
$element.on 'click', () ->
href = $element.attr('href')
if href
$rootScope.$broadcast '$turbolinksReload', href
])
.directive('turboReset', [
'$linkParser'
'$turboPageTemplate'
($linkParser, $turboPageTemplate) ->
directive =
restrict: "AC"
scope: false
link: ($scope, $element, $attrs) ->
$element.on 'click', ($event) ->
return unless $event.srcElement
href = $event.srcElement.href
return if !href
parser = $linkParser(href)
$turboPageTemplate.updateScroll(parser.fullpath,0)
])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment