@@ -64,6 +64,8 @@ module Text.Pandoc.Shared (
6464 charsInBalanced,
6565 charsInBalanced',
6666 romanNumeral,
67+ emailAddress,
68+ uri,
6769 withHorizDisplacement,
6870 nullBlock,
6971 failIfStrict,
@@ -105,6 +107,7 @@ import Text.Pandoc.CharacterReferences ( characterReference )
105107import Data.Char ( toLower, toUpper, ord, isLower, isUpper )
106108import Data.List ( find, isPrefixOf )
107109import Control.Monad ( join )
110+import Network.URI ( parseURI, URI (..), isAllowedInURI )
108111
109112--
110113-- List processing
@@ -404,6 +407,38 @@ romanNumeral upperCase = do
404407 then fail "not a roman numeral"
405408 else return total
406409
410+-- Parsers for email addresses and URIs
411+
412+emailChar = alphaNum <|> oneOf "-+_."
413+
414+domainChar = alphaNum <|> char '-'
415+
416+domain = do
417+ first <- many1 domainChar
418+ dom <- many1 (try (do{ char '.'; many1 domainChar }))
419+ return $ joinWithSep "." (first:dom)
420+
421+-- | Parses an email address; returns string.
422+emailAddress :: GenParser Char st [Char]
423+emailAddress = try $ do
424+ firstLetter <- alphaNum
425+ restAddr <- many emailChar
426+ let addr = firstLetter:restAddr
427+ char '@'
428+ dom <- domain
429+ return $ addr ++ '@':dom
430+
431+-- | Parses a URI.
432+uri = try $ do
433+ str <- many1 $ satisfy isAllowedInURI
434+ case parseURI str of
435+ Just uri' -> if uriScheme uri' `elem` [ "http:", "https:", "ftp:",
436+ "file:", "mailto:",
437+ "news:", "telnet:" ]
438+ then return $ show uri'
439+ else fail "not a URI"
440+ Nothing -> fail "not a URI"
441+
407442-- | Applies a parser, returns tuple of its results and its horizontal
408443-- displacement (the difference between the source column at the end
409444-- and the source column at the beginning). Vertical displacement