first basic OS customization added
[FireMan.git] / src / components / ManViewer.js
blobdc2cb2e66a7ab60a476d76fcac32989bb185724b
1 /*
2  * Copyright (c) 2008 Tobias Sarnowski <sarnowski@new-thoughts.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
17 // components defined in this file
18 const MANPROT_HANDLER_CONTRACTID        = "@mozilla.org/network/protocol;1?name=man";
19 const MANPROT_HANDLER_CID               = Components.ID("{c2e6b7ab-8141-45e9-8c84-e32a825bb105}");
21 // components used in this file
22 const NS_IOSERVICE_CID                          = "{9ac9e770-18bc-11d3-9337-00104ba0fd40}";
23 const NS_PREFSERVICE_CONTRACTID         = "@mozilla.org/preferences-service;1";
24 const URI_CONTRACTID                            = "@mozilla.org/network/simple-uri;1";
25 const NS_WINDOWWATCHER_CONTRACTID       = "@mozilla.org/embedcomp/window-watcher;1";
26 const STREAMIOCHANNEL_CONTRACTID        = "@mozilla.org/network/stream-io-channel;1";
28 // interfaces used in this file
29 const nsIProtocolHandler                = Components.interfaces.nsIProtocolHandler;
30 const nsIURI                            = Components.interfaces.nsIURI;
31 const nsISupports                       = Components.interfaces.nsISupports;
32 const nsIIOService                      = Components.interfaces.nsIIOService;
33 const nsIPrefService                    = Components.interfaces.nsIPrefService;
34 const nsIWindowWatcher                  = Components.interfaces.nsIWindowWatcher;
35 const nsIChannel                        = Components.interfaces.nsIChannel;
38 function disableHTML(textchar)
40         if (textchar == '<') {
41                 return '&lt;';
42         } else if (textchar == '>') {
43                 return '&gt;';
44         } else if (textchar == '&') {
45                 return '&amp;';
46         } else {
47                 return textchar;
48         }
51 function transformMan2HTML(manoutput, man_title, man_category)
53         var htmloutput = '';
55         var first_line = true;
56         var char_after_first_line = false;
58         var on_bold = false;
59         var on_underline = false;
60         for (var n = 0; n < manoutput.length; n++) {
61                 var s = manoutput.substr(n, 1);
62                 var c = s.charCodeAt(0);
64                 var s_next = '';
65                 var c_next = 0;
66                 if (n < manoutput.length - 1) {
67                         s_next = manoutput.substr(n + 1, 1);
68                         c_next = s_next.charCodeAt(0);
69                 }
71                 var s_nextnext = '';
72                 var c_nextnext = 0;
73                 if (n < manoutput.length - 2) {
74                         s_nextnext = manoutput.substr(n + 2, 1);
75                         c_nextnext = s_nextnext.charCodeAt(0);
76                 }
78                 var s_nextnextnext = '';
79                 var c_nextnextnext = 0;
80                 if (n < manoutput.length - 3) {
81                         s_nextnextnext = manoutput.substr(n + 3, 1);
82                         c_nextnextnext = s_nextnextnext.charCodeAt(0);
83                 }
85                 var s_prev = '';
86                 var c_prev = 0;
87                 if (n > 0) {
88                         s_prev = manoutput.substr(n - 1, 1);
89                         c_prev = s_prev.charCodeAt(0);
90                 }
92                 // start parsing
94                 if (s == '\n') {
95                         // new line, close the old one
96                         // clear bold/underline
97                         if (first_line) {
98                                 // strip the first line
99                                 htmloutput = '';
100                                 first_line = false;
101                         } else {
102                                 if (on_bold) {
103                                         htmloutput += '</span>';
104                                         on_bold = false;
105                                 }
106                                 if (on_underline) {
107                                         htmloutput += '</span>';
108                                         on_underline = false;
109                                 }
110                                 htmloutput += "\n";
111                         }
113                 } else if (s == '+' && c_next == 8 && s_nextnext == '+' && c_nextnextnext == 8) {
114                         if (on_underline) {
115                                 htmloutput += '</span>';
116                                 on_underline = false;
117                         }
118                         if (!on_bold) {
119                                 htmloutput += '<span class="man_bold">';
120                                 on_bold = true;
121                         }
122                         htmloutput += '* '; // it's a bullet
123                         n += 7;
125                 } else if (c == 8) {
126                         // should be already parsed
127                         continue;
129                 } else if (s == '_' && c_next == 8) {
130                         // underline
131                         if (on_bold) {
132                                 htmloutput += '</span>';
133                                 on_bold = false;
134                         }
135                         if (!on_underline) {
136                                 htmloutput += '<span class="man_underline">';
137                                 on_underline = true;
138                         }
139                         htmloutput += disableHTML(s_nextnext);
140                         n += 2;
141                 } else if (c_next == 8 && s_nextnext == s) {
142                         // bold
143                         if (on_underline) {
144                                 htmloutput += '</span>';
145                                 on_underline = false;
146                         }
147                         if (!on_bold) {
148                                 htmloutput += '<span class="man_bold">';
149                                 on_bold = true;
150                         }
151                         htmloutput += disableHTML(s);
152                         n += 2;
153                 } else {
154                         if (on_bold) {
155                                 htmloutput += '</span>';
156                                 on_bold = false;
157                         }
158                         if (on_underline) {
159                                 htmloutput += '</span>';
160                                 on_underline = false;
161                         }
162                         htmloutput += disableHTML(s);
163                 }
165         }
166         var tmp = htmloutput.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
168         // seperate special titles
169         tmp = tmp.replace(/^<span class="man_bold">(.*)<\/span> <span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1 $2" href="#bottomnav">$1 $2</a></h1>');
170         tmp = tmp.replace(/\n+<span class="man_bold">(.*)<\/span> <span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1 $2" href="#bottomnav">$1 $2</a></h1>');
171         tmp = tmp.replace(/^<span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1" href="#bottomnav">$1</a></h1>');
172         tmp = tmp.replace(/\n+<span class="man_bold">(.*)<\/span>/g, '<h1><a name="$1" href="#bottomnav">$1</a></h1>');
174         // link links
175         tmp = tmp.replace(/([\-.\w]+)\((\w+)\)/g, '<a href="man://$2/$1">$1($2)</a>');
177         // get os type and customize design
178         var shell = Components.classes['%%XPCOM_SHELL%%'].getService().wrappedJSObject;
179         var os = shell.exect("uname");
180         switch (os) {
181                 case "OpenBSD":
182                         break;
183                 case "NetBSD":
184                         break;
185                 case "FreeBSD":
186                         break;
187                 case "Darwin":
188                         break;
189                 case "Linux":
190                         break;
191                 default:
192                         os = false;
193                         break;
194         }
196         htmloutput  = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n';
197         htmloutput += '    "http://www.w3.org/TR/html4/loose.dtd">\n\n';
198         htmloutput += '<html>\n';
199         htmloutput += '    <head>\n';
200         htmloutput += '        <title>' + man_title;
201         if (man_category) {
202                 htmloutput += '(' + man_category + ')';
203         }
204         htmloutput += '</title>\n';
205         htmloutput += '        <link rel="shortcut icon" type="image/png" href="resource://fireman/img/logo16x16.png"/>\n';
206         htmloutput += '        <link rel="stylesheet" type="text/css" href="resource://fireman/css/screen.css" media="screen"/>\n';
207         htmloutput += '        <link rel="stylesheet" type="text/css" href="resource://fireman/css/print.css" media="print"/>\n';
208         htmloutput += '    </head>\n';
209         htmloutput += '    <body>\n';
210         htmloutput += '        <div id="main">\n';
211         htmloutput += '            <div id="header">\n';
212         htmloutput += '                <p>\n';
213         if (os) {
214                 htmloutput += '                    <img src="resource://fireman/img/os/' + os + '.png" alt="' + os + '" title="FireMan uses the ' + os + ' manpages."/>\n';
215                 htmloutput += '                    ' + os + ' Manual Page<br/>\n';
216         } else {
217                 htmloutput += '                    <img src="resource://fireman/img/os/default.png" alt="FireMan" title="FireMan uses the manpages."/>\n';
218                 htmloutput += '                    Manual Page<br/>\n';
219         }
220         htmloutput += '                    <strong>' + man_title;
221         if (man_category) {
222                 htmloutput += '(' + man_category + ')';
223         }
224         htmloutput += '</strong>\n';
225         htmloutput += '                </p>\n';
226         htmloutput += '            </div>\n';
227         htmloutput += '            <div id="manpage">\n';
228         htmloutput += '                <pre>\n';
229         htmloutput += tmp;
230         htmloutput += '                </pre>\n';
231         htmloutput += '            </div>\n';
232         htmloutput += '            <div id="footer">\n';
233         htmloutput += '                This manual page is rendered by <a href="#" title="The FireMan Homepage">FireMan</a> <a href="#" title="The FireMan Homepage"><img src="resource://fireman/img/logo24x24.png" alt="FireMan" /></a>\n';
234         htmloutput += '            </div>\n';
235         htmloutput += '        </div>\n';
236         htmloutput += '    </body>\n';
237         htmloutput += '</html>\n\n';
238         return htmloutput;
241 /***** ProtocolHandler *****/
243 function ManProtocolHandler(scheme)
245         this.scheme = scheme;
248 // attribute defaults
249 ManProtocolHandler.prototype.defaultPort = -1;
250 ManProtocolHandler.prototype.protocolFlags = nsIProtocolHandler.URI_NORELATIVE;
252 ManProtocolHandler.prototype.allowPort = function(aPort, aScheme)
254         return false;
257 ManProtocolHandler.prototype.newURI = function(aSpec, aCharset, aBaseURI)
259         var uri = Components.classes[URI_CONTRACTID].createInstance(nsIURI);
260         uri.spec = aSpec;
261         return uri;
264 ManProtocolHandler.prototype.newChannel = function(aURI)
266         var title = null;
267         var category = null;
268         var machine = null;
270         var urlparts = aURI.spec.substr(aURI.spec.indexOf("://") + "://".length).split("/");
272         // possible formats are:
273         //   man://<title>
274         //   man://<category>/<title>
275         //   man://<category>/<machine>/<title>
277         if (urlparts.length == 1) {
278                 title = urlparts[0];
279         } else
280         if (urlparts.length == 2) {
281                 category = urlparts[0];
282                 title = urlparts[1];
283         } else
284         if (urlparts.length == 3) {
285                 category = urlparts[0];
286                 machine = urlparts[1];
287                 title = urlparts[2];
288         }
290         // now get the man output and parse it
292         // get out components
293         var shell = Components.classes['%%XPCOM_SHELL%%'].getService().wrappedJSObject;
295         var mancmd = 'man';
296         if (category) {
297                 mancmd = mancmd + ' -s ' + category;
298         }
299         if (machine) {
300                 mancmd = mancmd + ' -S ' + machine;
301         }
302         mancmd = mancmd + ' ' + title;
303         var output = shell.exect(mancmd);
305         output = transformMan2HTML(output, title, category);
307         var stream = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
308         stream.setData(output, output.length);
310         // create a channel to stream it into firefox browser
311         var channel = Components.classes["@mozilla.org/network/input-stream-channel;1"].createInstance(Components.interfaces.nsIInputStreamChannel);
312         channel.setURI(aURI);
313         channel.contentStream = stream;
314         return channel;
318 /***** ManProtocolHandlerFactory *****/
320 function ManProtocolHandlerFactory(scheme)
322         this.scheme = scheme;
325 ManProtocolHandlerFactory.prototype.createInstance = function(outer, iid)
327         if(outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION;
329         if(!iid.equals(nsIProtocolHandler) && !iid.equals(nsISupports))
330                 throw Components.results.NS_ERROR_INVALID_ARG;
332         return new ManProtocolHandler(this.scheme);
335 var factory_man = new ManProtocolHandlerFactory("man");
337 /***** ManModule *****/
339 var ManModule = new Object();
341 ManModule.registerSelf = function(compMgr, fileSpec, location, type)
343         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
345         // register protocol handlers
346         compMgr.registerFactoryLocation(MANPROT_HANDLER_CID,
347                                         "MAN protocol handler",
348                                         MANPROT_HANDLER_CONTRACTID,
349                                         fileSpec, location, type);
352 ManModule.unregisterSelf = function(compMgr, fileSpec, location)
354         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
356         // unregister our components
357         compMgr.unregisterFactoryLocation(MANPROT_HANDLER_CID, fileSpec);
360 ManModule.getClassObject = function(compMgr, cid, iid)
362         if(!iid.equals(Components.interfaces.nsIFactory))
363                 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
365         if(cid.equals(MANPROT_HANDLER_CID)) return factory_man;
367         throw Components.results.NS_ERROR_NO_INTERFACE;
370 ManModule.canUnload = function(compMgr)
372         return true;    // our objects can be unloaded
375 /***** Entrypoint *****/
377 function NSGetModule(compMgr, fileSpec)
379     return ManModule;