REBOL [ Title: "REBOL browser" File: %browser.r Date: 2000-06-10 Author: "Ingo Hohmann" Email: ingo@2b1.de Site: http://www.2b1.de/ Rights: "(c) Ingo Hohmann" Purpose: {A simple WWW Browser} Based-upon: "Carls text-view" Category: [view VID networking] Changes: [ [ 0.0.4 {startmessage displayed correctly} {mailto: links removed from visitable links} ] [ 0.0.3 {some more errors corrected} ] [ 0.0.2 {translates   to " "} {evaluates } {prelimenary
 handling}
      {corrected some errors}
    ]
    [ 0.0.1
      {initial release (the unnumbered one)}
    ]
  ]
  Version: first first changes
  todo: [
    {use load-thru ?}
    {
 handling needs other font}
    {let buttons reflect presence of scripts}
    {some cleaning}
    {testing, of course}
  ]
]

browser: make object! [
  help: {The browser Object}
  
  start-url: form http://www.2b1.de/

  html: make object! [
    help: {A html parser}
    evaluate: false
    read-error: none
    skip: false
    spaces: charset " ^-^/"
    non-spaces: complement spaces
    delimiters: charset { ^-^/="} ;"
    non-delimiters: complement delimiters
    
    html-source: copy ""
    get-html: func [][ return html-source ]
    
    find-base: func [ url [url! file!] /local u2][
      if #"/" = last url [ return url ]
      if  exists? u2: to-url rejoin [ url "/" ] [
        return u2
      ]
      first split-path url
    ]
    
    conv-list: [ 
      "&" "&" "<" "<" ">" ">" """ {"} 
      "ä" "ä" "Ä" "Ä" "ö" "ö" "Ö" "Ö" "ü" "ü"
      "Ü" "Ü" "ß" "ß" " " " "]
    
    clean: func [
      {Converts html-entities to special-characters}
      text [string!]
      /local special entity
    ] [
      foreach [special entity] conv-list [
        replace/all text special entity
      ]
      text
    ]

    parse-tag: func [
      {parses a tag, returns block of tag-name arguments}
      tag /local tag-name tag-params] [
      name-rule: [ some non-delimiters ]
      param-rule: [
        any spaces [
          copy param-name name-rule (append tag-params param-name)
          any spaces [
            "=" any spaces [
              {"} copy param-val to {"} skip |
              {'} copy param-val to {'} skip |
              copy param-val some non-delimiters skip
            ]
            (append tag-params param-val) | (append tag-params true)
          ]
        ]
      ]
      tag-params: copy []
      parse/all tag [ copy tag-name name-rule any param-rule ]
      compose [ (tag-name) (tag-params) ]
    ]
    
    read: func [
      {read url and return the page as ...}
      url [url! file!]
      /html "html source" 
      /text "text" 
      /links "link-list"
      /local data txt lnk return-block
    ] [
      return-block: copy []
      either error? err: try [data: read url] [
        read-error: disarm err
      ] [
        read-error: none
        if html [ append return-block data ]
        if any [ txt links ] [
          set [txt lnk] to-text url data
          if text [ append return-block txt ]
          if links [ append/only return-block lnk ]
        ]
        print dir? url
      ]
    ]
    
    to-text: func [ 
      {Convert html to text, url is needed for handling of relative urls}
      url [url! file!] html [string!]
      /local elem txt links link lfd link-blk pos end-pos the-script script-funcs
    ] [
      script-funcs: make object! [
        print: func [val][ insert pos load/markup form join val newline ]
        prin: func [val][ insert pos load/markup form val ]
      ]
      url: find-base url
      links: copy []
      link-blk: copy []
      html-source: copy html
      html: load/markup html
      txt: make string! 500
      lfd: 0
      parse html [
        some [
          set elem string! (
            if not skip [
              if 0 < length? trim/lines elem [
                append txt rejoin [ elem " "]
              ]
            ]
          ) |
          pos: set elem tag! (
            elem: parse-tag elem
            switch first elem [
              "a" [
                lfd: lfd + 1
                append txt rejoin [ "(" lfd ")" ]
                elem: select elem "href"
                if elem [
                  if all [ not find elem "://" not find elem "mailto:"] [ ;  ???
                    elem: rejoin [ url elem ]
                  ]
                  
                  append links compose [ (lfd) (elem)]
                ]
              ]
              "img" [
                either elem: select elem "alt" [
                  append txt rejoin [ "[" elem "]" ]
                ] [
                  append txt "[graphic]"
                ]
              ]
              "p"  [append txt "^/^/"]
              "br" [append txt newline]
              "hr" [append txt "^/------------------------------------^/" ]
              "li" [append txt "^/* "]
              "ul" [append txt newline]
              "/ul" [append txt newline]
              "ol" [append txt newline]
              "/ol" [append txt newline]
              "div" [append txt newline]
              "/div" [append txt newline]
              "blockquote" [append txt newline]
              "/blockquote" [append txt newline]
              "style" [skip: true]
              "/style" [skip: false]
              "pre" [
                end-pos: find pos 
append txt rejoin [ newline copy/part next pos end-pos ] pos: end-pos ] "script" [ either all [ evaluate "rebol" = select elem "language" ] [ end-pos: find pos the-script: copy/part next pos end-pos remove/part pos next end-pos if error? err: try [ do bind load rejoin the-script in script-funcs 'print ] [ inform layout [ title red "Error in script !" text mold disarm err ] ] pos: back pos ][ skip: true ] ] ; do bind load rejoin n in t 'print "/script" [skip: false] "/title" [append txt "^/^/"] ] pos: next pos ) :pos ] ] txt: clean txt append txt "^/^/^/The links:^/-------^/^/" foreach [lfd link] links [ append txt rejoin [ lfd " " link newline]] foreach [lfd link] links [ if not find link "mailto:" [append link-blk rejoin [ lfd " " link]]] return compose/deep [ (txt) [(link-blk)]] ] ] size: 0x0 link-list: copy [] visited-links: copy [] ; Browsers GUI browser: layout [ backdrop effect [gradient -1x-1 0.200.0] title rejoin [ "Very basic WWW browser ... (Ver: " system/script/header/version ")"] box 656x4 effect [gradient 1x0 200.0.0] across space 6 text bold "File:" f1: field 516x24 start-url [ either all [ not error? try [ url: load f1/text ] any [ url? url file? url ] ] [ either not error? err: try [ txt: read url ] [ insert visited-links url set [txt link-list] html/to-text url txt t3/text: either length? txt [txt][none] show t3 size: size-text t3 c1/text: either empty? link-list ["no links found"]["Goto link"] show c1 true ][ inform layout [ title red "Error" text mold disarm err ] ] ] [ inform layout [ title red "Error" text rejoin [ "Not a valid url: " f1/text ] ] ] ] button "Load" #"^l" [ do f1/action none none] return indent 37 c1: button 516x24 #"^g" "No Links found" [ choose link-list func [f a] [f1/text: copy find f/text "http://" show f1] c1 ] button "Back" #"^b" [ remove visited-links if not empty? visited-links [ f1/text: first visited-links show f1 ] ] return space 0 t3: text 640x480 with [color: 250.250.250 ] s1: slider 16x480 [ t3/para/origin/y: s1/data - 1 * size/y / -480 + 2 show t3 ] return space 6 pad 0x6 button "broWse" #"^w" [ browse f1/text ] tab bEval: button "Evaluate" #"^e" [ html/evaluate: not html/evaluate bEval/text: either html/evaluate ["don't Eval"]["Evaluate"]] button "Run" #"^r" [ if error? err: try [ do load f1/text 'true ] [ inform layout [ title red rejoin ["Can't 'do " f1/text] text mold disarm err ] ] ] tab button "CloSE" #"^(ESC)" [ unview/all ] ] t3/text: trim/auto { Me little Rebol Browser ... ^/ Well, I just couldn't get the time to write a real Reblet, so I just settled to this one. You can type in an url, and press return. If the page contains further links, then the links will be displayed as a link-list, and can be choosen from the "Goto"-Button. ^^g goto ^^l load ^^w start WWW browser on url ESC close window Bugs: ^^b back (doesn't work yet) AND NOW PRESS LOAD ... } view browser ]