PDA

View Full Version : DOM createElement/appendChild/etc generator?



jlizarraga
10-24-2008, 08:55 PM
Hi all,

Does anyone know of a generator that will take markup input like so:


<div>
<p>
Hello world!
</p>
</div>

And output it like this?


oDiv = document.createElement("div");
oP = document.createElement("p");
oText = document.createTextNode("Hello world!");

oP.appendChild(oText);
oDiv.appendChild(oP);

Sure would save some time!

magicyte
10-24-2008, 10:05 PM
I have never seen one before. If it were to be created, someone would have probably used C/C++ or Java for a compiled program. I don't think so... By the way: your JS code is bad: you need var before those variables.

Now, it may be possible for someone to make a JavaScript that will read an HTML file as XML (XHTML) to count the tags, their names, and their values. Maybe, just maybe...

-magicyte

Trinithis
10-25-2008, 09:02 AM
Here's some Haskell code that does the job. I won't claim that it will parse any HTML, but it should be adequate. Also, my code does not check for legal HTML. That's your job. I did a little testing, but there may be bugs in it. If you find any or have any comments/suggestions just post a reply.


Main.hs


module Main (
) where

import Prelude hiding ((.))

import System.Environment (getArgs)

import ApplicativeParsec

import Control.Monad.State

import Data.List (intercalate)
import Data.Char (toLower, toUpper)

-----------------------------------------------------------

main :: IO ()
main = do
args <- getArgs
case args of
[] -> putStrLn "use: htmlToDom inputFile [outputFile]"
file : dest -> let
out = case dest of
[] -> putStrLn
file' : _ -> writeFile file'
in readFile file >>= out . either show show . parse (spaces *> tag) ""

-----------------------------------------------------------

infixr 9 .
(.) :: (Functor f) => (a -> b) -> f a -> f b
(.) = fmap

mapFst :: (a -> b) -> (a, c) -> (b, c)
mapFst f (x, y) = (f x, y)

compose :: [a -> a] -> (a -> a)
compose = foldr (.) id

-----------------------------------------------------------

type Name = String
type Attribute = (Name, String)
type Text = String

data Tag = Tag Name [Attribute] [Either Tag Text]

instance Show Tag where
showsPrec _ tag = snd $ evalState (showsTag tag) 0
where
ss = showString
showsTag (Tag name attrs cs) = do
num <- get
modify (+1)
(cIdents, innerDefs) <- unzip . mapM showsChild cs
let ident = '$' : show num
decl = ss "var " . ss ident . ss " = document.createElement(" . shows name . ss ");\n"
appendChildren = compose $ map (
\cIdent -> let
arg = either ss (\txt -> ss "document.createTextNode(" . shows txt . ss ")") cIdent
in ss ident . ss ".appendChild(" . arg . ss ");\n"
) cIdents
setAttrs = compose $ map (
\(name, val) -> ss ident . ss ".setAttribute(" . shows name . ss ", " . shows val . ss ");\n"
) attrs
return (ident, compose innerDefs . decl . setAttrs . appendChildren)
showsChild (Left tag) = mapFst Left . showsTag tag
showsChild (Right txt) = return (Right txt, id)

-----------------------------------------------------------

ws :: [Char]
ws = " \v\f\t\r\n"

optionalWs :: CharParser st String
optionalWs = option "" $ (: []) . oneOf ws

ichar :: Char -> CharParser st Char
ichar c = oneOf [toUpper c, toLower c]

istring :: String -> CharParser st String
istring = mapM ichar

brackets :: CharParser st a -> CharParser st a
brackets p = char '<' *> p <* char '>'

ident :: CharParser st Name
ident = many1 alphaNum <* spaces

tag :: CharParser () Tag
tag = do
(tagName, attrs, slash) <- brackets $ liftM3 (,,) ident attrs $ not . null . option "" (string "/")
let mkTag = return . Tag tagName attrs
if slash
then mkTag []
else (children >>= mkTag) <* istring tagName <* char '>'
where
children = manyTill (Left . tag <|> Right . text) $ try $ string "</"

text :: CharParser st Text
text = do
space1 <- optionalWs
spaces
text <- intercalate " " . sepBy (many (noneOf $ '<' : ws)) (many1 space)
space2 <- optionalWs
spaces
return $ space1 ++ text ++ space2

attr :: CharParser st Attribute
attr = do
name <- many1 alphaNum
char '='
quote <- oneOf "'\""
value <- manyTill anyChar $ char quote
return (name, value)

attrs :: CharParser st [Attribute]
attrs = sepEndBy (try attr) (many1 space)


ApplicativeParsec.hs


module ApplicativeParsec (
module Control.Applicative
, module Text.ParserCombinators.Parsec
) where

import Control.Applicative
import Control.Monad (MonadPlus(..), ap)
import Text.ParserCombinators.Parsec hiding (many, optional, (<|>))

instance Applicative (GenParser s a) where
pure = return
(<*>) = ap

instance Alternative (GenParser s a) where
empty = mzero
(<|>) = mplus

codeexploiter
10-25-2008, 09:28 AM
I think you are looking for something like this:

http://ejohn.org/blog/pure-javascript-html-parser/

Hope this helps.

jscheuer1
10-25-2008, 10:01 AM
See:

http://www.dynamicdrive.com/forums/showthread.php?p=52620#post52620

Works best in FF, has some limitations. Good for most short snippets. Can handle most complex HTML markup if it is valid.

magicyte
10-26-2008, 03:11 AM
That is an ASTONISHING script!! I wonder how long it took for you to make it. Could you explain how you identified the HTML elements? I have an idea, but I am not so sure: did you split the strings between '<' and '>'? Just REALLY wondering...

:D

-magicyte

jscheuer1
10-26-2008, 08:33 AM
The main job of writing this only took a number of hours, not sure how many, as it was some time ago. As with any complex script I write though, the process generally takes a number of days because I need to break it into phases and cannot always just switch gears at the drop of a hat to handle each phase (conception, writing, testing, etc.). I identified the HTML elements by their tagName property. If I were to write such a script today, I might prefer to use the nodeName property, though I would want to test out how that would work before committing to it. A well annotated version of the code (in two parts) starts here:

http://www.dynamicdrive.com/forums/showthread.php?p=52536&highlight=annotated+version#post52536

Twey
10-26-2008, 11:37 AM
If you're doing this, probably you should consider revising your script. I generally find it useful to simply clone the appropriate structure from a hidden node in the HTML. It's faster and easier.