-- | Module containing some specialized functions to deal with tags.-- This Module follows certain conventions. Stick with them.---- More concrete: all functions in this module assume that the tags are-- located in the @tags@ field, and separated by commas. An example file-- @foo.markdown@ could look like:---- > ----- > author: Philip K. Dick-- > title: Do androids dream of electric sheep?-- > tags: future, science fiction, humanoid-- > ----- > The novel is set in a post-apocalyptic near future, where the Earth and-- > its populations have been damaged greatly by Nuclear...---- All the following functions would work with such a format. In addition to-- tags, Hakyll also supports categories. The convention when using categories-- is to place pages in subdirectories.---- An example, the page @posts\/coding\/2010-01-28-hakyll-categories.markdown@-- Tags or categories are read using the @readTagMap@ and @readCategoryMap@-- functions. Because categories are implemented using tags - categories can-- be seen as tags, with the restriction that a page can only have one-- category - all functions for tags also work with categories.---- When reading a @TagMap@ (which is also used for category maps) using the-- @readTagMap@ or @readCategoryMap@ function, you also have to give a unique-- identifier to it. This identifier is simply for caching reasons, so Hakyll-- can tell different maps apart; it has no other use.--moduleText.Hakyll.Tags(TagMap,readTagMap,readCategoryMap,withTagMap,renderTagCloud,renderTagLinks)whereimportqualifiedData.MapasMimportData.List(intercalate)importData.Maybe(fromMaybe,maybeToList)importControl.Arrow(second,(>>>))importControl.Applicative((<$>))importSystem.FilePathimportText.Blaze.Renderer.String(renderHtml)importText.Blaze.Html5((!),string,stringValue)importqualifiedText.Blaze.Html5asHimportqualifiedText.Blaze.Html5.AttributesasAimportText.Hakyll.Context(Context(..))importText.Hakyll.ContextManipulations(changeValue)importText.Hakyll.CreateContext(createPage)importText.Hakyll.HakyllMonad(Hakyll)importText.Hakyll.RegeximportText.Hakyll.HakyllActionimportText.Hakyll.UtilimportText.Hakyll.Internal.Cache-- | Type for a tag map.---- This is a map associating tags or categories to the appropriate pages-- using that tag or category. In the case of categories, each path will only-- appear under one category - this is not the case with tags.typeTagMap=M.MapString[HakyllAction()Context]-- | Read a tag map. This is a internally used function that can be used for-- tags as well as for categories.readMap::(Context->[String])-- ^ Function to get tags from a context.->String-- ^ Unique identifier for the tagmap.->[FilePath]->HakyllAction()TagMapreadMapgetTagsFunctionidentifierpaths=HakyllAction{actionDependencies=paths,actionUrl=Rightid,actionFunction=actionFunction'}wherefileName="tagmaps"</>identifieractionFunction'_=doisCacheMoreRecent'<-isCacheMoreRecentfileNamepathsassocMap<-ifisCacheMoreRecent'thenM.fromAscList<$>getFromCachefileNameelsedoassocMap'<-readTagMap'storeInCache(M.toAscListassocMap')fileNamereturnassocMap'return$M.map(mapcreatePage)assocMap-- TODO: preserve orderreadTagMap'::Hakyll(M.MapString[FilePath])readTagMap'=dopairs'<-concat<$>mapMpairspathsreturn$M.fromListWith(flip(++))pairs'-- | Read a page, and return an association list where every tag is-- associated with some paths. Of course, this will always be just one-- @FilePath@ here.pairs::FilePath->Hakyll[(String,[FilePath])]pairspath=docontext<-runHakyllAction$createPagepathlettags=getTagsFunctioncontextreturn$map(\tag->(tag,[path]))tags-- | Read a @TagMap@, using the @tags@ metadata field.readTagMap::String-- ^ Unique identifier for the map.->[FilePath]-- ^ Paths to get tags from.->HakyllAction()TagMapreadTagMap=readMapgetTagsFunctionwheregetTagsFunction=maptrim.splitRegex",".fromMaybe[].M.lookup"tags".unContext-- | Read a @TagMap@, using the subdirectories the pages are placed in.readCategoryMap::String-- ^ Unique identifier for the map.->[FilePath]-- ^ Paths to get tags from.->HakyllAction()TagMapreadCategoryMap=readMap$maybeToList.M.lookup"category".unContext-- | Perform a @Hakyll@ action on every item in the tag--withTagMap::HakyllAction()TagMap->(String->[HakyllAction()Context]->Hakyll())->Hakyll()withTagMaptagMapfunction=runHakyllAction(tagMap>>>action)whereaction=createHakyllAction(mapM_(uncurryfunction).M.toList)-- | Render a tag cloud.renderTagCloud::(String->String)-- ^ Function to produce an url for a tag.->Float-- ^ Smallest font size, in percent.->Float-- ^ Biggest font size, in percent.->HakyllActionTagMapStringrenderTagCloudurlFunctionminSizemaxSize=createHakyllActionrenderTagCloud'whererenderTagCloud'tagMap=return$intercalate" "$map(renderTagtagMap)(tagCounttagMap)renderTagtagMap(tag,count)=renderHtml$H.a!A.style(stringValue$"font-size: "++sizeTagtagMapcount)!A.href(stringValue$urlFunctiontag)$stringtagsizeTagtagMapcount=show(size'::Int)++"%"wheresize'=floor$minSize+relativetagMapcount*(maxSize-minSize)minCount=minimum.mapsnd.tagCountmaxCount=maximum.mapsnd.tagCountrelativetagMapcount=(count-minCounttagMap)/(maxCounttagMap-minCounttagMap)tagCount=map(second$fromIntegral.length).M.toList-- | Render all tags to links.-- -- On your site, it is nice if you can display the tags on a page, but-- naturally, most people would expect these are clickable.---- So, this function takes a function to produce an url for a given tag, and-- applies it on all tags.---- Note that it is your own responsibility to ensure a page with such an url-- exists.renderTagLinks::(String->String)-- ^ Function to produce an url for a tag.->HakyllActionContextContextrenderTagLinksurlFunction=changeValue"tags"renderTagLinks'whererenderTagLinks'=intercalate", ".map((\t->linkt$urlFunctiont).trim).splitRegex","