on master help sign in
Paginate simplification
738fd3dJasper Van der Jeugt
hakyll.cabal+1−1
@@ -111,7 +111,7 @@ Library
111 Hakyll.Web.Pandoc.Biblio
111 Hakyll.Web.Pandoc.Biblio
112 Hakyll.Web.Pandoc.FileType
112 Hakyll.Web.Pandoc.FileType
113 Hakyll.Web.Tags
113 Hakyll.Web.Tags
114− Hakyll.Web.Paginator
114+ Hakyll.Web.Paginate
115 Hakyll.Web.Template
115 Hakyll.Web.Template
116 Hakyll.Web.Template.Context
116 Hakyll.Web.Template.Context
117 Hakyll.Web.Template.List
117 Hakyll.Web.Template.List
src/Hakyll.hs+2−2
@@ -24,7 +24,7 @@ module Hakyll
24 , module Hakyll.Web.Pandoc.Biblio
24 , module Hakyll.Web.Pandoc.Biblio
25 , module Hakyll.Web.Pandoc.FileType
25 , module Hakyll.Web.Pandoc.FileType
26 , module Hakyll.Web.Tags
26 , module Hakyll.Web.Tags
27− , module Hakyll.Web.Paginator
27+ , module Hakyll.Web.Paginate
28 , module Hakyll.Web.Template
28 , module Hakyll.Web.Template
29 , module Hakyll.Web.Template.Context
29 , module Hakyll.Web.Template.Context
30 , module Hakyll.Web.Template.List
30 , module Hakyll.Web.Template.List
@@ -51,11 +51,11 @@ import Hakyll.Web.CompressCss
51import Hakyll.Web.Feed
51import Hakyll.Web.Feed
52import Hakyll.Web.Html
52import Hakyll.Web.Html
53import Hakyll.Web.Html.RelativizeUrls
53import Hakyll.Web.Html.RelativizeUrls
54+import Hakyll.Web.Paginate
54import Hakyll.Web.Pandoc
55import Hakyll.Web.Pandoc
55import Hakyll.Web.Pandoc.Biblio
56import Hakyll.Web.Pandoc.Biblio
56import Hakyll.Web.Pandoc.FileType
57import Hakyll.Web.Pandoc.FileType
57import Hakyll.Web.Tags
58import Hakyll.Web.Tags
58−import Hakyll.Web.Paginator
59import Hakyll.Web.Template
59import Hakyll.Web.Template
60import Hakyll.Web.Template.Context
60import Hakyll.Web.Template.Context
61import Hakyll.Web.Template.List
61import Hakyll.Web.Template.List
src/Hakyll/Web/Paginate.hsnew file+134
@@ -0,0 +1,134 @@
1+--------------------------------------------------------------------------------
2+{-# LANGUAGE OverloadedStrings #-}
3+module Hakyll.Web.Paginate
4+ ( PageNumber
5+ , Paginate (..)
6+ , buildPaginate
7+ , buildPaginateWith
8+ , paginateRules
9+ , paginateContext
10+ ) where
11+
12+
13+--------------------------------------------------------------------------------
14+import Control.Monad (forM_)
15+import Data.List (unfoldr)
16+import qualified Data.Map as M
17+import Data.Monoid (mconcat)
18+import Text.Printf (printf)
19+
20+
21+--------------------------------------------------------------------------------
22+import Hakyll.Core.Compiler
23+import Hakyll.Core.Identifier
24+import Hakyll.Core.Identifier.Pattern
25+import Hakyll.Core.Item
26+import Hakyll.Core.Metadata
27+import Hakyll.Core.Rules
28+import Hakyll.Web.Html
29+import Hakyll.Web.Template.Context
30+
31+
32+--------------------------------------------------------------------------------
33+type PageNumber = Int
34+
35+
36+--------------------------------------------------------------------------------
37+-- | Data about paginators
38+data Paginate = Paginate
39+ { paginatePages :: M.Map PageNumber [Identifier]
40+ , paginatePlaces :: M.Map Identifier PageNumber
41+ , paginateMakeId :: PageNumber -> Identifier
42+ , paginateDependency :: Dependency
43+ } deriving (Show)
44+
45+
46+--------------------------------------------------------------------------------
47+buildPaginate :: MonadMetadata m
48+ => Pattern
49+ -> m Paginate
50+buildPaginate pattern = do
51+ idents <- getMatches pattern
52+ let pagPages = M.fromList $ zip [1 ..] (map return idents)
53+ pagPlaces = M.fromList $ zip idents [1 ..]
54+ makeId pn = case M.lookup pn pagPages of
55+ Just [id'] -> id'
56+ _ -> error $
57+ "Hakyll.Web.Paginate.buildPaginate: " ++
58+ "invalid page number: " ++ show pn
59+
60+ return $ Paginate pagPages pagPlaces makeId
61+ (PatternDependency pattern idents)
62+
63+
64+--------------------------------------------------------------------------------
65+buildPaginateWith :: MonadMetadata m
66+ => Int
67+ -> (PageNumber -> Identifier)
68+ -> Pattern
69+ -> m Paginate
70+buildPaginateWith n makeId pattern = do
71+ -- TODO: there is no sensible order for `ids` here, for now it's random;
72+ -- but it should be `resectFirst` order because most recent posts should
73+ -- correspond to 1st paginator page and oldest one to last page
74+ idents <- getMatches pattern
75+ let pages = flip unfoldr idents $ \xs ->
76+ if null xs then Nothing else Just (splitAt n xs)
77+ nPages = length pages
78+ paginatePages' = zip [1..] pages
79+ pagPlaces' =
80+ [(ident, idx) | (idx,ids) <- paginatePages', ident <- ids] ++
81+ [(makeId i, i) | i <- [1 .. nPages]]
82+
83+ return $ Paginate (M.fromList paginatePages') (M.fromList pagPlaces') makeId
84+ (PatternDependency pattern idents)
85+
86+
87+--------------------------------------------------------------------------------
88+paginateRules :: Paginate -> (PageNumber -> Pattern -> Rules ()) -> Rules ()
89+paginateRules paginator rules =
90+ forM_ (M.toList $ paginatePages paginator) $ \(idx, identifiers) ->
91+ create [paginateMakeId paginator idx] $
92+ rulesExtraDependencies [paginateDependency paginator] $
93+ rules idx $ fromList identifiers
94+
95+
96+--------------------------------------------------------------------------------
97+-- | Takes first, current, last page and produces index of next page
98+type RelPage = PageNumber -> PageNumber -> PageNumber -> Maybe PageNumber
99+
100+
101+--------------------------------------------------------------------------------
102+paginateField :: Paginate -> String -> RelPage -> Context a
103+paginateField pag fieldName relPage = field fieldName $ \item ->
104+ let identifier = itemIdentifier item
105+ in case M.lookup identifier (paginatePlaces pag) of
106+ Nothing -> fail $ printf
107+ "Hakyll.Web.Paginate: there is no page %s in paginator map."
108+ (show identifier)
109+ Just pos -> case relPage 1 pos nPages of
110+ Nothing -> fail "Hakyll.Web.Paginate: No page here."
111+ Just pos' -> do
112+ let nextId = paginateMakeId pag pos'
113+ mroute <- getRoute nextId
114+ case mroute of
115+ Nothing -> fail $ printf
116+ "Hakyll.Web.Paginate: unable to get route for %s."
117+ (show nextId)
118+ Just rt -> return $ toUrl rt
119+ where
120+ nPages = M.size (paginatePages pag)
121+
122+
123+--------------------------------------------------------------------------------
124+paginateContext :: Paginate -> Context a
125+paginateContext pag = mconcat
126+ [ paginateField pag "firstPage"
127+ (\f c _ -> if c <= f then Nothing else Just f)
128+ , paginateField pag "previousPage"
129+ (\f c _ -> if c <= f then Nothing else Just (c - 1))
130+ , paginateField pag "nextPage"
131+ (\_ c l -> if c >= l then Nothing else Just (c + 1))
132+ , paginateField pag "lastPage"
133+ (\_ c l -> if c >= l then Nothing else Just l)
134+ ]
src/Hakyll/Web/Paginator.hsdeleted−206
@@ -1,206 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−module Hakyll.Web.Paginator
4− ( Paginator(..)
5− , PagState(..)
6− , NavigationLinkType(..)
7− , buildPaginator
8− , buildPaginatorWith
9− , paginatorRules
10− , renderPaginator
11− , renderPaginatorWith
12−
13− , paginatorFields
14− ) where
15−
16−--------------------------------------------------------------------------------
17−import Control.Monad (forM, forM_)
18−import Data.List (intercalate, unfoldr)
19−import Data.Monoid ((<>))
20−import Data.Maybe (fromMaybe)
21−import qualified Data.Map as M
22−import Text.Blaze.Html (toHtml, toValue, (!))
23−import Text.Printf (printf)
24−import Text.Blaze.Html.Renderer.String (renderHtml)
25−import qualified Text.Blaze.Html5 as H
26−import qualified Text.Blaze.Html5.Attributes as A
27−
28−
29−--------------------------------------------------------------------------------
30−import Hakyll.Core.Compiler
31−import Hakyll.Core.Identifier
32−import Hakyll.Core.Identifier.Pattern
33−import Hakyll.Core.Metadata
34−import Hakyll.Core.Rules
35−import Hakyll.Web.Template.Context
36−import Hakyll.Core.Item
37−import Hakyll.Web.Html
38−
39−-- | Data about paginators
40−data Paginator = Paginator
41− { pagPages :: M.Map Int [Identifier]
42− , pagPlaces :: M.Map Identifier Int
43− , pagMakeId :: PagState -> Identifier
44− , pagDependency :: Dependency
45− } deriving (Show)
46−
47−data PagState = PagState { pagPos :: Int
48− , pagLen :: Int }
49−
50−buildPaginatorWith :: MonadMetadata m
51− => Int
52− -> (PagState -> Identifier)
53− -> Pattern
54− -> m Paginator
55−buildPaginatorWith n makeId pattern = do
56− -- TODO: there is no sensible order for `ids` here, for now it's random;
57− -- but it should be `resectFirst` order because most recent posts should
58− -- correspond to 1st paginator page and oldest one to last page
59− idents <- getMatches pattern
60− let pages = unfoldr f idents
61− where
62− f [] = Nothing
63− f x = Just $ splitAt n x
64− nPages = length pages
65− pagPages' = zip [1..] pages
66− pagPlaces' = [(ident, idx) | (idx,ids) <- pagPages', ident <- ids] ++
67− [(makeId (PagState i nPages), i) | i <- [1 .. nPages]]
68− return $ Paginator (M.fromList pagPages') (M.fromList pagPlaces') makeId
69− (PatternDependency pattern idents)
70−
71−--------------------------------------------------------------------------------
72−
73−buildPaginator :: MonadMetadata m => Pattern -> m Paginator
74−buildPaginator = buildPaginatorWith 5 makeId
75− where
76− makeId (PagState pos n) = fromFilePath $ "index" ++ makeIndex pos n ++ ".html"
77− makeIndex i n = let nils = replicate (length (show n) - length (show i)) '0'
78− in nils ++ show i
79−
80−--------------------------------------------------------------------------------
81−
82−paginatorRules :: Paginator -> (PagState -> Pattern -> Rules ()) -> Rules ()
83−paginatorRules paginator rules =
84− forM_ (M.toList $ pagPages paginator) $ \(idx, identifiers) ->
85− let pagState = PagState idx (M.size $ pagPages paginator)
86− in create [pagMakeId paginator pagState] $
87− rulesExtraDependencies [pagDependency paginator] $
88− rules pagState $ fromList identifiers
89−
90−--------------------------------------------------------------------------------
91−
92−data NavigationLinkType = NFirst | NPrev | NNext | NLast
93−
94−instance Show NavigationLinkType where
95− show NFirst = "first"
96− show NPrev = "prev"
97− show NNext = "next"
98− show NLast = "last"
99−
100−renderPaginatorWith :: (String -> Int -> Int -> Int -> String)
101− -- ^ Produce a paginator menu item: url, index of menu element,
102− -- index of current page, amount of pages
103− -> (String -> NavigationLinkType -> Int -> String)
104− -- ^ Produce fast navigation links: url, type of navigation
105− -- element (e.g. last, prev...), index of corresponding page
106− -> ([String] -> String)
107− -- ^ Join items
108− -> Paginator
109− -> PagState
110− -> Compiler String
111−renderPaginatorWith makeHtml navigationHtml concatHtml paginator (PagState i n) = do
112− pags' <- forM (M.toList $ pagPages paginator) $ \(idx,_) -> do
113− let pagState = PagState idx (M.size $ pagPages paginator)
114−
115− url <- getRoute $ pagMakeId paginator pagState
116− return (url, idx)
117−
118− let -- Create a link for one item
119− makeHtml' (url, idx) =
120− makeHtml (toUrl $ fromMaybe "/" url) idx i n
121−
122− -- Fast-travel links logic: first, prev, next, last (<< < > >>)
123− navIdxs = [1, max 1 (i-1), min (i+1) n, n]
124− navIds = [NFirst, NPrev, NNext, NLast]
125− navUrls = map (\idx -> toFilePath $ pagMakeId paginator (PagState idx n))
126− navIdxs
127− navHtmlCode = zipWith3 navigationHtml navUrls navIds navIdxs
128− navLefts = if i==1
129− then []
130− else take 2 navHtmlCode
131− navRights= if i==n
132− then []
133− else drop 2 navHtmlCode
134−
135− return $ concatHtml $ navLefts ++ map makeHtml' pags' ++ navRights
136−
137−renderPaginator :: Paginator -> PagState -> Compiler String
138−renderPaginator =
139− renderPaginatorWith makeHtml navigationHtml concatHtml
140− where
141− navigationHtml url navType _idx =
142− let (caption, alt) = arrow navType
143− in renderHtml $ H.a ! (A.href (toValue url) <>
144− A.title (toValue alt))
145− $ toHtml caption
146− where
147− arrow :: NavigationLinkType -> (String, String)
148− arrow NPrev = ("<" , "prev")
149− arrow NNext = (">" , "next")
150− arrow NFirst = ("<<", "first")
151− arrow NLast = (">>", "last")
152− concatHtml = intercalate " " . filter (not . null)
153− makeHtml url menuItemIdx pageIdx nPages
154− | menuItemIdx == pageIdx = show menuItemIdx
155− | not shouldBeDisplayed = ""
156− | otherwise =
157− let caption = show menuItemIdx
158− in renderHtml $ H.a ! (A.href (toValue url) <>
159− A.title (toValue caption))
160− $ toHtml caption
161− where
162− shouldBeDisplayed =
163− let leg = 2
164− width = 1 + 2*leg
165− in abs (menuItemIdx - pageIdx) <= leg
166− || pageIdx - leg <= 0 && menuItemIdx - width <= 0
167− || pageIdx + leg >= nPages && menuItemIdx + width >= nPages
168−
169−
170−paginatorField :: Paginator -> String -> NavigationLinkType -> Context a
171−paginatorField pag fieldName arrowType = field fieldName $ \item -> do
172− let identifier = itemIdentifier item
173− nPages = M.size (pagPages pag)
174−
175− neededPage NNext pos | pos+1 > nPages = Nothing
176− neededPage NNext pos = Just (pos + 1)
177−
178− neededPage NPrev pos | pos-1 < 1 = Nothing
179− neededPage NPrev pos = Just (pos - 1)
180−
181− neededPage NFirst pos | pos == 1 = Nothing
182− neededPage NFirst _ = Just 1
183−
184− neededPage NLast pos | pos == nPages = Nothing
185− neededPage NLast _ = Just nPages
186−
187− case M.lookup identifier (pagPlaces pag) of
188− Nothing -> error $ printf "Hakyll.Web.Paginator: there is no page %s in paginator map."
189− (show identifier)
190− Just pos -> case neededPage arrowType pos of
191− Nothing -> fail $ printf "There is no %s page for page %s in position %s."
192− (show arrowType) (show identifier) (show pos)
193− Just pos' -> do
194− let nextId = pagMakeId pag (PagState pos' nPages)
195− mroute <- getRoute nextId
196− case mroute of
197− Nothing -> error $ printf "Hakyll.Web.Paginator: unable to get route of identifier %s."
198− (show nextId)
199− Just rt -> return $ toUrl rt
200−
201−paginatorFields :: Paginator -> Context a
202−paginatorFields pag = paginatorField pag "firstPage" NFirst
203− <> paginatorField pag "prevPage" NPrev
204− <> paginatorField pag "nextPage" NNext
205− <> paginatorField pag "lastPage" NLast
206−