packages feed

text-and-plots 0.1.1.0 → 0.2.0.0

raw patch · 4 files changed

+175/−39 lines, 4 filesdep +textdep −aesonPVP ok

version bump matches the API change (PVP)

Dependencies added: text

Dependencies removed: aeson

API changes (from Hackage documentation)

+ Text.DocL: (/:) :: ToObj a => String -> a -> Prop
+ Text.DocL: javascript :: String -> Obj
+ Text.DocL.Javascript: (/:) :: ToObj a => String -> a -> Prop
+ Text.DocL.Javascript: class ToObj a where toObjList = List . map toObj
+ Text.DocL.Javascript: data Obj
+ Text.DocL.Javascript: data Prop
+ Text.DocL.Javascript: encode :: Obj -> Text
+ Text.DocL.Javascript: instance IsString Obj
+ Text.DocL.Javascript: instance Show Obj
+ Text.DocL.Javascript: instance ToObj ()
+ Text.DocL.Javascript: instance ToObj Bool
+ Text.DocL.Javascript: instance ToObj Char
+ Text.DocL.Javascript: instance ToObj Double
+ Text.DocL.Javascript: instance ToObj Int
+ Text.DocL.Javascript: instance ToObj Integer
+ Text.DocL.Javascript: instance ToObj Obj
+ Text.DocL.Javascript: instance ToObj Prop
+ Text.DocL.Javascript: instance ToObj Text
+ Text.DocL.Javascript: instance ToObj a => ToObj [a]
+ Text.DocL.Javascript: javascript :: String -> Obj
+ Text.DocL.Javascript: merge :: Obj -> Obj -> Obj
+ Text.DocL.Javascript: toObj :: ToObj a => a -> Obj
+ Text.DocL.Javascript: toObjList :: ToObj a => [a] -> Obj
- Text.DocL: col' :: ToJSON b => String -> (a -> b) -> Column a
+ Text.DocL: col' :: ToObj b => String -> (a -> b) -> Column a
- Text.DocL: plot' :: Foldable f => f a -> Column a -> [Column a] -> Value -> Doc
+ Text.DocL: plot' :: Foldable f => f a -> Column a -> [Column a] -> [Prop] -> Doc
- Text.DocL: rawPlot :: ToJSON a => a -> Doc
+ Text.DocL: rawPlot :: [Prop] -> Doc

Files

CHANGELOG view
@@ -1,2 +1,5 @@+- 0.2.0.0+  * Using the Text.DocL.Javascript module instead of aeson+    (since not everything we want to ship to C3.js is JSON encodable). - 0.1.1.0   * Bumped blaze-html dependency to <0.9.
src/Text/DocL.hs view
@@ -30,10 +30,11 @@   Column, col, col',   plot, plot', rawPlot,   -- * Utilities-  linspace+  linspace,+  -- * Re-exports from "Text.DocL.Javascript"+  (/:), javascript   ) where -import Data.Aeson                    (ToJSON, object, toJSON, (.=)) import Data.Foldable import Data.HashMap.Strict           (insert, unionWith) import Data.Monoid@@ -41,15 +42,15 @@ import Data.String                   (IsString (..)) import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.Blaze.Html5              ((!))+import Text.DocL.Javascript -import qualified Text.Markdown               as Markdown-import qualified Data.Aeson                  as Aeson+import qualified Data.ByteString.Lazy        as B import qualified System.IO                   as IO import qualified Text.Blaze.Html5            as H import qualified Text.Blaze.Html5.Attributes as A-import qualified Data.ByteString.Lazy        as B+import qualified Text.Markdown               as Markdown -data Node = Chart Aeson.Value | Html H.Html+data Node = Chart [Prop] | Html H.Html  -- | Base type representing a document. This type allows composition via the -- 'Monoid' instance.@@ -59,19 +60,14 @@ -- details. data Column a = Column   { _header  :: String-  , _extract :: a -> Aeson.Value+  , _extract :: a -> Obj   } --- | Deep merge of two 'Aeson.Value's. Used to merge in options in 'plot''.-merge :: Aeson.Value -> Aeson.Value -> Aeson.Value-merge (Aeson.Object x) (Aeson.Object y) = Aeson.Object $ unionWith merge x y-merge _ x = x- -- | Polymorphic version of 'col'. This allows, for instance, 'Int' and 'String' -- values to be used in plots. It is up to the caller to ensure that the values -- make sense to <http://c3js.org/ C3.js>.-col' :: ToJSON b => String -> (a -> b) -> Column a-col' h f = Column h (toJSON . f)+col' :: ToObj b => String -> (a -> b) -> Column a+col' h f = Column h (toObj . f)  -- | Data sets are supplied to 'plot' as a collection of elements of type /a/. -- This function sets up a 'Column', which knows how to extract one 'Double'@@ -95,38 +91,38 @@ -- Plots the data in /ds/ using the column /x/ for the values on the /x/-axis -- and with one line on the plot for each column in /ys/. See also 'col'. plot :: Foldable f => f a -> Column a -> [Column a] -> Doc-plot d x ys = plot' d x ys (Aeson.Object mempty)+plot d x ys = plot' d x ys [] --- | Same as 'plot', but takes a final argument which should be a "Data.Aeson"--- object. This is merged with the configuration object supplied to--- <http://c3js.org/ C3.js>. This allows the caller to customize the plot.+-- | Same as 'plot', but takes a final argument which is merged with the+-- configuration object supplied to <http://c3js.org/ C3.js>. This allows the+-- caller to customize the plot. -- -- @ --  -- Plot x² vs x with the points hidden. --  plot' [1..10] (col "x" id) [col "x²" $ \\x -> x*x] $---    object ["point" .= object ["show" .= False]]+--    ["point" //: ["show" //: False]] -- @ -- -- See <http://c3js.org/reference.html> for the many properties that can be used -- here.-plot' :: Foldable f => f a -> Column a -> [Column a] -> Aeson.Value -> Doc-plot' d x ys options = rawPlot $ merge obj options+plot' :: Foldable f => f a -> Column a -> [Column a] -> [Prop] -> Doc+plot' d x ys options = rawPlot $ obj ++ options   where-    obj = object-      [ "data" .= object-        [ "rows" .= (toJSON (_header x : map _header ys) : map f (toList d))-        , "x" .= _header x+    obj =+      [ "data" /:+        [ "rows" /: (toObj (_header x : map _header ys) : map f (toList d))+        , "x" /: _header x         ]       ]-    f p = toJSON $ map (`_extract` p) (x:ys)+    f p = toObj $ map (`_extract` p) (x:ys) --- | This function sends the supplied json object directly to+-- | This function sends an object with the supplied properties directly to -- <http://c3js.org/ C3.js>. This is the do-it-yourself option which exposes all -- of <http://c3js.org/ C3.js>. The object receives a -- <http://c3js.org/reference.html#bindto "bindto"> property, -- targeting a @\<div\>@ tag placed appropriately.-rawPlot :: Aeson.ToJSON a => a -> Doc-rawPlot = Doc . singleton . Chart . Aeson.toJSON+rawPlot :: [Prop] -> Doc+rawPlot = Doc . singleton . Chart  -- | Creates a 'Doc' representing raw html. html :: H.Html -> Doc@@ -162,15 +158,14 @@       H.body $         foldMap f (zip [(0::Int)..] $ toList doc)     f (_, (Html t)) = t-    f (i, (Chart v)) = divTag <> scriptTag+    f (i, (Chart props)) = divTag <> scriptTag       where         name = "plot" <> show i         divTag = H.div ! A.id (fromString name) $ mempty-        (Aeson.Object obj) = v-        obj' = insert "bindto" (fromString $ "#" <> name) obj-        json = H.unsafeLazyByteString $ Aeson.encode obj'+        obj = props ++ ["bindto" /: ("#" ++ name)]+        js = encode $ toObj obj         scriptTag = H.script ! A.type_ "text/javascript" $-          H.preEscapedToMarkup $ "c3.generate(" <> json <> ");"+          H.preEscapedToMarkup $ "\nc3.generate(" <> js <> ");\n"  -- | If the file exists, it will be overwritten. renderToFile :: FilePath -> Doc -> IO ()
+ src/Text/DocL/Javascript.hs view
@@ -0,0 +1,136 @@+module Text.DocL.Javascript (+-- * Rationale+-- | This is basically a JSON encoding library, however it goes beyond JSON by+-- allowing raw javascript to be used as a value. This allows, for instance,+-- function calls and anonymous functions to be placed in the data when used in+-- a javascript context. This is needed to expose some options in+-- <http://c3js.org/ C3.js>, e.g. customizing tick formatting.++-- * The 'Obj' type+  Obj,+  ToObj (..),+-- * Constructing javascript types+  Prop, (/:), javascript,+-- * Encoding+  encode,+-- * Miscellanea+  merge+  ) where++import Data.Char      (isControl, ord)+import Data.Foldable  hiding (elem)+import Data.List      (intersperse)+import Data.Monoid+import Data.String    (IsString (..))+import Data.Text.Lazy (Text)+import Text.Printf    (printf)++import qualified Data.HashMap.Strict    as Map+import qualified Data.Text.Lazy         as T+import qualified Data.Text.Lazy.Builder as B++-- | A key\/value pair created with the '/:'-operator.+--+-- A list of type ['Prop'] can be converted to an object using 'toObj'. If the+-- list contains duplicate keys, then 'merge' is used to combine the values.+--+-- >>> toObj ["legs" /: 4, "ears" /: "floppy"]+-- {"ears":"floppy","legs":4}+data Prop = Prop Text Obj++-- | Represents a javascript object or scalar value.+data Obj = Obj (Map.HashMap Text Obj) | List [Obj] | Scalar Text++instance IsString Obj where+  fromString = toObj++-- | /'show' x/ returns /x/ encoded.+instance Show Obj where+  show = T.unpack . encode++-- | Class of types that can be converted to an 'Obj'.+class ToObj a where+  -- | Converts to an 'Obj'.+  toObj :: a -> Obj++  -- | Only for writing instances. This function serves the same role as+  -- 'showList'+  toObjList :: [a] -> Obj+  toObjList = List . map toObj++-- | A ['Prop'] becomes a javascript object.+instance ToObj Prop where+  toObj x = toObj [x]+  toObjList = Obj . Map.fromListWith merge . map (\(Prop k v) -> (k, v))++-- | Becomes a javascript array.+instance ToObj a => ToObj [a] where+  toObj = toObjList++-- | Constructs a 'Prop'.+--+-- >>> toObj ["tail" /: True, "nose" /: "wet"]+-- {"nose":"wet","tail":true}+(/:) :: ToObj a => String -> a -> Prop+k /: v = Prop (fromString k) $ toObj v++-- | /'merge' x y/ returns /y/ unless both /x/ and /y/ are javascript objects,+-- in which case the properties of the two are merged using 'merge' recursively+-- for duplicate keys.+merge :: Obj -> Obj -> Obj+merge (Obj x) (Obj y) = Obj $ Map.unionWith merge x y+merge _ x = x++-- | Raw unescaped javascript.+--+-- >>> toObj ["speak" /: javascript "function(){alert('Woof!');}"]+-- {"speak":function(){alert('Woof!');}}+javascript :: String -> Obj+javascript = Scalar . fromString++-- | 'ToObj' is the identity for an 'Obj' value.+instance ToObj Obj where+  toObj = id++instance ToObj Double where+  -- This trivial implementation even handles NaN and Infinity correctly.+  toObj = Scalar . fromString . show+instance ToObj Int where+  toObj = Scalar . fromString . show+instance ToObj Integer where+  toObj = Scalar . fromString . show++-- | Becomes a javascript /null/ object+instance ToObj () where+  toObj () = Scalar "null"++instance ToObj Bool where+  toObj x = Scalar $ if x then "true" else "false"++-- | A 'String' becomes a javascript string literal.+instance ToObj Char where+  toObj c = toObj [c]+  toObjList xs = Scalar . B.toLazyText $ "\"" <> foldMap esc xs <> "\""+    where+      mustEscape = "\"\\>\n\r\x2028\x2029"+      esc x =+        if isControl x || elem x mustEscape+          then fromString . printf "\\u%04x" $ ord x+          else B.singleton x++-- | Becomes a javascript string literal.+instance ToObj Text where+  toObj = toObj . T.unpack++-- | Encode an 'Obj' to a lazy 'Text'.+encode :: Obj -> Text+encode = B.toLazyText . encode'++encode' :: Obj -> B.Builder+encode' obj = case obj of+    Scalar x -> B.fromLazyText x+    List xs  -> "[" <> commas (map encode' xs) <> "]"+    Obj m    -> "{" <> commas (map f $ Map.toList m) <> "}"+  where+    commas = fold . intersperse ","+    f (k, v) = encode' (toObj k) <> ":" <> encode' v
text-and-plots.cabal view
@@ -1,5 +1,5 @@ name:                text-and-plots-version:             0.1.1.0+version:             0.2.0.0 synopsis:   EDSL to create HTML documents with plots based on the C3.js library. description:@@ -8,6 +8,7 @@   <https://github.com/andersjel/haskell-text-and-plots github page> for an   introduction. homepage:            https://github.com/andersjel/haskell-text-and-plots+bug-reports:         https://github.com/andersjel/haskell-text-and-plots/issues license:             MIT license-file:        LICENSE author:              Anders Jellinggaard@@ -16,7 +17,7 @@ category:            Text build-type:          Simple cabal-version:       >=1.10-Extra-source-files:+extra-source-files:   CHANGELOG  source-repository head@@ -26,19 +27,20 @@ source-repository this   type:     git   location: git://github.com/andersjel/haskell-text-and-plots.git-  tag:      v0.1.1.0+  tag:      v0.2.0.0  library   exposed-modules:     Text.DocL+    Text.DocL.Javascript   build-depends:-    aeson                >=0.8.0.0  && <0.9,     base                 >=4.7.0.0  && <4.8,     blaze-html           >=0.7.0.0  && <0.9,     bytestring           >=0.10.4.0 && <0.11,     containers           >=0.5.5.1  && <0.6,     markdown             >=0.1.7.1  && <0.2,-    unordered-containers >=0.2.3.3  && <0.3+    unordered-containers >=0.2.3.3  && <0.3,+    text                 >=1.2.0.2  && <1.3   hs-source-dirs:      src   default-language:    Haskell2010   default-extensions: