) -<< ()
 
                 returnA -< do let lastMod = toClockTime $ pageLastMod page
-
-                              case pageRevision page of
-                                Nothing  -> foundTimeStamp lastMod
-                                Just rev -> foundEntity (strongETag $ show rev) lastMod
+                              
+                              -- text/x-rakka の場合は、内容が動的に生
+                              -- 成されてゐる可能性があるので、ETag も
+                              -- Last-Modified も返す事が出來ない。
+                              case pageType page of
+                                MIMEType "text" "x-rakka" _
+                                    -> return ()
+                                _   -> case pageRevision page of
+                                         Nothing  -> foundTimeStamp lastMod
+                                         Just rev -> foundEntity (strongETag $ show rev) lastMod
 
                               outputXmlPage tree entityToXHTML
 
 
 
     , CommandType(..)
     , Attribute
+    , BlockCommand(..)
     , InlineCommand(..)
     )
     where
     | DefinitionList ![Definition]
     | Preformatted ![InlineElement]
     | Paragraph ![InlineElement]
+    | Div ![Attribute] ![BlockElement]
+    | BlockCmd !BlockCommand
     deriving (Eq, Show)
 
 
       , linkText     :: !(Maybe String)
       }
     | LineBreak ![Attribute]
+    | Span ![Attribute] ![InlineElement]
     | InlineCmd !InlineCommand
     deriving (Eq, Show)
 
 type Attribute = (String, String)
 
 
+data BlockCommand
+    = BlockCommand {
+        bCmdName       :: !String
+      , bCmdAttributes :: ![Attribute]
+      , bCmdContents   :: ![BlockElement]
+      }
+    deriving (Eq, Show)
+
+
 data InlineCommand
     = InlineCommand {
         iCmdName       :: !String
 
 interpretCommands env table page blocks = mapM interpBlock blocks
     where
       interpBlock :: BlockElement -> IO BlockElement
-      interpBlock (List           list) = interpList list >>= return . List
+      interpBlock (List list)           = interpList list >>= return . List
       interpBlock (DefinitionList defs) = mapM interpDefinition defs >>= return . DefinitionList
-      interpBlock (Preformatted   xs  ) = mapM interpInline xs >>= return . Preformatted
-      interpBlock (Paragraph      xs  ) = mapM interpInline xs >>= return . Paragraph
+      interpBlock (Preformatted xs)     = mapM interpInline xs >>= return . Preformatted
+      interpBlock (Paragraph xs)        = mapM interpInline xs >>= return . Paragraph
+      interpBlock (Div attrs xs)        = mapM interpBlock xs >>= return . Div attrs
+      interpBlock (BlockCmd cmd)        = interpBlockCmd cmd
       interpBlock others                = return others
 
       interpList :: ListElement -> IO ListElement
                                 desc <- mapM interpInline (defDesc def)
                                 return $ def { defTerm = term, defDesc = desc }
 
+      interpBlockCmd :: BlockCommand -> IO BlockElement
+      interpBlockCmd cmd
+          = case M.lookup (bCmdName cmd) table of
+              Nothing
+                  -> fail ("no such interpreter: " ++ bCmdName cmd)
+
+              Just interp
+                  -> bciInterpret interp cmd page (envStorage env) (envSysConf env)
+                     >>=
+                     interpBlock
+
       interpInline :: InlineElement -> IO InlineElement
-      interpInline (Italic    xs ) = mapM interpInline xs >>= return . Italic
-      interpInline (Bold      xs ) = mapM interpInline xs >>= return . Bold
+      interpInline (Italic xs)     = mapM interpInline xs >>= return . Italic
+      interpInline (Bold xs )      = mapM interpInline xs >>= return . Bold
+      interpInline (Span attrs xs) = mapM interpInline xs >>= return . Span attrs
       interpInline (InlineCmd cmd) = interpInlineCmd cmd
       interpInline others          = return others
 
 
          Paragraph inlines
              -> formatParagraph -< (baseURI, inlines)
 
+         Div attrs contents
+             -> ( eelem "div"
+                  += ( arrL (fst . snd)
+                       >>>
+                       attrFromPair
+                     )
+                  += ( (arr fst &&& arrL (snd . snd))
+                       >>>
+                       formatBlock
+                     )
+                ) -< (baseURI, (attrs, contents))
+
 
 formatHeading :: ArrowXml a => a (Int, String) XmlTree
 formatHeading 
     = proc (level, text)
-    -> selem ("h" ++ show level) [txt text] -<< ()
+    -> mkelem ("h" ++ show level)
+       [ sattr "id" text ]
+       [ txt text        ] -<< ()
 
 
 formatListElement :: (ArrowXml a, ArrowChoice a) => a (URI, ListElement) XmlTree
                   += (arrL id >>> attrFromPair)
                 ) -< attrs
 
+         Span attrs contents
+             -> ( eelem "span"
+                  += ( arrL (fst . snd)
+                       >>>
+                       attrFromPair
+                     )
+                  += ( (arr fst &&& arrL (snd . snd))
+                       >>>
+                       formatInline
+                     )
+                ) -< (baseURI, (attrs, contents))
+
 
 attrFromPair :: (ArrowXml a) => a (String, String) XmlTree
 attrFromPair = proc (name, value)
 
     , commandType -- private
 
     , pureInlineInterp
+    , pureBlockInterp
     )
     where
 
                      -> SystemConfig
                      -> IO InlineElement
       }
+    | BlockCommandInterpreter {
+        bciName      :: String
+      , bciInterpret :: BlockCommand
+                     -> Maybe Page
+                     -> Storage
+                     -> SystemConfig
+                     -> IO BlockElement
+      }
 
 
 commandName :: Interpreter -> String
 commandName (InlineCommandInterpreter name _) = name
+commandName (BlockCommandInterpreter  name _) = name
 
 
 commandType :: Interpreter -> CommandType
 commandType (InlineCommandInterpreter _ _) = InlineCommandType
+commandType (BlockCommandInterpreter  _ _) = BlockCommandType
 
 
 pureInlineInterp :: String
                  -> Interpreter
 pureInlineInterp name f
     = InlineCommandInterpreter name $ \ cmd page _ _ -> return $ f cmd page
+
+
+pureBlockInterp :: String
+                -> (BlockCommand -> Maybe Page -> BlockElement)
+                -> Interpreter
+pureBlockInterp name f
+    = BlockCommandInterpreter name $ \ cmd page _ _ -> return $ f cmd page
 
 
 
 baseInterpreters :: [Interpreter]
-baseInterpreters = [ lineBreakInterp ]
+baseInterpreters = [ lineBreakInterp
+                   , spanInterp
+                   , divInterp
+                   ]
 
 
 lineBreakInterp :: Interpreter
 lineBreakInterp = pureInlineInterp "br" interpret
     where
       interpret (InlineCommand _ attrs _) _ = LineBreak attrs
+
+
+spanInterp :: Interpreter
+spanInterp = pureInlineInterp "span" interpret
+    where
+      interpret (InlineCommand _ attrs contents) _ = Span attrs contents
+
+
+divInterp :: Interpreter
+divInterp = pureBlockInterp "div" interpret
+    where
+      interpret (BlockCommand _ attrs contents) _ = Div attrs contents
\ No newline at end of file
 
 
 wikiPage :: CommandTypeOf -> Parser WikiPage
 wikiPage cmdTypeOf
-    = do xs <- many $ try (blockElement cmdTypeOf)
+    = do xs <- many (blockElement cmdTypeOf)
          skipMany ( comment
                     <|>
                     (newline >> return ())
 
 blockElement :: CommandTypeOf -> Parser BlockElement
 blockElement cmdTypeOf
-    = skipMany ( comment
-                 <|>
-                 (newline >> return ())
-               )
-      >>
-      ( foldr (<|>) pzero [ heading
-                          , horizontalLine
-                          , listElement cmdTypeOf
-                          , definitionList cmdTypeOf
-                          , pdata
-                          , leadingSpaced cmdTypeOf
-                          , paragraph cmdTypeOf
-                          ]
-      )
+    = try $ do skipMany ( comment
+                          <|>
+                          (newline >> return ())
+                        )
+               foldr (<|>) pzero [ heading
+                                 , horizontalLine
+                                 , listElement cmdTypeOf
+                                 , definitionList cmdTypeOf
+                                 , pdata
+                                 , leadingSpaced cmdTypeOf
+                                 , paragraph cmdTypeOf
+                                 , blockCmd cmdTypeOf
+                                 ]
 
 
 heading :: Parser BlockElement
                             <|>
                             try ( do newline
                                      ((oneOf ('\n':blockSymbols) >> pzero) <|> return ())
-                                     ((blockCommand              >> pzero) <|> return ())
                                      ys <- (paragraph' <|> return [])
                                      return (Text "\n" : ys)
-                                  -- \n があり、その次に \n、ブロックタ
-                                  -- グまたは blockSymbols があれば、
-                                  -- fail して 最初の newline を讀んだ
-                                  -- æ\89\80ã\81¾ã\81§å\8d·ã\81\8dæ\88»ã\81\99ã\80\82
+                                  -- \n があり、その次に \n または
+                                  -- blockSymbols があれば、fail して
+                                  -- 最初の newline を讀んだ所まで卷き
+                                  -- 戻す。
                                 )
                             <|>
-                            try paragraph'
+                            paragraph'
                             -- それ以外の場合は次の inlineElement から
                             -- を讀んで見る。但し一つも無くても良い。
                             <|>
                       return (x:xs)
 
 
+blockCmd :: CommandTypeOf -> Parser BlockElement
+blockCmd cmdTypeOf
+    = (try $ do (tagName, tagAttrs) <- openTag
+                case cmdTypeOf tagName of
+                  Just BlockCommandType
+                      -> do xs <- contents
+                            closeTag tagName
+                            return $ BlockCmd $ BlockCommand {
+                                         bCmdName       = tagName
+                                       , bCmdAttributes = tagAttrs
+                                       , bCmdContents   = xs
+                                       }
+                  _   -> pzero
+      )
+      <|>
+      (try $ do (tagName, tagAttrs) <- emptyTag
+                case cmdTypeOf tagName of
+                  Just BlockCommandType
+                      -> return $ BlockCmd $ BlockCommand {
+                                         bCmdName       = tagName
+                                       , bCmdAttributes = tagAttrs
+                                       , bCmdContents   = []
+                                       }
+                  _   -> pzero
+      )
+      <?>
+      "block command"
+    where
+      contents :: Parser [BlockElement]
+      contents = do x  <- try $ blockElement cmdTypeOf
+                    xs <- contents
+                    return (x:xs)
+                 <|>
+                 (comment >> contents)
+                 <|>
+                 return []
+
+
 inlineElement :: CommandTypeOf -> Parser InlineElement
 inlineElement cmdTypeOf
     = try $ do skipMany comment
                             return $ InlineCmd $ InlineCommand {
                                          iCmdName       = tagName
                                        , iCmdAttributes = tagAttrs
-                                       , iCmdContents   =  xs
+                                       , iCmdContents   = xs
                                        }
                   _   -> pzero
       )
                                        }
                   _   -> pzero
       )
+      <?>
+      "inline command"
     where
       contents :: Parser [InlineElement]
       contents = do x  <- inlineElement cmdTypeOf
 
 
 blockSymbols :: [Char]
-blockSymbols = " =-*#;"
+blockSymbols = " =-*#;<"
 
 
 inlineSymbols :: [Char]
 
       type="text/x-rakka"
       isBoring="yes">
   <textData>= Main Page =
-This  is  the    main  page.<br />
+This  is  the    main  page.
 Hello, world!
 
 Another paragraph...
            preformatted
       text.
     [[Foo|Wiki markup is interpreted here.]]
+
+== Listing ==
+* foo
+** bar
+*** baz
+
+# foo
+## bar
+### baz
+
+* foo
+*# bar
+*#* baz
+*# bar
+
+== Link ==
+* [[Page]]
+* [[page]]
+* [[space in a page name]]
+* [[Page|Link to "Page"]]
+* [[Page#Heading]]
+* [[#Main Page]]
+* [[Page#Heading|Link to "Page#Heading"]]
+* [[#example]]
+
+<div id="example" />
 </textData>
 </page>
 
                                               , Text "\n"
                                               , Text "bar"
                                               ]) ] ]))
+
+           , (parseWiki "<div>foo</div>"
+              ~?=
+              (Right [ BlockCmd (BlockCommand "div" []
+                                 [ Paragraph [Text "foo"] ]) ]))
+
+           , (parseWiki "<div><!-- comment --></div>"
+              ~?=
+              (Right [ BlockCmd (BlockCommand "div" [] []) ]))
+
+           , (parseWiki "foo<div id=\"bar\"/>"
+              ~?=
+              (Right [ Paragraph [Text "foo"]
+                     , BlockCmd (BlockCommand "div" [("id", "bar")] [])
+                     ]))
            ]