(no subject) (Reply) [entries|reading|network|archive]
simont

[ userinfo | dreamwidth userinfo ]
[ archive | journal archive ]

[personal profile] simont Fri 2003-05-30 16:04

Whew. When someone looks back over my life of geekery and compiles my all-time list of Most Evil Hacks, I think this may well score somewhere near the top.

Now that my LiveJournal account allows me to tinker with the style system, I thought I'd redesign my friends page so that it's actually legible; the standard Generator style is pretty good, but not quite to my taste. So off I went and started fiddling.

The template for a friends-page entry is allowed to contain %%ljuser:user%%, which produces a standard reference-to-LJ-user for the author of that entry. Except that if the entry is in a community, it produces a ref-to-LJ-user for the name of the community. Not too helpful in itself; that's why it's also allowed to contain %%altposter%%, which allows you to provide an extra piece of HTML for when the poster name and the journal name differ. So my initial stab at the title line on my friends page went something like:

  • Define %%altposter%% to expand to ‘%%ljuser:poster%% on ’.
  • Define the full author label to be ‘%%altposter%%%%ljuser:user%%’.

Thus, on an ordinary post I got ‘username’, but on a community post I got ‘username on community’. All very simple.

Only trouble is, it looks a bit ugly having an LJ user icon before the community name. There's a separate community icon, after all, so why can't I use that?

Well, it's hard, because although the ‘altposter’ mechanism allows you to include extra HTML when there's a community involved, there's no easy way to take HTML out when there's a community involved (or equivalently to include extra HTML when there isn't a community involved). So any text I put in the main HTML template to display the name of an ordinary user with an LJ user icon will necessarily also appear in community posts, displaying the name of the community with an LJ user icon, which is the one thing I don't want.

So I had a think. My first idea was to use CSS: there's a CSS tag which completely inhibits display of any text it's applied to, to allow CSSed pages to provide fallback navigation stuff for non-CSS browsers. But I occasionally view my friends page in w3m, which doesn't support CSS, so that was no good.

What else in HTML inhibits display of any text enclosed in it? I slept on the problem and realised the answer is obvious: comments. And HTML comments, it turns out, don't nest! So this seemed the obvious solution:

  • Define %%altposter%% to expand to ‘%%ljuser:owner%% on %%ljcomm:community%% <!--’.
  • Define the full author label to be ‘%%altposter%% %%ljuser:user%% <!-- -->’.

So when the altposter text is present, it produces a single large comment covering the normal user name, the second open-comment is ignored, and the final --> closes the comment. When the altposter text is missing, you just get the ordinary user name followed by a trivial HTML comment. Simple and elegant, I thought.

Only it doesn't work. Perusing the LJ source code (yay for open-source websites!) I found that the value of %%altposter%% was being run through Perl's HTML::Parser to clean out any particularly malicious tags, and HTML::Parser was marking the unclosed comment as ‘text’ rather than ‘comment’ or ‘tag’. And LJ, taking no chances, therefore turned the angle bracket into an HTML entity just in case. So, back to the drawing board.

Now here's where it gets really unpleasant.

It suddenly occurred to me that the HTML sequence <!--> has a rather interesting function. If you see it outside a comment, it should open a new comment. But if you see it inside a comment, it should close one. It took some deep thinking to work out how to apply this, but eventually I came up with a solution which worked on paper:

  • Define %%altposter%% as ‘<!-->%%ljuser:owner%% on %%ljcomm:community%%<!-->’.
  • Define the full author label as ‘<!-- -%%altposter%%->%%ljuser:user%%<!-- -->’.

So when the altposter text is absent, the author label is a trivial HTML comment, followed by the ordinary user name, followed by another trivial HTML comment. But when the altposter text is present, the first <!--> closes the initial comment so that the community-specific text gets displayed, and then the second <!--> opens a new comment which swallows the -> and the ordinary user name. Then the final <!-- --> makes sure that we aren't in a comment no matter what happened. And meanwhile, HTML::Parser thinks the entire altposter text is a single large comment, so it doesn't mess with it at all.

Unfortunately, this still didn't work in practice, because Mozilla thinks <!--> is a completely self-contained comment equivalent to <!-- --> – i.e. it will leave you outside comment state no matter which state you saw it in. Fortunately I managed to work around this special case by adding a further space to the second <!-->, making it <!-- >. I'm not sure why that worked, but it seems to.

So now, in a community post, I get the user icon for the user name and the community icon for the community name; and for an ordinary post, I just get an ordinary user name with an ordinary user icon. I really don't think this should have had to be that hard; but I'm pretty pleased with myself for having risen to the challenge :-)

Link Read Comments
Reply:
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting