happstack-yui 7351.2.0 → 7351.3.0
raw patch · 3 files changed
+152/−29 lines, 3 filesdep +directorydep +mtl
Dependencies added: directory, mtl
Files
- happstack-yui.cabal +32/−11
- src/Happstack/Server/YUI.hs +85/−13
- src/Happstack/Server/YUI/Bundle.hs +35/−5
happstack-yui.cabal view
@@ -1,5 +1,5 @@ Name : happstack-yui-Version : 7351.2.0+Version : 7351.3.0 Category : Web, Happstack Synopsis : Utilities for using YUI3 with Happstack. Homepage : https://github.com/dag/happstack-yui@@ -12,10 +12,11 @@ Description : Bundles YUI 3.5.1 and includes a \"combo handler\" for use in Happstack which concatenates YUI modules server-side to send in a single- HTTP request. The YUI bundle is embedded in the library with Template- Haskell which means the files are served directly from memory, and also- that you can compile and deploy a single executable without having to- worry about deploying the YUI files as well.+ HTTP request. The YUI bundle can be embedded in compiled code with+ Template Haskell (install with @-fembed@) which means the files are+ served directly from memory, and also that you can compile and deploy a+ single executable without having to worry about deploying the YUI files+ as well. . The benefits of using this over the Yahoo! CDN is that you can work offline and that you can host YUI yourself without sacrificing the@@ -26,10 +27,11 @@ means \"Happstack 7, YUI 3.5.1\". The second part is the major version of this package itself, as defined by the Package Versioning Policy. .- In a future release, the plan is to add more utilities than just a- bundled YUI, for example a combo handler for YUI modules written using- JMacro, a bridge between HSX and YUI Node objects and tools for working- with the YUI CSS modules.+ The package also includes some utilities for working with the YUI CSS+ modules. In a future release, the plan is to add more utilities, for+ example a combo handler for YUI modules written using JMacro, a bridge+ between HSX and YUI Node objects and tools for making it easier to work+ with modules like Uploader and Pjax. . For an example application, see: <https://github.com/dag/happstack-yui/blob/master/demo/happstack-yui-demo.hs>@@ -2829,6 +2831,10 @@ bundle/yui-log/yui-log-min.js } +Flag embed+ Description : Embed the YUI bundle in compiled code.+ Default : False+ Source-Repository head Type : git Location : git://github.com/dag/happstack-yui.git@@ -2837,7 +2843,6 @@ HS-Source-Dirs : src Default-Language : Haskell2010 GHC-Options : -Wall- CPP-Options : -DYUI_VERSION=3.5.1 -DYUI_VERSION_STR="3.5.1" Exposed-Modules : Happstack.Server.YUI Other-Modules :@@ -2847,13 +2852,29 @@ boomerang == 1.3.*, bytestring == 0.9.*, containers >= 0.4 && < 0.6,- file-embed == 0.0.*, happstack-jmacro == 7.0.*, happstack-server == 7.0.*, jmacro == 0.5.*,+ mtl >= 2.0 && < 2.2, pretty == 1.1.*, template-haskell >= 2.5 && < 2.8, text == 0.11.*, web-routes == 0.27.*, web-routes-boomerang == 0.26.*, web-routes-happstack == 0.23.*++ if !flag(embed)+ CPP-Options :+ -DYUI_VERSION=3.5.1+ -DYUI_VERSION_STR="3.5.1"+ Other-Modules :+ Paths_happstack_yui+ Build-Depends :+ directory == 1.1.*+ else+ CPP-Options :+ -DYUI_VERSION=3.5.1+ -DYUI_VERSION_STR="3.5.1"+ -DEMBED+ Build-Depends :+ file-embed == 0.0.*
src/Happstack/Server/YUI.hs view
@@ -1,27 +1,35 @@ {-# LANGUAGE CPP, OverloadedStrings, TemplateHaskell, QuasiQuotes #-} module Happstack.Server.YUI- ( implYUISite- , bundle+ ( -- * Combo Handler+ implYUISite+ -- * CSS utilities+ , gridUnit+ , fontSize+ -- * Bundle utilities+ , isYUIFile+ , readYUIFile ) where import Prelude hiding ((.)) import qualified Data.ByteString as B-import qualified Data.Map as Map import qualified Data.Text as T import Control.Category (Category((.)))-import Control.Monad (liftM, void, mzero)+import Control.Monad (guard, liftM, void)+import Control.Monad.Trans (liftIO) import Data.List (intercalate)+import Data.Ratio ((%), numerator,denominator) import Data.Text.Encoding (encodeUtf8) import Happstack.Server (ServerPartT, Response, neverExpires, setHeaderM, badRequest, ok, toResponse, guessContentTypeM, mimeTypes, lookPairs) import Happstack.Server.Compression (compressedResponseFilter) import Happstack.Server.JMacro ()-import Happstack.Server.YUI.Bundle (bundle)+import Happstack.Server.YUI.Bundle (isYUIFile, readYUIFile) import Language.Javascript.JMacro (JStat(BlockStat), jmacro, renderJs, jhFromList, toJExpr) import Text.Boomerang.TH (derivePrinterParsers) import Text.PrettyPrint (Style(mode), Mode(OneLineMode), renderStyle, style)+import Text.Printf (printf) import Web.Routes (Site, RouteT, showURL) import Web.Routes.Boomerang (Router, (<>), (</>), rList, anyString, eos, boomerangSiteRouteT) import Web.Routes.Happstack (implSite)@@ -98,32 +106,96 @@ void compressedResponseFilter case url of BundleURL paths ->- do let filepath = intercalate "/" paths- mime <- guessContentTypeM mimeTypes filepath+ do let name = intercalate "/" paths+ exists <- liftIO $ isYUIFile name+ guard exists+ mime <- guessContentTypeM mimeTypes name setHeaderM "Content-Type" mime- maybe mzero (ok . toResponse) $ Map.lookup filepath bundle+ bytes <- liftIO $ readYUIFile name+ ok . toResponse $ bytes ComboHandlerURL -> do qs <- liftM (map fst) lookPairs- if null qs || any (`Map.notMember` bundle) qs+ exists <- liftIO $ mapM isYUIFile qs+ if null qs || any (== False) exists then badRequest $ toResponse () else do mime <- guessContentTypeM mimeTypes $ head qs setHeaderM "Content-Type" mime- ok $ toResponse $ B.concat $ map (bundle Map.!) qs+ bytes <- liftIO $ mapM readYUIFile qs+ ok $ toResponse $ B.concat bytes CSSComboURL -> do qs <- liftM (map (css . fst)) lookPairs- if null qs || any (`Map.notMember` bundle) qs+ exists <- liftIO $ mapM isYUIFile qs+ if null qs || any (== False) exists then badRequest $ toResponse () else do setHeaderM "Content-Type" "text/css"- ok $ toResponse $ B.concat $ map (bundle Map.!) qs+ bytes <- liftIO $ mapM readYUIFile qs+ ok $ toResponse $ B.concat bytes ConfigURL -> do config <- mkConfig ok $ toResponse config SeedURL -> do config <- mkConfig+ seed <- liftIO $ readYUIFile "yui/yui-min.js" setHeaderM "Content-Type" "application/javascript" ok $ toResponse $ seed `B.append` (encode . render) config where- seed = bundle Map.! "yui/yui-min.js" render = renderStyle (style { mode = OneLineMode }) . renderJs encode = encodeUtf8 . T.pack css fn = "css" ++ fn ++ "/css" ++ fn ++ "-min.css"++-- | Gets the class name for the grid unit of the ratio of the two argument+-- integers. YUI doesn't define redundant classes like \"6\/24\" because+-- that is the same as 1\/4 and presumably for sake of a smaller CSS file.+-- This helper function handles that for you, though:+--+-- >>> gridUnit 6 24+-- "yui3-u-1-4"+--+-- The intention is for this function to be used in templates to create+-- values for class attributes, for example with HSP:+--+-- ><div class=(gridUnit 6 24)>+-- > <% someContent %>+-- ></div>+gridUnit :: Integer -> Integer -> T.Text+gridUnit n d =+ T.concat [ "yui3-u-"+ , T.pack . show . numerator $ n % d+ , "-"+ , T.pack . show . denominator $ n % d+ ]++-- | Converts a pixel size to a percentage suitable for use+-- with the CSS fonts module:+--+-- >>> fontSize 16+-- "123.1%"+--+-- Useful in generated stylesheets, for example with HSP:+--+-- ><style>+-- > h1 { font-size: <% fontSize 26 %> }+-- ></style>+fontSize :: Integer -> T.Text+fontSize 10 = "77%"+fontSize 11 = "85%"+fontSize 12 = "93%"+fontSize 13 = "100%"+fontSize 14 = "108%"+fontSize 15 = "116%"+fontSize 16 = "123.1%"+fontSize 17 = "131%"+fontSize 18 = "138.5%"+fontSize 19 = "146.5%"+fontSize 20 = "153.9%"+fontSize 21 = "161.6%"+fontSize 22 = "167%"+fontSize 23 = "174%"+fontSize 24 = "182%"+fontSize 25 = "189%"+fontSize 26 = "197%"+fontSize px =+ T.pack . printf "%.1f%%" $ percentage+ where+ percentage :: Double+ percentage = fromIntegral px * (100 / 13)
src/Happstack/Server/YUI/Bundle.hs view
@@ -1,11 +1,41 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE CPP, TemplateHaskell #-} -module Happstack.Server.YUI.Bundle where+module Happstack.Server.YUI.Bundle+ ( isYUIFile+ , readYUIFile+ ) where import Data.ByteString (ByteString)-import Data.FileEmbed (embedDir)-import Data.Map (Map, fromList) --- | Maps the filenames of the bundled YUI build to their contents.+#if EMBED+import Data.FileEmbed (embedDir)+import Data.Map (Map, (!), fromList, member)+#else+import Paths_happstack_yui (getDataFileName)+import System.Directory (doesFileExist)+import qualified Data.ByteString as B+#endif++#if EMBED bundle :: Map FilePath ByteString bundle = fromList $(embedDir "bundle")+#endif++-- | Tells if a file is included in the YUI bundle.+--+-- >>> isYUIFile "yui/yui-min.js"+-- True+isYUIFile :: FilePath -> IO Bool+#if EMBED+isYUIFile name = return $ member name bundle+#else+isYUIFile name = getDataFileName ("bundle/" ++ name) >>= doesFileExist+#endif++-- | Reads the contents of a file included in the YUI bundle.+readYUIFile :: FilePath -> IO ByteString+#if EMBED+readYUIFile name = return $ bundle ! name+#else+readYUIFile name = getDataFileName ("bundle/" ++ name) >>= B.readFile+#endif