tag:blogger.com,1999:blog-80558712024-03-15T21:09:29.595-04:00Blatherbergkonberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.comBlogger324125tag:blogger.com,1999:blog-8055871.post-67874744514312692152011-08-14T00:39:00.005-04:002011-08-14T00:41:08.325-04:00Planet Eclipse Is Misrepresenting MeAll,<br />
<br />
Unfortunately the wrong feed URL for my Wordpress blog was fed into Planet Eclipse and so the following posts <b>are not mine</b>, even though they seem to have my name on them.<br />
<ul>
<li>Robert Konigsberg: Senior Software Engineer</li>
<li>Robert Konigsberg: Cure for the itch</li>
<li>Robert Konigsberg: Java Developers (ALL LEVELS) San Antonio TX
</li>
<li>Robert Konigsberg: 2 Fast thrice Furious
</li>
<li>Robert Konigsberg: Eclipse CREDENTIAL_ERROR
</li>
</ul>
The <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=350355">Eclipse bug</a> around which all of this has been based has been updated with a request to correct the feed.
<br />
<br />
Thanks to <a href="http://www.vogella.de/">Lars Vogel</a> for pointing this out.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com1tag:blogger.com,1999:blog-8055871.post-22916585163373777152011-06-25T11:29:00.001-04:002011-06-25T11:29:16.076-04:00WordPress: a three month experimentI'm starting a three month experiment of using WordPress solely for blogging.<br />
<br />
I still like Blogger's platform, but also the few times I've worked with WordPress I've really liked what it had to offer. So now's just as good a time as any to give it a three month trial.<br />
<br />
I will not be updating this blog during this time. You can follow me at <a href="http://blatherberg.wordpress.com/">blatherberg.wordpress.com</a>. I've already put up my <a href="http://blatherberg.wordpress.com/2011/06/25/a-three-month-experiment/">first post</a>, which talks a little bit about using the things I discovered while getting started.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-23250542798216341262011-05-26T23:41:00.014-04:002011-05-26T23:48:52.740-04:00Mac Word of the Day<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-3TidInk5PQs/Td8eszI6YaI/AAAAAAAAwDM/3l57PbeQWcE/s1600/polynomial.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="228" src="http://2.bp.blogspot.com/-3TidInk5PQs/Td8eszI6YaI/AAAAAAAAwDM/3l57PbeQWcE/s320/polynomial.jpg" width="320" /></a></div>
<br />
I want to see if using <a href="http://electricsheep.org/">Electric Sheep</a> as my screen saver is the cause of recent OSX workstation lock-ups. So I scanned the available screen savers and settled on Word of the Day screen saver.<br />
<br />
The first thing it published was:<br />
<br />
<br />
<div class="p1">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="s1">polynomial: </span><span class="s3">Mathematics </span>of, relating to, or denoting a polynomial or polynomials. </span></div>
<div class="p1">
<br /></div>
<div class="p1">
Yeah, that was useful. Thanks. I'll stick with dog photos.</div>
konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-77222069189357640722011-05-22T15:18:00.131-04:002011-05-22T17:21:13.578-04:00Tales of Pirx The Pilot<a href="http://brainkandy.org/pics/lem/Lem0027.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="http://brainkandy.org/pics/lem/Lem0027.JPG" style="cursor: move;" width="208" /></a>I learned about <a href="http://www.lem.pl/">Stanislaw Lem</a> in 1992 when a friend made a gift of <a href="http://en.wikipedia.org/wiki/The_Cyberiad">The Cyberiad</a> to me. He has become my favorite author ever since. I own all of his books translated to English, and one in Polish.<br />
<br />
That isn't to say I've read all those books, save the untranslated one. No, there are several which remained unread or partially read over the last 20 years. Two of these are the series <a href="http://en.wikipedia.org/wiki/Tales_of_Pirx_the_Pilot">Tales of Pirx the Pilot, and More Tales of Pirx the Pilot</a>. Though years back I read the first story in the series, <i>The Test</i>, a fantastic story that demonstrates a skill Lem excels at but rarely uses, that of slowly-building dramatic tension followed by a shocking and logical twist. Even while re-reading <i>The Test</i> this past week for the second time in fifteen years, even knowing how the story ended, I couldn't help but forget everything but what was going on at that moment in the story, and still felt a genuine sense of shock by the revelation of the story's unspoken conceit. The only other time I experienced this sense of being drawn in, hooked on every single word, despite knowing how the story ends, is the story-within-a-story in <a href="http://en.wikipedia.org/wiki/Fiasco_%28novel%29">Fiasco</a>.<br />
<br />
But now I've moved on to the second story in <i>Tales</i>, titled <i>The Conditioned Reflex</i> and it's delicious in the more standard way for him to write, which is his ability to describe fake science. Here Pirx is borrowing a space suit from the Russia's lunar station.<br />
<blockquote>
The Russian suit was unconventional in a number of ways. For one thing it had three, instead of two, visors: one for high Sun, one for low, and one - shaded dark orange - for dust. The air-valve arrangement was different, and it was rigged with inflatable boots that cushioned the impact of rocks and gripped even the slipperiest surface; they called it the "high-mountain model." Even the coloring was different: half in black and half in silver. When you stood with the black side facing the Sun, you broke out in a sweat; with the silver you were braced by a delicious coolness.<br />
<br />
Pirx found the idea had one basic flaw in it: a man couldn't always be pointed in the direction of the Sun. So what was he supposed to do? Walk backward?<br />
<br />
The others chuckled and called his attention to a color alternator located on his chest. If he adjusted the knob, the colors could be reversed: black in front, silver in back, and vice versa. The mechanics of it were interesting. The suit's outer ply was made of a clear, tear-resistant nylon fabric; between it and his body was a thin air barrier filled with two different kinds of dye, or rather semiliquid substances - one aluminized, the other carbonized...</blockquote>
Lem's view of space travel speaks to me more than the Roddenbery/Star Trek/space fantasy view. Space travel is inconvenient, difficult, a product of good and bad engineering, and can often fail because of small problems compounding on each other. From a favorite story of mine, <a href="http://english.lem.pl/home/bookshelf/the-seventh-voyage">The Seventh Voyage</a> from <i>The Star Diaries</i>:<br />
<blockquote>
<a href="http://brainkandy.org/pics/lem/Lem0028.JPG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="http://brainkandy.org/pics/lem/Lem0028.JPG" width="207" /></a>It was on a Monday, April second - I was cruising in the vicinity of Betelgeuse - when a meteor no larger than a lima bean pierced the hull, shattered the drive regulator and part of the rudder, as a result of which the rocket lost all maneuverability. I put on my spacesuit, went outside and tried to fix the mechanism, but found I couldn't possibly attach the spare rudder - which I'd had the foresight to bring along - without the help of another man. The constructors had foolishly designed the rocket in such a way, that it took one person to hold the head of the bolt in place with a wrench, and another to tighten the nut.</blockquote>
In the end, Lem's stories human failings and not technical ones, and typically that comes down to hubris - failure of the overconfident, the smart and foolish. A good dose of humility is your best asset in Lem's world, even when you're alone, in a damaged rocket, tearing out of control toward the Moon.<br />
<br />
<div class="" style="clear: both; text-align: center;">
</div>
<br />konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-68077771724006319692011-04-28T07:12:00.023-04:002011-04-28T07:21:18.847-04:00PSE&G, Your Laziness Is A LiabilityDear PSE&G,<br />
<br />
Thank you again for coming out in early November to repair the rotting gas line. To accomplish this you had to dig a deep hole, about three feet deep if I recall correctly. I was told the night of the repairs by your team on-site that someone would come fill that hole within that week. Nobody came. My wife has called you repeatedly. We've been given a variety of responses mostly between "We will dispatch somebody" to "It's not our problem" to "It's not our department". Whatever. It's April, and <b>this is a safety issue. It's been six months. Fill the damn hole.</b><br />
<br />
I'm pleased to show you pictures of your incomplete work.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-t28zfciWwN8/TblLJALhm8I/AAAAAAAAvyM/RllQFMv3mhY/s1600/DSC_0002.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="427" src="http://4.bp.blogspot.com/-t28zfciWwN8/TblLJALhm8I/AAAAAAAAvyM/RllQFMv3mhY/s640/DSC_0002.JPG" width="640" /></a></div>
Here's the strong piece of wood covering the hole. Look at those fun toys that kids can play with near by.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-P0_e7AEx2Qc/TblLJhMpidI/AAAAAAAAvyc/n8K-u7fFQec/s1600/DSC_0005.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="http://4.bp.blogspot.com/-P0_e7AEx2Qc/TblLJhMpidI/AAAAAAAAvyc/n8K-u7fFQec/s640/DSC_0005.JPG" width="428" /></a></div>
Look at that -- the whole pit is filled with water! Perfect for getting a quick jump on mosquito season.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-82129024323635645592011-04-26T20:09:00.000-04:002011-04-26T20:32:52.366-04:00Dygraphs Bugs and the Start of Automated Javascript TestingShort version:<br />
<br />
Someone contributed wrote a bunch of code to <a href="http://dygraphs.com/">Dygraphs</a>. It broke an existing behavior, but also inadvertently (or advertently) fixed another one that was broken before his patch was submitted. The new broken behavior was more of a problem than the old broken behavior, so Nealie provided a fix which restored the old broken behavior. And then I went ahead and did a bunch of tests. And as always, <a href="http://dygraphs.com/changes.html">patches are welcome!</a><br />
<br />
Long version:<br />
<br />
Nealie submitted a rather comprehensive <a href="https://github.com/danvk/dygraphs/commit/d33de6cabb99ea446961ee20da465e5974ebbdf9">patch</a> to Dygraphs that changed the way zooming events behaved. Here's the use case that he inadvertently broke:<br />
<ul>
<li>Open <a href="http://dygraphs.com/tests/zoom.html">http://dygraphs.com/tests/zoom.html</a></li>
<li>Use the mouse to zoom in on a graph along both the X and Y axes.</li>
<li>Perform an unzoom by clicking the “Unzoom” button. This effectively calls <code>g.updateOptions({dateRange: null, valueWindow: null})</code></li>
<li>Expected behavior: both the original x and y ranges are restored.</li>
<li>Actual behavior: Only the x-axis range is restored.</li>
</ul>
Now, here’s the use case that was broken before his patch, and subsequently fixed:<br />
<ul>
<li>Open <a href="http://dygraphs.com/tests/zoom.html">http://dygraphs.com/tests/zoom.html</a></li>
<li>Use the mouse to zoom in on a graph along both the X and Y axes.</li>
<li>Make the following call from a javascript console: <code>g.updateOptions({})</code></li>
<li>Expected behavior: this should effectively be a no-op.</li>
<li>Actual behavior: The y-axis restores to the original range.</li>
</ul>
In other words, Nealie’s patch made <code>g.updateOptions({})</code> work just fine, while breaking <code>g.updateOptions({dateRange: null, valueWindow: null});</code><br />
<br />
I figured this out by basically looking at a series of commits and checkout then out at different times along the main code path. You can see my work <a href="https://spreadsheets.google.com/ccc?key=0An7t5-rPAK9ZdG81dUR1ejF5U2M2V3ZKTjZZOV9xZ2c&hl=en">here</a>. If you read that document, you’ll notice that when performing the same zoom operations via <code>g.updateOptions({…})</code>, neither bug appears. It’s only when a zoom is performed via mouse operations.<br />
<br />
Much of this could have been avoided if we had automated tests. Fortunately, I’m working on that, and in fact an initial attempt at them is now in a <a href="https://github.com/danvk/dygraphs/pull/60">pull request</a>. It uses the <a href="http://code.google.com/p/js-test-driver/">js-test-driver</a> framework. If we had these automated tests in place beforehand, then it might be safe to say that neither bug would have made it in to the main branch.<br />
<br />
For instance, here’s a simple test that verifies that verifies calls to <code>updateOptions</code> should do what I expect:<br />
<code><br />
RangeTestCase.prototype.testRangeSetOperations = function() {<br />
var graph = document.getElementById("graph");<br />
var g = new Dygraph(graph, ZERO_TO_FIFTY, { });<br />
assertEquals([10, 20], g.xAxisRange());<br />
assertEquals([0, 55], g.yAxisRange(0));<br />
<br />
g.updateOptions({ dateWindow : [ 12, 18 ] });<br />
assertEquals([12, 18], g.xAxisRange());<br />
assertEquals([0, 55], g.yAxisRange(0));<br />
<br />
g.updateOptions({ valueRange : [ 10, 40 ] });<br />
assertEquals([12, 18], g.xAxisRange());<br />
assertEquals([10, 40], g.yAxisRange(0));<br />
<br />
g.updateOptions({ });<br />
assertEquals([12, 18], g.xAxisRange());<br />
assertEquals([10, 40], g.yAxisRange(0));<br />
<br />
g.updateOptions({ dateWindow : null, valueRange : null });<br />
assertEquals([10, 20], g.xAxisRange());<br />
assertEquals([0, 55], g.yAxisRange(0));<br />
};</code><br />
<br />
To be fair, that test doesn’t really test the use cases above, where the mouse operations are the cause of broken behavior. I haven’t written those tests yet, but here’s one I did write, which verifies the behavior of a mouse double-click:<br />
<br />
<code>RangeTestCase.prototype.testDoubleClick = function() {<br />
var graph = document.getElementById("graph");<br />
var g = new Dygraph(graph, ZERO_TO_FIFTY, { });<br />
<br />
assertEquals([10, 20], g.xAxisRange());<br />
assertEquals([0, 55], g.yAxisRange(0));<br />
<br />
g.updateOptions({ dateWindow : [ 12, 18 ] });<br />
g.updateOptions({ valueRange : [ 10, 40 ] });<br />
assertEquals([12, 18], g.xAxisRange());<br />
assertEquals([10, 40], g.yAxisRange(0));<br />
<br />
var evt = document.createEvent('MouseEvents');<br />
evt.initMouseEvent(<br />
'dblclick', true, true, document.defaultView,<br />
2, 0, 0, 0, 0<br />
0, false, false, false, false, 0, null);<br />
g.canvas_.dispatchEvent(evt);<br />
assertEquals([10, 20], g.xAxisRange());<br />
assertEquals([0, 55], g.yAxisRange(0));<br />
}<br />
</code><br />
<br />
You get the idea.<br />
<br />
If you read those tests carefully, you would probably have four criticisms:<br />
<ol>
<li>Accessing to the private attribute <code>canvas_ should be discouraged.</code></li>
<li>All those magic numbers used for initializing the event.</li>
<li>Having to actually dispatch the event via the DOM.</li>
<li>Questioning browser compatibility - what browsers would that work on?</li>
</ol>
Here are my answers:<br />
<ol>
<li>Yes, you’re right. That should be fixed.</li>
<li>Yes, you’re right. That should read like <code>DygraphTestUtils.dispatchDoubleClick(g);</code></li>
<li>You’re right about that one also. Right now the API doesn’t quite make that possible, but it could be worked on if necessary. (Psst … <a href="http://dygraphs.com/changes.html">patches welcome!</a>)</li>
<li>Yeah, I have no idea if that would work on anything other than Chrome. One thing at a time.</li>
</ol>
Finally, just what will we do about this behavior? Here's what I think:<br />
<br />
First, I think we should accept Nealie's bug fix. Second, figure out why and how the both cases can be fixed. Third, get some comprehensive automated tests in place, make the tests easy to write and the framework easy to run. Fourth, and probably most important, change the submission guidelines to require a) automated tests to be run as part of the patch submission process and b) require new tests to be part of patches of a certain level of complexity.<br />
<ol>
</ol>
konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com3tag:blogger.com,1999:blog-8055871.post-90440665659013512512011-04-16T23:52:00.109-04:002011-04-17T23:01:40.337-04:00Thoughts about Proxying the HTML5 canvasThis post is a blackboard for my thoughts around how to write tests that verify what's drawn on an HTML5 canvas. I'm still relatively new to Javascript, DOM and Canvas, so if there's an established solution to mocking canvas operations, let me know. My research hasn't yielded anything.<br /><br />As part of my recent <a href="http://konigsberg.blogspot.com/2011/04/you-should-try-dygraphs.html">involvement</a> with <a href="http://www.dygraphs.com/">Dygraphs</a>, I've become interested in automated Javascript testing. I'm currently writing some very basic automated sanity tests for Dygraphs using <a href="http://code.google.com/p/js-test-driver/">jstestdriver</a>, and those tests should be committed to the <a href="https://github.com/danvk/dygraphs">Dygraphs repository</a> fairly soon.<br /><br />Many of the tests I want to write have to do with the graph's internal state, and not its visual aspects, but at some point I will need to test what's <i>drawn</i>. Because Dygraphs has an HTML5 canvas, I have limited options:<br /><ol><li><b>Visual inspection.</b> To some degree this is what we already do. Dygraphs has over 80 pages that represent visual <a href="http://dygraphs.com/tests/">tests</a>. Most tests require a quick glance, but some require playing with values on the page to see how the rendering changes. (This last problem is something I've contributed to.) And finally we just can't look at every single test with every release. Besides, we're looking to create automated tests, so let's move on to the other choices.</li><li><b>Perform pixel-perfect tests against golden images.</b> This works well when setting up tests, but the lack of a consistent canvas implementation across all browsers means that I should expect to have to create a golden image per test per browser version. This type of test can also be a problem when making small changes (such as minor tweaks in color and so on) require generating new golden images across the board, which comes with its own problems.</li><li><b>Perform fuzzy-match tests against golden images.</b> This addresses many issues, but I still need to generate golden images. Not ideal.</li><li><b>Pixel count.</b> This isn't my idea; someone else suggested it. This could theoretically work, but it seems complicated. "Assert that the canvas has 50 blue pixels" reads less like an effect and more like a side-effect.</li><li><b>Implement a fake canvas of some kind.</b> This is the type of solution I want to implement. Replace the canvas with some object so I can verify that a line is drawn from <i>a</i> to <i>b</i> by writing something like <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">assertSegment(point(0, 0), point(50, 50));</span>.</li></ol>What I really want is a proxy that not only receives calls from a client and logs them for post-operation validation, but also actually draws on a real canvas. That way in the event of a regression I can view the backing canvas, which will serve as a clue to what changed. It also helps me understand just what kind of test is being written in the first place.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-Tn9DtH2c18U/TapdlFWkiLI/AAAAAAAAvRc/404DraOXM8Y/s1600/TheoreticalHTMLCanvasProxy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="http://2.bp.blogspot.com/-Tn9DtH2c18U/TapdlFWkiLI/AAAAAAAAvRc/404DraOXM8Y/s640/TheoreticalHTMLCanvasProxy.png" width="640" /></a></div><br />Today I set about to prove out the idea of a canvas proxy, and the truth is, it's just not going to happen the way I want. Here are the two showstopping problems I see so far:<br /><ol><li>You can't fake properties. (You really can't do that with Java and EasyMock either, but in Java you don't often see statements like <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">canvas.style.top = top;</span>.</li><li>The proxy has to be a DOM element. Otherwise calls like <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">document.appendChild(canvas)</span> will fail. HTML canvas elements, in the end, are just DOM elements. The Proxy is not. I could start by creating a DOM element, and add methods to it (such as <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">getContext()</span>) but while my experience with faking the DOM is limited, my intuition suggests that's a dead end.</li></ol>The DOM element problem can be mitigated by faking not the canvas, but the <a href="http://www.w3.org/TR/2dcontext/">rendering context</a> that comes from calls to <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">getContext('</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">2d')</span>. I always expected to need to fake out the context. This might work, but we will still have trouble with properties.<br /><blockquote><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">context.strokeStyle = "#000000";<br />context.fillStyle = "#FFFF00";<br />context.beginPath();<br />context.arc(100,100,50,0,Math.PI*2,true);</span></blockquote>It may be possible to mitigate the properties problem with a hack that keeps properties in sync between the proxy and proxied object. For example:<br /><blockquote><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ProxyContext = function(proxied) {<br /> this.__proxied = proxied;<br /> this.__copyIn();<br />}<br /><br />ProxyContext.prototype.__copyIn = function() {<br /> this.strokeStyle = proxied.strokeStyle;<br /> this.fillStyle = proxied.fillStyle;<br /> ...<br />}<br /><br />ProxyContext.prototype.__copyOut = function() {<br /> proxied.strokeStyle = this.strokeStyle;<br /> proxied.fillStyle = this.fillStyle;<br /> ...<br />}<br /><br />ProxyContext.prototype.beginPath = function() {<br /> this.__copyOut();<br /> proxied.beginPath();<br /> this.logCall("beginPath");<br /> this.__copyIn();<br />}<br /></span></blockquote>This could work. I'm pretty sure it could work. Hey Internet, why won't this work?<br /><br />I fear that a decent solution requires an abstraction layer that turns all mutations on the context into functions (e.g. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">context.strokeStyle</span> becomes <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">context.setStrokeStyle()</span>.) Is that good Javascript practice, or is that just not the Javascript way?konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-1862196561568038092011-04-16T23:06:00.000-04:002011-04-16T23:06:23.659-04:00Never Underestimate the Value of Good Timing.<div>The weather system causing hurricaines in North Carolina is causing heavy thunderstorms here in New Jersey.</div><div><br />
</div>Because my back yard is poorly graded and doesn't drain water well, I gave it the name "Lake New Jersey." Typically the rain creates several small ponds. For the first time our yard is a real lake.<br />
<div><br />
So I went to our basement to check for any signs of flooding. We have a french drain and two sump pumps. The pumps have been tested but never activated by the rain. While I didn't see any signs of flooding, I did see that one of our two sump pumps was unplugged. I plugged it in and for the first time I heard the whirr of a motor -- it was pumping! Good, and bad. I checked the other pump - it's not pumping -- yet.<br />
<br />
Never underestimate the importance of good timing.</div>konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-85507449474463095232011-04-06T23:17:00.004-04:002011-04-06T23:22:03.975-04:00You Should Try DygraphsOver the last six months I've been working with an open source Javascript library called <a href="http://dygraphs.com/">Dygraphs</a>. Dygraphs, run by <a href="http://www.danvk.org/wp/">Dan Vanderkam,</a> provides an interactive graphing library using the HTML5 canvas. <br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://www.dygraphs.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-deEtQvz8oak/TZ0skdgvd2I/AAAAAAAAvPk/5GcKrx6yZec/s1600/dygraphs.jpg" /></a></div><br />
Four things have made me appreciate Dygraphs: <br />
<ul><li>It's got an wide array of configurable <a href="http://www.dygraphs.com/options.html">options</a>.</li>
<li>It has an active community on the <a href="http://groups.google.com/group/dygraphs-users?pli=1">mailing list</a>.</li>
<li>The documentation continues to improve. The options listing above was a recent improvement that is now my regular go-to reference.</li>
<li>The tests. Really I should call them examples. Because I can just often analyze them and get 80% of the way to the solution of the problem I'm working on.</li>
</ul>I've recently made a few contributions to the project, including <a href="http://www.dygraphs.com/tests/zoom.html">2-dimensional pan and zoom</a> and displaying graphs in a <a href="http://www.dygraphs.com/tests/logscale.html">log scale</a>.<br />
<br />
If you need a graphing library I recommend you give Dygraphs a try.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com6tag:blogger.com,1999:blog-8055871.post-45025806863754124352011-03-28T20:56:00.000-04:002011-03-28T20:56:40.459-04:00Unlimited NYT access as a subscriberThe upcoming New York Times paywall is causing a bit of an uproar all over the place. But I haven't much paid attention to it because of an email which said that<i> "as a valued home delivery subscriber, you’ll get our All Digital Access package — which includes unlimited access* to NYTimes.com and the NYTimes app for your smartphone and tablet — free." </i>As for the asterisk, it says <i>"*Mobile apps are not supported on all devices. Does not include e-reader editions, Premium Crosswords or The New York Times Crosswords apps. Other restrictions apply."</i><br />
<br />
But that's enough for me.<br />
<br />
We get the weekend papers only, and it looks as if in my area it's available for $3.15 per week.<br />
<br />
If we didn't want the paper weekend edition it would be a different matter, but now you know why, while it's academically important to most of the world, I don't much care about this big todo at the moment.<br />
<br />
Copy of the email follows.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-257YkQ3-esI/TZEttRiKv1I/AAAAAAAAvL0/KA04Eof5gSU/s1600/nyt.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="http://3.bp.blogspot.com/-257YkQ3-esI/TZEttRiKv1I/AAAAAAAAvL0/KA04Eof5gSU/s640/nyt.jpg" width="444" /></a></div>konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-48481713160278635702011-03-21T23:08:00.000-04:002011-03-21T23:08:51.197-04:00Living with a smart, sweet dogTonight I came home late, at around 9:30PM and the first thing I always see when coming in the front door is her smiling face. Now, dogs don’t smile, but there’s definitely a very happy and excited look that Maggie has, and if you didn’t know her, or if you were afraid of dogs, I don’t think you would like that face very much because her front teeth and fangs are all out, and her eyes are narrow as if she were ready to attack.<br />
<br />
I must pause writing at this moment because hers truly came up to get my attention by sitting patently at my desk and giving the quiet “growl” sound she learned gets our attention. I typically respond to these rumbly “mmmmmm”s by standing and saying “show me” which she uses as a sign to lead me through the house to the thing she wants: either the front door for a walk, or back door for yard play, or a toy for tug, or the “food station” for the things that come from there. But tonight to my surprise when I said “show me” she didn’t respond, so when I kneeled she showed me what she really wanted by rolling on her back and exposing her belly for some affection. While I pet her stomach she shows her standard reciprocal affection by putting her paw somewhere on my person (in this case my stomach) and pressing to make some firm contact. <br />
<br />
Speaking of which, I’ll share a slightly personal story. Saturday morning I slept late, and Maggie didn’t bother me. So when she heard me trundle around upstairs, she came up to meet me. She was certainly in need of a walk, and she was coming up to ask me so. Now like I said, she will lead me in the direction where she wants to go. Typically she’ll stand facing the direction and then look back to see if I’m following. Now I should point out that since I just got out of bed, I wasn’t what you might call “dressed.” We met on the second floor landing, and rather than lead me downstairs, she led me back to my bedroom. In other words, she took me to get dressed before taking me downstairs for a walk. Neither of us taught her that. She figured that out on her own.<br />
<br />
Getting back to tonight - so, after the greeting and the pets and the licks, I told her we were going to do some “training”. Sometimes “training” means working on commands and responses, but in this case we were just going to play with some of her brainier toys. These are a set of three toys made by <a href="http://www.nina-ottosson.com/Dog-2.php">Nina Ottonson</a>.<br />
<br />
<table border="1"><tbody>
<tr><td><a href="http://www.nina-ottosson.com/Dogsmart-Wood-2.php">Dogsmart</a></td><td><div style="text-align: center;"><img src="https://lh6.googleusercontent.com/GAQcYGS5Iq8L8dfxIYvei105b8PgSplLkz9LdTOJtsbUomMN5CVt0pM_1VJg9yz7DIYt8EnvAJO5I3SA6CIAp3QGrSmWPVgZFEnb-7gmDVtgRNVlqnw" /></div></td></tr>
<tr><td><a href="http://www.nina-ottosson.com/Dogfighter-Wood-2.php">Dogfighter</a></td><td><div style="text-align: center;"><img src="https://lh5.googleusercontent.com/Kh-W-WV_hRtMZYLmsC4DisKHqqRxdLzVCD5xsjfsXWvTNY7IQbaRwpZrYt3BuhVOWavtgOWU6JUPdnpgtLmAEwGHUDiApWEthlFR5Nqh5h-yGph-og0" /></div></td></tr>
<tr><td><a href="http://www.nina-ottosson.com/Dogbrickwood.php">Dogbrick</a></td><td><div style="text-align: center;"><img height="132px;" src="https://lh6.googleusercontent.com/NpPHb8RxJrRnyskcqpgfvg9XmoTZX-f6IP9i6wrfr2hPuA5owfrIWGfFJg3lAeM6kWR9H5sZrcWuW0RgOS2EYKoHssNmfoKti1kuTgINexvH65cVtcM" width="275px;" /></div></td></tr>
</tbody></table><br />
<br />
When I bought the first of these toys I thought to myself “How the hell is Maggie going to learn how to use these?” But with some patient training from Beth, she was really able to master these. Here’s an old video of her doing the “Dog Smart” puzzle. I assure you she can do these puzzles much faster than she could back then in September.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="390" src="http://www.youtube.com/embed/pHv9hD7piEQ?rel=0" title="Maggie the Smart Dog" width="480"></iframe><br />
<br />
We did all three of these puzzles tonight, and her performance was typical -- she solves Dogsmart with surprising speed, Dogfighter with reasonable confidence, and with Dogbrick needs a little work and a little help. Watching her struggle with Dogbrick I thought about learning in general, and how she struggled with moving the bricks: with her nose, her teeth and her paws. I have no doubt that with more regular practice she will master it. And this made me think about my own career, and how practice is what makes you good at a task. It doesn’t make you smarter; it just makes you more competent. It helps that Maggie is a smart dog to begin with, but the practice made her an expert at Dogsmart.<br />
<br />
We finished the puzzles and with the few remaining pieces of <a href="http://www.zukes.com/woof/mini-naturals.html">her training treats</a> we went over some basic commands: “on your side”, “stand”, and “stay”. I tried testing her on the very last treat by putting her “on her side” and making her “stay” while a training treat was about two inches from her face. She patiently waited for 30 seconds, her eyes moving between me and the treat. I asked her to “stand” and instead she moved for the treat. I gave her a surprised “Hey!” because .. you know .. hey! Yelling would be wrong, but admonishing her with a loud noise is fine.<br />
<br />
Training must always end on a positive note, even if it’s for the stupidest thing, so I resolved to give her one more thing to do. I stood up and so did she so I told her to lie back “down”. After that I had her go “on your side”, and made her “stay”. But then I walked from the living room to the kitchen <i>out of her sight</i> and continued to repeat “stay”. I expected to hear the jingle of her collar tags when I opened the bag of treats, but nothing happened. I was stunned to return with her still “on your side” even though I had been out of sight for so long, and so she was rewarded with a large amount of treats.<br />
<br />
Now as I conclude this post she’s lying on the carpet in my office. She fell asleep and is making the distinct barking noise she makes when she’s dreaming. Is she dreaming about a “squirrel”? That’s another word she knows.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-45098402010696987032011-03-17T10:07:00.000-04:002011-03-17T10:07:42.712-04:00Rock, Paper, Scissors in today's New York Times.The NY Times published an <a href="http://www.nytimes.com/interactive/science/rock-paper-scissors.html?ref=science">online article</a> that lets you play Rock, Paper, Scissors against an AI opponent.<br />
<br />
Fantastic. Thinking that the best way to play this thing was by being completely random about it, I immediately went to the nearest shelf of board games to find a six-sided die. Out of eleven board games, only one of them had a six sided die, and that box was still shrink-wrapped.<br />
<br />
The computer can perform in a novice mode, where it learns as it plays, or in advanced mode, where it uses 200,000 previous games to inform its predictions on what I would do. Would the die result in a tie game?<br />
<br />
Not really. In advanced mode it was looking to predict my moves based on prior moves, and my random element confounded it:<br />
<br />
After 20 rounds: 9 W 6 T 5 L<br />
After 30 rounds: 12 W 11 T 7 L<br />
After 40 rounds: 16 W 14 T 10 L<br />
<br />
Then I went ahead and tried this against the novice, but limited it to 20 rounds (I have to work at some point.) I was surprised to see that I had a very early lead, though with a majority of ties. By round 16 I was winning 6W 9T 1L, but then it won the last four rounds ending with 6W 9T 5L.<br />
<br />
Not statistically significant, but I want to push more data through. That each round takes a couple of uninterruptable seconds didn't help much. The application also only shows the last few rounds, whereas I had hoped it would allow me to export game's full history.<br />
<br />
But it certainly has me thinking about how I can inject some form of randomness into my gameplay.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com4tag:blogger.com,1999:blog-8055871.post-59885651970509185412011-03-11T16:09:00.001-05:002011-03-11T23:07:48.177-05:00Workspace Mechanic 0.0.6 released to the testing update site<div class="separator" style="clear: both; text-align: center;"><a href="https://lh6.googleusercontent.com/-MOcUzpET8iw/TXqOxa0-sjI/AAAAAAAAvGI/hBZnixU3Gmw/s1600/popup.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://lh6.googleusercontent.com/-MOcUzpET8iw/TXqOxa0-sjI/AAAAAAAAvGI/hBZnixU3Gmw/s1600/popup.jpg" /></a></div>I think enough progress has been made on the <a href="http://code.google.com/a/eclipselabs.org/p/workspacemechanic">Workspace Mechanic for Eclipse</a> to give an update. Most of this you already know if you subscribe to the <a href="http://groups.google.com/group/workspacemechanic">mailing list</a>.<br />
<ol><li>There is now an update site for <a href="http://workspacemechanic.eclipselabs.org.codespot.com/hg.update/mechanic/">stable releases</a> and one for <a href="http://workspacemechanic.eclipselabs.org.codespot.com/hg.update/mechanic-testing/">testing releases</a>.</li>
<li>The Workspace Mechanic for Eclipse no longer depends on org.mortbay for JSON parsing. It also no longer depends on Mylyn because it has its own popup.</li>
<li>Tasks may be specified by URL. Actually, it's slightly more complicated than that, but you can read all about it in the <a href="http://code.google.com/a/eclipselabs.org/p/workspacemechanic/wiki/UrlScannerDesignDoc">design document</a>, which will eventually be turned in to proper documentation.</li>
<li> Prettier icons.</li>
<li>I've published tests for the project, and added many more in the last month.</li>
<li> Lots of issues closed - too many to mention.</li>
<li>I saved the most exciting thing for last. Michael Pellaton is giving a <a href="https://www.eclipsecon.org/submissions/2011/view_talk.php?id=2057"> talk at EclipseCon</a> which mentions the Workspace Mechanic for Eclipse, as well as his project: Eclipse Team Etceteras. If you're at EclipseCon, check it out.</li>
</ol>Please try it out by installing it from the <a href="http://workspacemechanic.eclipselabs.org.codespot.com/hg.update/mechanic-testing/">testing repository</a>, and be sure to <a href="http://groups.google.com/group/workspacemechanic">provide feedback</a>!<br />
<br />
<b>Update: 11:07PM</b> I've updated it to 0.0.7, adding relative URI task paths and supporting LASTMOD preference tasks supplied by URI.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com2tag:blogger.com,1999:blog-8055871.post-70535099651938030172011-02-07T16:39:00.002-05:002011-02-07T20:33:56.892-05:00The Reddit EffectEarly this morning an <a href="http://konigsberg.blogspot.com/2008/04/integergetinteger-are-you-kidding-me.html">old post of mine</a> got voted up to the main page on <a href="http://www.reddit.com/">Reddit</a>. I may sift through the <a href="http://www.reddit.com/r/programming/comments/fgdw3/">comments</a> when my day settles down but I just want to show you the impact of the exposure, thanks to my Analytics page:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TVBlmirzjvI/AAAAAAAAuj4/4W_LhMQKJ5U/s1600/analytics2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="38" src="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TVBlmirzjvI/AAAAAAAAuj4/4W_LhMQKJ5U/s640/analytics2.jpg" width="640" /></a></div><br />
a 4147% growth. That's not 40x, that's 4000x, as evidenced by this next graph:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_UQ8SBm-mQxI/TVBldmTV_yI/AAAAAAAAujw/ZLQ1iN3eqyQ/s1600/analytics.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="77" src="http://2.bp.blogspot.com/_UQ8SBm-mQxI/TVBldmTV_yI/AAAAAAAAujw/ZLQ1iN3eqyQ/s400/analytics.jpg" width="400" /></a></div><br />
Looks like the Reddit comments wound up on my post's page. Do I really have to delete all the chaff?konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-79957196772766166282011-02-06T14:51:00.000-05:002011-02-06T14:51:22.137-05:00@RunAsJUnitTest, @RunAsPluginTestAfter many idle months I'm starting to pick up work on the <a href="http://code.google.com/a/eclipselabs.org/p/workspacemechanic">Workspace Mechanic for Eclipse</a>, and that included finally publishing some of the tests that accompanied it.<br />
<br />
And then, after starting to work on the tests some more, I realized I was missing something I used while writing plug-ins internally at Google, and so I copied them into the internal tests package. They're mostly documentation, but useful documentation. Here's the slightly reduced code for both of them. Their purpose should be clear:<br />
<blockquote><span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse;"><pre style="white-space: pre-wrap; word-wrap: break-word;">/**
* Declares that this test can be run as a JUnit test, and does not require
* the trappings of running as an Eclipse plug-in test.
*/
@Retention(RetentionPolicy.<wbr></wbr>RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RunAsJUnitTest {
}</pre><pre style="white-space: pre-wrap; word-wrap: break-word;"> </pre></span></span></blockquote><blockquote><span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;">/**</span></span><br />
<span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;"> * Declares that this test must be run as a plug-in test.</span></span><br />
<span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;"> */</span></span><br />
<span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;">@Retention(RetentionPolicy.<wbr></wbr>RUNTIME)</span></span><br />
<span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;">@Target({ElementType.TYPE, ElementType.METHOD})</span></span><br />
<span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;">public @interface RunAsPluginTest {</span></span><br />
<span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="border-collapse: collapse; font-family: monospace; white-space: pre-wrap;">}</span></span></blockquote>Nobody likes running tests in the plug-in container if he or she doesn't have to. Surely, I'd much rather run nice fast JUnit tests, particularly when I've separated out code specifically for fast unit tests.<br />
<br />
Of course, without infrastructure to support these, they're little more than documentation, but documentation that has type completion in the IDE.<br />
<br />
So, what do you think? Would it be reasonable to have Eclipse support annotations like these? Do you think these have merit, or are utterly worthless?konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com5tag:blogger.com,1999:blog-8055871.post-45021842532315461592011-01-27T10:37:00.000-05:002011-01-27T10:37:49.740-05:00guava-osgi project gets a fresh update!Thanks to <a href="http://mdenutshell.blogspot.com/">Mikaël Barbero</a>, the <a href="http://code.google.com/p/guava-osgi/">guava-osgi</a> project has been revived!<br />
<br />
At the new <a href="http://guava-osgi.googlecode.com/svn/trunk/repository/">update site</a> you can find bundles for <a href="http://code.google.com/p/guava-libraries/">Guava</a> versions 3, 4, 5, 6 and 7. This is just in time for the imminent release of r08, and we will publish a plug-in shortly after its release.<br />
<br />
Between me and Mikaël, guava-osgi will henceforth be updated just days after Guava releases. This is because Mikaël wrote a script that automates most of the nasty work.<br />
<br />
Please give your thanks to Mikaël!konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com1tag:blogger.com,1999:blog-8055871.post-88953261302062415932011-01-13T22:18:00.002-05:002011-01-13T23:25:19.269-05:00fluid security<div class="separator" style="clear: both; text-align: center;"><a href="http://www.tsa.gov/graphics/tsa_logo.gif" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://www.tsa.gov/graphics/tsa_logo.gif" /></a></div>I flew out of SJC today. As mentioned on my <a href="http://www.google.com/buzz/konigsberg/BCQD9h6NNBT/Is-it-possible-to-take-prescription-cough-syrup">Buzz post</a>, I was worried about trying to bring a bottle of prescription cough syrup with <a href="http://en.wikipedia.org/wiki/Codeine">codeine</a> through an airport TSA checkpoint.<br />
<br />
According to the <a href="http://www.tsa.gov/311/index.shtm">TSA's website</a>, each container must be 3.4 oz (100ml) or less. Medications are allowed in reasonable quantities exceeding 3 oz, and must be declared for inspection. I was also told to be prepared to verify that the name and address on the bottle correspond with my identification.<br />
<br />
It's hard to read unless you view the full image but the bottle has tick marks for measuring contents. The bottle holds 120ml of liquid and at that time had only 100ml of liquid (which makes sense, I took two 10ml doses.) Of course, they wouldn't know that unless they examined the bottle.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/_UQ8SBm-mQxI/TS-r2Wwbt6I/AAAAAAAAueU/N8wHw3yZ3yQ/s1600/codeine-measurement.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="150" src="http://3.bp.blogspot.com/_UQ8SBm-mQxI/TS-r2Wwbt6I/AAAAAAAAueU/N8wHw3yZ3yQ/s200/codeine-measurement.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Tick marks are visible in this photo</td></tr>
</tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TS-r0kGDqMI/AAAAAAAAueQ/2bVFXB_vJpE/s1600/codeine-natural.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="150" src="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TS-r0kGDqMI/AAAAAAAAueQ/2bVFXB_vJpE/s200/codeine-natural.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Gratuitous second photo</td></tr>
</tbody></table><br />
So when I passed through TSA I dutifully presented the bottle to an agent on the outside of the security checkpoint, who told me to pass it through with the rest of my luggage. He did not ask for ID to verify that I owned the contents. I walked through the metal detector and waited for my belongings.<br />
<br />
However, there conveyer belt stopped moving followed by a long delay. The agent responsible for scanning the X-ray view for dangerous substances was definitely looking at <i>something</i>. I didn't know what he was looking at, nor did I care, I assumed I would be quizzed about the bottle after which I would present identification. Instead he called over another agent, and asked for her advice.<br />
<br />
Eventually I found out what they were making a fuss about: the Nalgene bottle attached to the exterior of my backpack.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TS-r4EwJP2I/AAAAAAAAueY/4hl3xslUgME/s1600/nalgene-full.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="200" src="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TS-r4EwJP2I/AAAAAAAAueY/4hl3xslUgME/s200/nalgene-full.jpg" width="150" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Don't they realize that only peace loving docile sheep travel with their own water bottles?</td></tr>
</tbody></table>There was a small quantity of water left in the bottle and they could not figure our what to do. I offered to drink the water. They ignored my suggestion and continued conferring. After yet a little more time, they passed the bottle through, allowing me to board my flight home. <b>They never inspected the bottle of cough syrup. </b>So, a 120ml bottle with 100ml of liquid was unexamined, but a Nalgene bottle with a little water in it underwent great scrutiny.<br />
<br />
Just how much water was in the Nalgene bottle? Let's look.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TS-r5BXMqjI/AAAAAAAAuec/nyhzo0S3VuQ/s1600/nalgene-measurement.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TS-r5BXMqjI/AAAAAAAAuec/nyhzo0S3VuQ/s200/nalgene-measurement.jpg" width="200" /></a></div><br />
You should be able to make out the liquid at the bottom of the bottle. Below is another view of the scale on the bottle's side for reference.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TS-r6A0pR6I/AAAAAAAAueg/OWPU27AM1tI/s1600/nalgene-scale.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TS-r6A0pR6I/AAAAAAAAueg/OWPU27AM1tI/s320/nalgene-scale.jpg" width="240" /></a></div><br />
Eyeball it .. two ounces? One ounce?<br />
<br />
I dumped the contents into a shotglass:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/_UQ8SBm-mQxI/TS-zuDRdvtI/AAAAAAAAuew/Cncdi8-LybE/s1600/IMG_20110113_212058.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="http://2.bp.blogspot.com/_UQ8SBm-mQxI/TS-zuDRdvtI/AAAAAAAAuew/Cncdi8-LybE/s320/IMG_20110113_212058.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Even criminals need water.</td></tr>
</tbody></table><br />
0.8 fluid ounces, which is 23.6 milliliters.<br />
<br />
So they ignored a 120ml bottle (which ostensibly could have had 120ml of fluid) but stared at 23.6ml of water.<br />
<br />
To be fair, it's possible they were discussing a finer point of procedure rather than the immediate potential risk of my water bottle, but it's not as if there wasn't a line of travelers behind waiting on this issue's resolution.<br />
<b><br />
</b><br />
<b>Chart Time!</b><br />
<br />
Here are the quantities listed in this discussion. Remember, it's that small value they made they made the big fuss about.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://chart.apis.google.com/chart?chxr=0,0,1000&chxt=y&chbh=a&chs=360x600&cht=bvg&chco=FF2222,FF7777,55dd66,7777FF,2222FF&chds=0,1000,0,1000,0,1000,0,1000,0,1000&chd=t:120|100|100|23.6|1000&chdl=Medicine+Bottle+Capacity|Medicine+Bottle+Contents|TSA+Per-container+fluid+limit|Nalgene+Bottle+Contents|Nalgene+Bottle+Capacity&chg=-1,0,1,4&chtt=Relative+Quantities+(ml)" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://chart.apis.google.com/chart?chxr=0,0,1000&chxt=y&chbh=a&chs=360x600&cht=bvg&chco=FF2222,FF7777,55dd66,7777FF,2222FF&chds=0,1000,0,1000,0,1000,0,1000,0,1000&chd=t:120|100|100|23.6|1000&chdl=Medicine+Bottle+Capacity|Medicine+Bottle+Contents|TSA+Per-container+fluid+limit|Nalgene+Bottle+Contents|Nalgene+Bottle+Capacity&chg=-1,0,1,4&chtt=Relative+Quantities+(ml)" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Apologies to the color-blind</td></tr>
</tbody></table><b>Fin</b><br />
<b><br />
</b><br />
Thanks for reading. You may now proceed with the eye-rolling and advising better ways to spend time.<br />
<br />
In the meanwhile, it's time to dose up on codeine and get some rest. Good night!<br />
<br />
(ps no nudie scanners in use at SJC.)konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com2tag:blogger.com,1999:blog-8055871.post-3531599768360346892010-11-24T20:47:00.003-05:002010-11-24T20:48:29.426-05:00Tenneseans, I hope there's no disaster, but if there is, you're welcome. In 2005, there was a hurricane which forever changed New Orleans. But not only that, it woke up many people who were in the business of ensuring that poor people affected by those disasters had access to funds and services to which they were due by their government, their employers, et cetera. My wife was one of those people. As far as I can cobble together, disaster legal services was a much more organized and effective years before that, and it went in to a bit of a decline. Until we met Katrina.<br />
<br />
So my wife, a former disaster legal aid attorney, asked if there was a way I could help coordinate communication between different groups. "Something like a <a href="http://en.wikipedia.org/wiki/LISTSERV">listserv</a>." I still find it adorable that she calls it a listserv. So I set up a mailing list so people around the country could share information. Nothing much, really, but it helped get people together.<br />
<br />
I have come to find out that the Memphis Bar association has a nice condensed <a href="http://www.google.com/url?sa=t&source=web&cd=68&ved=0CEIQFjAHODw&url=http%3A%2F%2Fwww.pblo.org%2Flawyers%2Fsearch%2Fattachment.74991&rct=j&q=beth%20osthimer&ei=urntTJuINcKblgfhitX9AQ&usg=AFQjCNHutOkafOB8wWz9fpk6S_xwr_Wgkg">disaster relief legal assistance reference guide for volunteer attorneys</a>. Stuff like this:<br />
<blockquote><b>Q. 7 I received my check for rental assistance, but there are no places to rent. </b><br />
<br />
If you are eligible for housing assistance from FEMA but are unable to find a rental house or apartment within a reasonable commuting distance of your damaged home, please contact FEMA at 1-800-621-FEMA (3362) or visit a nearby Disasater Recovery Center. FEMA will evaluate your situation, and, if appropriate, may authorize a travel trailer or mobile home.</blockquote>Going back to the title page it says:<br />
<blockquote>With special thanks to the following contributors:<br />
<br />
...<br />
Robert Konigsberg</blockquote>I didn't do much, but it's nice that it helped.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-41983479218040778812010-11-11T01:29:00.002-05:002010-11-11T01:37:29.862-05:00Replacing my Mac Keyboard with a Logitech Internet Navigator(Second in my Luddite Internet Technology Series)<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TNuDECyvnqI/AAAAAAAAuEE/eGJ5riHpt6M/s1600/Logitech+Internet+Navigator+Multimedia+Computer+Keyboard.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TNuDECyvnqI/AAAAAAAAuEE/eGJ5riHpt6M/s320/Logitech+Internet+Navigator+Multimedia+Computer+Keyboard.jpg" width="320" /></a></div>The Caps Lock key on my Mac keyboard was not responding so I pulled out the first keyboard I could find lying around: the <a href="http://www.logitech.com/en-gb/479/3373?WT.z_sp=Image">Logitech Internet Navigator</a> keyboard. You know the type: the one with buttons labeled "Search", "Shopping", "Favorites". It probably came with a machine I bought long back when. But you know what? I love it.<br />
<br />
It has some nice features I came to expect from my Mac keyboard: volume controls, and media play/rewind/pause buttons. All those work as you would expect. There's also a little scroll wheel on the keyboard which I don't really need since I have a scroll wheel on my mouse. Even the "Sleep" button brings up the sleep dialog box. The only thing missing is a replacement for the eject button. (Update: downloading the <a href="http://www.logitech.com/en-sg/479/3373?section=downloads&bit=&osid=9">Logitech Control Center</a> for OSX did the trick. Now the only thing missing is a USB port.)<br />
<br />
The only change I had to make for it to work well with OSX was to swap the behavior of the Command and Option keys (Pressing Command was sending Option and vice versa.) So thankfully the OSX keyboard settings allow me to reprogram the function of those keys, so I implemented a modifier swap. (Update: Once I installed the Logitech Control Center, the keys were swapped once more, Restoring original behavior of keyboard modifiers did the trick, effectively making this paragraph "Remember to install the Logitech Control Center and do nothing else."<br />
<br />
As for general keyboard behavior, the keys have a nice tactile feel, you don't feel like you're constantly hitting the bottom of the keyboard tray as you do when using the Mac keyboard. It comes with a keypad on the right side, which I've come to expect with all my keyboards. The keys are slightly more smooshed together. But hey, nothing is going to do it for me quite like my <a href="http://en.wikipedia.org/wiki/Model_M_keyboard">IBM Model M</a>. I just wish in this case that for "IBM Model M" the M stood for "Macintosh."<br />
<br />
In retrospect, the keyboard and mouse that came with my Mac Pro were probably the first two such peripherals that I have ever thrown out due to mechanical defect. Oh, and their earbuds. And on the other side of that is Logitech: with the exception of one piece of hardware, I've never been disappointed with their products, and as of today I'm no longer disappointed with that one exception.<br />
<br />
It's nice to be able to bring a 2002 peripheral with so many silly buttons to just work naturally with a modern OS. Back in 2002, all those buttons were pie in the sky. Today they're launching my apps.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com1tag:blogger.com,1999:blog-8055871.post-67954450960301577672010-11-07T00:01:00.003-04:002010-11-07T08:00:04.064-05:00Making a Table of Contents for Sites with Google Apps ScriptAt work I was building a document using <a href="http://sites.google.com/">Google Sites</a>. While building them, the section headings were given their own numbers, like so:<br />
<br />
Introduction<br />
Part 1: Apples<br />
Introduction<br />
1.1: Fuji<br />
1.2: Macintosh<br />
1.3: Cameo<br />
Summary<br />
Part 2: Pears<br />
Introduction<br />
2.1: Bosc<br />
2.2:D'Anjou<br />
2.3:Warden<br />
Summary<br />
<br />
Yes, of course there's already a gadget you can use to create a table of contents for your page (Menu > Insert > Table of Contents). It's a rather nice gadget, but because I've intentionally numbered some sections and not others, there's a mental mismatch.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TNYamo3NF8I/AAAAAAAAuD0/PJ0qF6t6-hI/s1600/before.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TNYamo3NF8I/AAAAAAAAuD0/PJ0qF6t6-hI/s1600/before.jpg" /></a></div>As you see, the Table of Contents generator gave section numbers to everything, so depending where you look, 2.2 is either Fuji apples or Bosc pears.<br />
<br />
I didn't want to remove the section headings, and there was no real way to remove them via the Table of Contents gadget.<br />
<br />
If you're not familiar with <a href="http://code.google.com/googleapps/appsscript/">Google Apps Script</a>, it's as you think: a scripting engine for Google properties. It's particularly powerful for scripting spreadsheets, and. as <a href="http://googleenterprise.blogspot.com/2010/10/automating-business-processes-in-google.html">of October 22</a>, you can process Google Sites with Google Apps Script.<br />
<br />
Before going any farther, let me show you what I generated with Google Apps Script:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TNYbo-H-oII/AAAAAAAAuD4/Krzk0Vgv-mI/s1600/after.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/_UQ8SBm-mQxI/TNYbo-H-oII/AAAAAAAAuD4/Krzk0Vgv-mI/s1600/after.jpg" /></a></div><div class="separator" style="clear: both; text-align: left;">I'm no HTML Picasso, but it's not bad. Not bad at all. The links work, thanks to Google Sites automatically injecting <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a name></span> tags with every <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><h[1-6]></span> tag.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><b>The code</b></div><br />
Let me share the code with you. First, here's how it's invoked:<br />
<br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><span class="js-keyword" style="color: #770088;">function </span><span class="js-variable" style="color: black;">run</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-variable" style="color: black;">createToc</span><span class="js-punctuation" style="color: #666666;">(</span><br />
<span class="whitespace"> </span><span class="js-string" style="color: #aa2222;">'https://sites.google.com/site/<i>sitename</i>/<i>source-page</i>'</span><span class="js-punctuation" style="color: #666666;">,</span><br />
<span class="whitespace"> </span><span class="js-string" style="color: #aa2222;">'https://sites.google.com/site/<i>sitename/destination-page'</i></span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="js-punctuation" style="color: #666666;">}</span></span><br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: x-small;"><span class="Apple-style-span" style="white-space: nowrap;"><br />
</span></span><br />
Yes, so you can specify a page from which to read and generate a second page with the table-of-contents included. (Side note: This actually gives me lots of ideas about workflow -- envision if the source URL was a private site where drafts were written, you could use Google Apps Script as a way to push from a staging area to production.)<br />
<br />
OK, the code. It's mostly processing XML and generating HTML.<br />
<br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><span class="js-comment" style="color: #aa7700;">// I do not warrant this code in any way.</span></span><br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><span class="js-comment" style="color: #aa7700;">// If you break something important, don't come crying to me.</span></span><br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><span class="js-comment" style="color: #aa7700;">// Actually, check your Sites revision history first.</span></span><br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><span class="js-comment" style="color: #aa7700;">// Then go cry.</span></span><br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><br />
<span class="js-comment" style="color: #aa7700;">// Load content from fromUrl. Find the table of contents, and stick it in the document</span><br />
<span class="js-comment" style="color: #aa7700;">// Save at toUrl.</span><br />
<span class="js-keyword" style="color: #770088;">function </span><span class="js-variable" style="color: black;">createToc</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-variabledef" style="color: blue;">fromUrl</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-variabledef" style="color: blue;">toUrl</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">page </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">SitesApp</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getPageByUrl</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">fromUrl</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">content </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">page</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getHtmlContent</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">doc </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">Xml</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">parse</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-atom" style="color: #228811;">true</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">root </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">doc</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getElement</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">toc</span><span class="js-operator" style="color: #666666;">=</span><span class="js-punctuation" style="color: #666666;">[</span><span class="js-punctuation" style="color: #666666;">]</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-variable" style="color: black;">parse</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">toc</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-localvariable" style="color: #004499;">root</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">prefixDiv </span><span class="js-operator" style="color: #666666;">= </span><span class="js-string" style="color: #aa2222;">"<div id='kberg-toc'>"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">postfixHtml </span><span class="js-operator" style="color: #666666;">= </span><span class="js-string" style="color: #aa2222;">"</div><div id='kberg-toc-coda'/>"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">codaDiv </span><span class="js-operator" style="color: #666666;">= </span><span class="js-string" style="color: #aa2222;">"<div id='kberg-toc-coda'/>"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">html </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">prefixDiv </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-variable" style="color: black;">tocToHtml</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">toc</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">postfixHtml</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">index </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">indexOf</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">prefixDiv</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">newContent</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">if </span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">index </span><span class="js-operator" style="color: #666666;">== </span><span class="js-operator" style="color: #666666;">-</span><span class="js-atom" style="color: #228811;">1</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">x </span><span class="js-operator" style="color: #666666;">= </span><span class="js-string" style="color: #aa2222;">"<div dir='ltr'>"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">index </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">indexOf</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-string" style="color: #aa2222;">"<div dir='ltr'>"</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">x</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">length</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">Logger</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">log</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-string" style="color: #aa2222;">"3: " </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">index</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">outdex </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">index</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">newContent </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">substr</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-atom" style="color: #228811;">0</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-localvariable" style="color: #004499;">index</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">html </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">substr</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">outdex</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">} </span><span class="js-keyword" style="color: #770088;">else </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">outdex </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">indexOf</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">codaDiv</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">newContent </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">substr</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-atom" style="color: #228811;">0</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-localvariable" style="color: #004499;">index</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">html </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">content</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">substr</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">outdex </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-localvariable" style="color: #004499;">codaDiv</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">length</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">newpage </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">SitesApp</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getPageByUrl</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-variable" style="color: black;">toUrl</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">newpage</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">setHtmlContent</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">newContent</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="js-punctuation" style="color: #666666;">}</span><br />
<br />
<span class="js-comment" style="color: #aa7700;">// Recurse through the XML, finding <h[1-5]> tags.</span><br />
<span class="js-keyword" style="color: #770088;">function </span><span class="js-variable" style="color: black;">parse</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-variabledef" style="color: blue;">toc</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-variabledef" style="color: blue;">element</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">name </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">element</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getName</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getLocalName</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">toUpperCase</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">if </span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">name </span><span class="js-operator" style="color: #666666;">== </span><span class="js-string" style="color: #aa2222;">"H1" </span><span class="js-operator" style="color: #666666;">|| </span><span class="js-localvariable" style="color: #004499;">name </span><span class="js-operator" style="color: #666666;">== </span><span class="js-string" style="color: #aa2222;">"H2" </span><span class="js-operator" style="color: #666666;">|| </span><span class="js-localvariable" style="color: #004499;">name </span><span class="js-operator" style="color: #666666;">== </span><span class="js-string" style="color: #aa2222;">"H3" </span><span class="js-operator" style="color: #666666;">|| </span><span class="js-localvariable" style="color: #004499;">name </span><span class="js-operator" style="color: #666666;">== </span><span class="js-string" style="color: #aa2222;">"H4" </span><span class="js-operator" style="color: #666666;">|| </span><span class="js-localvariable" style="color: #004499;">name </span><span class="js-operator" style="color: #666666;">== </span><span class="js-string" style="color: #aa2222;">"H5"</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">level </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">parseInt</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">name</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">substring</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-atom" style="color: #228811;">1</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">anchorChildren </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">element</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getElements</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-string" style="color: #aa2222;">"a"</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">anchorText </span><span class="js-operator" style="color: #666666;">= </span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">anchorChildren</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">length </span><span class="js-operator" style="color: #666666;">> </span><span class="js-atom" style="color: #228811;">0</span><span class="js-punctuation" style="color: #666666;">)</span><br />
<span class="whitespace"> </span><span class="js-operator" style="color: #666666;">? </span><span class="js-localvariable" style="color: #004499;">anchorChildren</span><span class="js-punctuation" style="color: #666666;">[</span><span class="js-atom" style="color: #228811;">0</span><span class="js-punctuation" style="color: #666666;">]</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getAttribute</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-string" style="color: #aa2222;">"name"</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getValue</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">: </span><span class="js-string" style="color: #aa2222;">""</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">item </span><span class="js-operator" style="color: #666666;">= </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-property" style="color: black;">level </span><span class="js-punctuation" style="color: #666666;">: </span><span class="js-localvariable" style="color: #004499;">level</span><span class="js-punctuation" style="color: #666666;">,</span><br />
<span class="whitespace"> </span><span class="js-property" style="color: black;">anchor </span><span class="js-punctuation" style="color: #666666;">: </span><span class="js-localvariable" style="color: #004499;">anchorText</span><span class="js-punctuation" style="color: #666666;">,</span><br />
<span class="whitespace"> </span><span class="js-property" style="color: black;">description </span><span class="js-punctuation" style="color: #666666;">: </span><span class="js-localvariable" style="color: #004499;">element</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getText</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">}</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">toc</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">push</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">item</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">} </span><span class="js-keyword" style="color: #770088;">else </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">for </span><span class="js-variable" style="color: black;">each </span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">child </span><span class="js-keyword" style="color: #770088;">in </span><span class="js-localvariable" style="color: #004499;">element</span><span class="js-punctuation" style="color: #666666;">.</span><span class="js-property" style="color: black;">getElements</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-variable" style="color: black;">parse</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-localvariable" style="color: #004499;">toc</span><span class="js-punctuation" style="color: #666666;">, </span><span class="js-localvariable" style="color: #004499;">child</span><span class="js-punctuation" style="color: #666666;">)</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="whitespace"> </span><br />
<span class="js-comment" style="color: #aa7700;">// Convert the table of contents entries into HTML.</span><br />
<span class="js-keyword" style="color: #770088;">function </span><span class="js-variable" style="color: black;">tocToHtml</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-variabledef" style="color: blue;">toc</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variabledef" style="color: blue;">html </span><span class="js-operator" style="color: #666666;">=</span><br />
<span class="whitespace"> </span><span class="js-string" style="color: #aa2222;">"<div style='width: 250px; background-color: #e8e8e8;'>\n"</span><br />
<span class="whitespace"> </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-string" style="color: #aa2222;">"<br/><b>Contents</b><br/><br/>\n"</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">for </span><span class="js-variable" style="color: black;">each </span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variable" style="color: black;">entry </span><span class="js-keyword" style="color: #770088;">in </span><span class="js-localvariable" style="color: #004499;">toc</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variable" style="color: black;">html2 </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">indent</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-variable" style="color: black;">entry</span><span class="js-punctuation" style="color: #666666;">[</span><span class="js-string" style="color: #aa2222;">"level"</span><span class="js-punctuation" style="color: #666666;">]</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-string" style="color: #aa2222;">"<a href='#" </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-variable" style="color: black;">entry</span><span class="js-punctuation" style="color: #666666;">[</span><span class="js-string" style="color: #aa2222;">"anchor"</span><span class="js-punctuation" style="color: #666666;">] </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-string" style="color: #aa2222;">"'>" </span><span class="js-operator" style="color: #666666;">+</span><br />
<span class="whitespace"> </span><span class="js-variable" style="color: black;">entry</span><span class="js-punctuation" style="color: #666666;">[</span><span class="js-string" style="color: #aa2222;">"description"</span><span class="js-punctuation" style="color: #666666;">] </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-string" style="color: #aa2222;">"</a><br/>\n"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-localvariable" style="color: #004499;">html </span><span class="js-operator" style="color: #666666;">= </span><span class="js-localvariable" style="color: #004499;">html </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-variable" style="color: black;">html2</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="whitespace"> </span><span class="js-variable" style="color: black;">html </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">html </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-string" style="color: #aa2222;">"<br/></div>"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">return </span><span class="js-variable" style="color: black;">html</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="js-punctuation" style="color: #666666;">}</span><br />
<br />
<span class="js-comment" style="color: #aa7700;">// Given an indentation level, return (2 * (level - 1)) non-breaking spaces.</span><br />
<span class="js-keyword" style="color: #770088;">function </span><span class="js-variable" style="color: black;">indent</span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-variable" style="color: black;">level</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variable" style="color: black;">x </span><span class="js-operator" style="color: #666666;">= </span><span class="js-string" style="color: #aa2222;">""</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">for </span><span class="js-punctuation" style="color: #666666;">(</span><span class="js-keyword" style="color: #770088;">var </span><span class="js-variable" style="color: black;">i </span><span class="js-operator" style="color: #666666;">= </span><span class="js-atom" style="color: #228811;">0</span><span class="js-punctuation" style="color: #666666;">; </span><span class="js-variable" style="color: black;">i </span><span class="js-operator" style="color: #666666;">< </span><span class="js-variable" style="color: black;">level </span><span class="js-operator" style="color: #666666;">- </span><span class="js-atom" style="color: #228811;">1</span><span class="js-punctuation" style="color: #666666;">; </span><span class="js-variable" style="color: black;">i</span><span class="js-operator" style="color: #666666;">++</span><span class="js-punctuation" style="color: #666666;">) </span><span class="js-punctuation" style="color: #666666;">{</span><br />
<span class="whitespace"> </span><span class="js-variable" style="color: black;">x </span><span class="js-operator" style="color: #666666;">= </span><span class="js-variable" style="color: black;">x </span><span class="js-operator" style="color: #666666;">+ </span><span class="js-string" style="color: #aa2222;">"&nbsp;&nbsp;"</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="whitespace"> </span><span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="whitespace"> </span><span class="js-keyword" style="color: #770088;">return </span><span class="js-variable" style="color: black;">x</span><span class="js-punctuation" style="color: #666666;">;</span><br />
<span class="js-punctuation" style="color: #666666;">}</span><br />
<span class="Apple-style-span" style="color: black;"></span></span><br />
<span class="Apple-style-span" style="color: #666666; font-family: monospace; font-size: 13px; white-space: nowrap;"><span class="js-variable" style="color: black;"></span></span><br />
<div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;"><b>Final Thoughts</b></div><div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;"><b><br />
</b></div><div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;">The remaining issue is triggering this script. Google Apps Script does have an event notification mechanism (e.g. <i>run this script when the spreadsheet opens</i>) but right now, Google Sites only has a time-based trigger (e.g. <i>run this script every n minutes/hours</i>.) To be honest, if an <i>onSave</i> trigger was written, would <i>createToc(url, url)</i>, which would save the TOC to itself, trigger another <i>onSave</i> event? No big deal, if it results in a no-change, I could tweak the code accordingly.</div><div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;"><br />
</div><div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;">But the lack of an <i>onSave</i> trigger makes this just slightly usable for me. Having this run every two minutes is too frequent for when the site isn't updated. I don't know the plans for the Google Apps Script team and/or the Google Sites team, but this is a good use case for <i>onSave</i>.</div><div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;"><br />
</div><div style="font-family: Times; font-size: medium; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: normal;">I wonder how hard it would be to hide the default TOC renderers' section numbers with clever CSS, or just write my own <a href="http://code.google.com/apis/gadgets/">Google Gadget</a>...</div>konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com9tag:blogger.com,1999:blog-8055871.post-75176763630372415082010-11-02T23:59:00.001-04:002010-11-03T04:09:54.752-04:00My new iPod Nano 6g<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TNDd-HeTUNI/AAAAAAAAuDg/cIHDB2EypKc/s1600/nano6g.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TNDd-HeTUNI/AAAAAAAAuDg/cIHDB2EypKc/s1600/nano6g.jpg" /></a></div>I wear out equipment pretty quickly. In fact I'm surprised my Nexus One has lasted for almost a full year without a major scratch. But iPods, well, those I wear out pretty quickly. And in the last two years I've really started to listen to podcasts and audio books, and so my iPods have been well loved.<br />
<div><br />
</div><div>My last one, the iPod Nano 4g had one newer feature that I really relied upon: sleep mode. I'd put something in the dock, and set it to sleep in 30 minutes. By then I'd be fast asleep. It was a good, reliable pattern.<br />
<br />
Then my Nano broke. I pulled out a 2G Nano which worked, well, it worked OK but didn't have the sleep function. I'm still not ready to go all-out with my Nexus One. The battery life isn't quite ready for it, and, let's face it, I break stuff like that, and I'd rather keep it around for phone calls.<br />
<br />
So I ordered the iPod Nano 6G.<br />
<br />
In short, I just love it. <a href="http://www.engadget.com/">Engadget</a> has a <a href="http://www.engadget.com/2010/09/07/ipod-nano-review-2010/">review</a> that covers the iPod's features; feel free to read that for something thorough.<br />
<br />
<b>The good:</b><br />
<ul><li><b>The new UI is snappy and easy to use.</b> The replacement of a the scroll wheel with a touch screen makes for easy interaction. I didn't have to learn how things worked, I figured it out naturally. It's meant to look like iOS, but isn't iOS. For instance, if my research is right, you can't write apps for it. Given that there are five, pages for apps in the home screen and only fifteen apps, there seems to be natural room for expansion in future firmware updates.</li>
<li><b>Radio!</b> When I showed this to my wife, she brought out her iPod envy. It's got a sufficiently workable radio receiver.</li>
<li><b>Pedometer.</b> Nice feature, it's even gotten me to walk more. Allegedly you can publish your pedometer data to some Nike service, but I'm not particularly interested.</li>
<li><b>Sleep Mode</b> is still there. Given that they removed so many other features (see below) I was glad to see the sleep mode was still there. It took a while to find it (it's in the Clock application.)</li>
<li>Also, When it's showing a clock face, it feels a bit like a pocket-watch. A very lightweight back-lit faux-analog pocket watch, but a pocket-watch nonetheless.</li>
</ul><div>The bad:</div><div><ul><li><b>Clumsy buttons.</b> The use of more hard buttons on the iPod Nano feels like a bit of a cop-out, and even on my smaller hands, they're a bit hard to fumble with.</li>
<li><b>No video playback.</b> Not even for video podcasts. I don't mind much, they're clearly drawing a line between iPod and iPhone (or iPod touch.)</li>
<li><b>No games, no video camera, no contacts, no notes.</b> I don't care about most of those, well, except for games, but I can always fall back on my phone if needs must.</li>
<li><b>Clumsy access to the dock connector.</b> The headphone jack is so close to the dock connector that you have to remove the headphones before disconnecting the iPod. It's an irritating usability fumble, but understanding given the tiny form factor.</li>
<li><b>Tiny Engraving.</b> I opted for engraving the device with my email address in case it gets lost. It's difficult to read the engraving without a magnifying glass. I'm guessing it's a 6-point font for a 20-character message.</li>
</ul><div>The fact that they've removed features that might make someone more likely to purchase an iPod touch makes this feel much like the IBM PC Jr (a little nod to my fellow dinosaurs.) In some ways, it's like the PC Jr (replace chicklet keyboard with clumsy buttons.) Sure, they want you to buy an iPod Touch, but I'd rather have the lightweight device, and I already have a large bulky video-playing computer.</div></div><div><br />
</div><div>In short, this is great as a companion lower-power-consuming audio device.</div></div>konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-79015648228697816162010-10-28T00:25:00.002-04:002010-10-28T00:38:51.563-04:00Plot Predictions for Avatar 2 and Avatar 3<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TMj7IbZuIXI/AAAAAAAAuC4/oko6z1UvO1A/s1600/images1876350_avatar-JakeSully.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="http://1.bp.blogspot.com/_UQ8SBm-mQxI/TMj7IbZuIXI/AAAAAAAAuC4/oko6z1UvO1A/s320/images1876350_avatar-JakeSully.jpg" width="213" /></a></div><a href="http://blogs.barrons.com/techtraderdaily/2010/10/27/news-corp-cameron-agrees-to-make-two-sequels-to-avatar/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+barrons/techtraderdaily/feed+(BARRONS.com+Blog:+Tech+Trader+Daily)&utm_content=Google+Feedfetcher">Barron's</a> reports that James Cameron has been given the go-ahead to produce two sequels to Avatar, to be released in 2014 and 2015. Here's my prediction of the critical plot points for next two films. These are not jokey predictions, I'm going for gold.<br />
<br />
<b>Avatar 2:</b> The Na'vi are slowly rebuilding their home world. Sully is struggling to find his place on Na'vi, not only on this new world, but in his new body. The RDA returns, with a larger army, but also mega-avatars that are bigger and stronger. Also the technology is much better; the projection devices are portable. They're prepared, and ready to do business. In order to provide key defensive support to the Na'vi, which is shown to us as a threat to the now pregnant Neytiri, Sully is led by a yet-unknown Na'vi spiritual leader who restores Sully's original human (and paraplegic) form. Sully realizes this was a mistake, and manages to become a mega-Navi himself to, along with several new friends, destroy the mega-avatars. Sully fully accepts his Na'vi identity.<br />
<br />
<b>Avatar 3</b>: Neytiri and Sully have their baby. Sully is also now an established leader among the Na'vi, but now things are turning political, and frankly, irritating. This was not what he expected in this new world! Sully's struggle is now with Neytiri. A couple of domestic fights and Sully turns all these negative feelings inward. There are some Na'vi who dislike the way Sully wants to lead the Na'vi, some of whom have strong spiritual powers. One of them feigns friendship and dupes him into a mystical event that creates a spiritual clone of the new mega-Sully, made only of his anger and fear. It also saps him of the power that makes him strong and confident, leaving him being less than half of himself. Sully needs to learn to trust in himself before the evil Sully can destroy Pandora ... which of course he does.<br />
<br />
Come back here in 2016, tell me if I'm wrong.<br />
<br />
(Credit: Louis Gray's <a href="http://www.google.com/buzz/louisgray/h2iZ7k4Wxr5/Oh-no-I-guess-we-should-have-anticipated-this-News">share</a> on buzz)konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-24128064403551744662010-10-14T09:11:00.001-04:002010-10-14T20:21:11.754-04:00On Teaching Ultimate Frisbee to Children<a href="http://3.bp.blogspot.com/_UQ8SBm-mQxI/TLaL2SachdI/AAAAAAAAuA8/LmkrYmGG7jI/s1600/play_ultimate.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="238" src="http://3.bp.blogspot.com/_UQ8SBm-mQxI/TLaL2SachdI/AAAAAAAAuA8/LmkrYmGG7jI/s320/play_ultimate.jpg" width="320" /></a>In September I started teaching co-ed Ultimate Frisbee to local middle school children. I spend three hours a week every Sunday assisting the coach, an impressive woman who balances teaching skills against just having fun, and somehow manages to corral the 30 boys and girls into doing drills and wearing pinnies.<br />
<br />
And I don’t know if you’ve seen middle school children recently, but it's <i>scary</i> to be an isolated adult surrounded by 30 kids you've never met. What if they don’t listen to you? What if they don’t <i>like</i> you? So I laid back, helped the coach, and focused on very basic things: throwing, catching, one-on-one attention and congratulating good effort.<br />
<br />
I saw a girl look dejected on her first day. I asked her what was wrong but I already knew: the boys weren't throwing to her, and when the disc was at her feet, another boy on the field would tell her to not pick it up so he could throw it. There wasn’t too much I could do about getting the boys to throw to her; that’s a problem anyone who's played any co-ed sport can understand (though I explained to both sides that you're less likely to score when you only look to throw to your friends). But when the Frisbee is at her feet?<br />
<br />
“Just ignore them and pick it up!” I said.<br />
<br />
Two weeks later during a pick-up game the Frisbee fell at her feet. She turned to get away from it when I yelled “Pick it up!”<br />
<br />
She threw to another girl for a goal.<br />
<br />
Another boy had the opposite problem.<br />
<br />
“Nobody on my team picks up the Frisbee, and I have to pick it up every time!” he complained.<br />
<br />
Apparently he didn’t like being forced to put the disc in play. OK. Simple advice, of course. Don’t pick it up! Someone else will eventually get it. He picked it up twenty minutes later.<br />
<br />
“Hey! I thought you weren’t going to do that anymore.”<br />
<br />
A third child is quite a good player. He likes making farther-distance throws, but long unfocused throws have a low-percentage chance of being caught. So his skill isn't translating into completed passes. I suspected his problem was a lack of focus and a desire to huck the disc (colloquial for winding up and letting go), so after his third low-percentage throw I spoke with him.<br />
<br />
“The next throw you make, I don’t care how far it goes, I want it to connect.”<br />
<br />
He looked shocked. But sure enough, his next throw was a good high percentage throw. But it was blocked! Someone on the other team was on the ball and blocked it. He turned to face me and yelled “It wasn’t my fault!”<br />
<br />
“I know, I know. Good throw!” I yelled back.<br />
<br />
His next two throws were excellent short-distance throws that found their targets.<br />
<br />
The thing I had to remember is that kids that age are more emotionally delicate than they let on, so negativity has no place there. And I have to hand it to them: none of the kids call each other bad names, and nobody gets chastised over making mistakes.<br />
<br />
This past Saturday the kids had their first scrimmage of the season against another team. I helped by motivating the team with non-stop encouragement and high-fives. Near the end of the game, I caught a player from the other team defending by blocking an area with his arm. A potentially illegal move, and an un-spirited play! I got angry! What a little punk! I let the anger dissolve and spoke with the other team’s coach so he would know to help the boy understand the non-contact rule. The coach explained. “Sure thing. But so you know, he’s only been playing for two weeks.”<br />
<br />
I was relieved that I waited before speaking and awful at how poorly I read the situation.<br />
<br />
After the game I approached the boy.<br />
<br />
“Hey, nice game! You played well!” I said.<br />
<br />
“Really?” he asked. He spoke with a soft voice. Such a boy!<br />
<br />
“Sure! Did I hear you have only been playing for two weeks?”<br />
<br />
“Yes.” Again with the soft voice.<br />
<br />
“Well I wouldn’t have known. You play like someone who knows what he’s doing. Keep it up!”<br />
<br />
“Thanks,” he said.<br />
<br />
I am looking forward to next Sunday.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com2tag:blogger.com,1999:blog-8055871.post-91069349736929263932010-09-08T22:50:00.000-04:002010-09-08T22:50:47.566-04:00Desktop specs for the entry level programmerA friend recently asked for advice. His son is looking to become a professional computer programmer and wants specs for a new desktop computer. I'm no longer up to date on the subject, but that didn't stop me from writing a 900-word opinion on the matter, which I present to you know.<br />
<br />
---<br />
<br />
This is a tough question to answer in a specific sort of way, made more difficult since I hardly buy desktops anymore. You can get an idea for basic specs by looking at any development IDE package (e.g. Visual Studio.) Here are some overall suggestions, though:<br />
<br />
<b>Important items</b>: CPU, Memory, 7200RPM disk (as opposed to 5400 RPM), expandability<br />
<b>Less important items</b>: Video card, audio card, USB ports, Firewire, hard disk quantity.<br />
<br />
<b>CPU +</b><br />
<br />
CPU is important, not necessarily because of clock speed, which has recently been topping out, but because of the "number of cores" or "number or processors". Cores and processors aren't the same thing, but for the sake of this conversation let's say they are. More processors means more things the computer do at the same time. This can sometimes be valuable when you're writing software. But don't let yourself get carried away, particularly with a starting computer. If there's only one processor, I'm sure your son will do fine.<br />
<br />
<b>Memory +</b><br />
<br />
This is pretty important, particularly as you deal with larger pieces of software. Developer tools can take up lots of memory, it's certainly possible. That depends on how large the products are that your son plans to write. But don't let 'budding professional programmer' get in the way here. I think getting memory is good. And you don't need to max it out now. Memory always gets cheaper (In the as houses always increase in value, well, not really, the takeaway from that is I really mean "mostly always" instead of "always")<br />
<br />
<b>Disks +</b><br />
<br />
Disk: Disks are cheap. I mean, wow I can't believe how cheap they get. Unless your son is planning on processing lots of video (in which case a better video card might be necessary, but only <i>might</i>.) When it comes to disks, though, performance can matter a great deal. Think about how long it takes to load Microsoft Word, for instance. You could think about it for a looooong time. Disk drives operate like platters, which is why they're measured in RPM, just like LP records, but instead of 33 1/3, 45, 78 RPM, we're looking at 5400 and 7200 RPM. So drives that spin at 7200 RPM spin 33% faster than those that spin at 5400 RPM. That's useful.<br />
<br />
Most desktop PCs will support 7200 RPM disks, shouldn't be a problem.<br />
<br />
<b>Expandability</b><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif;"><b> +</b></span><br />
<br />
<br />
<b></b>I hesitate to put this on the list, because sometimes you find that the idea of expandability is nice, but in reality, by the time you expand anything more than memory, you're out of room, and you need a new motherboard to keep up with new operating systems anyway. But at the same time, I've always appreciated the ability to make room for a second disk somewhere.<br />
<br />
<b>Video card -</b><br />
<br />
<b></b>High quality video cards are good for playing games, or writing games, writing complex graphics software (memory and more CPUs help here as well.) Even then, I'm sure you'll do fine for an entry level computer with almost any video card they give. I'd probably do some research personally on video card technology to supplement it, but honestly, he can do fine with almost any entry level video card.<br />
<br />
<b>Audio -</b><br />
<br />
Take the standard, get something better if he's specializing in audio, which means games or music.<br />
<br />
<b>Ports, Firewire -</b><br />
<br />
<b></b>Most of that is expandable and can be after market purchases.<br />
<br />
<b>Disk Capacity -</b><br />
<br />
<b></b>Not critical for getting started. Nowadays, hard disk quantity is usually taken up my media files such as video and audio. If the upgrade for more hard disk space is small, then, sure, go for it, but don't stress about it.<br />
<br />
---<br />
<br />
And even after all of this, more questions linger: memory is more than just "How Many Gee Bees?" One computer advertises its memory as "8GB Dual Channel DDR3 SDRAM3at 1333MHz - 4 DIMMs." Which of that matters and why? Whooooo, I'm getting woozy just thinking about it. (Hint: a DIMM is what you might call a single memory "chip", the thing you stick into the computer. If the computer supports up to 4 slots, then 8GB taking up 4 DIMMs (at 2GB apiece) means you can't upgrade, well, at least, not without pulling out one of the 2GB DIMMs. But an 8GB DIMM taking up a single slot means you can add much more memory. But watch out: 1 8GB DIMM is more expensive than 4 2GB DIMMs, sometimes much more expensive.)<br />
<br />
Of course, this is vanilla advice for programming. Your son may be interested in hard core video games, in which case you need much better graphics capabilities, and even more memory. Or just playing games, which requires less so, but still. And while I didn't give a concrete number, splurge on 8GB of memory, with room to expand. If you find yourself spending close to $1K you ... well I spend $3K on my last machine, but $1K for a starting development machine is probably more than enough. As a reference point, the Dell Studio XPS 8100 is a decent starting point.<br />
<br />
So, that's my advice. It's all over the place, lacks a reference to current standards, yet somehow has the gall to pass itself off as authoritative. You should do fine.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com0tag:blogger.com,1999:blog-8055871.post-162823804284226322010-08-22T09:00:00.000-04:002010-08-22T09:00:50.518-04:00Presenting the Google/Eclipse/Enterprise talk in Reston VA Oct 13Brief note: I'm excited to be presenting the talk "<a href="http://konigsberg.blogspot.com/2010/03/learn-about-how-google-uses-eclipse.html">Eclipse in the Enterprise: Lessons from Google</a>" at <a href="http://eclipse-ecosystem.blogspot.com/2010/08/eclipse-government-day-and-eclipse.html">Eclipse Enterprise Days</a> on Oct 13 in Reston, VA.<br />
<br />
This looks like a great conference for people who support large Eclipse user bases -- you know who you are -- if you attended the <a href="http://www.eclipsecon.org/2010/sessions/?page=sessions&id=1633">Eclipse in the Enterprise BoF at EclipseCon</a>, this conference is for you. From the <a href="http://wiki.eclipse.org/Eclipse_Enterprise_Days">conference</a> page:<br />
<blockquote>"Attendees must be an employee or contractor of an "Enterprise User" of Eclipse technology. Enterprise users typically are responsible for the use of Eclipse by hundreds and even thousands of developers in their organizations."</blockquote>Are you planning to attend this conference? Let me know.konberghttp://www.blogger.com/profile/04616226121996611123noreply@blogger.com4