     ] map ;
 
 : find-by-id ( vector id -- vector' elt/f )
-    '[ attributes>> "id" swap at _ = ] find ;
+    '[ "id" attribute _ = ] find ;
     
 : find-by-class ( vector id -- vector' elt/f )
-    '[ attributes>> "class" swap at _ = ] find ;
+    '[ "class" attribute _ = ] find ;
 
 : find-by-name ( vector string -- vector elt/f )
     >lower '[ name>> _ = ] find ;
 
 : find-by-id-between ( vector string -- vector' )
     dupd
-    '[ attributes>> "id" swap at _ = ] find find-between* ;
+    '[ "id" attribute _ = ] find find-between* ;
     
 : find-by-class-between ( vector string -- vector' )
     dupd
-    '[ attributes>> "class" swap at _ = ] find find-between* ;
+    '[ "class" attribute _ = ] find find-between* ;
     
 : find-by-class-id-between ( vector class id -- vector' )
-    '[
-        [ attributes>> "class" swap at _ = ]
-        [ attributes>> "id" swap at _ = ] bi and
-    ] dupd find find-between* ;
+    [
+        '[
+            [ "class" attribute _ = ]
+            [ "id" attribute _ = ] bi and
+        ] find
+    ] [
+        2drop find-between*
+    ] 3bi ;
 
 : find-by-attribute-key ( vector key -- vector' elt/? )
     >lower

    link boa ;

: link. ( vector -- )
    [ attributes>> "href" swap at write nl ]
    [ clickable>> [ bl bl text>> print ] each nl ] bi ;

: find-by-text ( seq quot -- tag )
    [ dup name>> text = ] prepose find drop ; inline

: find-opening-tags-by-name ( name seq -- seq )
    [ [ name>> = ] [ closing?>> not ] bi and ] with find-all ;

: href-contains? ( str tag -- ? )
    attributes>> "href" swap at* [ subseq? ] [ 2drop f ] if ;

: find-hrefs ( vector -- vector' )
    find-links
    [ [
        [ name>> "a" = ]
        [ attributes>> "href" swap key? ] bi and ] filter
    ] map sift
    [ [ attributes>> "href" swap at ] map ] map concat
    [ >url ] map ;

: find-frame-links ( vector -- vector' )
    [ name>> "frame" = ] find-between-all
    [ [ attributes>> "src" swap at ] map sift ] map concat sift
    [ >url ] map ;

: find-all-links ( vector -- vector' )
    [ find-hrefs ] [ find-frame-links ] bi union ;

: find-forms ( vector -- vector' )
    "form" over find-opening-tags-by-name
    swap [ [ first2 ] dip find-between* ] curry map
    [ [ name>> { "form" "input" } member? ] filter ] map ;

: find-html-objects ( vector string -- vector' )
    dupd find-opening-tags-by-name
    [ first2 find-between* ] curry map ;

: form-action ( vector -- string )
    [ name>> "form" = ] find nip 
    attributes>> "action" swap at ;

: hidden-form-values ( vector -- strings )
    [ attributes>> "type" swap at "hidden" = ] filter ;

: input. ( tag -- )
    dup name>> print
    attributes>>
    [ bl bl bl bl [ write "=" write ] [ write bl ] bi* nl ] assoc-each ;

: form. ( vector -- )
    [ closing?>> not ] filter
    [
        {
            { [ dup name>> "form" = ]
                [ "form action: " write attributes>> "action" swap at print ] }
            { [ dup name>> "input" = ] [ input. ] }
            [ drop ]
        } cond
    ] each ;

: query>assoc* ( str -- hash )
    "?" split1 nip query>assoc ;
    
: html-class? ( tag string -- ? )
    swap attributes>> "class" swap at = ;
    
: html-id? ( tag string -- ? )
    swap attributes>> "id" swap at = ;

: opening-tag? ( tag -- ? )
    closing?>> not ;

! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors assocs combinators combinators.short-circuit
fry html.parser http.client io kernel locals math sequences
sets splitting unicode.case unicode.categories urls
urls.encoding ;
IN: html.parser.analyzer

: scrape-html ( url -- headers vector )
    http-get parse-html ;

: attribute ( tag string -- obj/f )
    swap attributes>> [ at ] [ drop f ] if* ;

: attribute* ( tag string -- obj ? )
    swap attributes>> [ at* ] [ drop f f ] if* ;

: attribute? ( tag string -- obj )
    swap attributes>> [ key? ] [ drop f ] if* ;

: find-all ( seq quot -- alist )
   [ <enum> >alist ] [ '[ second @ ] ] bi* filter ; inline

: loopn-index ( ... pred: ( ... n -- ... ? ) n -- ... )
    dup 0 > [
        [ swap call ] [ 1 - ] 2bi
        [ loopn-index ] 2curry when
    ] [
        2drop
    ] if ; inline recursive

: loopn ( ... pred: ( ... -- ... ? ) n -- ... )
    [ [ drop ] prepose ] dip loopn-index ; inline

:: find-nth ( n seq quot -- i/f elt/f )
    0 t [
        [ drop seq quot find-from ] dip 1 = [
            over [ [ 1 + ] dip ] when
        ] unless over >boolean
    ] n loopn-index ; inline

: find-first-name ( vector string -- i/f tag/f )
    >lower '[ name>> _ = ] find ; inline
