c4a664022ac8445c7482db7b7aee762d46b331e6
[gnus] / lisp / eww.el
1 ;;; eww.el --- Emacs Web Wowser
2
3 ;; Copyright (C) 2013 Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;; Keywords: html
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;;; Code:
26
27 (eval-when-compile (require 'cl))
28 (require 'shr)
29 (require 'url)
30
31 (defvar eww-current-url nil)
32 (defvar eww-history nil)
33
34 (defun eww (url)
35   "Fetch URL and render the page."
36   (interactive "sUrl: ")
37   (url-retrieve url 'eww-render (list url)))
38
39 (defun eww-render (status url &optional point)
40   (let* ((headers (eww-parse-headers))
41          (content-type
42           (mail-header-parse-content-type
43            (or (cdr (assoc "content-type" headers))
44                "text/plain")))
45          (charset (intern
46                    (downcase
47                     (or (cdr (assq 'charset (cdr content-type)))
48                         "utf8"))))
49          (data-buffer (current-buffer)))
50     (unwind-protect
51         (progn
52           (cond
53            ((equal (car content-type) "text/html")
54             (eww-display-html charset url))
55            ((string-match "^image/" (car content-type))
56             (eww-display-image))
57            (t
58             (eww-display-raw charset)))
59           (when point
60             (goto-char point)))
61       (kill-buffer data-buffer))))
62
63 (defun eww-parse-headers ()
64   (let ((headers nil))
65     (while (and (not (eobp))
66                 (not (eolp)))
67       (when (looking-at "\\([^:]+\\): *\\(.*\\)")
68         (push (cons (downcase (match-string 1))
69                     (match-string 2))
70               headers))
71       (forward-line 1))
72     (unless (eobp)
73       (forward-line 1))
74     headers))
75
76 (defun eww-display-html (charset url)
77   (unless (eq charset 'utf8)
78     (decode-coding-region (point) (point-max) charset))
79   (let ((document
80          (list
81           'base (list (cons 'href url))
82           (libxml-parse-html-region (point) (point-max)))))
83     (eww-setup-buffer)
84     (setq eww-current-url url)
85     (let ((inhibit-read-only t))
86       (shr-insert-document document))
87     (goto-char (point-min))))
88
89 (defun eww-display-raw (charset)
90   (let ((data (buffer-substring (point) (point-max))))
91     (eww-setup-buffer)
92     (let ((inhibit-read-only t))
93       (insert data))
94     (goto-char (point-min))))
95
96 (defun eww-display-image ()
97   (let ((data (buffer-substring (point) (point-max))))
98     (eww-setup-buffer)
99     (let ((inhibit-read-only t))
100       (shr-put-image data nil))
101     (goto-char (point-min))))
102
103 (defun eww-setup-buffer ()
104   (pop-to-buffer (get-buffer-create "*eww*"))
105   (let ((inhibit-read-only t))
106     (erase-buffer))
107   (eww-mode))
108
109 (defvar eww-mode-map
110   (let ((map (make-sparse-keymap)))
111     (suppress-keymap map)
112     (define-key map "q" 'eww-quit)
113     (define-key map [tab] 'widget-forward)
114     (define-key map [backtab] 'widget-backward)
115     (define-key map [delete] 'scroll-down-command)
116     (define-key map "\177" 'scroll-down-command)
117     (define-key map " " 'scroll-up-command)
118     (define-key map "p" 'eww-previous-url)
119     ;;(define-key map "n" 'eww-next-url)
120     map))
121
122 (defun eww-mode ()
123   "Mode for browsing the web.
124
125 \\{eww-mode-map}"
126   (interactive)
127   (setq major-mode 'eww-mode
128         mode-name "eww")
129   (set (make-local-variable 'eww-current-url) 'author)
130   (set (make-local-variable 'browse-url-browser-function) 'eww-browse-url)
131   (setq buffer-read-only t)
132   (use-local-map eww-mode-map))
133
134 (defun eww-browse-url (url &optional new-window)
135   (push (list eww-current-url (point))
136         eww-history)
137   (eww url))
138
139 (defun eww-quit ()
140   "Exit the Emacs Web Wowser."
141   (interactive)
142   (setq eww-history nil)
143   (kill-buffer (current-buffer)))
144
145 (defun eww-previous-url ()
146   "Go to the previously displayed page."
147   (interactive)
148   (when (zerop (length eww-history))
149     (error "No previous page"))
150   (let ((prev (pop eww-history)))
151     (url-retrieve (car prev) 'eww-render (list (car prev) (cadr prev)))))
152
153 (provide 'eww)
154
155 ;;; eww.el ends here