myportal in XML

Michael Urman <[email protected]>
Newsgroups gmane.comp.web.galeon.devel
Message-ID <[email protected]>
I've patched galeon (debian's 20030216 snapshot) to use XML for the
myportal handler.  What i like about it is that the myportal xml source
is much cleaner, although it does depend on javascript for links and
forms.  What i dislike is that it is noticeably slower on my personal
batch of way too many bookmarks (around 400-500).

Attached are the patch to mozilla/MyportalProtocolHandler.cpp,
the totally redone myportal.css, and the new myportal.js.

If you're curious how this actually looks and don't want to rebuild for
it, see http://zeta.dtdns.net/~mu/galportal/ in which you'll find:

    galportal.xml
	a small test file used to figure out the current layout
    myportal.xml
	an edited output of the new handler, reflecting the local .css
	and .js links (and removing personal bookmarks)
    stripped.xml
	like myportal.xml, but stripped of indentation for a "better"
	file-size comparison; it's still about 3/2 the size of the html,
	which is considerably better than the 5/2 it is beforehand.
    myportal.css, myportal.js [see note]
	the final versions of the above included files
    myportal.html
	a matching edited output of the old handler
    galportal.css
	the corresponding css for the html file
    galeonportal.png
	a screenshot of a the html version before i started the xml
    MyportalProtocolHandler.diff
	the diff i've included

I'd appreciate any feedback, or suggestions, particularly if there are
better ways to handle the links, forms, or to regain the lost speed.

[ Note: the javascript is strongly based on examples at
  http://developer.apple.com/internet/css/xmltransformations.html.  I
  personally feel the given snippets are small enough to be devoid of
  copyright, but i'd rather let someone knowledgeable decide that.
  
  Everything else is either my original code and thus available under
  the GPL, or directly derived or modified GPL code. ]

-m
-- 
Michael Urman  [- [email protected] -]
MyportalProtocolHandler.diff (text/plain, 10.4 KB)
--- MyportalProtocolHandler.cpp.orig	2003-02-20 14:31:46.000000000 -0600
+++ MyportalProtocolHandler.cpp	2003-02-20 14:31:57.000000000 -0600
@@ -58,8 +58,8 @@
 			      const char *prefix, gint depth);
 static void render_folder (OutStream *stream, GbFolder *b,
 			      const char *prefix, gint depth);
-static void render_url (OutStream *stream, GbSite *b);
-static void render_search_form (OutStream *stream, GbSmartSite *b);
+static void render_url (OutStream *stream, GbSite *b, gint depth);
+static void render_search_form (OutStream *stream, GbSmartSite *b, gint depth);
 
 /* hackish shorthand to render HTML into the browser */
 #if USE_INTERMEDIATE_FILE
@@ -192,11 +192,13 @@
 	/* get the stylesheet filename */
 		gchar *stylesheet_filename =
 			gul_general_user_file ("myportal.css", FALSE);
+		gchar *javascript_filename =
+			gul_general_user_file ("myportal.js", FALSE);
 	
 	/* open the rendering stream */
 #if USE_INTERMEDIATE_FILE
 	gchar *filename = g_strconcat (g_get_home_dir (),
-				       "/.galeon/myportal.html",
+				       "/.galeon/myportal.xml",
 				       NULL);
 	gchar *myportalURI = g_strconcat ("file://",filename, NULL);
 	FILE *stream = fopen (filename, "w");
@@ -204,6 +206,7 @@
 	{
 		g_warning ("unable to create `%s'", filename);
 		g_free (stylesheet_filename);
+		g_free (javascript_filename);
 		g_free (filename);
 		return NS_ERROR_FAILURE;
 	}
@@ -222,28 +225,19 @@
 	/* render the complete portal */
 	gchar full_path[PATH_MAX];
 	gchar* myportal_utfstr = _("My Portal");
-	RENDER ("<html><head>\n");
-	RENDER ("<link rel=\"stylesheet\" href=\"file:");
+	RENDER ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+	RENDER ("<?xml-stylesheet href=\"file://");
 	RENDER (realpath (stylesheet_filename, full_path));
-	RENDER ("\" type=\"text/css\">\n");
-	RENDER ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">\n");
-	RENDER ("<title>");
+	RENDER ("\" type=\"text/css\"?>\n");
+	RENDER ("<myportal root=\"\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\n");
+	RENDER ("    <title>");
 	RENDER (myportal_utfstr);
-	RENDER ("</title>");
-	RENDER("</head>\n");
-	RENDER ("<body>\n");
-	/* RENDER ("<a href=\"" GALEON_HOMEPAGE_URL "\">"
-	       "<img class=\"logo\" src=\"file:" SHARE_DIR
-	       "/logo.png\" alt=\"\"></a>"); */
-	RENDER ("<div class=\"top_bar\"><table cellpadding=\"2\" cellspacing=\"0\" width=\"100%\" border=0><tr><td><img src=\"file:" SHARE_DIR "/galeon.png\">myportal:</td>\n")
-	RENDER ("<td valign=\"top\" align=\"right\" class=\"slogan\"></td></tr></table></div>\n")
-	RENDER ("<div class=\"top2\"><a href=\"http://galeon.sourceforge.net\"><img class=\"logo\" src=\"file:" SHARE_DIR "/logo.png\" alt=\"\"></a></div>\n")
-	RENDER ("<p><br></p> \n")
-	RENDER("<!-- BOOKMARKS -->\n\n");
+	RENDER ("</title>\n\n");
 	render_from_path (stream, PromiseFlatCString(path).get());
-	RENDER("\n\n<!-- END -->");
-	RENDER ("<div class=\"bottom_bar\"><center><b>Galeon</b> : <i>the web, the only web</i></center></div>");
-	RENDER ("</body></html>\n");
+	RENDER ("\n    <xhtml:script type=\"text/javascript\" src=\"file://");
+	RENDER (realpath (javascript_filename, full_path));
+	RENDER ("\"/>\n");
+	RENDER ("</myportal>\n");
 //	g_free(myportal_utfstr);
 
 	/* finish the rendering */
@@ -265,7 +259,7 @@
 
 #if MOZILLA_SNAPSHOT > 4
 	rv = NS_NewInputStreamChannel(getter_AddRefs(mChannel), mURI,
-				      iStream, NS_LITERAL_CSTRING("text/html"),
+				      iStream, NS_LITERAL_CSTRING("text/xml"),
 				      NS_LITERAL_CSTRING("utf-8"));
 #else
 	PRUint32 size;  
@@ -392,19 +386,26 @@
 	if (b == NULL)
 		return;
 
+
 	/* otherwise do the appropriate thing */
 	if (GB_IS_FOLDER(b)) {
-		RENDER ("<blockquote>");
+		gchar *indent, *indent2;
+		indent = g_strdup_printf("%*s", (depth+1)*4, " ");
+		indent2 = g_strdup_printf("%*s", (depth+2)*4, " ");
+		RENDER (indent);
+		RENDER ("<folder>\n");
 		strippedname = gul_string_strip_uline_accel (b->name);
-		RENDER ("<a class=\"heading\" ");
 		id = g_strdup_printf ("%lu", (gulong) b);
 		if (!GB_IS_ALIAS_PLACEHOLDER(b))
 		{
-			RENDER ("name=\"");
+			RENDER (indent2);
+			RENDER ("<id>");
 			RENDER (id);
-			RENDER ("\" ");
+			RENDER ("</id>\n");
 		}
-		RENDER ("href=\"");
+
+		RENDER (indent2);
+		RENDER ("<link>")
 		if (depth == 0)
 		{
 			newprefix = g_strdup (prefix);
@@ -416,36 +417,43 @@
 			g_free(escapedname);
 		}
 		RENDER (newprefix);
-		RENDER ("\">");
-		RENDER ("<strong>");
+		RENDER ("</link>\n");
+
+		RENDER (indent2);
+		RENDER ("<label>");
 		RENDER (strippedname);
-		RENDER ("</strong>");
 		g_free (strippedname);
-		RENDER ("</a>");
+		RENDER ("</label>\n");
+
 		if (GB_IS_ALIAS_PLACEHOLDER(b))
 		{
 		/* we point to the root portal since 1) the real bm might not
 		 * be in this same level we are viewing, and 2) relative
 		 * anchors don't seem to work with myportal://  ;) */
-			RENDER (" -> <a href=\"myportal://#");
-			RENDER (id);
-			RENDER ("\">#");
+			RENDER (indent2);
+			RENDER ("<ref>");
 			RENDER (id);
-			RENDER ("</a>");
+			RENDER ("</ref>\n");
 		}
 		g_free (id);
-		RENDER ("<br>");
+
 		if (!GB_IS_ALIAS_PLACEHOLDER(b) || depth==0)
 			render_folder (stream, GB_FOLDER(b), 
 				       newprefix, depth+1);
 		g_free (newprefix);
-		RENDER ("</div></blockquote>\n");
+		RENDER (indent);
+		RENDER ("</folder>\n");
+		g_free (indent);
+		g_free (indent2);
 	} else if (GB_IS_SMART_SITE(b)) {
-		render_search_form (stream, GB_SMART_SITE(b));
+		render_search_form (stream, GB_SMART_SITE(b), depth);
 	} else if (GB_IS_SITE(b)) {
-		render_url (stream, GB_SITE(b));
+		render_url (stream, GB_SITE(b), depth);
 	} else if (GB_IS_SEPARATOR(b)) {		
-		RENDER ("<br>\n");
+		gchar *indent;
+		indent = g_strdup_printf("%*s", (depth+1)*4, " ");
+		RENDER (indent);
+		RENDER ("<br/>\n");
 	}
 }
 
@@ -468,11 +476,17 @@
  * render_url: render a bookmark url
  */
 static void
-render_url (OutStream *stream, GbSite *b)
+render_url (OutStream *stream, GbSite *b, gint depth)
 {
-	gchar *url;
-
-	RENDER ("<a href=\"");
+	gchar *url, *url2;
+	gchar *indent, *indent2;
+	indent = g_strdup_printf("%*s", (depth+1)*4, " ");
+	indent2 = g_strdup_printf("%*s", (depth+2)*4, " ");
+
+	RENDER (indent);
+	RENDER ("<bookmark>\n");
+	RENDER (indent2);
+	RENDER ("<link>");
 
 	/* If url has no preceding "http://" or "ftp://" presume "http://" */
 	/* FIXME cant we just test for : ? */
@@ -488,11 +502,18 @@
 	}
 
 	/* escape quotes, so we don't accidentally break out of the URI */
-	url = gul_string_strdup_replace (b->url, "\"", "&quot;");
+	url2 = gul_string_strdup_replace (b->url, "&", "&amp;");
+
+	url = gul_string_strdup_replace(url2, "\"", "&quot;");
+	if (url2) g_free (url2); url2 = url;
+	url = gul_string_strdup_replace(url2, "<", "&lt;");
+	if (url2) g_free (url2); url2 = url;
+	url = gul_string_strdup_replace(url2, ">", "&gt;");
+	if (url2) g_free (url2);
 	RENDER (url);
 	if (url) g_free (url);
+	RENDER ("</link>\n");
 
-	RENDER ("\">");
 	if (gb_bookmark_get_image(GB_BOOKMARK(b)) == NULL)
 	{
 		gchar *name, *name2;
@@ -514,21 +535,31 @@
 		}
 
 		/* escape some more characters */
-		name2 = gul_string_strdup_replace (name, "<", "&lt;");
+		name2 = gul_string_strdup_replace (name, "&", "&amp;");
 		if (name) g_free (name);
 		name = gul_string_strdup_replace (name2, ">", "&gt;");
 		if (name2) g_free (name2);
+		name2 = name;
+		name = gul_string_strdup_replace (name2, "<", "&lt;");
+		if (name2) g_free (name2);
 
+		RENDER (indent2);
+		RENDER ("<label>");
 		RENDER (name);
 		if (name) g_free (name);
+		RENDER ("</label>\n");
 	}
 	else
 	{
-		RENDER ("<img src=\"file://");
+		RENDER (indent2);
+		RENDER ("<xhtml:img src=\"file://");
 		RENDER (GB_BOOKMARK(b)->pixmap_file);
-		RENDER ("\">");
+		RENDER ("\"/>\n");
 	}
-	RENDER ("</a>\n");
+	RENDER (indent);
+	RENDER ("</bookmark>\n");
+	g_free(indent);
+	g_free(indent2);
 }
 
 /**
@@ -536,12 +567,13 @@
  * FIXME: tidy
  */
 static void 
-render_search_form (OutStream *stream, GbSmartSite *b)
+render_search_form (OutStream *stream, GbSmartSite *b, gint depth)
 {
 	gchar *path;    /* static part of URL */
 	gchar **query;  /* query string, broken into parameters */
 	int num_inputs = 0, pathlen;
 	gchar *tmp;
+	gchar *indent, *indent2;
 	int i;
 
 	/* parse URL to separate static parts from parts needing user input */
@@ -552,15 +584,21 @@
 		return; 
 	}
 
+	indent = g_strdup_printf("%*s", (depth+1)*4, " ");
+	indent2 = g_strdup_printf("%*s", (depth+2)*4, " ");
+
 	pathlen = tmp - b->smarturl;
 	path = g_strndup (b->smarturl, pathlen);
-	RENDER ("<form action=\"");
+	RENDER (indent);
+	RENDER ("<xhtml:form>\n");
+	RENDER (indent2);
+	RENDER ("<link>");
 	if (!strstr (b->smarturl, "://"))
 	{
 		RENDER ("http://");
 	}
 	RENDER (path);
-	RENDER ("\" method=get>");
+	RENDER ("</link>\n");
 	g_free (path);
 
 	/* count occurrences of %s in b->smarturl */
@@ -577,8 +615,8 @@
 		if (strcmp (query[i], "%s") == 0)
 		{
 			/* anonymous field */
-			RENDER ("<input type=text name=\"\" size=");
-			RENDER (num_inputs>1?"10>":"30>");
+			RENDER (indent2);
+			RENDER ("<xhtml:input name=\"\">\n");
 			/* skip the remaining logic */
 			continue;
 		}
@@ -589,24 +627,24 @@
 			gchar *val=g_strdup_printf ("%.*s%s", smtpos-param[1],
 					param[1],param[1]+(smtpos-param[1]+2));
 			/* add a form input */
-			RENDER (num_inputs>1?param[0]:"");
-			RENDER ("<input type=text name=\"");
+			RENDER (indent2);
+			RENDER ("<xhtml:input name=\"");
 			RENDER (param[0]);
 			RENDER ("\" value=\"");
 			RENDER (val);
-			RENDER ("\" size=");
-			RENDER (num_inputs>1?"10>":"30>");
+			RENDER ("\"/>\n");
 			g_free (val);
 		}
 		else
 		{
 			/* add the static parts of URL as hidden form inputs */
-			RENDER ("<input type=hidden name=\"");
+			RENDER (indent2);
+			RENDER ("<xhtml:input hidden=\"");
 			RENDER (param[0]);
 			RENDER ("\" value=\"");
 			if (param[1])
 				RENDER (param[1]);
-			RENDER ("\">");
+			RENDER ("\"/>\n");
 		}
 		g_strfreev (param);
 	}
@@ -616,16 +654,23 @@
 	{
 		gchar *strippedname = 
 		     gul_string_strip_uline_accel (GB_BOOKMARK(b)->name);
-		RENDER ("<input type=submit value=\"");
+		RENDER (indent2);
+		RENDER ("<xhtml:input label=\"");
 		RENDER (strippedname);
 		g_free (strippedname);
+		RENDER ("\"/>\n");
 	}
 	else
 	{
-		RENDER ("<input type=image src=\"file://");
-			RENDER (GB_BOOKMARK(b)->pixmap_file);
+		RENDER (indent2);
+		RENDER ("<xhtml:input image=\"file://");
+		RENDER (GB_BOOKMARK(b)->pixmap_file);
+		RENDER ("\"/>\n");
 	}
-	RENDER ("\"></form>\n");
+	RENDER (indent);
+	RENDER ("</xhtml:form>\n");
+	g_free(indent);
+	g_free(indent2);
 }
 
 #undef RENDER
myportal.css (text/css, 3.4 KB)
@namespace xlink url(http://www.w3.org/1999/xlink);

myportal
{
    display: block;
    margin: 0 4px;
    background-color: #444;
    border-top: 30px solid #97b1d8;
}

myportal:before
{
    display: block;
    font-size: x-large;
    font-style: italic;

    background-color: white;
    border: 1px solid black;
    border-top-width: 2px;
    padding: 3px;
    margin-bottom: 0.5em;

    content: url(file:/usr/share/galeon-snapshot/galeon.png)
	     "  myportal://"attr(root);
}
myportal:after
{
    display: block;
    font-weight: bold;
    font-style: italic;
    content: "Galeon: the web, the only web";
    background-color: #97b1d8;
    border-color: 1px solid black;
    text-align: center;
    margin: 0px 200px;
    margin-top: 10px;
}

myportal > title
{
    display: none;
}

/*
myportal > folder:before
{
    content: url(file:/usr/share/galeon-snapshot/logo.png);
    position: relative;
    left: -20px;
}
*/

folder
{
    display: block;
    margin-top: 1em;
	margin-left:	8px;
	padding-left:	4px;
	margin-right:	0px;
	padding-bottom:	4px;
	border-left:	solid 5px black;
	border-top:	solid 5px black;
	border-bottom:	solid 4px #888;
	-moz-border-left-colors: #444 #666 #888 #aaa #ccc;
	-moz-border-top-colors: #444 #666 #888 #aaa #ccc;
	-moz-border-bottom-colors: #666 #888 #aaa #ccc;
	-moz-border-radius-bottomleft: 1px;
	background-color: #f0f0f0;
}

folder>folder { background-color: #e8e8e8; }
folder>folder>folder { background-color: #e0e0e0; }
folder>folder>folder>folder { background-color: #d8d8d8; }
folder>folder>folder>folder>folder { background-color: #d0d0d0; }
folder>folder>folder>folder>folder>folder { background-color: #c8c8c8; }

folder > id
{
    display: none;
}
folder > label
{
	display: inline;
	border:			2px solid black;
	padding-bottom:		2px;
	padding-top		0px;
	padding-right:		1em;
	padding-left:		1em;        
	margin-left:		-16px;
	margin-top:		3em;
	margin-bottom:		-1px;
	color:			black;
	background-color:	#97b1d8;
	font-size:		120%;
	font-weight: bold;
	-moz-border-radius-bottomleft:	6px;
	-moz-border-radius-topleft:	12px;
	-moz-border-radius-topright:	6px;
	-moz-border-radius-bottomright:	12px;
	position: relative;
	top: -5px;
}
folder > label:hover
{
    color: white;
    background-color: black;
}
folder > link
{
    display: none;
}

bookmark
{
    cursor: pointer;
    display: inline;
    border-bottom: 1px solid gray;
    content: attr(xlink:label);
}

bookmark + bookmark
{
    margin-left: 1em;
}

bookmark > label:hover
{
    border-bottom: 1px solid black;
    color: blue;
}

bookmark > link
{
    display: none;
}

form	/* really should be xhtml|form, i think... */
{
    display: block;
}
form > link
{
    display: none;
}
input[type="text"]
{
    border-style: groove;
    background-color: transparent;
}
input[type="text"]:focus
{
    border-style: inset;
    background-color: auto;
}
input[type="submit"]
{
    border: none;
    padding: 0px;
    background-color: transparent;
    border-bottom: 1px solid gray;
}
input[type="submit"]:hover
{
    color: blue;
    cursor: pointer;
    border-bottom: solid 1px black;
}
input[type="hidden"]
{
    display: none;
}

/*
smartbookmark
{
    display: block;
    margin-top: 5px;
    margin-bottom: 5px;
}
smartbookmark + smartbookmark
{
    margin-top: 10px;
}
smartbookmark > link
{
    display: none;
}
smartbookmark > submit:after
{
    display: inline;
}
smartbookmark > input
{
    padding: 1px 2px;
    border: 3px inset gray;
    width: 30px;
}
*/
myportal.js (application/x-javascript, 2.5 KB) - not displayed
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.