30 instead of 10. Python tutorial looks better.
[elinks.git] / contrib / python / hooks.py
blob5e1c60ed6c7d4e95e4303e1a8ab7875374b87df5
1 """Example Python hooks for ELinks.
3 If ELinks is compiled with an embedded Python interpreter, it will try
4 to import a Python module called hooks when the browser starts up. To
5 use Python code from within ELinks, create a file called hooks.py in
6 the ~/.elinks directory, or in the system-wide configuration directory
7 (defined when ELinks was compiled), or in the standard Python search path.
8 An example hooks.py file can be found in the contrib/python directory of
9 the ELinks source distribution.
11 The hooks module may implement any of several functions that will be
12 called automatically by ELinks in appropriate circumstances; it may also
13 bind ELinks keystrokes to callable Python objects so that arbitrary Python
14 code can be invoked at the whim of the user.
16 Functions that will be automatically called by ELinks (if they're defined):
18 follow_url_hook() -- Rewrite a URL for a link that's about to be followed.
19 goto_url_hook() -- Rewrite a URL received from a "Go to URL" dialog box.
20 pre_format_html_hook() -- Rewrite a document's body before it's formatted.
21 proxy_for_hook() -- Determine what proxy server to use for a given URL.
22 quit_hook() -- Clean up before ELinks exits.
24 """
26 import elinks
28 dumbprefixes = {
29 "7th" : "http://7thguard.net/",
30 "b" : "http://babelfish.altavista.com/babelfish/tr/",
31 "bz" : "http://bugzilla.elinks.cz/",
32 "bug" : "http://bugzilla.elinks.cz/",
33 "d" : "http://www.dict.org/",
34 "g" : "http://www.google.com/",
35 "gg" : "http://www.google.com/",
36 "go" : "http://www.google.com/",
37 "fm" : "http://www.freshmeat.net/",
38 "sf" : "http://www.sourceforge.net/",
39 "dbug" : "http://bugs.debian.org/",
40 "dpkg" : "http://packages.debian.org/",
41 "pycur" : "http://www.python.org/doc/current/",
42 "pydev" : "http://www.python.org/dev/doc/devel/",
43 "pyhelp" : "http://starship.python.net/crew/theller/pyhelp.cgi",
44 "pyvault" : "http://www.vex.net/parnassus/",
45 "e2" : "http://www.everything2.org/",
46 "sd" : "http://www.slashdot.org/"
49 def goto_url_hook(url):
50 """Rewrite a URL that was entered in a "Go to URL" dialog box.
52 This function should return a string containing a URL for ELinks to
53 follow, or an empty string if no URL should be followed, or None if
54 ELinks should follow the original URL.
56 Arguments:
58 url -- The URL provided by the user.
60 """
61 if url in dumbprefixes:
62 return dumbprefixes[url]
64 def follow_url_hook(url):
65 """Rewrite a URL for a link that's about to be followed.
67 This function should return a string containing a URL for ELinks to
68 follow, or an empty string if no URL should be followed, or None if
69 ELinks should follow the original URL.
71 Arguments:
73 url -- The URL of the link.
75 """
76 google_redirect = 'http://www.google.com/url?sa=D&q='
77 if url.startswith(google_redirect):
78 return url.replace(google_redirect, '')
80 def pre_format_html_hook(url, html):
81 """Rewrite the body of a document before it's formatted.
83 This function should return a string for ELinks to format, or None
84 if ELinks should format the original document body. It can be used
85 to repair bad HTML, alter the layout or colors of a document, etc.
87 Arguments:
89 url -- The URL of the document.
90 html -- The body of the document.
92 """
93 if "cygwin.com" in url:
94 return html.replace('<body bgcolor="#000000" color="#000000"',
95 '<body bgcolor="#ffffff" color="#000000"')
96 elif url.startswith("https://www.mbank.com.pl/ib_navibar_3.asp"):
97 return html.replace('<td valign="top"><img',
98 '<tr><td valign="top"><img')
99 elif url.startswith("http://lp3.polskieradio.pl/"):
100 from lp3 import lp3
101 return lp3(html)
104 def proxy_for_hook(url):
105 """Determine what proxy server to use for a given URL.
107 This function should return a string of the form "hostname:portnumber"
108 identifying a proxy server to use, or an empty string if the request
109 shouldn't use any proxy server, or None if ELinks should use its
110 default proxy server.
112 Arguments:
114 url -- The URL that is about to be followed.
117 pass
119 def quit_hook():
120 """Clean up before ELinks exits.
122 This function should handle any clean-up tasks that need to be
123 performed before ELinks exits. Its return value is ignored.
126 pass
129 # The rest of this file demonstrates some of the functionality available
130 # within hooks.py through the embedded Python interpreter's elinks module.
131 # Full documentation for the elinks module (as well as the hooks module)
132 # can be found in doc/python.txt in the ELinks source distribution.
135 # This class shows how to use elinks.input_box() and elinks.open(). It
136 # creates a dialog box to prompt the user for a URL, and provides a callback
137 # function that opens the URL in a new tab.
139 class goto_url_in_new_tab:
140 """Prompter that opens a given URL in a new tab."""
141 def __init__(self):
142 """Prompt for a URL."""
143 elinks.input_box("Enter URL", self._callback, title="Go to URL")
144 def _callback(self, url):
145 """Open the given URL in a new tab."""
146 if 'goto_url_hook' in globals():
147 # Mimic the standard "Go to URL" dialog by calling goto_url_hook().
148 url = goto_url_hook(url) or url
149 if url:
150 elinks.open(url, new_tab=True)
151 # The elinks.bind_key() function can be used to create a keystroke binding
152 # that will instantiate the class.
154 elinks.bind_key("Ctrl-g", goto_url_in_new_tab)
157 # This class can be used to enter arbitrary Python expressions for the embedded
158 # interpreter to evaluate. (Note that it can only evalute Python expressions,
159 # not execute arbitrary Python code.) The callback function will use
160 # elinks.info_box() to display the results.
162 class simple_console:
163 """Simple console for passing expressions to the Python interpreter."""
164 def __init__(self):
165 """Prompt for a Python expression."""
166 elinks.input_box("Enter Python expression", self._callback, "Console")
167 def _callback(self, input):
168 """Evalute input and display the result (unless it's None)."""
169 if input is None:
170 # The user canceled.
171 return
172 result = eval(input)
173 if result is not None:
174 elinks.info_box(result, "Result")
175 elinks.bind_key("F5", simple_console)
178 # If you edit ~/.elinks/hooks.py while the browser is running, you can use
179 # this function to make your changes take effect immediately (without having
180 # to restart ELinks).
182 def reload_hooks():
183 """Reload the ELinks Python hooks."""
184 import hooks
185 reload(hooks)
186 elinks.bind_key("F6", reload_hooks)
189 # This example demonstrates how to create a menu by providing a sequence of
190 # (string, function) tuples specifying each menu item.
192 def menu():
193 """Let the user choose from a menu of Python functions."""
194 items = (
195 ("~Go to URL in new tab", goto_url_in_new_tab),
196 ("Simple Python ~console", simple_console),
197 ("~Reload Python hooks", reload_hooks),
198 ("Read my favorite RSS/ATOM ~feeds", feedreader),
200 elinks.menu(items)
201 elinks.bind_key("F4", menu)
204 # Finally, a more elaborate demonstration: If you install the add-on Python
205 # module from http://feedparser.org/ this class can use it to parse a list
206 # of your favorite RSS/ATOM feeds, figure out which entries you haven't seen
207 # yet, open each of those entries in a new tab, and report statistics on what
208 # it fetched. The class is instantiated by pressing the "!" key.
210 # This class demonstrates the elinks.load() function, which can be used to
211 # load a document into the ELinks cache without displaying it to the user;
212 # the document's contents are passed to a Python callback function.
214 my_favorite_feeds = (
215 "http://rss.gmane.org/messages/excerpts/gmane.comp.web.elinks.user",
216 # ... add any other RSS or ATOM feeds that interest you ...
217 # "http://elinks.cz/news.rss",
218 # "http://www.python.org/channews.rdf",
221 class feedreader:
223 """RSS/ATOM feed reader."""
225 def __init__(self, feeds=my_favorite_feeds):
226 """Constructor."""
227 if elinks.home is None:
228 raise elinks.error("Cannot identify unread entries without "
229 "a ~/.elinks configuration directory.")
230 self._results = {}
231 self._feeds = feeds
232 for feed in feeds:
233 elinks.load(feed, self._callback)
235 def _callback(self, header, body):
236 """Read a feed, identify unseen entries, and open them in new tabs."""
237 import anydbm
238 import feedparser # you need to get this module from feedparser.org
239 import os
241 if not body:
242 return
243 seen = anydbm.open(os.path.join(elinks.home, "rss.seen"), "c")
244 feed = feedparser.parse(body)
245 new = 0
246 errors = 0
247 for entry in feed.entries:
248 try:
249 if not seen.has_key(entry.link):
250 elinks.open(entry.link, new_tab=True)
251 seen[entry.link] = ""
252 new += 1
253 except:
254 errors += 1
255 seen.close()
256 self._tally(feed.channel.title, new, errors)
258 def _tally(self, title, new, errors):
259 """Record and report feed statistics."""
260 self._results[title] = (new, errors)
261 if len(self._results) == len(self._feeds):
262 feeds = self._results.keys()
263 feeds.sort()
264 width = max([len(title) for title in feeds])
265 fmt = "%*s new entries: %2d errors: %2d\n"
266 summary = ""
267 for title in feeds:
268 new, errors = self._results[title]
269 summary += fmt % (-width, title, new, errors)
270 elinks.info_box(summary, "Feed Statistics")
272 elinks.bind_key("!", feedreader)