<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><id>tag:blogger.com,1999:blog-15626356</id><updated>2009-11-08T02:14:31.126+01:00</updated><title type="text">ecmanaut</title><subtitle type="html">Thoughts and experiences on my hobby (and professional) projects, most revolving around interesting applications of ecmascript in relation to other open web standards. I was born in 1977 and live in Mountain View, California.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://ecmanaut.blogspot.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default?start-index=26&amp;max-results=25" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>210</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><geo:lat>58.415294</geo:lat><geo:long>15.602978</geo:long><logo>http://www.feedburner.com/fb/images/pub/fb_pwrd.gif</logo><link rel="self" href="http://feeds.feedburner.com/ecmanaut" type="application/atom+xml" /><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry><id>tag:blogger.com,1999:blog-15626356.post-3587816935092465859</id><published>2009-10-15T13:28:00.002+02:00</published><updated>2009-10-15T14:49:31.226+02:00</updated><title type="text">Plan B 4.0</title><content type="html">It's &lt;a href="http://blogactionday.org/about/en"&gt;Blog Action Day&lt;/a&gt;, and 2009's topic is climate change. With the most enlightening read on my 2008 list being Earth Policy Institute's Plan B 3.0, I was &lt;a href="http://twitter.com/ecmanaut/status/4684057456"&gt;really glad&lt;/a&gt; to see &lt;a href="http://earth-policy.org/index.php?/books/pb4/pb4_table_of_contents"&gt;Plan B 4.0&lt;/a&gt; released a while ago -- it is available in its entirety in both HTML and PDF formats online, which happily means we can augment the material using tools like &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;Greasemonkey&lt;/a&gt; and &lt;a href="http://mashlogic.com/"&gt;MashLogic&lt;/a&gt; (I am one of the developers and maintainers of both extensions).&lt;br /&gt;&lt;br /&gt;Both extensions, in different ways, allow us to easily improve our reading experience by running code that someone else already wrote once, for material that was published by yet another unrelated person or organization, without any sort of organized cooperation among either of us. I find that notion incredibly powerful, and an uplifting thought about where decentralized cooperation is taking us online.&lt;br /&gt;&lt;br /&gt;When I read Plan B 3.0 (the paper copy I bought at the release party of the Swedish translation, as it were), I found myself wishing I could more effortlessly look up the many high quality source references that are strewn about the book for the immense amount of facts about recent macro events affecting our planet, on all sorts of levels, instead of flicking back and forth as soon as I got curious. Footnotes work really well in hypertext. The online versions actually don't yet do that for footnotes.&lt;br /&gt;&lt;br /&gt;So I wrote a handy user script that not only &lt;a href="http://userscripts.org/scripts/show/59859"&gt;adds bidirectional footnote hyperlinks&lt;/a&gt;, but also pulls them into the end of the paragraph that references them, the first time you click them. Try it (&lt;a href="http://userscripts.org/scripts/source/59859.user.js"&gt;direct install link&lt;/a&gt;); it's a very comfortable reading style.&lt;br /&gt;&lt;br /&gt;If you run MashLogic too, you get essentially the same feature for bringing in content from Wikipedia (and sometimes references to people from LinkedIn), without losing more focus on the material than you find you want to, getting up to date about relevant data about, say, the World Food Programme's operations, or where the Xinjiang Province is. I find it a rather rich and low-effort way of broadening your general knowledge about everything.&lt;br /&gt;&lt;br /&gt;It would be delightful if Google Books would export an index of their entire repository of books, and provide a linking scheme so we could turn those book and page references in the footnotes into actual hyperlinks too, the way we currently do with Wikipedia article names. Take &lt;a href="http://www.earth-policy.org/index.php?/books/pb4/PB4ch1_intro#f1n-back"&gt;the first reference in the introductory chapter&lt;/a&gt;, for instance (full precision of the deep-linkage only available after you install the above mentioned &lt;a href="http://userscripts.org/scripts/source/59859.user.js"&gt;Greasemonkey script&lt;/a&gt; :-) -- with a catalog of all book titles to book id:s available, we could turn that into a link like &lt;span style="color:#999"&gt;"[&lt;a href="http://www.earth-policy.org/index.php?/books/pb4/PB4ch1_intro#f1n-back"&gt;1&lt;/a&gt;] Sandra Postel, &lt;a href="http://books.google.com/books?id=QbCxGMTWWmwC&amp;pg=PA13"&gt;&lt;em&gt;Pillar of Sand&lt;/em&gt; (New York: W. W. Norton &amp; Company, 1999), pp. 13&lt;/a&gt;–21.]"&lt;/span&gt; -- just like that.&lt;br /&gt;&lt;br /&gt;The web is a very exciting medium, only just starting to show its potential. And as for &lt;a href="http://earth-policy.org/index.php?/books/pb4/pb4_table_of_contents"&gt;Plan B 4.0&lt;/a&gt;, I can warmly recommend sinking into it, letting yourself fascinate with richer understanding of where we're heading, and all the fascinatingly intricate ways our global economy and ecology connect, affect each other, consequences and solutions.&lt;br /&gt;&lt;br /&gt;There's even a good number of tips for &lt;a href="http://www.earth-policy.org/index.php?/action_center/C30/"&gt;ways we can all join in making the world a better place&lt;/a&gt; on the Earth Policy Institute's site. Happy Blog Action Day, and do spread the word!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-3587816935092465859?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1kxq_Ft6mQw:BkDNf0PMWrU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1kxq_Ft6mQw:BkDNf0PMWrU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1kxq_Ft6mQw:BkDNf0PMWrU:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1kxq_Ft6mQw:BkDNf0PMWrU:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1kxq_Ft6mQw:BkDNf0PMWrU:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/3587816935092465859/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=3587816935092465859" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/3587816935092465859" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/3587816935092465859" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/1kxq_Ft6mQw/plan-b-40.html" title="Plan B 4.0" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/10/plan-b-40.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-418624807230848985</id><published>2009-09-05T00:52:00.004+02:00</published><updated>2009-09-05T01:46:15.588+02:00</updated><title type="text">How to convert a FAT32 disk to NTFS</title><content type="html">I had a near-Windows experience today.&lt;br /&gt;&lt;br /&gt;I have had an old &lt;a href="http://www.amazon.com/exec/obidos/redirect?path=/Western-Digital-WDG1C5000N-Firewire-External/dp/B000EXTXSI&amp;tag=diaryohayou-20"&gt;500GB USB disk&lt;/a&gt; sitting around, that I used for manual backups in pre-Time Machine times, back when I deemed FAT32 to be the comfy file system choice for hooking up a disk with any computer I might want to reach it from. Today, I think that sweet spot is NTFS.&lt;br /&gt;&lt;br /&gt;Especially as Time Machine doesn't want anything to do with FAT32 devices as backup targets.&lt;br /&gt;&lt;br /&gt;I figured the easiest way to convert the filesystem in place (as I didn't have any other disk around) was to boot up a Windows XP virtual machine in Parallels and &lt;a href="http://technet.microsoft.com/en-us/library/bb456984.aspx"&gt;let Windows do it&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That procedure was &lt;a href="http://dexxter.wordpress.com/2007/04/26/remove-windows-update-restart-now-prompt/"&gt;perilous&lt;/a&gt;. If you ever embark on doing it yourself, here is how you probably want to do it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; net stop wuauserv&lt;br /&gt;The Automatic Updates service is stopping....&lt;br /&gt;The Automatic Updates service was stopped successfully.&lt;br /&gt;&lt;br /&gt;&amp;gt; convert E: /fs:ntfs&lt;br /&gt;The type of the file system is FAT32.&lt;br /&gt;Enter current volume label for drive E: [...]&lt;br /&gt;&lt;br /&gt;&amp;gt; net start wuauserv&lt;br /&gt;The Automatic Updates service is starting.&lt;br /&gt;The Automatic Updates service was started successfully.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you skip that first step, you are wise to remember that a Windows machine needs baby sitting not to do bad things to itself. Like deciding it has found and applied security updates that it really wants to restart to complete in ten, nine, eight, ...all the while that long file system conversion pass is humming about in the background.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-418624807230848985?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=yvCCJD2e2mE:KmI77H6mF24:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=yvCCJD2e2mE:KmI77H6mF24:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=yvCCJD2e2mE:KmI77H6mF24:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=yvCCJD2e2mE:KmI77H6mF24:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=yvCCJD2e2mE:KmI77H6mF24:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/418624807230848985/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=418624807230848985" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/418624807230848985" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/418624807230848985" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/yvCCJD2e2mE/how-to-convert-fat32-disk-to-ntfs.html" title="How to convert a FAT32 disk to NTFS" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/09/how-to-convert-fat32-disk-to-ntfs.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-4811637145980402512</id><published>2009-08-08T05:09:00.003+02:00</published><updated>2009-08-08T05:46:46.087+02:00</updated><title type="text">Fixing $(document).ready() to work for any document</title><content type="html">The first time I saw jQuery's &lt;code&gt;$(document).ready(callback)&lt;/code&gt; idiom, I thought it was a great idea: not only is it readable as plain English, &lt;code&gt;grep(1)&lt;/code&gt;able (it is available as &lt;code&gt;$(callback)&lt;/code&gt; too, but I advise against using that in larger code bases, where you might need to dig up all references to it, at some time) and intuitive, it also lets you provide a different document object you are waiting for.&lt;br /&gt;&lt;br /&gt;The first time I used it in the latter capacity I was a bit saddened to find that it was not quite that good yet; it always tests the same document that jQuery itself was loaded into. (At present time, meaning jQuery 1.3.2 and earlier.) The document object you pass isn't actually even fed to all the code that together implements the ready function.&lt;br /&gt;&lt;br /&gt;As we needed it to do that at &lt;a href="http://mashlogic.com/"&gt;MashLogic&lt;/a&gt; though, my colleague Dave took to fixing it so it does indeed work that way, as per the &lt;a href="http://dev.jquery.com/ticket/1124"&gt;jQuery ticket&lt;/a&gt; that has been sitting around for two years (from someone else that apparently wanted and expected the same thing), and I submitted a patch upstream. I'm hopeful it gets accepted for next jQuery release.&lt;br /&gt;&lt;br /&gt;We even prepared a little &lt;a href="http://bushong.net/dave/jquery-1124/"&gt;test case&lt;/a&gt; that loads jQuery in an iframe and puts document ready callbacks on both itself and its parent window, which seems to work fine in all browsers and OSes we have tested it in so far.&lt;br /&gt;&lt;br /&gt;I think the original ticket may have gotten closed over it often being a bit tricky to get a reference to the document object of an arbitrary frame or iframe for some frame you want to track -- the javascript security model forbidding DOM access to content in documents loaded from a different domain, for instance, and timing issues with grabbing a reference to the document object of a frame that is inlined in a document's source -- before we can get a handle to the document, it is a little difficult to have that parameter to pass to jQuery for listening in the first place, after all. But I hope such issues don't prevent the fix from landing, for the cases when we do have one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-4811637145980402512?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=oj-_Zi7tJLc:WzSBm5UGeMQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=oj-_Zi7tJLc:WzSBm5UGeMQ:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=oj-_Zi7tJLc:WzSBm5UGeMQ:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=oj-_Zi7tJLc:WzSBm5UGeMQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=oj-_Zi7tJLc:WzSBm5UGeMQ:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://dev.jquery.com/ticket/1124" title="Fixing $(document).ready() to work for any document" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/4811637145980402512/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=4811637145980402512" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/4811637145980402512" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/4811637145980402512" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/oj-_Zi7tJLc/fixing-documentready-to-work-for-any.html" title="Fixing $(document).ready() to work for any document" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/08/fixing-documentready-to-work-for-any.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-2706151639607581985</id><published>2009-07-23T05:57:00.005+02:00</published><updated>2009-07-23T07:53:35.094+02:00</updated><title type="text">Tracking multiple svn branches in git</title><content type="html">I've been using git-svn for a while, to track projects still trapped in the subversion age, with git. It's not too messy to &lt;a href="http://www.jukie.net/~bart/blog/svn-branches-in-git"&gt;set up&lt;/a&gt;, though in order to get nice names addresses you need to map all the subversion identities to their corresponding name and email address. I recommend taking the time to suck out the entire repository history -- it's a one-time operation, and you get free offline history browsing and inspection.&lt;br /&gt;&lt;br /&gt;If you have the names and email addresses of all svn committers, list them in a newline-separated file, each line reading &lt;code&gt;svn-id = Person's Name &amp;lt;email@address&amp;gt;&lt;/code&gt; -- I kind of started tracking a list of &lt;a href="http://pastie.textmate.org/private/cgm9gvwkuqke215bmselfg"&gt;jQuery committers&lt;/a&gt;, but got bored and/or side-tracked before finding the last ones. Anyway, if you go through the trouble, pass along this file on the command line (&lt;code&gt;git svn clone -A committers.txt ...&lt;/code&gt;) when you create your local copy.&lt;br /&gt;&lt;br /&gt;The docs for git-svn are not the best, and didn't mention how to import news into your local git repository for more than the branch you have checked out at the moment. The trick, which might be obvious for some but was not for me (thanks, &lt;a href="http://singpolyma.net/"&gt;singpolyma&lt;/a&gt;!), is to run &lt;code&gt;git svn fetch --all&lt;/code&gt; to update your local repository with data for all branches, instead of (well, or besides) &lt;code&gt;git svn rebase&lt;/code&gt; that just updates your currently checked-out branch.&lt;br /&gt;&lt;br /&gt;And Bob's your uncle.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-2706151639607581985?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=K3uXyaWjmoA:n6Ebrht98Rw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=K3uXyaWjmoA:n6Ebrht98Rw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=K3uXyaWjmoA:n6Ebrht98Rw:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=K3uXyaWjmoA:n6Ebrht98Rw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=K3uXyaWjmoA:n6Ebrht98Rw:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/2706151639607581985/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=2706151639607581985" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2706151639607581985" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2706151639607581985" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/K3uXyaWjmoA/tracking-multiple-svn-branches-in-git.html" title="Tracking multiple svn branches in git" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/07/tracking-multiple-svn-branches-in-git.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-3993864993236179711</id><published>2009-06-19T13:37:00.001+02:00</published><updated>2009-06-19T22:43:33.861+02:00</updated><title type="text">Updating Firefox extensions from 3.0 to 3.5</title><content type="html">When I started writing this post, two weeks ago, 46% of the top 95% most used Firefox extensions on &lt;abbr title="addons.mozilla.org"&gt;AMO&lt;/abbr&gt; (according to &lt;a href="https://addons.mozilla.org/en-US/firefox/compatibility/report"&gt;Firefox Add-on Compatibility Report&lt;/a&gt;) still did not manifest 3.5 support. The Mozilla team has been checking in with at least the topmost few of those projects (being one of the &lt;a href="http://www.greasespot.net/"&gt;Greasemonkey&lt;/a&gt; maintainers, I got one of those pings, a few weeks ago), about whether we were having any issues upping the max-version of their extension to 3.5. We had not tried, at the time, but (happily) did not see any big issues, so we just let the release already out claim 3.5.* working.&lt;br /&gt;&lt;br /&gt;For other extensions, like &lt;a href="http://www.mashlogic.com/"&gt;MashLogic&lt;/a&gt; which I hack on at work, that transition was not as trivial. Our integration surface towards Mozilla being larger than Greasemonkey's, more things changing underfoot get a shot at breaking things, and did.&lt;br /&gt;&lt;br /&gt;The &lt;a href="https://developer.mozilla.org/En/Updating_extensions_for_Firefox_3.5"&gt;information about backwards incompatible changes&lt;/a&gt; available at &lt;abbr title="mozilla development center"&gt;MDC&lt;/abbr&gt; was and is still a bit sketchy, and did not cover issues we were hitting, at the time -- it seemed XUL documents were getting XPCNativeWrapper treatment in 3.5, that they did not have in 3.0, so we had to start adopting Greasemonkey-like roundabout practices to read and write properties of the XUL DOM. That's the kind of information that is good to be able to read about -- and there, specifically.&lt;br /&gt;&lt;br /&gt;It's a wiki, though, so if you find something that isn't already there, while you are scratching your head about why things are not fine and dandy in 3.5 (due rather soon), please add your findings too. I did a brief writeup on my own findings.&lt;br /&gt;&lt;br /&gt;For MashLogic, we actually ended up removing the xul page we had used for configuration to do the same thing in HTML instead; work we had already mostly done for our IE port, anyway. Other tidbits remain here and there, though, before we can claim having fully working functionality under 3.5.&lt;br /&gt;&lt;br /&gt;On top of that, there is the not so enjoyable multi-week delay at &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/10050"&gt;AMO&lt;/a&gt;, getting our latest release through their review system. Our current release is thus so far only available if you look at their &lt;a href="https://addons.mozilla.org/en-US/firefox/addons/versions/10050"&gt;see all versions&lt;/a&gt; page, and grab the so-called "experimental" build on top. User reviews seem to help get the ball rolling a little, though, so if you try it out, we're really glad if you share your opinion with other users there by writing a review.&lt;br /&gt;&lt;br /&gt;Whether you are a user or extension developer, sharing your experience like this really helps others. Especially if you do it where others go looking for it. MDC and AMO are rather good funnels for developer and user centric Firefox extension related things, and the more we use both, the better they get. Thanks for your help!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-3993864993236179711?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=7Y_myED0t9Q:zkmlrkYMVLc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=7Y_myED0t9Q:zkmlrkYMVLc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=7Y_myED0t9Q:zkmlrkYMVLc:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=7Y_myED0t9Q:zkmlrkYMVLc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=7Y_myED0t9Q:zkmlrkYMVLc:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="https://developer.mozilla.org/En/Updating_extensions_for_Firefox_3.5" title="Updating Firefox extensions from 3.0 to 3.5" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/3993864993236179711/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=3993864993236179711" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/3993864993236179711" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/3993864993236179711" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/7Y_myED0t9Q/updating-firefox-extensions-from-30-to.html" title="Updating Firefox extensions from 3.0 to 3.5" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/06/updating-firefox-extensions-from-30-to.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-195785581511000818</id><published>2009-06-07T03:42:00.004+02:00</published><updated>2009-06-07T21:06:47.821+02:00</updated><title type="text">Greasemonkey and bottom-up user scripting</title><content type="html">I reread Paul Graham's essay &lt;a href="http://paulgraham.com/progbot.html"&gt;Programming Bottom-Up&lt;/a&gt; yesternight, and mused for a while about a set of ideas Olivier Cornu launched on &lt;a href="http://groups.google.com/group/greasemonkey-dev/"&gt;Greasemonkey-dev&lt;/a&gt; around Christmas last year, about offering user scripts some functionality to cooperate with each other. It is, I realize now, the same territory.&lt;br /&gt;&lt;br /&gt;That &lt;a href="http://groups.google.com/group/greasemonkey-dev/browse_thread/thread/ba9d958eb6955940/908f5766eec84d15"&gt;initial message&lt;/a&gt; quickly grew to astronomous proportions as we developed the territory (him and I sharing similar experiences of wanting user scripts to be able to share functionality with each other), trying to come up with how, while also selling the idea to remaining Greasemonkey maintainers Aaron and Anthony, neither of which wrote huge site specific user script applications. I will not attempt to sum up much of the proposals or their implementation, but &lt;a href="http://github.com/ocornu/webmonkey/tree/master"&gt;Olivier's fork&lt;/a&gt; of the project has the latter, in what state it reached at the time, plus a healthy bit of refactoring and polishing of corners here and there, in case someone is curious.&lt;br /&gt;&lt;br /&gt;What I will do in this post, however, is to transplant Graham's post to the domain of web development in general, and user scripting in particular -- moving it into the language domain of Javascript and the implementation domain of web browsers in general, and, lastly, user script programming under Greasemonkey in particular. A few steps away from Lisp and web server applications, but where all the same principles still apply. (The remainder of this post will not make as much sense if you haven't read Graham's &lt;a href="http://paulgraham.com/progbot.html"&gt;Programming Bottom-Up&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;In client side web development, we face diverse host environments where most specified APIs work (and break) subtly different, dependent on the user's browser. The largely adopted solution to that has been the breadth of ajax libraries such as jQuery, YUI, Dojo, Prototype, Base2 and others. To the client side web world these are the first step towards &lt;em&gt;changing the language to suit the problem&lt;/em&gt;: bridging the (largely unwebby) DOM API into something more tractable.&lt;br /&gt;&lt;br /&gt;Thus your javascript programs become about implementing sought functionality, more than about jumping the hoops of the browser environment.&lt;br /&gt;&lt;br /&gt;User scripting, by comparison, is a much smaller domain in terms of body of programmers that do much of their work there, and in terms of ajax library coverage. Greasemonkey, probably still its biggest sub class, in addition to the hoops of the web, adds all the hoops of the Mozilla javascript sandbox, which is an even more hostile environment than the web: there are XPCNativeWrappers around your DOM nodes and lots of other gotchas to watch out for.&lt;br /&gt;&lt;br /&gt;And due to that added baggage on top, you will likely find that most or all of the ajax libraries subtly break here and there, if invoked as you would any other code under Greasemonkey, and/or need slight changes here and there in order to work as intended. So writing user scripts for this environment, becomes even more in need of libraries if you aim to do anything beyond very tiny hacks, or you waste dev time jumping the hoops.&lt;br /&gt;&lt;br /&gt;Greasemonkey got @require support last year, to give some syntactic sugar for inlining code from a remote URL at installation time, letting us at least hide away them from the main body of code, which was a slight blessing. But that only does install-time composition.&lt;br /&gt;&lt;br /&gt;This is where Olivier's work comes in, letting scripts export API methods, usable from other user scripts -- effectively acting as a middleware between a web page or site and the other user script. This is yet another step towards &lt;em&gt;changing the language to suit the problem&lt;/em&gt;, or more precisely still, &lt;em&gt;changing the provisions of the host environment&lt;/em&gt; to suit the problem.&lt;br /&gt;&lt;br /&gt;Because not only do Greasemonkey user scripts suffer the penalties of normal client side development and the traction penalties of the Mozilla javascript sandbox, but also the constraints of working with whole web sites (where data or functionality needed by a user script could be spread across different urls) through the eyes of one single web page at a time. When you develop a browser helper user script for GMail, a dating site, some web forum, game or similar, some of the functionality you want to add on one page might require keeping track of data only found at other pages.&lt;br /&gt;&lt;br /&gt;That quickly becomes a great endeavour keeping track of in a user script, meaning that you will spend more time jumping through hoops and wasting more code in your script on building and maintaining site interfacing code than the function of the script itself. The host environment starts invading your core logic, again, leading you off what your script is really about.&lt;br /&gt;&lt;br /&gt;Attempts at bridging that with @require will force you (read: your script's user base) to reinstall every script you use that library in every time something on the site has changed that requires a fix in the library. Slight improvement, but we could do better.&lt;br /&gt;&lt;br /&gt;What Olivier and I wanted (and evolved through much discussion, together with all the other smart voices on the dev list) was a system that would let a user script abstract away that functionality for other scripts, maintaining rules for which pages to run on, keeping local storage for whatever state info it see fit and so on, exporting some abstract API to third party scripts that in turn want to focus solely on site related functionality, and not on site plumbing.&lt;br /&gt;&lt;br /&gt;It proved staggeringly difficult to reach consensus about the soundness of these ideas on-list, though two or three of four or five had a very good grasp of what value they would bring to user script development. I think we may soon see a somewhat different take on how the Greasemonkey project evolves and is maintained. I hope we will keep about the same track record of stability and security in what we release to you at &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;addons.mozilla.org&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I also hope we might make it easier to bring about new tooling like this in the future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-195785581511000818?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=jOkuJbG4MLU:IphEArAbGHM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=jOkuJbG4MLU:IphEArAbGHM:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=jOkuJbG4MLU:IphEArAbGHM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=jOkuJbG4MLU:IphEArAbGHM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=jOkuJbG4MLU:IphEArAbGHM:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/195785581511000818/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=195785581511000818" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/195785581511000818" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/195785581511000818" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/jOkuJbG4MLU/greasemonkey-and-bottom-up-user.html" title="Greasemonkey and bottom-up user scripting" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/06/greasemonkey-and-bottom-up-user.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-1555710746778161913</id><published>2009-06-01T13:08:00.004+02:00</published><updated>2009-06-01T20:12:04.170+02:00</updated><title type="text">XPath bookmarks</title><content type="html">A few years ago, annoyed with web pages that don't offer in-page permalinks where they ought to, I wrote a tiny &lt;a href="http://userscripts.org/scripts/show/8924"&gt;XPath bookmark&lt;/a&gt; user script to solve half of the problem -- scrolling to the location in the document to which a URL fragment formatted as &lt;code&gt;&lt;a href="http://code.google.com/p/support/wiki/DVCSAnalysis#xpath:%28id%28%22commentlist%22%29//*[@class=%22artifactcomment%22]%29[15]"&gt;#xpath:&lt;i&gt;some-xpath-expression&lt;/i&gt;&lt;/a&gt;&lt;/code&gt; was pointing, when visiting any such URL.&lt;br /&gt;&lt;br /&gt;I didn't end up using those URLs much in practice, largely because it was a bit of a hassle crafting those XPath expressions, and to some extent because they wouldn't do much for people that did not run that user script too.&lt;br /&gt;&lt;br /&gt;At some later point in time, I seem to have realized that I didn't, and that my &lt;a href="http://userscripts.org/scripts/show/21947"&gt;custom keyboard bindings&lt;/a&gt; script, which I mainly use to scroll search results and other (typically paginated) similarly item-oriented pages, one item at a time (via &lt;code&gt;m&lt;/code&gt;/&lt;code&gt;p&lt;/code&gt; -- don't ask :-), knowing about XPath expressions for the stuff it was navigating, could easily offer the feature of creating those XPath bookmarks for me. [&lt;a href="#footnote-1" name="footnote-1-back" style="color:#AAA"&gt;1&lt;/a&gt;]&lt;br /&gt;&lt;br /&gt;So I apparently implemented that feature, called it &lt;code&gt;?&lt;/code&gt; and promptly forgot all about it. Needless to say, I still didn't use them any more than I used to before.&lt;br /&gt;&lt;br /&gt;Today, I realized that my (forever growing) &lt;a href="http://userscripts.org/scripts/show/19344"&gt;fold comments&lt;/a&gt; script knows a thing or two about XPath expressions to stuff in a page (visitor comments, typically) and ought to expose that to this magic bookmark creation script. Especially since it is typically bookmarking some specific comment I wanted, anyway. It at the same time dawned on me that if I just let it share this XPath expression referencing comments (that it would fold) with the &lt;code&gt;items-xpath&lt;/code&gt; meta tag of the &lt;a href="http://userscripts.org/scripts/show/23175"&gt;pagination microformat&lt;/a&gt; that the above keyboard bindings script groks, and that script, in turn, &lt;i&gt;always&lt;/i&gt; updated the URL when scrolling to a new place in the document, my primary use case would be covered for most of the sites I frequent a lot, at least.&lt;br /&gt;&lt;br /&gt;I was quite bemused finding that I had implemented the &lt;code&gt;?&lt;/code&gt; thing, and in short work updated it to do that automatically. So, at long last, setting XPath bookmarks to in-page items of opaque, permalink deprived web pages, is a breeze. I wonder how much they will end up being used. As I never even blogged about the &lt;a href="http://userscripts.org/scripts/show/8924"&gt;XPath bookmark&lt;/a&gt; resolver script in the first place, it hasn't picked up any mentionable user base (245 installs at present time of writing). I think it is a feature it would do the web a whole lot of good, if something like it gained traction and widespread adoption.&lt;br /&gt;&lt;br /&gt;Not all XPath expressions make good bookmarks, of course, on web pages that change over time, but very many do, and for static content especially, it's pretty much a guarantee -- even in the face of stuff like CSS layout changes, just as permalinks do.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;[&lt;a name="footnote-1" href="#footnote-1-back" style="color:#AAA"&gt;1&lt;/a&gt;] Zipping through template-formatted items one item at a time with some keyboard command, so your eyes do not have to shift focal point, and can focus on just the changing content (minus static template administrativia, which suddenly cognitively completely disappears from view), is a positively huge UI improvement over either of the normal practices of reading a little, scrolling a little, and reading a little more, or skimming a page, hitting page down, reorienting yourself, and repeating the procedure.&lt;br /&gt;&lt;br /&gt;It gets even worse with paginated web pages like search results, where, in addition to that, you have to click "Next" and wait for a moment somewhere in the work flow. Fixing this problem by pushing "endless pages" (that automatically load another pageful of content once you scroll to the end of the page) onto all your visitors, is the wrong way; many will hate you for it. As for my own browsing, I sooner or later tend to end up adding a &lt;a href="http://userscripts.org/tags/paginationmicroformat"&gt;pagination microformat producer&lt;/a&gt; script (if a site already has those meta tags present with correct XPath expressions in them, I of course would not need to do so myself) for my &lt;a href="http://userscripts.org/scripts/show/23175"&gt;unpaginate pagination microformated web pages&lt;/a&gt; script.&lt;br /&gt;&lt;br /&gt;Those scripts work well in unison, so I can key down an item at a time for pages upon pages, at worst having to wait for a moment here or there while next page is loading.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-1555710746778161913?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=G4l17bpUHc4:9EGuRXaQ5Qg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=G4l17bpUHc4:9EGuRXaQ5Qg:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=G4l17bpUHc4:9EGuRXaQ5Qg:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=G4l17bpUHc4:9EGuRXaQ5Qg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=G4l17bpUHc4:9EGuRXaQ5Qg:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://userscripts.org/scripts/show/8924" title="XPath bookmarks" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/1555710746778161913/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=1555710746778161913" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/1555710746778161913" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/1555710746778161913" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/G4l17bpUHc4/xpath-bookmarks.html" title="XPath bookmarks" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/06/xpath-bookmarks.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-6408889968192887001</id><published>2009-05-16T02:11:00.005+02:00</published><updated>2009-06-01T13:07:43.836+02:00</updated><title type="text">Resolving IDNA urls in browser javascript</title><content type="html">A handy feature I discovered a while ago: resolving (for want of a better word -- I can't make up my mind whether to call turning Unicode to ASCII encoding or decoding in this context :-) IDNA URLs (&lt;a href="http://www.ietf.org/rfc/rfc3490.txt"&gt;RFC 3490: International Domain Names in Applications&lt;/a&gt;) is a breeze:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;function resolveIDNA(url) {&lt;br /&gt;  var a = document.createElement("a");&lt;br /&gt;  a.href = url;&lt;br /&gt;  return a.href;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;a href='javascript:void prompt((function decodeIDNA(url){var a = document.createElement("a"); a.href = url; return a.href; })("http://räksmörgås.nu/")==="http://xn--rksmrgs-5wao1o.nu/"?"Yep, your browser handles it:" : "Nope, your browser does not handle it:", navigator.userAgent)'&gt;Test your browser&lt;/a&gt;, or &lt;a href='javascript:var url=prompt("Specify IDNA URL (not just domain name):", "http://räksmörgås.nu/");prompt(url, (function decodeIDNA(url){var a = document.createElement("a"); a.href = url; return a.href; })(url));void 0'&gt;try your own IDN domains&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;It doesn't seem to work in all browsers yet, though; of those I've tested, Firefox 3 and Safari 3 both support it, whereas Opera 10.4.11 and Google Chrome 1.0.154.65 do not.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; Both as my google memory didn't find this when I sought it out (decoding a non-ASCII, non-IDNA-encoded hostname to its punycode/IDNA-encoded ASCII counterpart), and for having found a &lt;a href="http://userscripts.org/scripts/show/19344"&gt;use for this in the wild&lt;/a&gt;, here is the altered function that just takes a unicode hostname and turns it into its punycode/IDNA form:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;function IDNAtoASCII(hostname) {&lt;br /&gt;  var a = document.createElement("a");&lt;br /&gt;  a.href = "http://" + hostname + "/";&lt;br /&gt;  return a.hostname;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-6408889968192887001?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=ppuumQUQCzU:5s73XqGxrwA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=ppuumQUQCzU:5s73XqGxrwA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=ppuumQUQCzU:5s73XqGxrwA:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=ppuumQUQCzU:5s73XqGxrwA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=ppuumQUQCzU:5s73XqGxrwA:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/6408889968192887001/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=6408889968192887001" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6408889968192887001" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6408889968192887001" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/ppuumQUQCzU/resolving-idna-urls-in-browser.html" title="Resolving IDNA urls in browser javascript" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/05/resolving-idna-urls-in-browser.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-8153816680196371369</id><published>2009-05-10T22:10:00.006+02:00</published><updated>2009-05-25T04:22:53.912+02:00</updated><title type="text">Little projects I'm warming up to</title><content type="html">I've been cooped up a lot between real life, work and a low energy budget for a while now, but think I'm getting a hankering for some ideas I'd like to develop or see developed, again. Here's a little off-the-top-of-my-head list of a few of them, small and large:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Data SVGs: Craft a few examples of how to encode data in its original raw form in SVG graph(ic)s and have the SVG render them right via coordinate transforms. Forward goal: get Wikipedia to incorporate this principle in their guidelines for how to represent data in vector graphics. Erik Möller (Deputy Director at Wikimedia Foundation) and Brion Vibber (CTO) both found this a cool idea. I ought to at least blog it if I'm not going to be the main advocate rallying people around the idea.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Migrate &lt;a href="http://kronos-forum.notlong.com/"&gt;Kronos Utils&lt;/a&gt; (&lt;a href="http://kronos-utils.notlong.com/"&gt;user script here&lt;/a&gt;), which I'm not maintaining or developing as much as I used to, and other ikariam related hackery I've done at some time or others, to &lt;a href="http://github.com/"&gt;github&lt;/a&gt;, so others that want to hack at it have an easier time forking it. I apparently started doing that, at some time or others, in an &lt;a href="http://github.com/johan/ikariam/tree/master"&gt;ikariam&lt;/a&gt; repository, and then promptly forgot about it. *pushes recent changes*&lt;br /&gt;&lt;br /&gt;So I guess this pretty much boils down to migrating my updates off of Google Code and onto githubg. And figuring out how to have my pushes and/or commits trigger a tweet to &lt;a href="http://twitter.com/ikariam_tools/"&gt;@ikariam_tools&lt;/a&gt;, which apparently people have already spotted and started following, despite its severe lack of content. :-) Does anyone remember how that is done? I seem to recall some setting somewhere for it at github, but wasn't successful finding it in all of a minute's scouting around.&lt;br /&gt;&lt;br /&gt;Oh, and figuring out how to have vc-mode in Emacs do git things.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Run the &lt;a href="http://jquery.com/test/"&gt;jQuery test suite&lt;/a&gt; from a Greasemonkey script, to figure out how much breaks under the Firefox javascript sandbox. It'll be nice to have fine grained data on which parts that remain useable and which don't, so maybe someone would even care to fix the latter.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Revive the live Exhibit web scraper thing I crafted some year ago and never got to the point of happiness with where I ever blogged about it. With &lt;a href="http://extjs.com/blog/2009/05/04/ext-js-30-rc11-released/"&gt;recent development in Ext JS&lt;/a&gt;, it seems it could get a nice UI indeed, this time in a pop-open window, so I don't have the CSS invasion problem, and don't invade the DOM we're trying to scrape.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Don't &lt;a href="http://userscripts.org/scripts/show/19344"&gt;fold comments&lt;/a&gt; on pages we arrived at through a permalink with a fragment pointing to something in the foldable section (at that point, we know we foremost wanted to read that specific comment, and not the article, so don't tuck it away). &lt;b&gt;Edit:&lt;/b&gt; Done!&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Taking down that specification in text instead of code seems indicative I'm not really in a coding mood today and ought to go have lunch. See you around!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-8153816680196371369?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=MIBZHOj6v3A:Qhi8_B-KFBI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=MIBZHOj6v3A:Qhi8_B-KFBI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=MIBZHOj6v3A:Qhi8_B-KFBI:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=MIBZHOj6v3A:Qhi8_B-KFBI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=MIBZHOj6v3A:Qhi8_B-KFBI:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/8153816680196371369/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=8153816680196371369" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/8153816680196371369" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/8153816680196371369" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/MIBZHOj6v3A/little-projects-im-warming-up-to.html" title="Little projects I'm warming up to" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/05/little-projects-im-warming-up-to.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-7356534230037901001</id><published>2009-04-03T02:26:00.002+02:00</published><updated>2009-04-03T02:38:41.391+02:00</updated><title type="text">A Mozilla bug</title><content type="html">CSS &lt;code&gt;rgba(...)&lt;/code&gt; colours are fun. Firefox 3 doesn't handle them quite perfectly when applied to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag, though. I haven't seen this sort of reflow bug (leaving shadows behind dragged objects) much since I, pretty much, ceased using Windows machines a few years ago.&lt;br /&gt;&lt;br /&gt;Nostalgia to be had! Try &lt;a href='javascript:void(document.documentElement.style.background="rgba(255,128,128,0.1)")'&gt;this bookmarklet&lt;/a&gt;, say, on &lt;a href="http://interface.eyecon.ro/demos/windows.html"&gt;this page&lt;/a&gt; and play around with the mock window at your heart's content.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-7356534230037901001?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=l5c3_B4Ds8c:6mpy2c71jQ0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=l5c3_B4Ds8c:6mpy2c71jQ0:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=l5c3_B4Ds8c:6mpy2c71jQ0:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=l5c3_B4Ds8c:6mpy2c71jQ0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=l5c3_B4Ds8c:6mpy2c71jQ0:2mJPEYqXBVI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=2mJPEYqXBVI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/7356534230037901001/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=7356534230037901001" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7356534230037901001" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7356534230037901001" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/l5c3_B4Ds8c/mozilla-bug.html" title="A Mozilla bug" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2009/04/mozilla-bug.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-6082322804940400584</id><published>2008-12-11T10:06:00.005+01:00</published><updated>2008-12-11T12:53:15.845+01:00</updated><title type="text">Wishlist items</title><content type="html">I attended Add-on-con's &lt;a href="http://www.facebook.com/event.php?eid=48573442145"&gt;open house meetup at Mozilla headquarters&lt;/a&gt; here in Mountain View earlier tonight, airing a wish list item of mine in the trailing discussion about what we want from Firefox, after some talks about what's coming up next, and where Mozilla want to go at the moment. It was a good session meeting friends and new acquaintances, and I even recognized a few faces from &lt;a href="http://www.mashupcamp.com/"&gt;Mashup Camp&lt;/a&gt; a few weeks ago. Being the season for wishlists, I thought I'd sum up some of mine, on a more interesting scope than those we might share with our friends and family about items, activities and whatnot. Anything goes, high and low -- I even mention the point I brought up earlier during the evening.&lt;br /&gt;&lt;br /&gt;This is a rather scattered list of stuff I want, and would find most incredibly practical, useful, powerful extensions of will, mind and capability. Consider it food for thought, if you will, or inspiration to wild, liberating, wonderful hackery, or even for being my Santa this year. If neither, please post your own, to your own blog. I've tagged mine "&lt;a href="http://delicious.com/tag/tech_wishlist"&gt;tech_wishlist&lt;/a&gt;" on Del.icio.us, and encourage you to do the same (both tagging your own post with it, and any other person's post that contains items that you want, too). That's a powerful way of connecting brains, folks, sharing and taking inspiration and cooperation to pretty wild levels. We owe it to ourselves and humanity to make the most of the tools and ideas we generate and adopt, and one very important aspect of that is sharing them.&lt;br /&gt;&lt;br /&gt;(This also includes music and other media, by the way, but those industries are too busy not understanding how to monetize it right, and doing all they can to tie us up in pre-wired ages where distribution and marketing cost money, to notice. But I digress.)&lt;br /&gt;&lt;br /&gt;&lt;dl&gt;&lt;dt&gt;I want file system access to the web&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;File systems are often conceived of a way of organizing information on disk, when the important part of them really is a way of accessing information. You'd be surprised how powerful an augmentation tool file systems are, and &lt;a href="http://www.youtube.com/watch?v=Yjdp70474LE"&gt;how easy it is today to write one today&lt;/a&gt;, as compared to the old bad days, now that we have great things like &lt;a href="http://fuse.sourceforge.net/"&gt;FUSE&lt;/a&gt; or &lt;a href="http://code.google.com/p/macfuse/"&gt;MacFUSE&lt;/a&gt;. File systems let us reach data and resources in a heavily standardized fashion using all the computing tools there is, whether made for the web, desktop, or any other conceivable environment. The entire web is a walled garden not available to all the tooling there is. We should do something about this.&lt;/dd&gt;&lt;br /&gt;&lt;br /&gt;&lt;dt&gt;I want XPath file system access to nodes in XML and HTML documents&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;File system paths don't have to have any posix semantics built into them by any necessity; they are identifiers that discern data storage location, or reference particular information, if you will. The file system is as little bound to following specific conventions about what a path means as a web server is forced to pick any particular semantics to how it interprets path and query segments of its URLs. In practice, this means that a file system path can very well contain an XPath expression digging out information of a web document, serving a smaller document than accessing the document itself would, after filtering out all the junk you were not interested in. Similarly, a file system path can contain SQL queries or poke search words into google. Doing these kinds of operations has historically locked you into particular tool chains, programming languages/environments and whatnot, for no good reason. Computers are here to liberate and augment our will, not force it into constraining walled gardens.&lt;/dd&gt;&lt;br /&gt;&lt;br /&gt;&lt;dt&gt;I want file system access to my running browser session&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;If you've become dependent on and enamoured with any kind of interactive command line / read-eval-print-loop environment like &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt;, favourite programming language and/or the unix shell, and do any kind of Firefox extension development, chances are you've encountered or would benefit from acquainting &lt;a href="http://github.com/bard/mozrepl/wikis/home"&gt;MozRepl&lt;/a&gt;, formerly part of the MozLab extension, which lets you telnet into your running Firefox session (I hear it works in most of the other stuff based the Mozilla toolkit too, but haven't tried myself). This lets you play around in chrome space as you would in the sandboxed content space with Firebug via a simple telnet session, or indeed right from inside of Emacs, if that's your fancy.&lt;br /&gt;&lt;br /&gt;Well, I think browsers have been divorced from the unix and the rest of the tool chain for too long, and I want to operate on and work with the files I have in my brower session without any silly fuss about having me store dead documents on disk, picking a location for them, directing my tool of choice to it, and losing all the benefits of live computing and convenience of access that entails in one blow. The web is mine, and I want to interact with it in &lt;i&gt;my&lt;/i&gt; fashion, whichever that might be at the time. I like some aspects of accessing it through the glossy chrome that is Firefox, or Google Chrome, or even the iPhone, but I don't want to be walled in there either, and getting file system access to my live session's tabs and documents means getting access to a specific view pointed at &lt;i&gt;me&lt;/i&gt; with the cookies and other me-centric data that some random tool like &lt;tt&gt;wget&lt;/tt&gt; wouldn't see without a lot of laboursome work.&lt;br /&gt;&lt;br /&gt;And it would let me into &lt;i&gt;the same&lt;/i&gt; walled garden that my current web browser session has already opened the doors to, when I first logged in to whichever site I am on, to do whatever I do there; socializing, shopping, blogging, banking, researching, mailing, playing, working, or whatnot. Cd:ing from window to window, tab to tab, and reaching the documents, images, audio, video and linked documents other referenced from there to do whatever my bidding. Scores of operations you wouldn't even come to think of as possible become downright simple, and useful, in an environment like this. Again, it breaks down silly walls that block our view and perception today.&lt;br /&gt;&lt;br /&gt;I'll wager our kids and grandchildren will find the web and computing environments of the early century very backwards indeed, scoffing at the insane amounts of effort spent theming, skinning and reshaping all the walls we locked ourselves up behind, with stuff that did not evolve together as an ecosystem, but separated, and screened off from one another in different dimensions pasted together only very lightly via human-bound I/O, and the monitor/speaker-mouse-keyboard read-eval-print loop. Silly, really. But I guess we really like these walls. It's so easy thinking inside the boxes we grew up with.&lt;br /&gt;&lt;br /&gt;Neither of these ideas are new, by the way; there have been wonderful pearls like Plan 9 in the past, but they were, you guessed it, walled away from the other stuff that was evolving at the time, and died out in niches few humans ever colonized and made their own. It's not enough making the best stuff humanity has ever seen, if humanity never sees it, for anyone but yourself, and your legacy and contribution back to the world is measured by how you both execute it, evangelize and show and tell it to others so that they, too, can benefit from it and keep evolving it, adapt and repurpose it, bring it across other walls you didn't care to cross, came up with the idea to, or even figured possible. Note also, how they tend to be orthogonal to one another, and how well they stack up when combined together again, building atop one another.&lt;br /&gt;&lt;br /&gt;Combine with all the tooling you had which was previously prevented from being part of the ecosystem, and not even the sky is your limit. Which begs my next item:&lt;/dd&gt;&lt;br /&gt;&lt;br /&gt;&lt;dt&gt;I want file system accessible cloud storage&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;This already exists, but not for long enough to have been commoditized yet, the way web mail is; &lt;a href="http://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt;, for instance, can already be &lt;a href="http://code.google.com/p/s3fs/wiki/FuseOverAmazon"&gt;combined with S3FS&lt;/a&gt; (free, besides applicable S3 fees) and FUSE or MacFUSE, or &lt;a href="http://www.subcloud.com/"&gt;SubCloud&lt;/a&gt; (expensive enterprise software).&lt;/dd&gt;&lt;br /&gt;&lt;br /&gt;&lt;dt&gt;I want browser standardized access to commodity cloud storage&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;In our walled-in browser reality of today, it would already be a big win having a user provided bucket in the sky to tie browser data to, rather than sticking the goo on local disk, and figuring out complex syncing schemes to connect one computer's local disk data with another one's. Just access the web with any device and have them all phone home to your bucket in the sky to pick up the you bound data.&lt;br /&gt;&lt;br /&gt;Note that it should be &lt;i&gt;your&lt;/i&gt; bucket, which you handle with ease and can transfer to some other bucket cupboard provider, should you for some reason want to; we want to avoid lock-in. Even though I kind of like the shape and sound you get from Amazon's buckets. Everybody won't. Heck, Google might give you a free bucket, some day, or at least charge you fewer bucks. Or maybe Mozilla would find some way of remaining a nonprofit by routing bucks their search engine integration, or a Mozilla held app store or market for extensions would give them, from your usage of said search engines, or a percentage of the profits of commercial apps sold through such an entity -- into the payment of your Mozilla sponsored bucket, for browsing with Firefox, when browsing the web through their particular chrome.&lt;br /&gt;&lt;br /&gt;Which reminds me I promised to share my tech-wishlist item from earlier tonight:&lt;/dd&gt;&lt;br /&gt;&lt;br /&gt;&lt;dt&gt;I want Firefox to remember the URLs it installed extensions from&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;That way, us extension authors (I speak as one of the maintainers and developers of &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;Greasemonkey&lt;/a&gt; and &lt;a href="http://mashlogic.com/"&gt;MashLogic&lt;/a&gt; alike) can pick up configuration data passed in, for instance, the query and/or fragment parts of that URL.&lt;br /&gt;&lt;br /&gt;This is primarily useful for improving and streamlining a most seamless installation experience for users, so that -- in the case of Greasemonkey for instance -- a user script link, when clicked by a user that has Greasemonkey installed, installs the script, and when clicked by a user not having Greasemonkey installed, prompts with the Greasemonkey extension installation sequence, which installs Greasemonkey, which then proceeds to install the script.&lt;br /&gt;&lt;br /&gt;Via the same link, which points to the Greasemonkey xpi parametrized with the URL for the user script to install. The same kind of streamlining and on-web-sharing of initial configuration data applies very much to other extensions too, perhaps particularly so commercial grade extensions where business models benefit from shaving down the install and setup threshold for users as much as conceivably possible, and what better way to do it than to allow the prospective user to do it on the familiar web rather than to mandate doing it within some walled-in confines of a XUL garden?&lt;br /&gt;&lt;br /&gt;I know; I wouldn't want to &lt;i&gt;have&lt;/i&gt; to do it on the web, but that's a familiar setting for web users, and it allows people to help one another on the web with doing actual configuration presets, rather than describing in words and pictures how to do manual labour in often contrived and messy user interfaces, which makes non-expert users feel it is way too difficult and well beyond their understanding. But clicking an install link after reading a &lt;a href="http://techcrunch.com/"&gt;TechCrunch&lt;/a&gt; or &lt;a href="http://lifehacker.com/"&gt;LifeHacker&lt;/a&gt; post about a specific use case and setup for it, shared in same post, is available to everybody. This is the kind of direction at least we at MashLogic want the web to take, but it's presently awful messy doing that kind of thing. (Not that we'll let that discourage us, but it's an unnecessary shortcoming of the platform forcing you to bring your own duct tape to do it.)&lt;/dd&gt;&lt;br /&gt;&lt;/dl&gt;&lt;br /&gt;&lt;br /&gt;A few of these ideas probably constitute quantum leaps in computing, if or when pulled out of idea space and reshaped as implemented functionality, given more detailed shape and form, convention, and figuring out idealized notions of how to come across the many mismatched and yet unmet problems that will come along the way, as we evolve the tools of the past into the tools of the future (what we always do as wizards and programmers, somewhat romanticized, and sometimes with more or less merit than otherwise). Figuring out ideal solutions is not important upfront; getting started and making incremental improvement over the present is. Doing even a subset of even one or a few of these will enable us to do many things previously much more difficult or undoable, and you, and/or others can keep polishing it up later. That's some of what open source and publication does best.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-6082322804940400584?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=he6siSkbkq8:3fPiWNKgRKE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=he6siSkbkq8:3fPiWNKgRKE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=he6siSkbkq8:3fPiWNKgRKE:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=he6siSkbkq8:3fPiWNKgRKE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/6082322804940400584/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=6082322804940400584" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6082322804940400584" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6082322804940400584" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/he6siSkbkq8/wishlist-items.html" title="Wishlist items" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/12/wishlist-items.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-6346949393033487834</id><published>2008-12-07T08:15:00.003+01:00</published><updated>2008-12-07T10:17:44.168+01:00</updated><title type="text">No-interface interactive maps</title><content type="html">I've wanted to do this kind of hack for ages, since social software became commonplace on the web. Being both very bad at geography (even worse so, US geography, having moved here only just recently), and rather well versed with mucking about with other web developers' user interfaces, I finally took to it yesterday night (I've admittedly done a similar Greasemonkey hack for Facebook at some time, but it wasn't nearly as fun, and has most likely rotted since then, anyway).&lt;br /&gt;&lt;br /&gt;The idea is basic: you tend to see location names (cities, addresses and the like) associated with users or events strewn out all over the place. Those don't tell me a whole lot unless I've been there, live near them, or similar. Maps do, though -- at least if I have some reference point I know to relate them to. For a site like &lt;a href="http://www.okcupid.com/"&gt;OkCupid&lt;/a&gt; which is all about getting to know people (or yourself, but that's a different story), the natural point is of course home. And these days, &lt;a href="http://maps.google.com/"&gt;Google Maps&lt;/a&gt; offers some really nifty geocoding and travel directions APIs. Add some Greasemonkey magic to the mix, and voilà -- a context sensitive map section which shows you where people are, in relation to yourself, as you hover a location name. It's quite snappy, and completely effortless from the user's point of view; no typing, clicking, dragging or otherwise involved:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.screencast.com/users/ecmanaut/folders/Jing/media/078c9674-3a5c-4fae-aedf-4fce42444ab2" style="float:left; margin-right: 1em; margin-bottom: 1em;"&gt;&lt;img src="http://farm4.static.flickr.com/3182/3089016070_6fbfafcfea_m.jpg" width="240" height="173" alt="OkCupid Maps" /&gt;&lt;/a&gt; This really is my favourite kind of interface: it doesn't have a learning curve, takes no clicking about, and has intuitive discovery properties; hover a place, and the same place shows up, name and all, in the map view.&lt;br /&gt;&lt;br /&gt;It can of course use some additional polish like drag-a-corner-to-resize, a get-out-of-my-face icon to turn it off and the like, but suddenly it's a whole little software project rather than a one-nighter. That said, I think I like it well enough to want it across the entire web. Add those, a little toolbar to drag it around with and whip up &lt;a href="http://microformats.org/wiki/geo"&gt;geo&lt;/a&gt; and &lt;a href="http://microformats.org/wiki/adr"&gt;adr&lt;/a&gt; micro formats detection for it, and all of a sudden it's a neat all-round tool.&lt;br /&gt;&lt;br /&gt;To finish off, closing the circle of the original OkCupid hack, replace that one with one that just sweeps OkCupids pages and converts its place name HTML to the adr micro format -- and the two tools snap together forming a &lt;a href="http://ecmanaut.blogspot.com/2006/04/stigmergic-user-script-pattern.html"&gt;stigmergy&lt;/a&gt; of &lt;a href="http://ecmanaut.blogspot.com/2008/02/cooperating-greasemonkey-scripts.html"&gt;cooperating Greasemonkey scripts&lt;/a&gt;. (It's a development pattern I &lt;i&gt;really&lt;/i&gt; enjoy, for the unanticipated emergent bonus effects it tends to bring.)&lt;br /&gt;&lt;br /&gt;Anyway, I thought I'd shine some light in the rest of the post on how I put it together, as it's painful doing things with Google Maps from Greasemonkey (can't inject the Maps API comfily, as its loader mucks about with document.write, which only works as intended for inline script tags, as opposed to dynamically injected code), and since my technique is rather easy to borrow.&lt;br /&gt;&lt;br /&gt;The map area and geocoding is all done in a little iframe I load from &lt;a href="http://hacks.ecmanaut.googlepages.com/geocoder-iframe.html?at=Mountain%20View,%20California,%20United%20States&amp;country=United%20States&amp;nav=3&amp;z=9"&gt;here&lt;/a&gt;, starting it off with some URL encoded query parameters like &lt;b&gt;at=Mountain View, California, United States&lt;/b&gt; (to center the initial map and put a little blob there), &lt;b&gt;country=United States&lt;/b&gt; (which helps the geocoder, IIRC), &lt;b&gt;nav=2&lt;/b&gt; (if we want a medium-size zoom/pan control) and &lt;b&gt;z=9&lt;/b&gt; for a useful default zoom level. (The latter adapts to an appropriate scale when we ask for routes.) It will fill the whole page, or iframe, so we don't need to state any pixel dimensions.&lt;br /&gt;&lt;br /&gt;So how do we tell it to look up places for us? This is where it's getting fun. Using &lt;a href="https://developer.mozilla.org/En/DOM:window.postMessage"&gt;window.postMessage&lt;/a&gt; on the iframe's window object, we send the geocoder its instructions, and using &lt;code&gt;window.addEventListener("message", listener, false)&lt;/code&gt;, we listen to whatever it figures out for us (Firefox 3+, and other modern web browsers).&lt;br /&gt;&lt;br /&gt;For convenience, I went for a rather minimal messaging API; send it a newline-separated list of locations, optionally preceded by a "focus" (to move the view and blob there) or "route" line (to try drawing a route between the first and last address). All places in the list get geocoded, and the return value passed is a JSON structure &lt;code&gt;{"coords":{"place name":[long, lat], "other place name":[long, lat], ...}, "took":t, "type":"geocode"}&lt;/code&gt; where t is the lookup time in milliseconds. Route requests are in addition populated with "distance" and "time" properties, either containing a "html" property (for a human readable form), and a "meters" or "seconds" property respectively.&lt;br /&gt;&lt;br /&gt;And that's really all you need to use it.&lt;br /&gt;&lt;br /&gt;Wanting it somewhat speedy (as the iframe only has page longevity, and thus won't learn and retain address to lat/long mappings over time), I opted to add a little local cache of places already looked up in the past with &lt;a href="https://developer.mozilla.org/En/DOM/Storage#globalStorage"&gt;globalStorage&lt;/a&gt; (Firefox 2+, non-standard). Quick and dirty, but functional, it puts places in a little JSON tree indexed on country, region, city, and so on, however deeply the hierarchy goes. As OkCupid's place names are all on a tidy machine-readable comma-separated list format with consistent capitalization this is a rather small representation; place names in the wild might find it less useful, if still not worthless. As your web browser's IP might get blocked from using the Google Maps geocoder if you use it too recklessly (&amp;gt;15,000 requests per day -- but still), the persistence might be good to have there, just in case.&lt;br /&gt;&lt;br /&gt;As usual, feel free to adopt, embrace, extend and hack about madly with it; code wants love too. The full source (and script, docs, et cetera) of the Greasemonkey script is on &lt;a href="http://userscripts.org/scripts/show/38203"&gt;userscripts.org&lt;/a&gt;, and the Google Maps part is linked above. Happy hacking!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-6346949393033487834?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=rbzvq6NERjU:dVRe7_FU8vc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=rbzvq6NERjU:dVRe7_FU8vc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=rbzvq6NERjU:dVRe7_FU8vc:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=rbzvq6NERjU:dVRe7_FU8vc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://userscripts.org/scripts/show/38203" title="No-interface interactive maps" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/6346949393033487834/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=6346949393033487834" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6346949393033487834" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6346949393033487834" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/rbzvq6NERjU/no-interface-interactive-maps.html" title="No-interface interactive maps" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/12/no-interface-interactive-maps.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-1217941826965136091</id><published>2008-10-11T19:38:00.006+02:00</published><updated>2008-10-12T13:02:31.369+02:00</updated><title type="text">MashLogic</title><content type="html">I'm taking a few moments' refuge from the growing absence of furniture in my echoing soon-to-be former home in Sweden (as my household and me are finally moving to the California Bay Area now -- after about two years more or less stuck in that ghastly red tape/inertia land), to join the little crowd of my coworkers at &lt;a href="http://mashlogic.com/"&gt;MashLogic&lt;/a&gt;, that are just cheerfully lifting the veil off our Firefox/Flock extension of the same name: &lt;a href="http://blog.mashlogic.com/2008/10/10/mashlogic-sets-sail/"&gt;MashLogic sets sail&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; &lt;a href="http://www.techcrunch.com/2008/10/12/mashlogic-take-back-the-web-by-getting-awesome-links/"&gt;TechCrunch liked us too&lt;/a&gt;. Yay! :-)&lt;br /&gt;&lt;br /&gt;A close sibling of Greasemonkey's, we try to shift how the web works, from the walled gardens we have today (where a feature you like would stay within the confines of a particular web site, or, at best, group of web sites) -- towards working more like how communicating vessels or a sea of information would: add a feature you like once, and have it propagate across the entire sea, wherever you may roam.&lt;br /&gt;&lt;br /&gt;Let's say you love how some web site offers explanations of the difficult words it uses, or even links advanced concepts to their Wikipedia article counterparts. While that is a neat feature, the rest of the web remains stuck on its now annoying lower evolutionary notch, devoid of it.&lt;br /&gt;&lt;br /&gt;We think such features should not be implemented in server side logic, once per site, to give you the benefit of it only on a small fraction of the web: they should be part of your browser's toolbox and normal web browsing experience, and subject to your own control and choices.&lt;br /&gt;&lt;br /&gt;That is what we have browser extensions for in the free software world. The extension user experience in Firefox isn't without its flaws, though; easy toggling of functions you might want or not is not is always subject to whether the extension implements its own such toggle, and if they do, they all end up having Yet Another User Interface, yet another icon or menu entry somewhere, and so on. And while you &lt;i&gt;can&lt;/i&gt; disable any extension, you have to restart the browser to be rid of it, and for reversing the operation, restart yet again. There is a very distinct lack of convenience there.&lt;br /&gt;&lt;br /&gt;Greasemonkey improved that picture a lot -- Greasemonkey user scripts all show up the same way in the monkey menu, and you have both an effective off switch for them all in one go, and each individually by checking or unchecking them, at the less insane cost of just reloading the page. MashLogic lets you toggle them and have them apply or undo their effects in real time instead, thus letting you play even comfier still, without even reloading the page, and much less restarting the browser.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It is some rather boring menial labour doing much of the text crunching that goes into a user script or extension that scurries through document node trees too, if you were to make one doing things like the above -- or perhaps an automated currency converter that translates weird foreign price tags, or articles or blog posts naming amounts in foreign currencies, into your own currency of choice.&lt;br /&gt;&lt;br /&gt;The lion's share of the work isn't in mashing up that currency conversion web API you bookmarked the other day with data for it, or even in the challenge of recognizing free form mentions of amount + currency, but in hacking up a little document crawler that finds the stuff and then changes it (without completely locking up the browser). And by the time you realize that, it's not very fun hackery any longer.&lt;br /&gt;&lt;br /&gt;We sort of wanted to address that too, making it more like plugging in your regexp, or a little dictionary, in a polished crawler that already does that boring job for you, so you don't end up reinventing those icky wheels over and over again client side too.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The particular examples above are, unsurprisingly, stuff we already provide, among a handful of other features. There is plenty of room to grow in still, even in features available to Greasemonkey users already (for instance, we lack the provisions for installing new mashes, which is what we call the separate MashLogic features), but we'll mature over time, as any tech worth having does.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I'm eager to get back to the hackery again soon, but you would not believe how long this whole real-worldly stuff of moving from one country to another is taking me; it's almost as if this world of physical matter of ours is immune to my wizardry skills. It just does not compute. I think it's broken somewhere. Where do we file tickets on it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-1217941826965136091?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1Qxr1GZ12nI:5ZsdI6StHo8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1Qxr1GZ12nI:5ZsdI6StHo8:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1Qxr1GZ12nI:5ZsdI6StHo8:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=1Qxr1GZ12nI:5ZsdI6StHo8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://blog.mashlogic.com/2008/10/10/mashlogic-sets-sail/" title="MashLogic" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/1217941826965136091/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=1217941826965136091" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/1217941826965136091" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/1217941826965136091" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/1Qxr1GZ12nI/mashlogic.html" title="MashLogic" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/10/mashlogic.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-7630920454684944752</id><published>2008-07-24T19:44:00.005+02:00</published><updated>2008-07-24T21:35:26.626+02:00</updated><title type="text">Fun with Greasemonkey: @require</title><content type="html">After &lt;a href="http://www.greasespot.net/2008/06/greasemonkey-version-08.html"&gt;the release of Greasemonkey 0.8&lt;/a&gt;, the door is open for &lt;code&gt;@require&lt;/code&gt; helper libraries, the user script take on Ruby's &lt;code&gt;require&lt;/code&gt;, or the C family's &lt;code&gt;#include&lt;/code&gt; pre-processor directive. For security reasons, the required library is downloaded a single time at script installation time, and from a functional perspective, is equivalent to pasting the script or scripts referenced at the top of your script, in the order of the &lt;code&gt;@require&lt;/code&gt; lines listed. (&lt;code&gt;==UserScript==&lt;/code&gt; headers in the referenced files are presently ignored, so a library may not yet use &lt;code&gt;@require&lt;/code&gt; or &lt;code&gt;@resource&lt;/code&gt; lines of its own.)&lt;br /&gt;&lt;br /&gt;&lt;code&gt;@require&lt;/code&gt; in practice makes user scripting &lt;a href="http://intertwingly.net/blog/2008/05/29/Scaling-Rails-Down"&gt;scale down&lt;/a&gt; better, which is essentially to lower the threshold towards getting a job done, to a level where implementation cost to scratch an itch is so low that rather than feeling that mild annoyance with something twice, you address the issue once, and never see it happen again -- on that site, or the web at large. We are still far from there (in as much as "getting there" would ever happen -- that threshold can always be chipped off further, I assure you) -- but &lt;code&gt;@require&lt;/code&gt; is healthy progress.&lt;br /&gt;&lt;br /&gt;This is what the feature can look like in an example script of mine which &lt;a href="http://userscripts.org/scripts/source/25846.user.js"&gt;adds image captions to online comics&lt;/a&gt;, by default &lt;a href="http://qwantz.com/"&gt;Dinosaur Comics&lt;/a&gt; and &lt;a href="http://xkcd.com/"&gt;xkcd&lt;/a&gt; (expect another upcoming post about &lt;code&gt;$x&lt;/code&gt; and &lt;code&gt;$X&lt;/code&gt;):&lt;br /&gt;&lt;pre&gt;&lt;code class="javascript"&gt;// ==UserScript==&lt;br /&gt;// @name      Image title captions&lt;br /&gt;// @namespace http://code.google.com/p/ecmanaut/&lt;br /&gt;// @require   http://ecmanaut.googlecode.com/svn/trunk/lib/gm/$x$X.js&lt;br /&gt;// ==/UserScript==&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I'll be sharing some of my own helpers here, in a little series of "Fun with Greasemonkey" posts. I encourage you to write, and share, your own, on your blog, as well. User script authorship is a very social form of software, especially when you share your results on &lt;a href="http://userscripts.org/"&gt;userscripts.org&lt;/a&gt;, and your best practices, for instance by way of blogging.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-7630920454684944752?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=42-80W1EdOs:QsXJvWcSKDA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=42-80W1EdOs:QsXJvWcSKDA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=42-80W1EdOs:QsXJvWcSKDA:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=42-80W1EdOs:QsXJvWcSKDA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/7630920454684944752/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=7630920454684944752" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7630920454684944752" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7630920454684944752" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/42-80W1EdOs/fun-with-greasemonkey-require.html" title="Fun with Greasemonkey: @require" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/07/fun-with-greasemonkey-require.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-2704948189344077505</id><published>2008-06-05T12:45:00.003+02:00</published><updated>2008-06-05T22:58:40.741+02:00</updated><title type="text">Javascript books</title><content type="html">I just received two javascript books I recently ordered from Amazon (the US branch, for once, since the dirt cheap dollar made shipping across the Atlantic notably favourable to shipping across the English Channel): &lt;a href="http://ejohn.org/"&gt;John Resig&lt;/a&gt;'s &lt;a href="http://jspro.org/"&gt;Pro javascript techniques&lt;/a&gt; and &lt;a href="http://crockford.com/"&gt;Douglas Crockford&lt;/a&gt;'s &lt;a href="http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742/"&gt;Javascript: the good parts&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I had for the longest time (since the nineties) been of the opinion that the only javascript book worth getting was &lt;a href="http://davidflanagan.com/"&gt;David Flanagan&lt;/a&gt;'s &lt;a href="http://www.amazon.com/gp/product/0596000480"&gt;Javascript: the definitive guide&lt;/a&gt;, which I at the time found excellent in it's third edition and still good (and much updated) in its fourth (by my cursory looks and used for reference, at least), but as two of my favourite authors on the subject of javascript (the last being &lt;a href="http://dean.edwards.name/"&gt;Dean Edwards&lt;/a&gt;, whose primarily means of expression on the subject, so far, at least, is terse, solid code) have been published since then, it was time to challenge that assumption. And I'm quite glad to say that both were up to par.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;a href="http://jspro.org/"&gt;Pro javascript techniques&lt;/a&gt;&lt;/i&gt; really shows Resig's experience in library writing, and shares lots of useful input and practices, and provides an enjoyable and rather useful walk-through of the popular javascript libraries of its day, which manages to still be fairly relevant, in spite of the evolution they have seen since its publication. Much the same applies to the bits and details it shares on browsers and the tool-chain, but that is to be expected. (I am really looking forward to its successor, &lt;a href="http://jsninja.com/"&gt;Secrets of the javascript ninja&lt;/a&gt;, much of which can already be read online.) Particularly enjoyably and usefully, he shows off strengths of each library without much fuss.&lt;br /&gt;&lt;br /&gt;As for Crockford's &lt;i&gt;&lt;a href="http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742/"&gt;Javascript: the good parts&lt;/a&gt;&lt;/i&gt;, it follows his usual seasoned narrative which has earned him nicknames such as "the Yoda of javascript", and keeps a close focus on the more timeless parts of the core language itself, devoid of all the clutter and temporally challenged aspects of the document and browser object models. It follows a clean-slate approach to describing the beautiful parts of the language most worth learning, while warning about the pitfalls of some of its worst warts, and is just as enlightening as the rest of Doug's work. It's something of a feel-good book for javascript buffs wanting to meditate over their use of the language -- or at least it is for myself.&lt;br /&gt;&lt;br /&gt;Great companions for a season where the weather merits some good outdoors rather than indoors geekery. ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-2704948189344077505?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=xbXVg_BRbvc:I0SXOw1nxO8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=xbXVg_BRbvc:I0SXOw1nxO8:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=xbXVg_BRbvc:I0SXOw1nxO8:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=xbXVg_BRbvc:I0SXOw1nxO8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/2704948189344077505/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=2704948189344077505" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2704948189344077505" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2704948189344077505" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/xbXVg_BRbvc/javascript-books.html" title="Javascript books" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/06/javascript-books.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-2325513603755370206</id><published>2008-04-10T09:40:00.001+02:00</published><updated>2008-04-10T09:42:03.566+02:00</updated><title type="text">Expensive, yet responsive, live interaction</title><content type="html">Let's say you have a slider widget that users can drag to filter a largeish table so it only shows rows where some column is greater than the a threshold, defined by the slider. That's an application with rather strict real-time constraints, but filtering the table is expensive; a naïve solution that refilters the table with every drag event you get, or even just every event that changes your threshold value, will still be too slow for the interface to feel responsive.&lt;br /&gt;&lt;br /&gt;I just came up with a rather elegant solution for that problem, which makes sure that your expensive code won't make the interface feel sluggish, by postponing calls to it that come in a more rapid succession than the time it would have taken for it to run. Or, to be precise, the time it took last time it was called. It's a simple wrapper function you can reuse anywhere:&lt;br /&gt;&lt;br /&gt;&lt;code class="javascript" style="white-space:pre"&gt;// returns a function that only runs expensive function&lt;br /&gt;// fn after no call to it has been made for n ms, or&lt;br /&gt;// 100, if not given, or the time fn took last time, if&lt;br /&gt;// it has been run at least once and no n was given.&lt;br /&gt;function expensive(fn, n) {&lt;br /&gt;  function run() {&lt;br /&gt;    fn.timeout = null;&lt;br /&gt;    var timer = n || new Date;&lt;br /&gt;    fn();&lt;br /&gt;    if (!n) duration = (new Date) - timer;&lt;br /&gt;  }&lt;br /&gt;  var duration;&lt;br /&gt;  return function postpone() {&lt;br /&gt;    if (fn.timeout)&lt;br /&gt;      clearTimeout(fn.timeout);&lt;br /&gt;    fn.timeout = setTimeout(run, n || duration || 100);&lt;br /&gt;  };&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You might of course want to add some argument and this re-binding to it, for cases where the callback isn't self-contained, but then it gets a bit more cluttered, and I really just wanted to show the principle here. Typical usage is replacing your &lt;code class="javascript"&gt;node.addEventListener("mousemove", callback, false)&lt;/code&gt; with the wrapped &lt;code class="javascript"&gt;node.addEventListener("mousemove", expensive(callback), false)&lt;/code&gt; version, and suddenly, the UI is not only bearable, but probably even rather pleasant to use.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-2325513603755370206?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gCX2qb2bcMc:o88EG-WHqhQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gCX2qb2bcMc:o88EG-WHqhQ:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gCX2qb2bcMc:o88EG-WHqhQ:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gCX2qb2bcMc:o88EG-WHqhQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/2325513603755370206/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=2325513603755370206" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2325513603755370206" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2325513603755370206" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/gCX2qb2bcMc/expensive-yet-responsive-live.html" title="Expensive, yet responsive, live interaction" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/04/expensive-yet-responsive-live.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-2549340381462554870</id><published>2008-03-10T12:05:00.002+01:00</published><updated>2008-03-10T12:47:23.758+01:00</updated><title type="text">Google site index bookmarklet</title><content type="html">The bookmarklet first, for anyone who wants to opt out of the verbiage: I call mine &lt;a href="javascript:location='http://www.google.com/search?q='+encodeURIComponent('site:'+ location.hostname+' inurl:/')"&gt;@site&lt;/a&gt;, which fits snugly in my bookmarks toolbar. It searches Google for any page Google has indexed on the site you are presently visiting, and presents them in Google's usual interface which you learn how to navigate once, and can then use for the rest of your life, pretty much without ever thinking about it.&lt;br /&gt;&lt;br /&gt;I am well aware that one of the things that made the web so hugely popular with content producers, is its very anarchic non-standardization of the user facing interface. Yes, your graphics and interaction designers are at full liberty to devise pretty much any user interface and interaction scheme they damn well want to, and they happily do so, too. The same applies to DVD menus too, though, thankfully, the dvd consortium at least recognized that their medium was one of entertainment primarily (or, through dumb luck) and somewhat constrained the technical possibilities a little.&lt;br /&gt;&lt;br /&gt;Web sites are fairly unconstrained about their navigation (im)possibilities, though, and as every web site ever made is a country of its own with conventions of their own (most -- thankfully -- even shared with or at least inspired by others, modeled from similar components, and so on), so lots of features you might want the medium to provide, like "show me the most popular pages of this site" have to be found, when they exist, identified as such, and learned, before you can use them, once &lt;i&gt;per site&lt;/i&gt;, or even once per content producer and site, when shared between several.&lt;br /&gt;&lt;br /&gt;That most of the web imitates most of the rest doesn't change the fact that visitors still have a level of interface burden that is not associated with media like books, where every language has a common convention for which order you flip the pages, where to start reading, and so on, and, even across languages, most agree on left to right, top to bottom. The web could have conventions like these, but has not evolved to that stage yet (and might never do). There is some consolation in that browsers have a set of basic functionality for finding text within a page, scrolling it and the like, which is rarely intruded upon by web pages (but little prevents them from doing so).&lt;br /&gt;&lt;br /&gt;While I don't know the particular sort criteria Google uses for open searches like the one this bookmarklet does, it often does seem to list the more popular pages first, at least on web sites like &lt;a href="http://www.useit.com/alertbox/"&gt;Alertbox&lt;/a&gt;, some of the first hits are the basic sections of the site. Reading Jacob Nielsen's &lt;a href="http://www.useit.com/alertbox/application-mistakes.html"&gt;top-10 application-design mistakes&lt;/a&gt;, how many perceive the web as &lt;i&gt;one&lt;/i&gt; application? I do. I think most naïve, non-technical users who could not care less about the detail inner life workings of the web, do too.&lt;br /&gt;&lt;br /&gt;And I think you should, too, despite knowing a lot more about it than that. Your web sites and design thoughts will surely benefit from it. I am rarely happier than when taming the web to behave more like one single, internally consistent, application, that responds to my actions in the same way, regardless of whichever of its zillion dialogs I happen to interact with. Try not to waste their brain power on working your part of that application, if you can. Yes, it is more fun for your own ego to have them adapt to your whims than you to their expectations and there are counter-examples to every rule, but those account for miniature fractions of a percentage, and it is pretty safe to assume that your application is not one of them.&lt;br /&gt;&lt;br /&gt;That said, most of my web sites and hacks venture far into the territory of doing things backward, one way or another. I can assure you that the visitor appeal of said hacks, measured in raw numbers, are inversely proportional to their obscurity. The same is true of my obscure writing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-2549340381462554870?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=5C9tcwQplZc:MkG4UGBb5j0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=5C9tcwQplZc:MkG4UGBb5j0:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=5C9tcwQplZc:MkG4UGBb5j0:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=5C9tcwQplZc:MkG4UGBb5j0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/2549340381462554870/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=2549340381462554870" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2549340381462554870" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2549340381462554870" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/5C9tcwQplZc/google-site-index-bookmarklet.html" title="Google site index bookmarklet" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/03/google-site-index-bookmarklet.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-7562036003496663846</id><published>2008-03-08T08:20:00.007+01:00</published><updated>2008-03-08T12:01:24.962+01:00</updated><title type="text">JSEnv live XPath visualizer</title><content type="html">I've had some productive hack weekends recently -- last weekend, among other things, I took a plunge into the Firebug 1.1 beta, both to re-apply a rather &lt;a href="http://ecmanaut.blogspot.com/2007/04/xpath-shorthands-x-and-x.html"&gt;useful feature patch&lt;/a&gt; I submitted upstreams a year ago (which was lost, and found, and lost and found again -- might make 1.2) and to add some handy keyboard shortcuts to the HTML view (deleting nodes with delete and backspace, and both folding/unfolding and stepping up/down the DOM tree with arrow left and right).&lt;br /&gt;&lt;br /&gt;Anyway, I ended up excavating Firebug's highlight-a-node code to turn it into a tiny lib and then use that lib for beefing up &lt;a href="http://www.squarefree.com/2005/05/16/new-version-of-javascript-environment/"&gt;Jesse Ruderman's jsenv bookmarklet&lt;/a&gt; with a live edit XPath field highlighting all matches in the page as you type:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/66491974@N00/2318346380/"&gt;&lt;img class="center" src="http://farm3.static.flickr.com/2153/2318346380_707fd4527c.jpg" alt="screenshot of the live XPath visualizer" title="jsenv += XPath" width="444" height="245" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It tells you what the expression matches in the title bar of the jsenv window, and signals some additional colour coded info via the XPath input field's border:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="border:2px inset #F00; padding:0 2px;"&gt;red for illegal expressions&lt;/span&gt;,&lt;/li&gt;&lt;li&gt;&lt;span style="border:2px inset #0C0; padding:0 2px;"&gt;green for matching nodes&lt;/span&gt;,&lt;/li&gt;&lt;li&gt;&lt;span style="border:2px inset #F0F0F0; padding:0 2px;"&gt;gray for 0 matched nodes&lt;/span&gt;,&lt;/li&gt;&lt;li&gt;&lt;span style="border:2px inset #CC0; padding:0 2px;"&gt;yellow for a string&lt;/span&gt;,&lt;/li&gt;&lt;li&gt;&lt;span style="border:2px inset #00C; padding:0 2px;"&gt;blue for a number&lt;/span&gt; and&lt;/li&gt;&lt;li&gt;&lt;span style="border:2px inset #C0C; padding:0 2px;"&gt;magenta for a boolean value&lt;/span&gt;.&lt;/li&gt;&lt;/ul&gt; Ah, one more feature -- you move the focus node you see with arrow up and down in the input field, or scroll wheel up/down when hovering it.&lt;br /&gt;&lt;br /&gt;The ideal spot for this feature would probably be as an extra feature to toggle on and off in Firebug's HTML view -- to filter the view by, for instance. The more I think of it, the more useful this feature would be in that shape. I hope the &lt;a href="http://www.getfirebug.com/workingGroup/"&gt;Firebug Working Group&lt;/a&gt; is as open to additional community developers as it &lt;a href="http://getfirebug.com/workingGroup/2007/FirebugFutures2007.html"&gt;seems to aim for&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Anyway, here is &lt;a href="javascript:with(window.open(%22%22,%22_blank%22,%22width%3d%22%2bscreen.width*.6%2b%22,left%3d%22%2bscreen.width*.35%2b%22,height%3d%22%2bscreen.height*.9%2b%22,resizable,scrollbars%3dyes%22)){%0adocument.write(%22%3c!DOCTYPE%20HTML%20PUBLIC%20%5c%22-%2f%2fW3C%2f%2fDTD%20HTML%204.01%20Frameset%2f%2fEN%5c%22%5cn%20%20%20%5c%22http:%2f%2fwww.w3.org%2fTR%2fhtml4%2fframeset.dtd%5c%22%3e%5cn%5cn%3chtml%3e%5cn%3chead%3e%5cn%3ctitle%3eJavaScript%20Development%20Environment%202.0.1%3c%2ftitle%3e%5cn%3c%2fhead%3e%5cn%5cn%3c!--%20about:blank%20confuses%20opera..%20--%3e%5cn%5cn%3cframeset%20rows%3d%5c%2225,*,*%5c%22%3e%5cn%5cn%20%20%3cframe%20name%3d%5c%22toolbarFrame%5c%22%20src%3d%5c%22toolbar.html%5c%22%20noresize%3e%5cn%5cn%20%20%3cframe%20name%3d%5c%22inputFrame%5c%22%20src%3d%5c%22input.html%5c%22%3e%5cn%5cn%20%20%3cframe%20name%3d%5c%22outputFrame%5c%22%20src%3d%5c%22about:blank%5c%22%3e%5cn%5cn%3c%2fframeset%3e%5cn%5cn%3c%2fhtml%3e%22)%3bdocument.close()%3b%0aframes[0].document.write(%22%3c!--%20no%20doctype%20-%20it%20makes%20IE%20ignore%20the%20height:%20100%25.%20--%3e%5cn%3chtml%3e%5cn%3chead%3e%5cn%3ctitle%3etoolbarFrame%3c%2ftitle%3e%5cn%3cstyle%20type%3d%5c%22text%2fcss%5c%22%3e%5cnhtml,body%20{%20width:%20100%25%3b%20height:%20100%25%3b%20border:%20none%3b%20margin:%200%3b%20padding:%200%3b%20overflow:%20hidden%3b%20}%5cnbutton%20{%20height:%20100%25%3b%20}%5cn%3c%2fstyle%3e%5cn%5cn%3cscript%20_src%3d%5c%22xpathinput.js%5c%22%3e%5cnfunction%20initXPather()%20{%5cn%20%20xpath%20%3d%20new%20XPathInput(%20document.getElementById(%5c%27xpath%5c%27),%5cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20parent.opener.document%20)%3b%5cn%20%20var%20width%20%3d%20(innerWidth%20-%20getViewOffset(xpath.node,%20false).x%20-%202)%3b%5cn%20%20xpath.node.style.width%20%3d%20width%20%2b%5c%22px%5c%22%3b%5cn}%5cn%5cn(function(_)%20{%5cn%5cnconst%20highlightCSS%20%3d%20%5c%22chrome:%2f%2ffirebug%2fcontent%2fhighlighter.css%5c%22%3b%5cnconst%20xhtmlNS%20%3d%20%5c%22http:%2f%2fwww.w3.org%2f1999%2fxhtml%5c%22%3b%5cn%5cn%2f%2f%20************************************************%20firebug%2flib.js:%5cn%5cnfunction%20move(element,%20x,%20y)%20{%5cn%20%20element.style.left%20%3d%20x%20%2b%20%5c%22px%5c%22%3b%5cn%20%20element.style.top%20%3d%20y%20%2b%20%5c%22px%5c%22%3b%5cn}%5cn%5cnfunction%20resize(element,%20w,%20h)%20{%5cn%20%20element.style.width%20%3d%20w%20%2b%20%5c%22px%5c%22%3b%5cn%20%20element.style.height%20%3d%20h%20%2b%20%5c%22px%5c%22%3b%5cn}%5cn%5cnfunction%20hasClass(node,%20name)%20{%5cn%20%20if%20(!node%20||%20node.nodeType%20!%3d%201)%5cn%20%20%20%20return%20false%3b%5cn%20%20else%20{%5cn%20%20%20%20var%20re%20%3d%20new%20RegExp(%5c%22(^|%5c%5c%5c%5cs)%5c%22%2bname%2b%5c%22($|%5c%5c%5c%5cs)%5c%22)%3b%5cn%20%20%20%20return%20re.exec(node.getAttribute(%5c%22class%5c%22))%20!%3d%20null%3b%5cn%20%20}%5cn}%5cn%5cnfunction%20setClass(node,%20name)%20{%5cn%20%20if%20(node%20%26%26%20!hasClass(node,%20name))%5cn%20%20%20%20node.className%20%2b%3d%20%5c%22%20%5c%22%20%2b%20name%3b%5cn}%5cn%5cnfunction%20removeClass(node,%20name)%20{%5cn%20%20if%20(node%20%26%26%20node.className)%20{%5cn%20%20%20%20var%20index%20%3d%20node.className.indexOf(name)%3b%5cn%20%20%20%20if%20(index%20%3e%3d%200)%20{%5cn%20%20%20%20%20%20var%20size%20%3d%20name.length%3b%5cn%20%20%20%20%20%20node.className%20%3d%5cn%20%20%20%20%20%20%20%20node.className.substr(0,%20index-1)%20%2b%5cn%20%20%20%20%20%20%20%20node.className.substr(index%2bsize)%3b%5cn%20%20%20%20}%5cn%20%20}%5cn}%5cn%5cnfunction%20getBody(doc)%20{%5cn%20%20return%20doc.body%20||%20doc.getElementsByTagName(%5c%22body%5c%22)[0]%3b%5cn}%5cn%5cnfunction%20getViewOffset(elt,%20singleFrame)%20{%5cn%20%20function%20addOffset(elt,%20coords,%20view)%20{%5cn%20%20%20%20var%20p%20%3d%20elt.offsetParent%3b%5cn%20%20%20%20coords.x%20%2b%3d%20elt.offsetLeft%20-%20(p%20?%20p.scrollLeft%20:%200)%3b%5cn%20%20%20%20coords.y%20%2b%3d%20elt.offsetTop%20-%20(p%20?%20p.scrollTop%20:%200)%3b%5cn%5cn%20%20%20%20if%20(p)%20{%5cn%20%20%20%20%20%20if%20(p.nodeType%20%3d%3d%201)%20{%5cn%20%20%20%20%20%20%20%20var%20parentStyle%20%3d%20view.getComputedStyle(p,%20%5c%22%5c%22)%3b%5cn%20%20%20%20%20%20%20%20if%20(parentStyle.position%20!%3d%20%5c%22static%5c%22)%20{%5cn%20%20%20%20%20%20%20%20%20%20coords.x%20%2b%3d%20parseInt(parentStyle.borderLeftWidth)%3b%5cn%20%20%20%20%20%20%20%20%20%20coords.y%20%2b%3d%20parseInt(parentStyle.borderTopWidth)%3b%5cn%5cn%20%20%20%20%20%20%20%20%20%20if%20(p.localName%20%3d%3d%20%5c%22TABLE%5c%22)%20{%5cn%20%20%20%20%20%20%20%20%20%20%20%20coords.x%20%2b%3d%20parseInt(parentStyle.paddingLeft)%3b%5cn%20%20%20%20%20%20%20%20%20%20%20%20coords.y%20%2b%3d%20parseInt(parentStyle.paddingTop)%3b%5cn%20%20%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20%20%20%20%20else%20if%20(p.localName%20%3d%3d%20%5c%22BODY%5c%22)%20{%5cn%20%20%20%20%20%20%20%20%20%20%20%20var%20style%20%3d%20view.getComputedStyle(elt,%20%5c%22%5c%22)%3b%5cn%20%20%20%20%20%20%20%20%20%20%20%20coords.x%20%2b%3d%20parseInt(style.marginLeft)%3b%5cn%20%20%20%20%20%20%20%20%20%20%20%20coords.y%20%2b%3d%20parseInt(style.marginTop)%3b%5cn%20%20%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20%20%20else%20if%20(p.localName%20%3d%3d%20%5c%22BODY%5c%22)%20{%5cn%20%20%20%20%20%20%20%20%20%20coords.x%20%2b%3d%20parseInt(parentStyle.borderLeftWidth)%3b%5cn%20%20%20%20%20%20%20%20%20%20coords.y%20%2b%3d%20parseInt(parentStyle.borderTopWidth)%3b%5cn%20%20%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20%20%20var%20parent%20%3d%20elt.parentNode%3b%5cn%20%20%20%20%20%20%20%20while%20(p%20!%3d%20parent)%20{%5cn%20%20%20%20%20%20%20%20%20%20coords.x%20-%3d%20parent.scrollLeft%3b%5cn%20%20%20%20%20%20%20%20%20%20coords.y%20-%3d%20parent.scrollTop%3b%5cn%20%20%20%20%20%20%20%20%20%20parent%20%3d%20parent.parentNode%3b%5cn%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20%20%20addOffset(p,%20coords,%20view)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%20%20%20%20else%20{%5cn%20%20%20%20%20%20if%20(elt.localName%20%3d%3d%20%5c%22BODY%5c%22)%20{%5cn%20%20%20%20%20%20%20%20var%20style%20%3d%20view.getComputedStyle(elt,%20%5c%22%5c%22)%3b%5cn%20%20%20%20%20%20%20%20coords.x%20%2b%3d%20parseInt(style.borderLeftWidth)%3b%5cn%20%20%20%20%20%20%20%20coords.y%20%2b%3d%20parseInt(style.borderTopWidth)%3b%5cn%5cn%20%20%20%20%20%20%20%20var%20htmlStyle%20%3d%20view.getComputedStyle(elt.parentNode,%20%5c%22%5c%22)%3b%5cn%20%20%20%20%20%20%20%20coords.x%20-%3d%20parseInt(htmlStyle.paddingLeft)%3b%5cn%20%20%20%20%20%20%20%20coords.y%20-%3d%20parseInt(htmlStyle.paddingTop)%3b%5cn%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20if%20(elt.scrollLeft)%5cn%20%20%20%20%20%20%20%20coords.x%20%2b%3d%20elt.scrollLeft%3b%5cn%20%20%20%20%20%20if%20(elt.scrollTop)%5cn%20%20%20%20%20%20%20%20coords.y%20%2b%3d%20elt.scrollTop%3b%5cn%5cn%20%20%20%20%20%20var%20win%20%3d%20elt.ownerDocument.defaultView%3b%5cn%20%20%20%20%20%20if%20(win%20%26%26%20(!singleFrame%20%26%26%20win.frameElement))%5cn%20%20%20%20%20%20%20%20addOffset(win.frameElement,%20coords,%20win)%3b%5cn%20%20%20%20}%5cn%20%20}%5cn%5cn%20%20var%20coords%20%3d%20{x:%200,%20y:%200}%3b%5cn%20%20if%20(elt)%5cn%20%20%20%20addOffset(elt,%20coords,%20elt.ownerDocument.defaultView)%3b%5cn%5cn%20%20return%20coords%3b%5cn}%5cn%5cnfunction%20readBoxStyles(style)%20{%5cn%20%20const%20styleNames%20%3d%20{%5cn%20%20%20%20%5c%22margin-top%5c%22:%20%5c%22marginTop%5c%22,%20%5c%22margin-right%5c%22:%20%5c%22marginRight%5c%22,%5cn%20%20%20%20%5c%22margin-left%5c%22:%20%5c%22marginLeft%5c%22,%20%5c%22margin-bottom%5c%22:%20%5c%22marginBottom%5c%22,%5cn%20%20%20%20%5c%22border-top-width%5c%22:%20%5c%22borderTop%5c%22,%20%5c%22border-right-width%5c%22:%20%5c%22borderRight%5c%22,%5cn%20%20%20%20%5c%22border-left-width%5c%22:%20%5c%22borderLeft%5c%22,%20%5c%22border-bottom-width%5c%22:%20%5c%22borderBottom%5c%22,%5cn%20%20%20%20%5c%22padding-top%5c%22:%20%5c%22paddingTop%5c%22,%20%5c%22padding-right%5c%22:%20%5c%22paddingRight%5c%22,%5cn%20%20%20%20%5c%22padding-left%5c%22:%20%5c%22paddingLeft%5c%22,%20%5c%22padding-bottom%5c%22:%20%5c%22paddingBottom%5c%22%5cn%20%20}%3b%5cn%5cn%20%20var%20styles%20%3d%20{}%3b%5cn%20%20for%20(var%20styleName%20in%20styleNames)%5cn%20%20%20%20if%20(styleNames.hasOwnProperty(styleName))%5cn%20%20%20%20%20%20styles[styleNames[styleName]]%20%3d%20parseInt(%5cn%20%20%20%20%20%20%20%20style.getPropertyCSSValue(styleName).cssText%5cn%20%20%20%20%20%20)%3b%5cn%20%20return%20styles%3b%5cn}%5cn%5cnfunction%20addStyleSheet(doc,%20link)%20{%5cn%20%20var%20heads%20%3d%20doc.getElementsByTagName(%5c%22head%5c%22)%3b%5cn%20%20if%20(heads.length)%5cn%20%20%20%20heads[0].appendChild(link)%3b%5cn%20%20else%5cn%20%20%20%20doc.documentElement.appendChild(link)%3b%5cn}%5cn%5cnfunction%20createStyleSheet(doc,%20url)%20{%5cn%20%20var%20link%20%3d%20doc.createElementNS(xhtmlNS,%20%5c%22link%5c%22)%3b%5cn%20%20link.setAttribute(%5c%22charset%5c%22,%20%5c%22utf-8%5c%22)%3b%5cn%20%20link.firebugIgnore%20%3d%20true%3b%5cn%20%20link.setAttribute(%5c%22rel%5c%22,%20%5c%22stylesheet%5c%22)%3b%5cn%20%20link.setAttribute(%5c%22type%5c%22,%20%5c%22text%2fcss%5c%22)%3b%5cn%20%20link.setAttribute(%5c%22href%5c%22,%20url)%3b%5cn%20%20return%20link%3b%5cn}%5cn%5cn%2f%2f%20************************************************%20firebug%2finspector.js:%5cn%5cnfunction%20pad(element,%20t,%20r,%20b,%20l)%20{%5cn%20%20element.style.padding%20%3d%20Math.abs(t)%20%2b%20%5c%22px%20%5c%22%20%2b%20Math.abs(r)%20%2b%20%5c%22px%20%5c%22%5cn%20%20%20%20%2b%20Math.abs(b)%20%2b%20%5c%22px%20%5c%22%20%2b%20Math.abs(l)%20%2b%20%5c%22px%5c%22%3b%5cn}%5cn%5cnfunction%20FrameHighlighter(edgeSize)%20{%5cn%20%20this.edgeSize%20%3d%20edgeSize%20||%202%3b%5cn}%5cn%5cnFrameHighlighter.prototype%20%3d%20{%5cn%20%20setEdgeSize:%20function(n)%20{%5cn%20%20%20%20return%20this.edgeSize%20%3d%20n%3b%5cn%20%20},%5cn%5cn%20%20highlight:%20function(context,%20element)%20{%5cn%20%20%20%20var%20offset%20%3d%20getViewOffset(element,%20true)%3b%5cn%20%20%20%20var%20x%20%3d%20offset.x,%20y%20%3d%20offset.y%3b%5cn%20%20%20%20var%20w%20%3d%20element.offsetWidth,%20h%20%3d%20element.offsetHeight%3b%5cn%5cn%20%20%20%20var%20nodes%20%3d%20this.getNodes(context,%20element)%3b%5cn%5cn%20%20%20%20var%20edgeSize%20%3d%20this.edgeSize%3b%5cn%20%20%20%20move(nodes.top,%20x,%20y-edgeSize)%3b%5cn%20%20%20%20resize(nodes.top,%20w,%20edgeSize)%3b%5cn%5cn%20%20%20%20move(nodes.right,%20x%2bw,%20y-edgeSize)%3b%5cn%20%20%20%20resize(nodes.right,%20edgeSize,%20h%2bedgeSize*2)%3b%5cn%5cn%20%20%20%20move(nodes.bottom,%20x,%20y%2bh)%3b%5cn%20%20%20%20resize(nodes.bottom,%20w,%20edgeSize)%3b%5cn%5cn%20%20%20%20move(nodes.left,%20x-edgeSize,%20y-edgeSize)%3b%5cn%20%20%20%20resize(nodes.left,%20edgeSize,%20h%2bedgeSize*2)%3b%5cn%5cn%20%20%20%20var%20body%20%3d%20getNonFrameBody(element)%3b%5cn%20%20%20%20if%20(!body)%5cn%20%20%20%20%20%20return%20this.unhighlight(context)%3b%5cn%5cn%20%20%20%20var%20needsAppend%20%3d%20!nodes.top.parentNode%20||%5cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nodes.top.ownerDocument%20!%3d%20body.ownerDocument%3b%5cn%20%20%20%20if%20(needsAppend)%20{%5cn%20%20%20%20%20%20attachStyles(context,%20body)%3b%5cn%20%20%20%20%20%20for%20(var%20edge%20in%20nodes)%20if%20(nodes.hasOwnProperty(edge))%20{%5cn%20%20%20%20%20%20%20%20try%20{%5cn%20%20%20%20%20%20%20%20%20%20body.appendChild(nodes[edge])%3b%5cn%20%20%20%20%20%20%20%20}%20catch(exc)%20{}%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%20%20},%5cn%5cn%20%20unhighlight:%20function(context)%20{%5cn%20%20%20%20var%20nodes%20%3d%20this.getNodes(context)%3b%5cn%20%20%20%20var%20body%20%3d%20nodes.top.parentNode%3b%5cn%20%20%20%20if%20(body)%20{%5cn%20%20%20%20%20%20for%20(var%20edge%20in%20nodes)%5cn%20%20%20%20%20%20%20%20if%20(nodes.hasOwnProperty(edge))%5cn%20%20%20%20%20%20%20%20%20%20body.removeChild(nodes[edge])%3b%5cn%20%20%20%20}%5cn%20%20},%5cn%5cn%20%20getNodes:%20function(context)%20{%5cn%20%20%20%20if%20(!context.frameHighlighter)%20{%5cn%20%20%20%20%20%20var%20doc%20%3d%20context.window.document%3b%5cn%5cn%20%20%20%20%20%20function%20createEdge(name)%20{%5cn%20%20%20%20%20%20%20%20var%20div%20%3d%20doc.createElementNS(xhtmlNS,%20%5c%22div%5c%22)%3b%5cn%20%20%20%20%20%20%20%20div.firebugIgnore%20%3d%20true%3b%5cn%20%20%20%20%20%20%20%20div.className%20%3d%20%5c%22firebugHighlight%5c%22%3b%5cn%20%20%20%20%20%20%20%20return%20div%3b%5cn%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20context.frameHighlighter%20%3d%20{%5cn%20%20%20%20%20%20%20%20top:%20createEdge(%5c%22Top%5c%22),%5cn%20%20%20%20%20%20%20%20right:%20createEdge(%5c%22Right%5c%22),%5cn%20%20%20%20%20%20%20%20bottom:%20createEdge(%5c%22Bottom%5c%22),%5cn%20%20%20%20%20%20%20%20left:%20createEdge(%5c%22Left%5c%22)%5cn%20%20%20%20%20%20}%3b%5cn%20%20%20%20}%5cn%5cn%20%20%20%20return%20context.frameHighlighter%3b%5cn%20%20}%5cn}%3b%5cn%5cn%2f%2f%20************************************************%5cn%5cnfunction%20BoxModelHighlighter(showRulers)%20{%5cn%20%20this.showRulers%20%3d%20showRulers%3b%5cn}%5cn%5cnBoxModelHighlighter.prototype%20%3d%20{%5cn%20%20highlight:%20function(context,%20element,%20boxFrame)%20{%5cn%20%20%20%20var%20nodes%20%3d%20this.getNodes(context)%3b%5cn%20%20%20%20var%20highlightFrame%20%3d%20boxFrame%20?%20nodes[boxFrame]%20:%20null%3b%5cn%5cn%20%20%20%20if%20(context.highlightFrame)%5cn%20%20%20%20%20%20removeClass(context.highlightFrame,%20%5c%22firebugHighlightBox%5c%22)%3b%5cn%5cn%20%20%20%20context.highlightFrame%20%3d%20highlightFrame%3b%5cn%5cn%20%20%20%20if%20(highlightFrame)%20{%5cn%20%20%20%20%20%20setClass(nodes.offset,%20%5c%22firebugHighlightGroup%5c%22)%3b%5cn%20%20%20%20%20%20setClass(highlightFrame,%20%5c%22firebugHighlightBox%5c%22)%3b%5cn%20%20%20%20}%5cn%20%20%20%20else%5cn%20%20%20%20%20%20removeClass(nodes.offset,%20%5c%22firebugHighlightGroup%5c%22)%3b%5cn%5cn%20%20%20%20var%20win%20%3d%20element.ownerDocument.defaultView%3b%5cn%20%20%20%20if%20(!win)%5cn%20%20%20%20%20%20return%3b%5cn%5cn%20%20%20%20var%20offsetParent%20%3d%20element.offsetParent%3b%5cn%20%20%20%20if%20(!offsetParent)%5cn%20%20%20%20%20%20return%3b%5cn%5cn%20%20%20%20var%20parentStyle%20%3d%20win.getComputedStyle(offsetParent,%20%5c%22%5c%22)%3b%5cn%20%20%20%20var%20parentOffset%20%3d%20getViewOffset(offsetParent,%20true)%3b%5cn%20%20%20%20var%20parentX%20%3d%20parentOffset.x%20%2b%20parseInt(parentStyle.borderLeftWidth)%3b%5cn%20%20%20%20var%20parentY%20%3d%20parentOffset.y%20%2b%20parseInt(parentStyle.borderTopWidth)%3b%5cn%20%20%20%20var%20parentW%20%3d%20offsetParent.offsetWidth-1%3b%5cn%20%20%20%20var%20parentH%20%3d%20offsetParent.offsetHeight-1%3b%5cn%5cn%20%20%20%20var%20style%20%3d%20win.getComputedStyle(element,%20%5c%22%5c%22)%3b%5cn%20%20%20%20var%20styles%20%3d%20readBoxStyles(style)%3b%5cn%5cn%20%20%20%20var%20offset%20%3d%20getViewOffset(element,%20true)%3b%5cn%20%20%20%20var%20x%20%3d%20offset.x%20-%20Math.abs(styles.marginLeft)%3b%5cn%20%20%20%20var%20y%20%3d%20offset.y%20-%20Math.abs(styles.marginTop)%3b%5cn%20%20%20%20var%20w%20%3d%20element.offsetWidth%20-%20(styles.paddingLeft%20%2b%20styles.paddingRight%5cn%20%20%20%20%20%20%20%20%2b%20styles.borderLeft%20%2b%20styles.borderRight)%3b%5cn%20%20%20%20var%20h%20%3d%20element.offsetHeight%20-%20(styles.paddingTop%20%2b%20styles.paddingBottom%5cn%20%20%20%20%20%20%20%20%2b%20styles.borderTop%20%2b%20styles.borderBottom)%3b%5cn%5cn%20%20%20%20move(nodes.offset,%20x,%20y)%3b%5cn%20%20%20%20pad(nodes.margin,%20styles.marginTop,%20styles.marginRight,%5cn%20%20%20%20%20%20%20%20styles.marginBottom,%20styles.marginLeft)%3b%5cn%20%20%20%20pad(nodes.border,%20styles.borderTop,%20styles.borderRight,%5cn%20%20%20%20%20%20%20%20styles.borderBottom,%20styles.borderLeft)%3b%5cn%20%20%20%20pad(nodes.padding,%20styles.paddingTop,%20styles.paddingRight,%5cn%20%20%20%20%20%20%20%20styles.paddingBottom,%20styles.paddingLeft)%3b%5cn%20%20%20%20resize(nodes.content,%20w,%20h)%3b%5cn%5cn%20%20%20%20var%20showLines%20%3d%20this.showRulers%20%26%26%20boxFrame%3b%5cn%20%20%20%20if%20(showLines)%20{%5cn%20%20%20%20%20%20move(nodes.parent,%20parentX,%20parentY)%3b%5cn%20%20%20%20%20%20resize(nodes.parent,%20parentW,%20parentH)%3b%5cn%5cn%20%20%20%20%20%20if%20(parentX%20%3c%2014)%5cn%20%20%20%20%20%20%20%20setClass(nodes.parent,%20%5c%22overflowRulerX%5c%22)%3b%5cn%20%20%20%20%20%20else%5cn%20%20%20%20%20%20%20%20removeClass(nodes.parent,%20%5c%22overflowRulerX%5c%22)%3b%5cn%5cn%20%20%20%20%20%20if%20(parentY%20%3c%2014)%5cn%20%20%20%20%20%20%20%20setClass(nodes.parent,%20%5c%22overflowRulerY%5c%22)%3b%5cn%20%20%20%20%20%20else%5cn%20%20%20%20%20%20%20%20removeClass(nodes.parent,%20%5c%22overflowRulerY%5c%22)%3b%5cn%5cn%20%20%20%20%20%20var%20left%20%3d%20x%3b%5cn%20%20%20%20%20%20var%20top%20%3d%20y%3b%5cn%20%20%20%20%20%20var%20width%20%3d%20w-1%3b%5cn%20%20%20%20%20%20var%20height%20%3d%20h-1%3b%5cn%5cn%20%20%20%20%20%20if%20(boxFrame%20%3d%3d%20%5c%22content%5c%22)%20{%5cn%20%20%20%20%20%20%20%20left%20%2b%3d%20Math.abs(styles.marginLeft)%20%2b%20Math.abs(styles.borderLeft)%5cn%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.paddingLeft)%3b%5cn%20%20%20%20%20%20%20%20top%20%2b%3d%20Math.abs(styles.marginTop)%20%2b%20Math.abs(styles.borderTop)%5cn%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.paddingTop)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20%20%20else%20if%20(boxFrame%20%3d%3d%20%5c%22padding%5c%22)%20{%5cn%20%20%20%20%20%20%20%20left%20%2b%3d%20Math.abs(styles.marginLeft)%20%2b%20Math.abs(styles.borderLeft)%3b%5cn%20%20%20%20%20%20%20%20top%20%2b%3d%20Math.abs(styles.marginTop)%20%2b%20Math.abs(styles.borderTop)%3b%5cn%20%20%20%20%20%20%20%20width%20%2b%3d%20Math.abs(styles.paddingLeft)%20%2b%20Math.abs(styles.paddingRight)%3b%5cn%20%20%20%20%20%20%20%20height%20%2b%3d%20Math.abs(styles.paddingTop)%20%2b%20Math.abs(styles.paddingBottom)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20%20%20else%20if%20(boxFrame%20%3d%3d%20%5c%22border%5c%22)%20{%5cn%20%20%20%20%20%20%20%20left%20%2b%3d%20Math.abs(styles.marginLeft)%3b%5cn%20%20%20%20%20%20%20%20top%20%2b%3d%20Math.abs(styles.marginTop)%3b%5cn%20%20%20%20%20%20%20%20width%20%2b%3d%20Math.abs(styles.paddingLeft)%20%2b%20Math.abs(styles.paddingRight)%5cn%20%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.borderLeft)%20%2b%20Math.abs(styles.borderRight)%3b%5cn%20%20%20%20%20%20%20%20height%20%2b%3d%20Math.abs(styles.paddingTop)%20%2b%20Math.abs(styles.paddingBottom)%5cn%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.borderTop)%20%2b%20Math.abs(styles.borderBottom)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20%20%20else%20if%20(boxFrame%20%3d%3d%20%5c%22margin%5c%22)%20{%5cn%20%20%20%20%20%20%20%20width%20%2b%3d%20Math.abs(styles.paddingLeft)%20%2b%20Math.abs(styles.paddingRight)%5cn%20%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.borderLeft)%20%2b%20Math.abs(styles.borderRight)%5cn%20%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.marginLeft)%20%2b%20Math.abs(styles.marginRight)%3b%5cn%20%20%20%20%20%20%20%20height%20%2b%3d%20Math.abs(styles.paddingTop)%20%2b%20Math.abs(styles.paddingBottom)%5cn%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.borderTop)%20%2b%20Math.abs(styles.borderBottom)%5cn%20%20%20%20%20%20%20%20%20%20%2b%20Math.abs(styles.marginTop)%20%2b%20Math.abs(styles.marginBottom)%3b%5cn%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20move(nodes.lines.top,%200,%20top)%3b%5cn%20%20%20%20%20%20move(nodes.lines.right,%20left%2bwidth,%200)%3b%5cn%20%20%20%20%20%20move(nodes.lines.bottom,%200,%20top%2bheight)%3b%5cn%20%20%20%20%20%20move(nodes.lines.left,%20left,%200)%5cn%20%20%20%20}%5cn%5cn%20%20%20%20var%20body%20%3d%20getNonFrameBody(element)%3b%5cn%20%20%20%20if%20(!body)%5cn%20%20%20%20%20%20return%20this.unhighlight(context)%3b%5cn%5cn%20%20%20%20var%20needsAppend%20%3d%20!nodes.offset.parentNode%5cn%20%20%20%20%20%20||%20nodes.offset.parentNode.ownerDocument%20!%3d%20body.ownerDocument%3b%5cn%5cn%20%20%20%20if%20(needsAppend)%20{%5cn%20%20%20%20%20%20attachStyles(context,%20body)%3b%5cn%20%20%20%20%20%20body.appendChild(nodes.offset)%3b%5cn%20%20%20%20}%5cn%5cn%20%20%20%20var%20lines%20%3d%20nodes.lines%3b%5cn%20%20%20%20if%20(showLines)%20{%5cn%20%20%20%20%20%20if%20(!lines.top.parentNode)%20{%5cn%20%20%20%20%20%20%20%20body.appendChild(nodes.parent)%3b%5cn%5cn%20%20%20%20%20%20%20%20for%20(var%20line%20in%20lines)%5cn%20%20%20%20%20%20%20%20%20%20if%20(lines.hasOwnProperty(line))%5cn%20%20%20%20%20%20%20%20%20%20%20%20body.appendChild(lines[line])%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%20%20%20%20else%20if%20(lines.top.parentNode)%20{%5cn%20%20%20%20%20%20body.removeChild(nodes.parent)%3b%5cn%5cn%20%20%20%20%20%20for%20(var%20line%20in%20lines)%5cn%20%20%20%20%20%20%20%20if%20(lines.hasOwnProperty(line))%5cn%20%20%20%20%20%20%20%20%20%20body.removeChild(lines[line])%3b%5cn%20%20%20%20}%5cn%20%20},%5cn%5cn%20%20unhighlight:%20function(context)%20{%5cn%20%20%20%20var%20nodes%20%3d%20this.getNodes(context)%3b%5cn%20%20%20%20if%20(nodes.offset.parentNode)%20{%5cn%20%20%20%20%20%20var%20body%20%3d%20nodes.offset.parentNode%3b%5cn%20%20%20%20%20%20body.removeChild(nodes.offset)%3b%5cn%5cn%20%20%20%20%20%20var%20lines%20%3d%20nodes.lines%3b%5cn%20%20%20%20%20%20if%20(lines.top.parentNode)%20{%5cn%20%20%20%20%20%20%20%20body.removeChild(nodes.parent)%3b%5cn%5cn%20%20%20%20%20%20%20%20for%20(var%20line%20in%20lines)%5cn%20%20%20%20%20%20%20%20%20%20body.removeChild(lines[line])%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%20%20},%5cn%5cn%20%20getNodes:%20function(context)%20{%5cn%20%20%20%20if%20(!context.boxModelHighlighter)%20{%5cn%20%20%20%20%20%20var%20doc%20%3d%20context.window.document%3b%5cn%5cn%20%20%20%20%20%20function%20createRuler(name)%20{%5cn%20%20%20%20%20%20%20%20var%20div%20%3d%20doc.createElementNS(xhtmlNS,%20%5c%22div%5c%22)%3b%5cn%20%20%20%20%20%20%20%20div.firebugIgnore%20%3d%20true%3b%5cn%20%20%20%20%20%20%20%20div.className%20%3d%20%5c%22firebugRuler%20firebugRuler%5c%22%2bname%3b%5cn%20%20%20%20%20%20%20%20return%20div%3b%5cn%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20function%20createBox(name)%20{%5cn%20%20%20%20%20%20%20%20var%20div%20%3d%20doc.createElementNS(xhtmlNS,%20%5c%22div%5c%22)%3b%5cn%20%20%20%20%20%20%20%20div.firebugIgnore%20%3d%20true%3b%5cn%20%20%20%20%20%20%20%20div.className%20%3d%20%5c%22firebugLayoutBox%20firebugLayoutBox%5c%22%2bname%3b%5cn%20%20%20%20%20%20%20%20return%20div%3b%5cn%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20function%20createLine(name)%20{%5cn%20%20%20%20%20%20%20%20var%20div%20%3d%20doc.createElementNS(xhtmlNS,%20%5c%22div%5c%22)%3b%5cn%20%20%20%20%20%20%20%20div.firebugIgnore%20%3d%20true%3b%5cn%20%20%20%20%20%20%20%20div.className%20%3d%20%5c%22firebugLayoutLine%20firebugLayoutLine%5c%22%2bname%3b%5cn%20%20%20%20%20%20%20%20return%20div%3b%5cn%20%20%20%20%20%20}%5cn%5cn%20%20%20%20%20%20var%20nodes%20%3d%20context.boxModelHighlighter%20%3d%20{%5cn%20%20%20%20%20%20%20%20parent:%20createBox(%5c%22Parent%5c%22),%5cn%20%20%20%20%20%20%20%20rulerH:%20createRuler(%5c%22H%5c%22),%5cn%20%20%20%20%20%20%20%20rulerV:%20createRuler(%5c%22V%5c%22),%5cn%20%20%20%20%20%20%20%20offset:%20createBox(%5c%22Offset%5c%22),%5cn%20%20%20%20%20%20%20%20margin:%20createBox(%5c%22Margin%5c%22),%5cn%20%20%20%20%20%20%20%20border:%20createBox(%5c%22Border%5c%22),%5cn%20%20%20%20%20%20%20%20padding:%20createBox(%5c%22Padding%5c%22),%5cn%20%20%20%20%20%20%20%20content:%20createBox(%5c%22Content%5c%22),%5cn%20%20%20%20%20%20%20%20lines:%20{%5cn%20%20%20%20%20%20%20%20%20%20top:%20createLine(%5c%22Top%5c%22),%5cn%20%20%20%20%20%20%20%20%20%20right:%20createLine(%5c%22Right%5c%22),%5cn%20%20%20%20%20%20%20%20%20%20bottom:%20createLine(%5c%22Bottom%5c%22),%5cn%20%20%20%20%20%20%20%20%20%20left:%20createLine(%5c%22Left%5c%22)%5cn%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20}%3b%5cn%5cn%20%20%20%20%20%20nodes.parent.appendChild(nodes.rulerH)%3b%5cn%20%20%20%20%20%20nodes.parent.appendChild(nodes.rulerV)%3b%5cn%20%20%20%20%20%20nodes.offset.appendChild(nodes.margin)%3b%5cn%20%20%20%20%20%20nodes.margin.appendChild(nodes.border)%3b%5cn%20%20%20%20%20%20nodes.border.appendChild(nodes.padding)%3b%5cn%20%20%20%20%20%20nodes.padding.appendChild(nodes.content)%3b%5cn%20%20%20%20}%5cn%5cn%20%20%20%20return%20context.boxModelHighlighter%3b%5cn%20%20}%5cn}%3b%5cn%5cnfunction%20getNonFrameBody(elt)%20{%5cn%20%20var%20body%20%3d%20getBody(elt.ownerDocument)%3b%5cn%20%20return%20body.localName.toUpperCase()%20%3d%3d%20%5c%22FRAMESET%5c%22%20?%20null%20:%20body%3b%5cn}%5cn%5cnfunction%20attachStyles(context,%20body)%20{%5cn%20%20var%20doc%20%3d%20body.ownerDocument%3b%5cn%20%20if%20(!context.highlightStyle)%5cn%20%20%20%20context.highlightStyle%20%3d%20createStyleSheet(doc,%20highlightCSS)%3b%5cn%5cn%20%20if%20(!context.highlightStyle.parentNode%20||%5cn%20%20%20%20%20%20%20context.highlightStyle.ownerDocument%20!%3d%20doc)%5cn%20%20%20%20addStyleSheet(body.ownerDocument,%20context.highlightStyle)%3b%5cn}%5cn%5cn_.getViewOffset%20%3d%20getViewOffset%3b%5cn_.FrameHighlighter%20%3d%20FrameHighlighter%3b%5cn_.BoxModelHighlighter%20%3d%20BoxModelHighlighter%3b%5cn%5cn})(this)%3b%5cn%5cn%5cnfunction%20OutlineHighlighter(edgeSize)%20{%5cn%20%20this.edgeSize%20%3d%20edgeSize%20||%202%3b%5cn}%5cn%5cnOutlineHighlighter.prototype%20%3d%20{%5cn%20%20highlight:%20function(context,%20element)%20{%5cn%20%20%20%20this.saveOutline(context,%20element)%3b%5cn%20%20%20%20element.style.outline%20%3d%20this.edgeSize%20%2b%5c%22px%20solid%20SlateBlue%5c%22%3b%5cn%20%20},%5cn%5cn%20%20unhighlight:%20function(context)%20{%5cn%20%20%20%20var%20highlighter%20%3d%20this.saveOutline(context)%3b%5cn%20%20%20%20if%20(highlighter)%20{%5cn%20%20%20%20%20%20highlighter.element.style.outline%20%3d%20highlighter.outline%3b%5cn%20%20%20%20%20%20delete%20context.outlineHighlighter%3b%5cn%20%20%20%20}%5cn%20%20},%5cn%5cn%20%20saveOutline:%20function(context,%20element)%20{%5cn%20%20%20%20if%20(!context.outlineHighlighter%20%26%26%20element)%20{%5cn%20%20%20%20%20%20var%20doc%20%3d%20context.window.document%3b%5cn%5cn%20%20%20%20%20%20context.outlineHighlighter%20%3d%20{%5cn%20%20%20%20%20%20%20%20outline:%20element.style.outline,%5cn%20%20%20%20%20%20%20%20element:%20element%5cn%20%20%20%20%20%20}%3b%5cn%20%20%20%20}%5cn%5cn%20%20%20%20return%20context.outlineHighlighter%3b%5cn%20%20}%5cn}%3b%5cn%5cn%5cn%5cn%2f%2f%20uses:%5cn%2f%2f%20%20%20http:%2f%2fecmanaut.googlecode.com%2fsvn%2ftrunk%2flib%2fbind.js%5cn%2f%2f%20%20%20http:%2f%2fecmanaut.googlecode.com%2fsvn%2ftrunk%2flib%2ffirebug%2fhighlighter.js%5cn%5cnfunction%20bind(fn,%20self)%20{%5cn%20%20var%20args%20%3d%20[].slice.call(arguments,%202)%3b%5cn%20%20return%20function()%20{%5cn%20%20%20%20fn.apply(self,%20args.concat([].slice.call(arguments)))%3b%5cn%20%20}%3b%5cn}%5cn%5cn%2f%2f%20(function(_)%20{%5cn%5cnfunction%20throttle(fn,%20self,%20delay)%20{%5cn%20%20function%20fire()%20{%5cn%20%20%20%20delete%20throttle[fn]%3b%5cn%20%20%20%20self[fn].apply(self,%20args)%3b%5cn%20%20}%5cn%20%20window.console%20%26%26%20console.count%20%26%26%20console.count(fn)%3b%5cn%20%20if%20(throttle[fn])%20return%3b%5cn%20%20var%20args%20%3d%20[].slice.call(arguments,%203)%3b%5cn%20%20throttle[fn]%20%3d%20setTimeout(fire,%20delay||50)%3b%5cn}%5cn%5cnfunction%20scrollToNode(node)%20{%5cn%20%20var%20x%20%3d%20node,%20y%20%3d%20arguments[1]%3b%5cn%20%20if%20(typeof%20node%20%3d%3d%20%5c%22object%5c%22)%20{%5cn%20%20%20%20node%20%3d%20getViewOffset(node,%20true)%3b%5cn%20%20%20%20x%20%3d%20node.x%3b%5cn%20%20%20%20y%20%3d%20node.y%3b%5cn%20%20}%20else%20if%20(arguments.length%20%3d%3d%201)%20{%5cn%20%20%20%20y%20%3d%20x%3b%5cn%20%20%20%20x%20%3d%200%3b%5cn%20%20}%5cn%20%20window.scrollTo(x,%20y)%3b%5cn}%5cn%5cnfunction%20XPathInput(node,%20doc)%20{%5cn%20%20this.dirty%20%3d%20false%3b%5cn%20%20this.focused%20%3d%200%3b%5cn%20%20this.node%20%3d%20node%3b%5cn%20%20this.doc%20%3d%20doc%20||%20document%3b%5cn%20%20this.init(node)%3b%5cn}%5cn%5cnXPathInput.prototype%20%3d%20{%5cn%20%20init:%20function(node)%20{%5cn%20%20%20%20function%20listen(node,%20event)%20{%5cn%20%20%20%20%20%20%2f%2fconsole.info(%5c%22%25x.listen(%25x,%20%25x)%5c%22,%20this,%20node,%20event)%3b%5cn%20%20%20%20%20%20var%20fn%20%3d%20bind(this[%5c%22on%5c%22%2bevent],%20this)%3b%5cn%20%20%20%20%20%20node.addEventListener(event,%20fn,%20true)%3b%5cn%20%20%20%20%20%20this.undo[event]%20%3d%20function()%20{%5cn%20%20%20%20%20%20%20%20node.removeEventListener(event,%20fn,%20true)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%5cn%20%20%20%20%2f%2fconsole.log(%5c%22%25x.init(%25x)%20-%20%25x%5c%22,%20this,%20node,%20this.node%20%3d%3d%20node)%3b%5cn%20%20%20%20if%20(this.node%20!%3d%20node)%5cn%20%20%20%20%20%20this.undo()%3b%5cn%20%20%20%20this.node%20%3d%20node%3b%5cn%5cn%20%20%20%20[%5c%22blur%5c%22,%20%5c%22focus%5c%22,%20%2f%2f%5c%22mouseover%5c%22,%20%5c%22mouseout%5c%22,%5cn%20%20%20%20%20%5c%22keyup%5c%22,%20%5c%22DOMMouseScroll%5c%22].forEach(bind(listen,%20this,%20node))%3b%5cn%20%20%20%20%2f%2flisten.call(this,%20this.doc,%20%5c%22DOMNodeInserted%5c%22)%3b%5cn%5cn%20%20%20%20%2f%2fthis.test(true)%3b%5cn%20%20},%5cn%5cn%20%20undo:%20function%20undo()%20{%5cn%20%20%20%20for%20(var%20event%20in%20undo)%5cn%20%20%20%20%20%20if%20(undo.hasOwnProperty(event)%20%26%26%20%5c%22function%5c%22%20%3d%3d%20typeof%20undo[event])%5cn%20%20%20%20%20%20%20%20undo[event]()%3b%5cn%20%20},%5cn%5cn%5cn%20%20onDOMNodeInserted:%20function()%20{%20this.dirty%20%3d%20true%3b%20},%5cn%5cn%20%20onblur:%20%20%20%20%20%20function()%20{%20throttle(%5c%22unhighlight%5c%22,%20this)%3b%20},%5cn%20%20onmouseout:%20%20function()%20{%20throttle(%5c%22unhighlight%5c%22,%20this)%3b%20},%5cn%5cn%20%20onfocus:%20%20%20%20%20function()%20{%20throttle(%5c%22test%5c%22,%20this)%3b%20},%5cn%20%20onmouseover:%20function()%20{%20throttle(%5c%22test%5c%22,%20this)%3b%20},%5cn%5cn%20%20onkeyup:%20function(event)%20{%5cn%20%20%20%20%2f%2fconsole.log(%5c%22onkeyup(%25x)%5c%22,%20event)%3b%5cn%20%20%20%20var%20key%20%3d%20{%2038:%20%5c%22up%5c%22,%2040:%20%5c%22down%5c%22%20}[event.keyCode]%3b%5cn%20%20%20%20if%20(key)%5cn%20%20%20%20%20%20this[%5c%22on%5c%22%2bkey]()%3b%5cn%20%20%20%20else%5cn%20%20%20%20%20%20throttle(%5c%22test%5c%22,%20this,%20100)%3b%5cn%20%20},%5cn%5cn%20%20onDOMMouseScroll:%20function(event)%20{%5cn%20%20%20%20%2f%2fconsole.log(%5c%22onDOMMouseScroll(%25x)%5c%22,%20event)%3b%5cn%20%20%20%20var%20delta%20%3d%200%3b%5cn%20%20%20%20if%20(event.wheelDelta)%20{%20%2f%2f%20Opera%5cn%20%20%20%20%20%20delta%20%3d%20event.wheelDelta%2f120%3b%5cn%20%20%20%20}%20else%20if%20(event.detail)%20{%5cn%20%20%20%20%20%20delta%20%3d%20event.detail%2f3%3b%20%2f%2f%20In%20Mozilla,%20delta%20is%20a%20multiple%20of%203%5cn%20%20%20%20}%5cn%20%20%20%20if%20(delta%20%3c%200)%20this.onup()%3b%5cn%20%20%20%20if%20(delta%20%3e%200)%20this.ondown()%3b%5cn%20%20%20%20event.stopPropagation()%3b%5cn%20%20%20%20event.preventDefault()%3b%5cn%20%20},%5cn%5cn%20%20onup:%20%20%20function()%20{%20this.focus(-1)%3b%20},%5cn%20%20ondown:%20function()%20{%20this.focus(1)%3b%20},%5cn%20%20focus:%20function(n)%20{%5cn%20%20%20%20%2f%2fconsole.log(%5c%22focus(%25x)%5c%22,%20n)%3b%5cn%20%20%20%20var%20count%20%3d%20(this.contexts%20||%20[]).length%3b%5cn%20%20%20%20var%20focused%20%3d%20this.focused%3b%5cn%20%20%20%20if%20(count)%20{%5cn%20%20%20%20%20%20while%20(focused%20%3c%200)%5cn%20%20%20%20%20%20%20%20focused%20%2b%3d%20count%3b%5cn%20%20%20%20%20%20this.focused%20%3d%20(focused%20%2b%20(n||0)%20%2b%20count)%20%25%20count%3b%5cn%20%20%20%20%20%20this.unfocus()%3b%5cn%20%20%20%20%20%20var%20context%20%3d%20this.contexts[focused]%3b%5cn%20%20%20%20%20%20if%20(context.node.nodeType%20%3d%3d%201)%20{%5cn%20%20%20%20%20%20%20%20context.fill%20%3d%20new%20BoxModelHighlighter(false)%3b%5cn%20%20%20%20%20%20%20%20context.fill.highlight(context,%20context.node)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%20%20},%5cn%5cn%20%20unfocus:%20function()%20{%5cn%20%20%20%20function%20undo(context)%20{%5cn%20%20%20%20%20%20if%20(context.fill)%20{%5cn%20%20%20%20%20%20%20%20context.fill.unhighlight(context)%3b%5cn%20%20%20%20%20%20%20%20delete%20context.fill%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%5cn%20%20%20%20this.contexts.forEach(undo)%3b%5cn%20%20},%5cn%5cn%20%20highlight:%20function(nodes)%20{%5cn%20%20%20%20function%20go(node)%20{%5cn%20%20%20%20%20%20var%20context%20%3d%20{%20window:win,%20node:node%20}%3b%5cn%20%20%20%20%20%20if%20(node.nodeType%20%3d%3d%201)%20{%5cn%20%20%20%20%20%20%20%20context.outline%20%3d%20new%20OutlineHighlighter(1)%3b%5cn%20%20%20%20%20%20%20%20context.outline.highlight(context,%20node)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20%20%20return%20context%3b%5cn%20%20%20%20}%5cn%20%20%20%20%2f%2fdefaultStatus%20%3d%20(%5c%22highlight()%20%25x%5c%22%20%2b%20arguments.callee.caller)%3b%5cn%20%20%20%20if%20(this.contexts)%5cn%20%20%20%20%20%20this.unhighlight()%3b%5cn%20%20%20%20console.time(%5c%22Highlight%20%5c%22%2b%20nodes.length)%3b%5cn%20%20%20%20var%20win%20%3d%20this.doc.defaultView%3b%5cn%20%20%20%20this.contexts%20%3d%20nodes.map(go)%3b%5cn%20%20%20%20this.focus()%3b%5cn%20%20%20%20console.timeEnd(%5c%22Highlight%20%5c%22%2b%20nodes.length)%3b%5cn%20%20},%5cn%5cn%20%20unhighlight:%20function()%20{%5cn%20%20%20%20function%20undo(context)%20{%5cn%20%20%20%20%20%20if%20(context.outline)%5cn%20%20%20%20%20%20%20%20context.outline.unhighlight(context)%3b%5cn%20%20%20%20%20%20if%20(context.fill)%5cn%20%20%20%20%20%20%20%20context.fill.unhighlight(context)%3b%5cn%20%20%20%20}%5cn%20%20%20%20%2f%2fdefaultStatus%20%3d%20(%5c%22unhighlight()%20%25x%5c%22%2barguments.callee.caller)%3b%5cn%20%20%20%20var%20n%20%3d%20(this.contexts%20||%20[]).length%5cn%20%20%20%20console.time(%5c%22Unhighlight%20%5c%22%2b%20n)%3b%5cn%20%20%20%20(this.contexts%20||%20[]).forEach(undo)%3b%5cn%20%20%20%20delete%20this.contexts%3b%5cn%20%20%20%20this.dirty%20%3d%20true%3b%5cn%20%20%20%20console.timeEnd(%5c%22Unhighlight%20%5c%22%2b%20n)%3b%5cn%20%20},%5cn%5cn%20%20test:%20function(nohilight)%20{%5cn%20%20%20%20var%20xpath%20%3d%20this.node.value,%20doc%20%3d%20this.doc,%20col%20%3d%20%5c%22%5c%22,%20title%20%3d%20%5c%22%5c%22%3b%5cn%20%20%20%20if%20(xpath%20%3d%3d%20this.lastXPath%20%26%26%20!this.dirty)%20return%20undefined%3b%5cn%20%20%20%20%2f%2fconsole.log(%5c%22test(%25x)%20%25x%5c%22,%20nohilight,%20arguments.callee.caller)%3b%5cn%20%20%20%20try%20{%5cn%20%20%20%20%20%20var%20got%20%3d%20doc.evaluate(xpath,%20doc,%20null,%200,%20null),%20result%20%3d%20[],%20next%3b%5cn%20%20%20%20%20%20switch%20(got.resultType)%20{%5cn%20%20%20%20%20%20%20%20case%20got.STRING_TYPE:%20result%20%3d%20got.stringValue%3b%20col%20%3d%20%5c%22%23CC0%5c%22%3b%20break%3b%5cn%20%20%20%20%20%20%20%20case%20got.NUMBER_TYPE:%20result%20%3d%20got.numberValue%3b%20col%20%3d%20%5c%22%2300C%5c%22%3b%20break%3b%5cn%20%20%20%20%20%20%20%20case%20got.BOOLEAN_TYPE:result%20%3d%20got.booleanValue%3bcol%20%3d%20%5c%22%23C0C%5c%22%3b%20break%3b%5cn%20%20%20%20%20%20%20%20default:%20col%20%3d%20%5c%22%230C0%5c%22%3b%5cn%20%20%20%20%20%20%20%20%20%20while%20(next%20%3d%20got.iterateNext())%5cn%20%20%20%20%20%20%20%20%20%20%20%20result.push(next)%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20%20%20if%20(%5c%22object%5c%22%20%3d%3d%20typeof%20result)%20{%5cn%20%20%20%20%20%20%20%20title%20%3d%20%5c%22NodeList:%20%5c%22%2b%20result.length%20%2b%5c%22%20item%5c%22%2b%20(result.length%3d%3d1?%5c%22%5c%22:%5c%22s%5c%22)%3b%5cn%20%20%20%20%20%20%20%20if%20(!result.length)%20col%20%3d%20%5c%22%5c%22%3b%5cn%20%20%20%20%20%20%20%20var%20eq%20%3d%20function(a,%20b)%20{%5cn%20%20%20%20%20%20%20%20%20%20if%20(!a%20||%20!b%20||%20a.length%20!%3d%20b.length)%20return%20false%3b%5cn%20%20%20%20%20%20%20%20%20%20for%20(var%20i%20%3d%200%3b%20i%20%3c%20a.length%3b%20i%2b%2b)%5cn%20%20%20%20%20%20%20%20%20%20%20%20if%20(a[i]%20!%3d%3d%20b[i])%20return%20false%3b%5cn%20%20%20%20%20%20%20%20%20%20return%20true%3b%5cn%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20%20%20if%20(!eq(result,%20this.lastResult))%20{%5cn%20%20%20%20%20%20%20%20%20%20this.lastResult%20%3d%20result%3b%5cn%20%20%20%20%20%20%20%20%20%20nohilight%20||%20this.highlight(result)%3b%5cn%20%20%20%20%20%20%20%20}%5cn%20%20%20%20%20%20}%20else%20{%5cn%20%20%20%20%20%20%20%20title%20%3d%20typeof%20result%20%2b%5c%22:%20%5c%22%2b%20result%3b%5cn%20%20%20%20%20%20%20%20nohilight%20||%20this.unhighlight()%3b%5cn%20%20%20%20%20%20}%5cn%20%20%20%20}%20catch(e)%20{%5cn%20%20%20%20%20%20nohilight%20||%20this.unhighlight()%3b%5cn%20%20%20%20%20%20result%20%3d%20undefined%3b%5cn%20%20%20%20%20%20title%20%3d%20e.message%3b%5cn%20%20%20%20%20%20col%20%3d%20%5c%22%23F00%5c%22%3b%5cn%20%20%20%20}%5cn%20%20%20%20this.node.title%20%3d%20title%3b%5cn%20%20%20%20this.node.style.borderColor%20%3d%20col%3b%5cn%20%20%20%20this.lastXPath%20%3d%20xpath%3b%5cn%20%20%20%20this.dirty%20%3d%20nohilight%20||%20false%3b%5cn%20%20%20%20%2f%2fconsole.warn(title)%3b%5cn%20%20%20%20window.result%20%3d%20result%3b%5cn%20%20%20%20parent.document.title%20%3d%20title%3b%5cn%20%20%20%20return%20result%3b%5cn%20%20}%5cn}%3b%5cn%5cn%2f%2f})(this)%3b%5cn%5cn%3c%2fscript%3e%5cn%5cn%3cscript%20type%3d%5c%22text%2fjavascript%5c%22%3e%5cn%5cnvar%20outputFrame%20%3d%20parent.outputFrame%3b%5cnvar%20inputFrame%20%3d%20parent.inputFrame%3b%5cnvar%20framesetElement%20%3d%20parent.document.documentElement.getElementsByTagName(%5c%22frameset%5c%22)[0]%3b%5cn%5cnvar%20savedRows%3b%5cn%5cn%5cn%2f%2f%20Need%20to%20use%20C-style%20comments%20in%20handleError()%20and%20print()%20%5cn%2f%2f%20because%20IE%20retains%20them%20when%20decompiling%20a%20function.%5cn%5cn%5cn%5cnfunction%20print(s,%20c)%20{%5cn%20%20var%20outputFrame%20%3d%20parent.outputFrame%3b%20%2f*%20duplicated%20here%20in%20case%20this%20function%20is%20elsewhere%20*%2f%5cn%20%20var%20doc%20%3d%20outputFrame.document%3b%5cn%5cn%20%20var%20newdiv%20%3d%20doc.createElement(%5c%22div%5c%22)%3b%5cn%20%20newdiv.appendChild(doc.createTextNode(s))%3b%5cn%20%20if%20(c)%5cn%20%20%20%20newdiv.style.color%20%3d%20c%3b%5cn%20%20doc.body.appendChild(newdiv)%3b%5cn}%5cn%5cnfunction%20handleError(er,%20file,%20lineNumber)%20%5cn{%5cn%20%20print(%5c%22Error%20on%20line%20%5c%22%20%2b%20lineNumber%20%2b%20%5c%22:%20%5c%22%20%2b%20er,%20%5c%22red%5c%22)%3b%20%5cn%20%20%20%20%5cn%20%20%2f*%20Find%20the%20character%20offset%20for%20the%20line%20*%2f%5cn%20%20%2f*%20(code%20adapted%20from%20blogidate%20xml%20well-formedness%20bookmarklet)%20*%2f%5cn%20%20var%20ta%20%3d%20inputFrame.document.getElementById(%5c%22input%5c%22)%3b%5cn%20%20var%20lines%20%3d%20ta.value.split(%5c%22%5c%5cn%5c%22)%3b%5cn%20%20var%20cc%20%3d%200%3b%20%5cn%20%20var%20i%3b%5cn%20%20for(i%3d0%3b%20i%20%3c%20(lineNumber%20-%201)%3b%20%2b%2bi)%20%5cn%20%20%20%20cc%20%2b%3d%20lines[i].length%20%2b%201%3b%5cn%5cn%20%20%2f*%20Hacky(?)%20workaround%20for%20IE%5c%27s%20habit%20of%20including%20%5c%5cr%5c%27s%20in%20strings%20*%2f%5cn%20%20if%20(ta.value.split(%5c%22%5c%5cr%5c%22).length%20%3e%201)%5cn%20%20%20%20cc%20-%3d%20lineNumber%20-%201%3b%5cn%5cn%20%20%2f*%20Select%20the%20line%20*%2f%5cn%20%20if(document.selection)%20{%20%5cn%20%20%20%20%2f*%20IE%20(Leonard%20Lin%20gave%20me%20this%20code)%20*%2f%5cn%20%20%20%20var%20sel%20%3d%20ta.createTextRange()%3b%20%5cn%20%20%20%20sel.moveStart(%5c%22character%5c%22,%20cc)%3b%20%5cn%20%20%20%20sel.collapse()%3b%20%5cn%20%20%20%20sel.moveEnd(%5c%22character%5c%22,%20lines[i].length)%3b%20%5cn%20%20%20%20sel.select()%3b%5cn%20%20}%20else%20{%20%5cn%20%20%20%20%2f*%20Mozilla%20*%2f%5cn%20%20%20%20ta.selectionStart%20%3d%20cc%3b%20%5cn%20%20%20%20ta.selectionEnd%20%3d%20cc%20%2b%20lines[i].length%3b%20%5cn%20%20}%5cn%20%5cn%20%20%2f*%20return%20true%3b%20*%2f%20%2f*%20nah,%20let%20the%20error%20go%20through%20to%20IE%5c%27s%20js%20consolish%20thing!%20*%2f%5cn}%5cn%5cn%5cn%5cn%5cn%5cnfunction%20showHideOutput()%5cn{%5cn%20%20if%20(outputFrame.document.body.clientHeight%20%3e%20100)%20{%5cn%20%20%20%20%2f%2f%20hide%5cn%20%20%20%20savedRows%20%3d%20framesetElement.rows%3b%20%20%20%20%5cn%20%20%20%20framesetElement.rows%20%3d%20%5c%2225,*,0%5c%22%3b%5cn%20%20}%5cn%20%20else%20{%5cn%20%20%20%20%2f%2f%20show.%20use%20the%20previous%20size,%20if%20possible%5cn%20%20%20%20if%20(savedRows)%20{%5cn%20%20%20%20%20%20framesetElement.rows%20%3d%20savedRows%3b%5cn%20%20%20%20%20%20savedRows%20%3d%20null%3b%5cn%20%20%20%20}%5cn%20%20%20%20else%20{%5cn%20%20%20%20%20%20framesetElement.rows%20%3d%20%5c%2225,*,*%5c%22%3b%5cn%20%20%20%20}%5cn%20%20}%5cn}%5cn%5cnfunction%20refocus()%5cn{%5cn%20%20inputFrame.document.getElementById(%5c%22input%5c%22).focus()%3b%5cn}%5cn%5cn%5cnfunction%20clearOutput()%5cn{%5cn%20%20var%20b%20%3d%20outputFrame.document.body%3b%5cn%20%20while(b.firstChild)%5cn%20%20%20%20b.removeChild(b.firstChild)%3b%5cn}%5cn%5cnfunction%20stripLineBreaks(s)%5cn{%5cn%20%20return%20s.replace(%2f%5c%5cn%2fg,%20%5c%22%5c%22).replace(%2f%5c%5cr%2fg,%20%5c%22%5c%22)%3b%20%2f%2f%20stripping%20%5c%5cr%20is%20for%20IE%5cn}%5cn%5cnfunction%20execute()%5cn{%5cn%20%20var%20js%20%3d%20inputFrame.document.getElementById(%5c%22input%5c%22).value%3b%5cn%5cn%20%20var%20useOpener%20%3d%20parent.opener%20%26%26%20!parent.opener.closed%3b%5cn%20%20var%20oldStyle%20%3d%20!!%20document.all%3b%20%2f%2f%20lame%20but%20meh.%5cn%20%20%5cn%20%20print(%5c%22Running%5c%22%20%2b%20(useOpener%20?%20%5c%22%20in%20bookmarklet%20mode%5c%22%20:%20%5c%22%5c%22)%20%2b%20(oldStyle%20?%20%5c%22%20in%20make-IE-happy%20mode%5c%22%20:%20%5c%22%5c%22)%20%2b%20%5c%22...%5c%22,%20%5c%22orange%5c%22)%3b%5cn%5cn%20%20if%20(useOpener)%5cn%20%20%20%20executeWithJSURL(js,%20parent.opener)%3b%20%2f%2f%20only%20way%20to%20execute%20against%20another%20frame%5cn%20%20else%20if%20(oldStyle)%5cn%20%20%20%20executeWithDW(js,%20execFrame)%3b%20%2f%2f%20only%20way%20to%20get%20line%20numbers%20in%20IE%5cn%20%20else%5cn%20%20%20%20executeWithJSURL(js,%20execFrame)%3b%20%2f%2f%20faster%20in%20Mozilla%20%20%5cn}%5cn%5cn%2f%2f%20Advantages:%20can%20get%20line%20numbers%20in%20IE.%5cnfunction%20executeWithDW(js,%20win)%5cn{%5cn%20%20win.document.open()%3b%5cn%20%20win.inputFrame%20%3d%20inputFrame%3b%5cn%20%20win.outputFrame%20%3d%20outputFrame%3b%5cn%20%20win.document.write(%5cn%20%20%20%20stripLineBreaks(%5cn%20%20%20%20%20%20%20%20%5c%27%3c!DOCTYPE%20HTML%20PUBLIC%20%5c%22-%2f%2fW3C%2f%2fDTD%20HTML%204.01%2f%2fEN%5c%22%20%5c%22http:%2f%2fwww.w3.org%2fTR%2fhtml4%2fstrict.dtd%5c%22%3e%5c%27%20%2b%5cn%20%20%20%20%20%20%20%20%5c%27%3chtml%3e%3chead%3e%3ctitle%3eexecFrame%3c%5c%5c%2ftitle%3e%3cscript%20type%3d%5c%22text%2fjavascript%5c%22%3e%5c%27%5cn%20%20%20%20%20%20%2b%20print%20%20%20%20%20%20%20%20%20%2f%2f%20yay%20for%20decompilation!%5cn%20%20%20%20%20%20%2b%20handleError%20%5cn%20%20%20%20%20%20%2b%20%5c%22window.onerror%20%3d%20handleError%3b%5c%22%5cn%20%20%20%20%20%20%2b%20%5c%22%3c%5c%5c%2fscript%3e%3c%5c%5c%2fhead%3e%5c%22%5cn%20%20%20%20%20%20)%20%5cn%20%20%20%20%2b%20%5c%27%3cbody%3e%3cscript%20type%3d%5c%22text%2fjavascript%5c%22%3e%5c%27%5cn%20%20%20%20%2b%20js%20%20%20%20%20%20%20%20%20%2f%2f%20should%20escape%20it%20a%20little%20to%20remove%20the%20string%20%3c%5c%5c%2fscript%3e%20at%20least...%5cn%20%20%20%20%2b%20%5c%22%3c%5c%5c%2fscript%3e%3c%5c%5c%2fbody%3e%3c%5c%5c%2fhtml%3e%5c%22%5cn%20%20%20%20)%3b%5cn%20%20win.document.close()%3b%5cn}%5cn%5cn%2f%2f%20Advantages:%20can%20be%20used%20to%20inject%20a%20script%20into%20another%20window,%20faster%20in%20Mozilla.%5cnfunction%20executeWithJSURL(js,%20win)%5cn{%5cn%20%20%2f%2f%20isolate%20scope%5cn%20%20js%20%3d%20%5c%22(function(){%20%5c%22%20%2b%20js%20%2b%20%5c%22%20%5c%5cn%20})()%5c%22%3b%5cn%5cn%20%20win.print%20%3d%20print%3b%5cn%20%20win.onerror%20%3d%20handleError%3b%5cn%5cn%20%20%2f%2f%20double%20encodeURIComponent%20because%20javascript:%20URLs%20themselves%20are%20encoded!%5cn%20%20win.location.href%20%3d%20%5c%27javascript:eval(decodeURIComponent(%5c%22%5c%27%20%2b%20encodeURIComponent(encodeURIComponent(js))%20%2b%20%5c%27%5c%22))%3b%20void%200%3b%5c%27%3b%5cn%20%20%5cn%20%20refocus()%3b%5cn}%5cn%5cn%2f%2f%20Other%20ideas%20I%20haven%5c%27t%20tried%20lately:%20create%20a%20%3cscript%3e%20element,%20eval.%5cn%5cn%5cnfunction%20makeUserScript(userScriptLink)%5cn{%5cn%20%20%20%20userScriptLink.href%20%3d%20%5cn%20%20%20%20%20%20%20%20%5c%22data:text%2fjavascript%3bcharset%3dutf-8,%5c%22%20%2b%20%5cn%20%20%20%20%20%20%20%20encodeURIComponent(inputFrame.document.getElementById(%5c%22input%5c%22).value%20%2b%20%5c%22%2f%2f.user.js%5c%22)%3b%5cn}%5cn%5cn%3c%2fscript%3e%5cn%5cn%3c%2fhead%3e%5cn%5cn%3cbody%20onload%3d%5c%22initXPather()%5c%22%3e%5cn%5cn%3cbutton%20accesskey%3d%5c%22E%5c%22%20onclick%3d%5c%22execute()%3b%20refocus()%3b%5c%22%3e%3cu%3eE%3c%2fu%3execute%3c%2fbutton%3e%5cn%3c!--%20%3cbutton%20accesskey%3d%5c%22R%5c%22%20onclick%3d%5c%22reloadAndExecute()%3b%20refocus()%3b%5c%22%3e%3cu%3eR%3c%2fu%3eeload%20and%20execute%3c%2fbutton%3e%20--%3e%5cn%3cbutton%20accesskey%3d%5c%22C%5c%22%20onclick%3d%5c%22clearOutput()%3b%20refocus()%3b%5c%22%3e%3cu%3eC%3c%2fu%3elear%20output%3c%2fbutton%3e%5cn%3cbutton%20accesskey%3d%5c%22S%5c%22%20onclick%3d%5c%22showHideOutput()%3b%20refocus()%3b%5c%22%3e%3cu%3eS%3c%2fu%3ehow%2fhide%20output%3c%2fbutton%3e%5cn%3c!--%20%3cbutton%20accesskey%3d%5c%22H%5c%22%20onclick%3d%5c%22help()%3b%20refocus()%3b%5c%22%3e%3cu%3eH%3c%2fu%3eelp%3c%2fbutton%3e%20--%3e%5cn%5cn%3ca%20href%3d%5c%22data:text%2fhtml,...%5c%22%20onfocus%3d%5c%22makeUserScript(this)%3b%5c%22%20onmouseover%3d%5c%22makeUserScript(this)%3b%5c%22%20target%3d%5c%22_blank%5c%22%3eInstall%20as%20user%20script%3c%2fa%3e%5cn%5cn%3cinput%20type%3d%5c%22text%5c%22%20id%3d%5c%22xpath%5c%22%20%2f%3e%5cn%5cn%3cdiv%20style%3d%5c%22visibility:%20hidden%5c%22%3e%5cn%3ciframe%20name%3d%5c%22execFrame%5c%22%20width%3d%5c%225%5c%22%20height%3d%5c%225%5c%22%20src%3d%5c%22about:blank%5c%22%3e%3c%2fiframe%3e%5cn%3c%2fdiv%3e%5cn%5cn%3c%2fbody%3e%5cn%3c%2fhtml%3e%5cn%22)%3bframes[0].document.close()%3b%0aframes[1].document.write(%22%3c!--%20no%20doctype%20-%20it%20makes%20IE%20ignore%20the%20height:%20100%25.%20--%3e%5cn%3chtml%3e%5cn%3chead%3e%5cn%5cn%3ctitle%3einputFrame%3c%2ftitle%3e%5cn%5cn%3cstyle%20type%3d%5c%22text%2fcss%5c%22%3e%5cn%5cnhtml,body,form,textarea%20{%20width:%20100%25%3b%20height:%20100%25%3b%20border:%20none%3b%20margin:%200px%3b%20padding:%200px%3b%20}%5cnhtml,body%20{%20overflow:%20hidden%3b%20}%5cn%5cn%3c%2fstyle%3e%5cn%5cn%3c%2fhead%3e%5cn%5cn%3cbody%20onload%3d%5c%22document.getElementById(%5c%27input%5c%27).select()%3b%5c%22%3e%5cn%3ctextarea%20style%3d%5c%22background-color:%20%23def%5c%22%20id%3d%5c%22input%5c%22%20%5cn%3e%2f%2f%20%3d%3dUserScript%3d%3d%5cn%2f%2f%20%40namespace%20%20%20%20%20http:%2f%2fwww.squarefree.com%2fjsenv%2fautogenerated%5cn%2f%2f%20%40name%20%20%20%20%20%20%20%20%20%20Unnamed%20script%5cn%2f%2f%20%40description%20%20%20Undescribed%20script%5cn%2f%2f%20%3d%3d%2fUserScript%3d%3d%5cn%5cnprint(%5c%22Squares%20of%20numbers%200%20through%204:%5c%22)%3b%5cnfor%20(var%20i%20%3d%200%3b%20i%20%26lt%3b%205%3b%20%2b%2bi)%5cn%20%20print(i%20*%20i)%3b%5cn%5cnthis.statement.causes(an.error)%3b%5cn%3c%2ftextarea%3e%5cn%3c%2fbody%3e%5cn%3c%2fhtml%3e%5cn%22)%3bframes[1].document.close()%3b%0a}void%200%0a"&gt;the XPath jsenv&lt;/a&gt;; enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-7562036003496663846?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=6OM8H9fF4z0:7-hDCOexsWo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=6OM8H9fF4z0:7-hDCOexsWo:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=6OM8H9fF4z0:7-hDCOexsWo:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=6OM8H9fF4z0:7-hDCOexsWo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/7562036003496663846/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=7562036003496663846" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7562036003496663846" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7562036003496663846" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/6OM8H9fF4z0/jsenv-live-xpath-visualizer.html" title="JSEnv live XPath visualizer" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/03/jsenv-live-xpath-visualizer.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-4358053990783188863</id><published>2008-03-01T19:38:00.005+01:00</published><updated>2008-03-01T20:43:00.785+01:00</updated><title type="text">Google safing passwords</title><content type="html">Apparently, lots of places on the net, prominently including all WordPress blogs, &lt;a href="http://www.lightbluetouchpaper.org/2007/11/16/google-as-a-password-cracker/"&gt;use unsalted hashes for password verification&lt;/a&gt;, i e you get to log in if hash(given password) == hash stored in the password database; commonly using cryptographic hash functions like MD5 or SHA1. If that password database leaks, people can fairly effortlessly use Google as an O(1) lookup function from hash to plaintext, for most weak passwords.&lt;br /&gt;&lt;br /&gt;The problem is easily avoided by using salted passwords -- i e hashing the password concatenated with a random component, say 32 bits long, and storing this salt next to the hash, in plaintext. Net effect: your password database, if compromised, will not be susceptible to the same attack, as the search space is 4,294,967,296 times as large, making it less likely that Google knows how to reverse map the hash for you.&lt;br /&gt;&lt;br /&gt;I whipped up a quick tool to see whether a password is susceptible to the Google lookup or not (for MD5 and SHA1, and optionally, whether the plaintext password itself is found on the web too, though I discourage using it for reasons of potential snooping of your traffic). The hack uses some neat client side tools to query Google's Searchmash, using Yahoo! Pipes to decorate its JSON result with the JSONP callback they apparently lack (Google unfortunately still rarely gets that right), and some &lt;a href="http://ecmanaut.googlecode.com/svn/trunk/lib/"&gt;micro-libraries&lt;/a&gt; I've started accumulating recently: &lt;div style="display:none"&gt;&lt;!-- the occasional pain of using automatic nl-to-br :-] --&gt;&lt;br /&gt;&lt;script src="http://ecmanaut.googlecode.com/svn/trunk/lib/jsonp.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src="http://ecmanaut.googlecode.com/svn/trunk/lib/bind.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src="http://ecmanaut.googlecode.com/svn/trunk/lib/md5.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src="http://ecmanaut.googlecode.com/svn/trunk/lib/sha1.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src="http://ecmanaut.googlecode.com/svn/trunk/lib/ask-google.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src="http://ecmanaut.googlecode.com/svn/trunk/sites/ecmanaut.blogspot.com/widgets/ask-google.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p id="to"&gt;&lt;/p&gt;&lt;label for="pwd"&gt;Sample Google for a password dictionary attack on:&lt;/label&gt;&lt;br /&gt;&lt;input type="text" id="pwd" /&gt;&lt;input type="checkbox" id="leak" title="Sample the password itself too?" /&gt;&lt;input type="button" value="Good password?" onclick="test()" /&gt;&lt;br /&gt;&lt;label for="leak"&gt;&lt;p&gt;Check the checkbox to query google for the plaintext version of your prospective password, and not just cryptographic hashes of it. (Anyone snooping your, Google's or Yahoo's network traffic, will see it.)&lt;/p&gt;&lt;/label&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-4358053990783188863?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=nqhcJYO-mRc:BVCkfLJ7Pdc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=nqhcJYO-mRc:BVCkfLJ7Pdc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=nqhcJYO-mRc:BVCkfLJ7Pdc:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=nqhcJYO-mRc:BVCkfLJ7Pdc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://www.lightbluetouchpaper.org/2007/11/16/google-as-a-password-cracker/" title="Google safing passwords" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/4358053990783188863/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=4358053990783188863" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/4358053990783188863" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/4358053990783188863" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/nqhcJYO-mRc/google-safing-passwords.html" title="Google safing passwords" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/03/google-safing-passwords.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-5056112054349863055</id><published>2008-03-01T15:27:00.002+01:00</published><updated>2008-03-01T15:53:36.260+01:00</updated><title type="text">User scripting new GMail messages</title><content type="html">With the recent change in how GMail works, lots of prior user scripts improving the service, like &lt;a href="http://userscripts.org/scripts/show/1611"&gt;a small hack&lt;/a&gt; that removes &lt;code&gt;&amp;lt;font size="+1"&amp;gt;&lt;/code&gt; tags from mails broke, in the wake of the DHTML improvements (which make the site more difficult to augment via user scripting).&lt;br /&gt;&lt;br /&gt;The GMail developers, generously, were not very far behind to &lt;a href="http://googlesystem.blogspot.com/2007/11/gmail-api-for-greasemonkey.html"&gt;release&lt;/a&gt; a smallish &lt;a href="http://code.google.com/p/gmail-greasemonkey/wiki/GmailGreasemonkey10API"&gt;Greasemonkey API&lt;/a&gt;, somewhat alleviating the problem -- but it still is less than trivial to target mail bodies with your user scripts.&lt;br /&gt;&lt;br /&gt;I took a stab at it the other night, to figure out what would go into it, and managed to come up with &lt;a href="http://userscripts.org/scripts/show/23408"&gt;a new font manhandler&lt;/a&gt; (it targets a rather local nuisance on the Greasemonkey mailing lists). As I have been trying out &lt;a href="http://mailplaneapp.com/"&gt;MailPlane&lt;/a&gt; for a while for reading my mail I decided to see if I could make the script run both in Firefox on Greasemonkey and Safari/WebKit/MailPlane on the &lt;a href="http://8-p.info/greasekit/"&gt;GreaseKit&lt;/a&gt; user script manager. That turned out quite doable. The script, in its entirety:&lt;br /&gt;&lt;code class="javascript"&gt;&lt;pre&gt;// ==UserScript==&lt;br /&gt;// @name           Gmail - deBill:ifier&lt;br /&gt;// @namespace      http://code.google.com/p/ecmanaut/&lt;br /&gt;// @description    Manhandles all font size tags to stop all the yellin'&lt;br /&gt;// @include        https://mail.google.com/*&lt;br /&gt;// @include        http://mail.google.com/*&lt;br /&gt;// ==/UserScript==&lt;br /&gt;&lt;br /&gt;window.addEventListener("load", loader, false);&lt;br /&gt;&lt;br /&gt;function loader() {&lt;br /&gt;  var api = typeof unsafeWindow != "undefined" &amp;amp;&amp;amp; unsafeWindow.gmonkey ||&lt;br /&gt;    (frames.js ? frames.js.gmonkey : null);&lt;br /&gt;  if (api) api.load("1.0", init);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function init(gmail) {&lt;br /&gt;  function viewChanged() {&lt;br /&gt;    var view = gmail.getActiveViewType();&lt;br /&gt;    if ("cv" == view) {&lt;br /&gt;      var div = gmail.getActiveViewElement();&lt;br /&gt;      div.addEventListener("DOMNodeInserted", deBill, false);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  gmail.registerViewChangeCallback(viewChanged);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function deBill(event) {&lt;br /&gt;  var font = event.target.getElementsByTagName("font");&lt;br /&gt;  for (var i = 0; i &amp;lt; font.length; i++)&lt;br /&gt;    font[i].removeAttribute("size");&lt;br /&gt;}&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Feel very free to base your own hacks on the recipe. I recommend starting out with an action function that does something basic like setting &lt;code&gt;event.target.outline = "1px solid red"&lt;/code&gt;, to see where it does what it does -- since the callback runs for every conversation view change, including updates to the ad pane on the right, not only when expanding mails you read.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-5056112054349863055?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tUakOVcb-7U:M2pciJcHXIA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tUakOVcb-7U:M2pciJcHXIA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tUakOVcb-7U:M2pciJcHXIA:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tUakOVcb-7U:M2pciJcHXIA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://userscripts.org/scripts/show/23408" title="User scripting new GMail messages" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/5056112054349863055/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=5056112054349863055" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/5056112054349863055" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/5056112054349863055" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/tUakOVcb-7U/user-scripting-new-gmail-messages.html" title="User scripting new GMail messages" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/03/user-scripting-new-gmail-messages.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-7734667431170665574</id><published>2008-02-24T23:55:00.007+01:00</published><updated>2008-02-25T15:37:26.528+01:00</updated><title type="text">Cooperating Greasemonkey scripts</title><content type="html">&lt;i&gt;I should apologize in advance that most of the scripts I link here use a not-yet-released &lt;code&gt;@require&lt;/code&gt; feature of the svn development version of Greasemonkey (the equivalent of &lt;code&gt;#include&lt;/code&gt; in C/C++, so if you exert yourself, you can paste together the scripts by hand by inlining the code at the top of the script). Pardon the nuisance; at least it won't ruin this post for you.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I have taken another stab at &lt;a href="http://ecmanaut.blogspot.com/2006/04/stigmergic-user-script-pattern.html"&gt;stigmergy computing&lt;/a&gt;, or writing scripts that communicate by changing common environment, here the web page they execute in.&lt;br /&gt;&lt;br /&gt;Anyway, last time, I wanted a decent &lt;a href="http://some-assembly-required.blogspot.com/2006/03/generic-image-browser-greasemonkey.html"&gt;portable web photo album browser&lt;/a&gt;; functionality that would follow me around irregardless of where I roam on the web. Something to flip through images with the arrow keys, perhaps with some additional handy features too.&lt;br /&gt;&lt;br /&gt;The album browser would consume the very basic microformat &lt;code&gt;&amp;lt;a href="*.&lt;i&gt;image extension&lt;/i&gt;"&amp;gt;&lt;/code&gt;, and turn it into a gallery. This meant that I could use my familiar album browser mostly everywhere without any work at all. It also meant that, when encountering some photo site working differently, I could adapt not my album to the site, but the site to my album. A minimal script that changes a single site only very slightly, is trivial -- adapting old code rarely is. So this little ecology convened around a minimal ad-hoc image album microformat. &lt;br /&gt;&lt;br /&gt;This time I have several microformat consumers convening around a microformat I came up with for destructuring pagination into what is popularly (if somewhat incorrectly) called endless scrolling; a page that, when you approach the end of it, loads another pageful or so by means of XMLHttpRequest. Usually an abomination, when some site designer forces that browsing mode for you, but ever so handy at times, if you only got to choose for yourself.&lt;br /&gt;&lt;br /&gt;Anyway, I started off basing it on the old &lt;code&gt;&amp;lt;link rel="next"&amp;gt;&lt;/code&gt; tag, which is actually standardized, hoping that I would, at least once in a while, not need to produce this bit of the microformat myself, but could ride on data in the web page itself. So far, that has not saved me any work but cluttered the microformat into picking next links in two orthogonal ways, depending on what is available, so I'm inclined to write that off as a bad, yet well-intended, idea (see my first reply below for the messy details of how standards bloat like this typically come about).&lt;br /&gt;&lt;br /&gt;I needed three data items: an url to the next page in the sequence, an XPath expression that matches the items on every page, and an (optional) XPath expression that identifies a pagination pane to update with the last page loaded so far. I came up with three meta tags, named "next-xpath", "items-xpath" and "pagination-container". If the web page gave me those, my pagination script could deal only with undoing pagination, and care less about whether the data it actually handles is &lt;a href="http://userscripts.org/scripts/show/23211"&gt;Flickr photos&lt;/a&gt;, &lt;a href="http://userscripts.org/scripts/show/23224"&gt;vBullentin forum posts&lt;/a&gt;, &lt;a href="http://userscripts.org/scripts/show/23176"&gt;Google&lt;/a&gt; or &lt;a href="http://userscripts.org/scripts/show/23213"&gt;Yahoo&lt;/a&gt; search results, or &lt;a href="http://userscripts.org/scripts/show/23177"&gt;something else entirely&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After doing some crazy limbo (which deserves a post of its own) loading the next page into a hidden iframe (both for letting Greasemonkey scripts producing the microformat run, and to get a correct DOM for the page to run the XPaths -- as it turns out that, amazing as it may sound, browsers can't turn an html string into a DOM without ruining the html, head and body tags; at least not Firefox 2 and Opera 9), &lt;a href="http://userscripts.org/scripts/show/23175"&gt;the remainder of the script&lt;/a&gt; was a walk in the park.&lt;br /&gt;&lt;br /&gt;Having written up &lt;a href="http://userscripts.org/tags/microformat%20producer"&gt;a few microformat producers for various sites&lt;/a&gt; to exercise the microformat and see if it addresses the problems that show up well enough, found and straightened out some minor quirks, I felt the urge to do more with this smallish, but oh, so useful, microformat, telling apart content from non-content in web pages, so I decided to hook up my &lt;a href="http://userscripts.org/scripts/show/21947"&gt;custom keybindings script&lt;/a&gt; with this hack, making it assign "p" and "m" to scrolling up and down among the items of these pages. That came down to a whopping ten new lines of code, to make a dozen, or, factoring in future microformat producers, an unbounded number of sites scrollable in that way.&lt;br /&gt;&lt;br /&gt;And -- best saved for last -- both microformat consumer scripts coexist happily, independently and work orthogonally; the unpaginated whatevers that get added to the end of the page are browsable with the new keyboard bindings, so you can work mostly any web page as comfortably as ever your feed, mail or whatnot reader, right there.&lt;br /&gt;&lt;br /&gt;If you craft any &lt;a href="http://userscripts.org/tags/microformat%20producer"&gt;microformat producer&lt;/a&gt; or &lt;a href="http://userscripts.org/tags/microformat%20consumer"&gt;microformat consumer&lt;/a&gt; user scripts, please tag them as such on userscripts.org, and also tag them with the name or names of the microformat(s) they handle too (I picked, somewhat arbitrarily, &lt;a href="http://userscripts.org/tags/photo%20album%20microformat"&gt;photo album microformat&lt;/a&gt; and &lt;a href="http://userscripts.org/tags/pagination%20microformat"&gt;pagination microformat&lt;/a&gt; for mine), so they form &lt;i&gt;findable&lt;/i&gt; microformat ecologies for other users. Any site your microformat gets a producer for, improves the value of all the microformat consumers of that format, and any microformat consumer using the format in some useful way, improves the value of all the producers.&lt;br /&gt;&lt;br /&gt;It is very rewarding doing stigmergy / ecology programming; the sense of organics and growth is very palpable. There are some sharp corners to overcome, and Greasemonkey could help out more, but I'll have to get back to that at some later time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-7734667431170665574?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=SQFC1px7jOc:vlPq68mT1Vk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=SQFC1px7jOc:vlPq68mT1Vk:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=SQFC1px7jOc:vlPq68mT1Vk:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=SQFC1px7jOc:vlPq68mT1Vk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/7734667431170665574/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=7734667431170665574" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7734667431170665574" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/7734667431170665574" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/SQFC1px7jOc/cooperating-greasemonkey-scripts.html" title="Cooperating Greasemonkey scripts" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/02/cooperating-greasemonkey-scripts.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-1977732676907538319</id><published>2008-02-24T00:23:00.003+01:00</published><updated>2008-02-24T01:04:23.137+01:00</updated><title type="text">High cpu load web page diagnosis</title><content type="html">For web development, a laptop with a case fan that starts screaming like a jet engine when your cpu load skyrockets is sometimes a Really Good Thing, since it gives you cues to what happens in your browser, whether you set out do do profiling or not when, bad enough things happen. (A bit like how you could debug your programs on the C64 from the overtones that would leak from cpu to audio circuitry, giving distinct audio signatures to the occasional choke point in your code.)&lt;br /&gt;&lt;br /&gt;Anyway, a Swedish social site I occasionally frequent used to have this effect for me, and get worse the more tabs I had open to it (and it was bad already with a modest three). I used to think it was a bugged Flash app (which they use for ads), but when I realized I had killed them all with AdBlock and FlashBlock, it occurred to me that it might be run-amok javascript.&lt;br /&gt;&lt;br /&gt;Only that javascript that runs amok usually gives you the annoying modal dialog asking whether you  want to keep running the cpu hogging script. That, however, only happens when page javascript hogs the cpu from the UI, so the single UI thread gets starved, and you, the user, get annoyed with an unresponsive UI. That can be avoided by running smaller loop fragments that call one another with setTimeout calls, leaving some breathing room for the UI thread in-between.&lt;br /&gt;&lt;br /&gt;So how do you debug if that is what is happening? Well, for one thing, at least in Firefox 2, you can call setTimeout yourself two times in a row, some second or so apart, and compare the timeout id:s you get back, because consecutive calls get consecutive id:s. So if the difference is one, no setTimeout call was made in-between.&lt;br /&gt;&lt;br /&gt;I crafted a bookmarklet to &lt;a href='javascript:void(function(){var t=new Date,i=setTimeout(function(){t=(new Date)-t;i=(setTimeout(function(){},0)-i-1)/(t/1e3);alert("This page is making "+Math.round(i)+" setTimeout() calls per second");},2e3)})()'&gt;count setTimeout() calls per second&lt;/a&gt; (well, I honestly started with something smaller in the Firebug console, but figured it was useful enough to save, share and reuse later), and sure enough, that site was calling setTimeout 178 times per second. (It's not very interesting to run it on this site at present, but you can always race with yourself and the UI responsivity of your browser, attack clicking it to see how many setTimeout calls you can fire off in a second by hand. I believe it is not one of the performance tests we will see in any side by side browser shootouts any time soon. My personal highscore with Firefox 2 is 3. :-)&lt;br /&gt;&lt;br /&gt;To do what? Well, time for another &lt;a href='javascript:void(function(){var o=setTimeout;setTimeout=function(){var a=[].slice.call(arguments);if(prompt("These were the arguments passed to setTimeout(). Reinstate the original version?",a.toSource().slice(1,-1)))setTimeout=o;o.apply(window,[a[0],0,a.slice(2)]);};})()'&gt;bookmarklet that shows which arguments setTimeout gets called with&lt;/a&gt; (it won't run on Internet Explorer; sorry: kindly advise Microsoft to implement the toSource prototype for all javascript native datatypes -- I &lt;i&gt;would&lt;/i&gt; want to say it would not be a decade too late, but that would make me a liar :-), and lets you restore the original setTimeout implementation when you are done peeking.&lt;br /&gt;&lt;br /&gt;It turned out the site was trying to fetch a node with an advertisement I had AdBlocked away. Over and over again. A tiny Greasemonkey script later, and the laptop fan was happily put to sleep, once again. A toast to audial debugging!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-1977732676907538319?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tTdaUZPdi6E:04ppLZYFu5Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tTdaUZPdi6E:04ppLZYFu5Q:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tTdaUZPdi6E:04ppLZYFu5Q:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=tTdaUZPdi6E:04ppLZYFu5Q:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/1977732676907538319/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=1977732676907538319" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/1977732676907538319" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/1977732676907538319" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/tTdaUZPdi6E/high-cpu-load-web-page-diagnosis.html" title="High cpu load web page diagnosis" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/02/high-cpu-load-web-page-diagnosis.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-141448640878913637</id><published>2008-02-21T17:14:00.002+01:00</published><updated>2008-02-21T18:42:48.786+01:00</updated><title type="text">Reducing user scripts to atoms</title><content type="html">Over the past few weeks, I have found myself to prod ever more frequently at two user scripts; &lt;a href="http://userscripts.org/scripts/show/19344" title="fold comments"&gt;a UI reducing one&lt;/a&gt; and &lt;a href="http://userscripts.org/scripts/show/21947" title="add keyboard shortcuts"&gt;a UI extending one&lt;/a&gt;. Both @include the entire web, but are tailored to sites individually on a site by site basis, as I realize that I miss either feature somewhere, and add it, for my own comfort -- some cruft I rarely want to see here, some action I want bound to a key there.&lt;br /&gt;&lt;br /&gt;It was only a question of time, and I just hit that tell-tale moment where these two growing (yet, due to factoring it down into convention based descriptive more than programmatic notation, still fairly maintainable) beasts want to marry one another and &lt;s&gt;finally gain independent sentient thought and a soul&lt;/s&gt;, uhm, augment features of one another to become greater than the sum of their respective parts.&lt;br /&gt;&lt;br /&gt;I'll be back to that in a moment, after summarizing how they came to be and what they do. Each started on the premise of recognizing an improvement I would like to see for web sites mostly everywhere, that would entail approximately the same code for any site I wanted to apply it on, but with minute changes to parameters.&lt;br /&gt;&lt;br /&gt;In the first case it was the all-too-popular tendency on the web (if, however, probably mostly also equally unintentional) -- of giving &lt;a href="http://ecmanaut.blogspot.com/2008/01/unbreak-scrollbar.html"&gt;gratuitous focus to visitor voices&lt;/a&gt;, at the expense of my taking in the author voice -- that is less popular with me. A few swift DOM cuts, and the excess commentary is folded away.&lt;br /&gt;&lt;br /&gt;In the second case, it was recognizing what a great improvement in usability changing web pages to offer comfortable keyboard commands, and more so, comfortable &lt;i&gt;standardized&lt;/i&gt; keyboard commands, that I could pick myself, but would work on lots of web sites that I use in a similar fashion. (More on that after this hyperbole. :-)&lt;br /&gt;&lt;br /&gt;A few years ago, I was wary of web comics, mostly because I read them seldom and would never remember where I trailed off last time, so I could continue from there, yet I neither wanted to reread lots of them to find my bookmark (and browser bookmarks were too much labour overhead), or miss a strip. One early hack was a bookmarklet that would set a cookie to the present url, or, if invoked from the frontpage, go to the url in that cookie. Instant recollection of last location, and a single bookmarklet for any web site. Excellent.&lt;br /&gt;&lt;br /&gt;Eventually, I found and had a love affair with Greasemonkey, and it seems in 2006, or perhaps some year earlier, that bookmarklet, too, was too much to keep track of, and I crafted an &lt;a href="http://userscripts.org/scripts/show/5882"&gt;automated user script to keep track of last-read&lt;/a&gt; for me. The main net benefit with crafting hacks like that is often finding the additional features that are so easy to add, yet prove much more valuable than hard to implement. In this case, it was realizing that this little repository keeping track of all comics I read could link them to a little comics ring that would follow &lt;i&gt;me&lt;/i&gt; around, always linking to the next in the ring, and specifically the last one I had read, ready to continue traversing. Better still, that way I wouldn't forget those least frequently updated or read comics, as I often would otherwise. Handy!&lt;br /&gt;&lt;br /&gt;And when I started assigning keyboard shortcuts to web sites, adding them for comics was another eye opener -- arrow left and right (or j/k) to head backwards and forwards in the archive, "n" for next site in the ring. Instant gratification, all over again. After I fell in love with &lt;a href="http://questionablecontent.net/view.php?comic=1079"&gt;Questionable Content&lt;/a&gt;, I extended that to also include hotkeys to scroll up/down a panel, p/m (for giving that comfy lazy restful right hand resting on m/k/p, to zoom through its bountiful archive in no time flat), and next thing I knew, my spine expected those handy scrolling keys to work on &lt;a href="http://arclanguage.org/forum"&gt;interesting forums&lt;/a&gt; too.&lt;br /&gt;&lt;br /&gt;If Jon Udell has a thing for &lt;a href="http://blog.jonudell.net/2008/02/20/overcoming-data-friction/"&gt;overcoming data friction&lt;/a&gt;, I guess one of my bigger things is overcoming user interface friction. With the tiny convention based system I crafted, adding a keyboard binding somewhere was just pasting a hostname, deciding on a regexp for what on-site pages to augment, deciding on keys and crafting a quick xpath expression for picking up which link or page entity to click on with that button, or one to slice out which nodes to snap to for the scroll actions. Done in essentially no time at all, for a geek such as myself. Complete comfort, at essentially no time or work investment on my part.&lt;br /&gt;&lt;br /&gt;And then it dawned on me. I want that handy scroll-to-the-next-something on all those sites where I taught that first script how to fold comments. And, as I had stylishly also taught that script how to count comments for each site, it already knew how to find each comment node, so with a quick marriage of these scripts, lots of sites would gain that feature in the blink of an eye.&lt;br /&gt;&lt;br /&gt;Except I don't really want to do that. It's conceptually wrong; it makes every script that could use a detail of another clump up into big chunks of things to satisfy the exact preferences of me at the expense of other users that only want, say, the hotkey feature, or perhaps only want it for a single site they care about. It would be much, much better if these scripts could &lt;a href="http://ecmanaut.blogspot.com/2006/04/stigmergic-user-script-pattern.html"&gt;cooperate&lt;/a&gt; like in a symbiotic biological environment, each script specializing in one thing and doing that well.&lt;br /&gt;&lt;br /&gt;It would reduce each script to a small axiom; one script handling the core aspects of registering keyboard listeners that do not interfere with form input, while assigning key bindings to specific page elements or page actions common throughout the web, from a recipe entailing which keys to bind to what features of pages on a specific site. One script handling the logic of folding entities of some sort, given recipes for the same thing. Lots of little dumb work drone scripts that handle recognizing and marking up page features for other scripts -- comments on this page are here, a link to the next, previous and index pages of this site are here. For the latter, there is even a meta tag microformat which good browsers like Opera handle natively, so these scripts, when possible, should decorate the page with that microformat so other entities which consume the microformat get it for free. And so on; one script to connect microformat with keyboard shortcuts, one to connect comments with folding functionality.&lt;br /&gt;&lt;br /&gt;That way, we would get specialization, and we would get decentralization, as anybody on the web can easily, independently, craft a new recognizer targeting sites they use, and I wouldn't even hear about it; they would post their scripts to userscripts.org, and all the Japanese who frequent that site would pick it up, and we basically have a lovely &lt;a href="http://en.wikipedia.org/wiki/Service-oriented_architecture" title="Service-Oriented Architecture"&gt;SOA&lt;/a&gt; system right there. Each part can evolve and iterate independently, at landslide speeds, and people would find these scripts by looking for scripts catering their sites, not through looking for keyboard shortcut scripts, which no doubt very few do.&lt;br /&gt;&lt;br /&gt;I think this could use some catering in Greasemonkey, though, to tie the components together and help users tie together producer (those tiny custom drones) and consumer (the feature implementing agents) with one another, maybe even associate them with one another visually in the Greasemonkey script manager. Signals in the user script header like &lt;code&gt;@produces&lt;/code&gt; and &lt;code&gt;@consumes&lt;/code&gt; tags, of some sort, seem like one good way of doing it.&lt;br /&gt;&lt;br /&gt;I am not sure of exactly which all this plumbing should do, nor how, but I think that there is &lt;i&gt;a lot&lt;/i&gt; of good that would come out of it, just like how all the above evolution of three user scripts has changed my web experience to a very luxorous one, compared by the counterpart of only last year. I had no idea how much I would lavish in neither using the mouse nor arrow keys nor page up/down to do my web navigation, but factor in the amount of time you spend with those tools, and it should be quite apparent to anyone.&lt;br /&gt;&lt;br /&gt;Your thoughts and ideas are welcome, especially if you blog them after noodling on it for a while; I think we deal in largish ideas that fit the comments field below less than well. Especially as my own site, too, so far, still suffers the "comments dwarf original posts" syndrome when subjected to great visitor attention.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-141448640878913637?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=bwEmJCGjHvA:sI-Dl6hRr6U:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=bwEmJCGjHvA:sI-Dl6hRr6U:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=bwEmJCGjHvA:sI-Dl6hRr6U:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=bwEmJCGjHvA:sI-Dl6hRr6U:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/141448640878913637/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=141448640878913637" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/141448640878913637" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/141448640878913637" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/bwEmJCGjHvA/reducing-user-scripts-to-atoms.html" title="Reducing user scripts to atoms" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/02/reducing-user-scripts-to-atoms.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-2544359900465158879</id><published>2008-01-14T11:58:00.000+01:00</published><updated>2008-01-14T12:23:42.379+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Firebug" /><title type="text">Fun with Firebug: Error Messages!</title><content type="html">When working in the Firebug console, you might have noticed that syntax errors and the like show up in red on executing the command line. Great success! Exceptions thrown by the DOM, however, for some reason don't, so there is no telling whether your &lt;code&gt;document.getElementById()&lt;/code&gt; call happily returned &lt;code&gt;undefined&lt;/code&gt; or threw an exception on you.&lt;br /&gt;&lt;br /&gt;If you go to &lt;tt&gt;&lt;a href="about:config" target="_blank"&gt;about:config&lt;/a&gt;&lt;/tt&gt; and type &lt;tt&gt;firebug.showChromeErrors&lt;/tt&gt; into the Filter: field, and double-click that line to read &lt;b&gt;true&lt;/b&gt; -- presto! You can suddenly see where you and the DOM disagree, and when you made some XPath syntax error, so you can tell apart cases like &lt;code&gt;$X('//div[@class="post"')&lt;/code&gt; and &lt;code&gt;$X('//div[@class="post"]')&lt;/code&gt; and the like.&lt;br /&gt;&lt;br /&gt;I've &lt;a href="http://code.google.com/p/fbug/issues/detail?id=428"&gt;filed a ticket&lt;/a&gt; on this weird default behaviour, but am glad I finally found this trivial cure. (I really don't want to know how many times I've wrapped a statement on the command line into a &lt;code&gt;try{ ... }catch(e){e}&lt;/code&gt; block to figure out just if or what went wrong.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-2544359900465158879?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=lSQZIWBB2DM:lMB0FUEGjpc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=lSQZIWBB2DM:lMB0FUEGjpc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=lSQZIWBB2DM:lMB0FUEGjpc:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=lSQZIWBB2DM:lMB0FUEGjpc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="related" href="http://code.google.com/p/fbug/issues/detail?id=428" title="Fun with Firebug: Error Messages!" /><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/2544359900465158879/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=2544359900465158879" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2544359900465158879" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/2544359900465158879" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/lSQZIWBB2DM/fun-with-firebug-error-messages.html" title="Fun with Firebug: Error Messages!" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/01/fun-with-firebug-error-messages.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-15626356.post-6909622920545759565</id><published>2008-01-06T16:18:00.002+01:00</published><updated>2008-08-13T01:22:45.118+02:00</updated><title type="text">Unbreak the scrollbar</title><content type="html">This is a post on visitor centric design and usability, which first and foremost applies to popular blogs: don't break the native window scrollbar. It's in a way related to the advice &lt;a href="http://ecmanaut.blogspot.com/2007/11/embrace-your-constraints.html"&gt;don't reinvent native browser scrollbars&lt;/a&gt;, and boils down to this short tip:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;If visitor comments on your posts or articles add up to more than a single-digit percentage of your page height, don't show them by default.&lt;/blockquote&gt;&lt;br /&gt;To get nice order-of-magnitude measures of page sizes, try this bookmarklet which shows &lt;a href="javascript:alert((scrollMaxY+innerHeight)/innerHeight)"&gt;how many pagefuls&lt;/a&gt;, at present window dimensions, your document fills; drag it to your bookmark toolbar, if you want to measure other pages than the one you are presently reading (visit the post, if you read this by feed reader, as feeds and feed readers try to save you from badness by stripping away potentially harmful bookmarklets).&lt;br /&gt;&lt;br /&gt;If you visit some interesting post which does not heed this tip, perhaps Steve Yegge on &lt;a href="http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html"&gt;size being code's worst enemy&lt;/a&gt;, or perhaps the sci-fi writer Charles Stross about &lt;a href="http://www.antipope.org/charlie/blog-static/2007/06/the_high_frontier_redux.html"&gt;why space colonization might not be such a great prospect&lt;/a&gt;, you might find that it's 300 pagefuls tall. Without its 825 comments, it comes down to about five pages, making visitor commentary a whopping 98% of the document's size, which by a margin breaks the sub-ten-percent bound.&lt;br /&gt;&lt;br /&gt;The reason this is bad is that it breaks two important functions of the document / scrollbar symbiosis: first, it prevents a visitor from the size overview offered by the scrollbar size to scrollbar height ratio -- "How long is this document, and how far into it have I scrolled?" -- and second, it prevents you from quickly scrolling to the bottom of the document using browser native "Page end" bindings, as there is no snapping to where the post ends and comments start.&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://www.joehewitt.com/blog/"&gt;Joe Hewitt's blog&lt;/a&gt; for an excellent example of showing comments only after having clicked the number of comments hidden. (If you hide comments, by default, it's a very good measure to show how many.)&lt;br /&gt;&lt;br /&gt;I've whipped up a quick'n'dirty user script to turn a few blogs I frequently frequent into &lt;a href="http://userscripts.org/scripts/show/19344"&gt;click-to-unfold-comments reading mode&lt;/a&gt;, as I care for the author's view first, and reader reaction only second, if at all. I didn't go the extra mile to come up with a good UI for making it extend to any blog you might encounter, but XPath geeks and Hoodwink'd dialers will probably find themselves right at home to extend it for their own reading habits, and perhaps post their own additions to the script homepage. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15626356-6909622920545759565?l=ecmanaut.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gcrZ5brRprQ:WepO2TtOVmE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gcrZ5brRprQ:WepO2TtOVmE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gcrZ5brRprQ:WepO2TtOVmE:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ecmanaut?a=gcrZ5brRprQ:WepO2TtOVmE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ecmanaut?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://ecmanaut.blogspot.com/feeds/6909622920545759565/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=15626356&amp;postID=6909622920545759565" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6909622920545759565" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/15626356/posts/default/6909622920545759565" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ecmanaut/~3/gcrZ5brRprQ/unbreak-scrollbar.html" title="Unbreak the scrollbar" /><author><name>Johan Sundström</name><uri>http://www.blogger.com/profile/04076097346172610543</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="13419531255881648751" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://ecmanaut.blogspot.com/2008/01/unbreak-scrollbar.html</feedburner:origLink></entry></feed>
