<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JG</title>
	<atom:link href="http://jacksongabbard.com/feed" rel="self" type="application/rss+xml" />
	<link>http://jacksongabbard.com</link>
	<description>Web hijinx, software, and other things</description>
	<lastBuildDate>Tue, 03 Jul 2012 05:40:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.2</generator>
		<item>
		<title>Vignette on Connecting</title>
		<link>http://jacksongabbard.com/vignette-on-connecting.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=vignette-on-connecting</link>
		<comments>http://jacksongabbard.com/vignette-on-connecting.html#comments</comments>
		<pubDate>Tue, 03 Jul 2012 05:30:04 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=103</guid>
		<description><![CDATA[A faint flicker glides into dark space. It reveals just the shadow of a thing. In time a flicker returns, a different hue and intensity. Its return exposes the faintest bit of details in the nascent form. Somewhere far off &#8230; <a href="http://jacksongabbard.com/vignette-on-connecting.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>A faint flicker glides into dark space. It reveals just the shadow of a thing. In time a flicker returns, a different hue and intensity. Its return exposes the faintest bit of details in the nascent form. Somewhere far off there&#8217;s another mass, hard but porous waiting for the next tingling reception. Swirling in its own half dark, the nearer hulk begins to articulate another signal. Textured and writhing with hints and careful details then off into the open air lightning&#8217;s courier darts with the parcel. The atmospheric din crackles and hums with non-interference. Message received. Interest flickers in each mass. They grow nearer in the now quieter space. In their murky distance, the rocky forms soften to receive, swell to send. Flecks of sparkling delight beging lacing throughout every next wisp. At length, a wiry, bidirectional stream spins out from one to the other. Each offering a more solid stream from an ever softer hulk. Slow pulsations carry the ebbs and flows of connection into convergence. Then synchronicity.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/vignette-on-connecting.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vignette in the Midst of Change</title>
		<link>http://jacksongabbard.com/vignette-in-the-midst-of-change.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=vignette-in-the-midst-of-change</link>
		<comments>http://jacksongabbard.com/vignette-in-the-midst-of-change.html#comments</comments>
		<pubDate>Wed, 13 Jun 2012 03:45:04 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=98</guid>
		<description><![CDATA[Time tumbles forward. Complication and business pervade every moment and every moment twinkles with transience. In moments between moments you look around and see flecks of the world outside of the slipstream. Anchor points. A beautiful girl&#8217;s smile in the &#8230; <a href="http://jacksongabbard.com/vignette-in-the-midst-of-change.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Time tumbles forward. Complication and business pervade every moment and every moment twinkles with transience. In moments between moments you look around and see flecks of the world outside of the slipstream. Anchor points. A beautiful girl&#8217;s smile in the sunlight of a high school summer afternoon turning evening. Cars in the parking lot stand out like sparse freckles. Stragglers pass through the walk ways at work. The seats of the office are mostly empty and the screens dark. The sun sinks into the ocean through western windows. A handful of smiles surround you. Eyes and words shine in the evening sky over top of looming mountains. You push a little, pull a little. Impatience tells you you&#8217;ve got enough control. You fall your own fall. The water is your water. You are a plunging rock.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/vignette-in-the-midst-of-change.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>This Web Page is a Dick: T-Mobile</title>
		<link>http://jacksongabbard.com/this-web-page-is-a-dick-t-mobile.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=this-web-page-is-a-dick-t-mobile</link>
		<comments>http://jacksongabbard.com/this-web-page-is-a-dick-t-mobile.html#comments</comments>
		<pubDate>Sun, 10 Jun 2012 22:57:42 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[UI]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[dickmove]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=94</guid>
		<description><![CDATA[It boggles me endlessly when sites have draconian requirements for passwords. I use a password generator/store application called 1Password. This application lets me create crazy strong passwords for every site I interact with. This means I can ratchet up the &#8230; <a href="http://jacksongabbard.com/this-web-page-is-a-dick-t-mobile.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div id="attachment_95" class="wp-caption alignnone" style="width: 1010px"><a href="http://jacksongabbard.com/wp-content/uploads/2012/06/Screen-Shot-2012-06-10-at-3.52.37-PM.png"><img class="size-full wp-image-95" title="This Web Page is a Dick" src="http://jacksongabbard.com/wp-content/uploads/2012/06/Screen-Shot-2012-06-10-at-3.52.37-PM.png" alt="Screen shot of a browser inspector showing the HTML that governs the behavior of a webpage" width="1000" height="634" /></a><p class="wp-caption-text">T-Mobile, why do you hate your users?</p></div>
<p>It boggles me endlessly when sites have draconian requirements for passwords. I use a password generator/store application called <a href="https://agilebits.com/onepassword" target="_blank">1Password</a>. This application lets me create crazy strong passwords for every site I interact with. This means I can ratchet up the special characters and length without any worry that I won&#8217;t remember the password. I know I won&#8217;t remember, I use a computer to do the remembering. Anyway, this works great unless I&#8217;m up against sites like T-Mobile that offer ridiculous rules:</p>
<ul>
<li>Must be between 8 and 15 characters.</li>
<li>Must include letters and numbers.</li>
<li>Must contain uppercase and lowercase letters.</li>
<li>Cannot contain spaces or special characters (!, @, $, %, ‘).</li>
</ul>
<p>So the first rule makes some sense. Short passwords are less secure. However, length of password should have no restriction because more bits means longer crack times in the case someone does get a hold of the hash. Senseless.</p>
<p>Requiring letters and numbers is good as is requiring upper and lower case letters. Adding more variety to the symbol set means someone trying to brute force the password will be at it for a lot longer.</p>
<p>No spaces or special characters&#8230; wtf? There are dozens of special characters available on a standard keyboard. Thousands if you count multi-key sequences that produce special characters. Allowing these characters would make it extremely burdensome to try to haxX0r people&#8217;s passwords.</p>
<p>As if that isn&#8217;t bad enough T-Mobile goes even further to make life miserable for its users: complete restriction on the input methods the user can use to set their password. In the screen shot above, you can see that the UI engineer who built this page has disabled pasting into the fields. They&#8217;ve also made the refuse to accept drag and drop text. I can&#8217;t fathom the product decision making that would require this sort of input. For me, this means that if I want to use a highly varied, randomly generated password that I would have to manually enter all 15 characters of it exactly correctly&#8230; twice. Fucking insanity. Fortunately for me at least, the HTML DOM is a dynamic place so I can just remove these dubious &#8220;features&#8221; via the web inspector and get back to what I was trying to do.  This web page is a dick.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/this-web-page-is-a-dick-t-mobile.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Generating a Color Picker Style Color Wheel in Python</title>
		<link>http://jacksongabbard.com/generating-a-color-picker-style-color-wheel-in-python.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=generating-a-color-picker-style-color-wheel-in-python</link>
		<comments>http://jacksongabbard.com/generating-a-color-picker-style-color-wheel-in-python.html#comments</comments>
		<pubDate>Mon, 28 May 2012 07:48:13 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=85</guid>
		<description><![CDATA[For the record, I am very bad at Python. Yet, it has become more and more my go-to language for doing little things that aren&#8217;t web. For instance, I recently undertook to create a color picker/color wheel in Python. For &#8230; <a href="http://jacksongabbard.com/generating-a-color-picker-style-color-wheel-in-python.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div id="attachment_86" class="wp-caption alignnone" style="width: 510px"><a href="http://jacksongabbard.com/wp-content/uploads/2012/05/gamut.png"><img class="size-full wp-image-86" title="gamut" src="http://jacksongabbard.com/wp-content/uploads/2012/05/gamut.png" alt="color gamut generated from Python" width="500" height="500" /></a><p class="wp-caption-text">This color wheel was generated with Python. Check out the code on GitHub.</p></div>
<p>For the record, I am very bad at Python. Yet, it has become more and more my go-to language for doing little things that aren&#8217;t web. For instance, I recently undertook to create a color picker/color wheel in Python.</p>
<p>For the uninitiated, a color wheel or color spectrum is a series of hues that run from red to indigo (roughly purple to our eyes). It&#8217;s the rainbow. The color wheel is this same thing but wrapped around in a way that shows all the hues the human eye can see. In reality, indigo doesn&#8217;t converge on red as you go up in light frequency, but the hues that you would see if it did are colors we know well &#8212; magenta/pink.</p>
<p>So, generating a color wheel is an interesting challenge. The primary colors can be created from the hex colors 0xff0000 (red), 0x00ff00 (green), and 0x0000ff (blue). You probably see the pattern easily. A color breaks down into three channels, each of which has up to 0xff or 256 values from 0, completely absent, to 0xff, completely displayed. Blending from blue to green implies moving from 0x00ff00 to 0x0000ff by decrementing the green channel monotonically as you monotonically increment the blue channel. So, you can probably start to see how the logic for generating the color spectrum graphic will have to work. Of course I&#8217;m ignoring a few colors that are important. Red, green, and blue are only half the colors that are necessary for the color wheel to look complete. The others are 0x00ffff (cyan), magenta (0xff00ff), and yellow (0xffff00). These colors appear in color spectrum interwoven between the primary colors. If you&#8217;re wondering why red and green make yellow, you&#8217;ll probably want to read about <a href="http://www.physicsclassroom.com/class/light/u12l2d.cfm">Color by Addition</a>. Though, from a numbers perspective it&#8217;s easy to see a relationship between 0xff0000 -&gt; 0xffff00 -&gt; 0x00ff00.</p>
<p>Creating the gamut requires calculating how much of each color to use in the blending. One important aspect of a color wheel is that any color you can select is a blend of at most two colors. So, one way to generate a color wheel is to go pixel-by-pixel through the image you want to create figuring out what sextant you&#8217;re in, deciding which color you&#8217;re closest to, then figuring out how much of the other color to blend into it. That&#8217;s the approach I took. It&#8217;s true that you can do this other ways. In fact, I&#8217;m pretty sure there is a slick algorithm for this that I didn&#8217;t realize. I was on a plane when I wrote this and consider it more about learning Python than about making the most compact implementation of color spectrum generation.</p>
<p>Anyway, <a href="https://github.com/jacksongabbard/Python-Color-Gamut-Generator">here&#8217;s the code</a> (using the <a title="Python Image Library" href="http://www.pythonware.com/products/pil/" target="_blank">Python Image Library</a>) on GitHub.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/generating-a-color-picker-style-color-wheel-in-python.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vignette on Big Transitions</title>
		<link>http://jacksongabbard.com/vignette-on-big-transitions.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=vignette-on-big-transitions</link>
		<comments>http://jacksongabbard.com/vignette-on-big-transitions.html#comments</comments>
		<pubDate>Wed, 18 Apr 2012 01:32:01 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Vignettes]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=82</guid>
		<description><![CDATA[Gravity is misbehaving. The rooms in the apartment are rearranged. Reality ticks detectably and yet life feels like a dream. It&#8217;s not a nightmare. It&#8217;s not settling. It is transit. You can sense where it&#8217;s going, but the way is &#8230; <a href="http://jacksongabbard.com/vignette-on-big-transitions.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Gravity is misbehaving. The rooms in the apartment are rearranged. Reality ticks detectably and yet life feels like a dream. It&#8217;s not a nightmare. It&#8217;s not settling. It is transit. You can sense where it&#8217;s going, but the way is ambiguous. Some moments have the fuzzy intensity of holding your breath too long. Sitting down seems natural, but the ground falls away. The floor feels as foreign as the open air. What will happen from here? The canvas is blank but shaped like where you&#8217;ve been so far. Where are the paints?</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/vignette-on-big-transitions.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vignette on Making Horrible Mistakes</title>
		<link>http://jacksongabbard.com/vignette-on-making-horrible-mistakes.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=vignette-on-making-horrible-mistakes</link>
		<comments>http://jacksongabbard.com/vignette-on-making-horrible-mistakes.html#comments</comments>
		<pubDate>Wed, 11 Apr 2012 01:52:31 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Vignettes]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=78</guid>
		<description><![CDATA[This is the moment of your fatal error. You can feel it happening. Your hand is in the water. The water feels natural but you realize that something is wrong. It&#8217;s very wrong. You can feel the itch against your &#8230; <a href="http://jacksongabbard.com/vignette-on-making-horrible-mistakes.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is the moment of your fatal error. You can feel it happening. Your hand is in the water. The water feels natural but you realize that something is wrong. It&#8217;s very wrong. You can feel the itch against your bones. Like two sheets of coarse paper shifting back and forth and the fizzing of overly carbonated soda under your tendons. The water is scalding. The switch flips. There is nothing you can do now. You can pull your scorched skin out. You can leave it until the water turns tepid and the burnt skin is equal parts red and wrinkled. Your face is soiled with yolk and grime and shame. If everyone is looking, let them. This moment is pure and golden and yours. It is your failure to relive a hundred times over every next mistake. Lay back into it. The bed sheets are cool. The pillow case is cool where your tears soaked in. Roll in it. Wait until the wounds healed enough to hide.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/vignette-on-making-horrible-mistakes.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hey Android, Where&#8217;s the USB Data?</title>
		<link>http://jacksongabbard.com/wheres-the-data.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wheres-the-data</link>
		<comments>http://jacksongabbard.com/wheres-the-data.html#comments</comments>
		<pubDate>Mon, 19 Dec 2011 15:15:53 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=42</guid>
		<description><![CDATA[Does your ping look like this when using Android&#8217;s sweet USB tethering feature? I tried lots of things: Disabling/Enabling USB Tethering Disconnecting/Reconnecting the USB cable Restarting the phone Disabling/Enabling the network interface on the computer Restarting the computer All of &#8230; <a href="http://jacksongabbard.com/wheres-the-data.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://jacksongabbard.com/wp-content/uploads/2011/12/Screen-Shot-2011-12-19-at-7.01.22-AM.png"><img class="size-full wp-image-64" title="Screen Shot 2011-12-19 at 7.01.22 AM" src="http://jacksongabbard.com/wp-content/uploads/2011/12/Screen-Shot-2011-12-19-at-7.01.22-AM.png" alt="" width="466" height="110" /></a></p>
<p>Does your ping look like this when using Android&#8217;s sweet USB tethering feature? I tried lots of things:</p>
<ul>
<li>Disabling/Enabling USB Tethering</li>
<li>Disconnecting/Reconnecting the USB cable</li>
<li>Restarting the phone</li>
<li>Disabling/Enabling the network interface on the computer</li>
<li>Restarting the computer</li>
</ul>
<p>All of this got me exactly no where. The phone is still connected to the interwho, but it&#8217;s just not sharing it with my laptop. So sad. So, thinking a little bit more laterally (read as, poking around looking for a sign from the gods), I tried <strong>toggling the data access of the phone</strong>. This means going to<strong> Menu &gt; Wireless &amp; networks &gt; Mobile network settings &gt; Data enabled</strong> and unchecking the box.</p>
<p><a href="http://jacksongabbard.com/wp-content/uploads/2011/12/2011-12-19-07.11.40.png"><img class="alignnone size-full wp-image-67" title="Isn't it annoying that Android title cases some menu items but not others?" src="http://jacksongabbard.com/wp-content/uploads/2011/12/2011-12-19-07.11.40.png" alt="" width="480" height="800" /></a></p>
<p>Why does it make sense to turn off data when what you want is data? Well&#8230; it doesn&#8217;t. But, if you then turn the data back on, the phone may well realize that it has been fraudulently denying your poor computer all the data goodness the marvelous (if mystifying) Android platform promises.  I say &#8220;may well&#8221; because with the fragmentation of the Android market and the amount of irresponsible hackery the carriers subject the OS to, this may not actually fix your problem though it fixes mine. I&#8217;m using a vanilla-tainted-by-T-Mobile-and-LG version of Android 2.3 running on the T-Mobile G2X. This phone also goes by the name of Optimus 2X. Soon enough I&#8217;ll ditch it for the Galaxy Nexus, but I digress.</p>
<p><a href="http://jacksongabbard.com/wp-content/uploads/2011/12/Screen-Shot-2011-12-19-at-7.02.03-AM.png"><img class="size-full wp-image-65" title="Screen Shot 2011-12-19 at 7.02.03 AM" src="http://jacksongabbard.com/wp-content/uploads/2011/12/Screen-Shot-2011-12-19-at-7.02.03-AM.png" alt="" width="474" height="162" /></a></p>
<p>This what I get trying to ping after turning back on the &#8220;Enable Data&#8221; option. Hooray. All it took was doing an action equivalently counterintuitive to the famous &#8220;Oh you want to shut down? First, go to start&#8230;&#8221; paradigm. At least this one is unintentional.</p>
<p>Also, you may be wondering why on earth I would be using USB Tethering when my phone provides WiFi Hotspot capability. The answer is that I&#8217;ve found the latency to be lower and the signal to be more consistent using USB Tethering. The bandwidth is also higher, which I suppose makes sense because one phone using the same wireless antenna for sending and receiving data probably isn&#8217;t sending as fast as it could be. I should be careful to specify that I don&#8217;t actually know that is what&#8217;s happening. Anyway, USB Tethering gives me a faster connection. The other perk is that my laptop, which I&#8217;m going to plug in when I get to work, can power the phone while it&#8217;s on USB Tethering. With WiFi Hotspotting, I&#8217;m pretty guaranteed to drain my phone to the point where I will <em>have</em> to plug it in to make it through the day. There you have it.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/wheres-the-data.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Baby Steps: Vim Macros</title>
		<link>http://jacksongabbard.com/vim-macros.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=vim-macros</link>
		<comments>http://jacksongabbard.com/vim-macros.html#comments</comments>
		<pubDate>Sun, 11 Dec 2011 09:52:34 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=41</guid>
		<description><![CDATA[I spend probably a third of my maker time writing code (another third doing root cause analysis on bugs and another third reviewing code, more or less). In my coding time, I do almost all of my work in Vim and &#8230; <a href="http://jacksongabbard.com/vim-macros.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I spend probably a third of my <a title="maker time" href="http://www.paulgraham.com/makersschedule.html" target="_blank">maker time</a> writing code (another third doing root cause analysis on bugs and another third reviewing code, more or less). In my coding time, I do almost all of my work in Vim and Bash, mostly Vim. You IDE buffs out there can scoff if you will. Vim is a tool that has proven its worth over two decades. Two decades. That&#8217;s like two milennia in computer years. I&#8217;m an admittedly bad Vim user, but I&#8217;m making strides to recover. One stride I&#8217;ve just taken is using Vim Macros to automate stuff, like converting the format of text files.</p>
<p>If you&#8217;ve never used macros in any context, you may not have a sense of how powerful a tool they really are. Macros give you the ability to take a series of actions that achieve some goal and put them into a recording. You can then replay those actions again and again. This means that complex transformations on some text only have to be done correctly one time. The instructions can be captured and reused. We humans are pretty good at getting something hard done correctly once. Our error rate goes up the more times we do the thing. So, macros are helpful because when you get the steps right once, you get to use your own actions again and again. The replay is exact. The computer doesn&#8217;t accidentaly skip step three like you or I might the fifteenth time we walk through the process.</p>
<p>The other benefit is that the steps get executed at human speed (i.e., slow and clumsy speed) only the first time. Each time the actions are replayed, all the time you spent head scratching and double checking that you&#8217;re doing the right thing disappears and the actions get executed as fast as the computer can do it. Vim has macros built-in as do a lot of applications. Oh how I long for real life macrobot. Imagine being able to program a robot, one time, to fold laundry. I would never match a pair of socks again. Baby steps.</p>
<p>The background for all this macro talk is a task I took on recently. I needed to get the data out of a bunch of hand-rolled, inconsistently formatted PDFs and into some reasonable format. The goal was to have a structured data representation of the data that could be easily maintained over time. I guess it&#8217;s not strictly insane to try to keep a bunch of willy-nilly PDF files full of timetables up to date, but it&#8217;s far from efficient. To achieve this textual cleaning of the stables, I wanted to use Vim Macros because I knew they were a good tool for the job and I didn&#8217;t already know how to use them. Before this little jaunt, my exposure to Vim&#8217;s macros was pretty much just accidentally starting the recording process and trying every combination of colon, escape, and q to get out of it. Like I said, I aspire to be a competent Vim user. Baby steps.</p>
<p>Anyway, my tactic was to copy and paste the tables from the PDF files into a Vim document and determine an automated way to turn the data into a CSV format. Okay, actually I&#8217;m lying. I started by pasting that data into TextMate and trying to use find and replace hacks in addition to my amazing repetitive typing abilities to get the data into a CSV format. As I started to get into the rhythm of it, I could feel my inner smarty pants engineer sighing at me. Yes, I know there is no good reason to be using my pitiful human brain for simple, repeated tasks, but I don&#8217;t already know how to use them.  Yes, I should use the generic tools available for automating the process so I can get it right and do it fast, but I just want to get it done. This is a classic dilemma. Option one: Do it the slow, hard way that is guaranteed to get finished in a fixed amount of time but may introduce errors at worst and at best is a pain in the ass. Option two: <em>Maybe</em> do it the super fast way, assuming you can actually figure out how to use the scary new tools properly. <a href="http://xkcd.com/974/">XKCD 974</a> comes to mind.</p>
<p>After a few moments of complete philosophical system lock, I decided it was time to brain-up and make the computer do my bidding. Since I knew I needed to learn macros, I decided that would be the weapon with which I would dispatch these gobs of poorly structured nonsense.</p>
<p>Copying and pasting from the PDF, I get pretty garbled data, like this:</p>
<p><code>9:00 AM Location 1 6:30 PM Location 5<br />
9:05 AM Location 2 6:35 PM Location 4<br />
9:20 AM Location 3 7:45 PM Location 6<br />
10:25 AM Location 4 7:55 PM Location 2<br />
10:30 AM Location 5 8:00 PM Location 1<br />
9:30 AM Location 1 7:30 PM Location 5<br />
9:35 AM Location 2 7:35 PM Location 4<br />
9:50 AM Location 3 8:45 PM Location 6<br />
10:55 AM Location 4 8:55 PM Location 2<br />
11:00 AM Location 5 9:00 PM Location 1<br />
10:15 AM Location 1 8:10 PM Location 5<br />
10:20 AM Location 2 8:15 PM Location 4<br />
10:35 AM Location 3 9:25 PM Location 6<br />
11:35 AM Location 4 9:35 PM Location 2<br />
11:40 AM Location 5 9:40 PM Location 1<br />
...and so on for about a hundred lines.</code></p>
<p>Though it&#8217;s bad, it&#8217;s a bearable starting place because it&#8217;s consistent in at least one way. For every time, the location that follows it is correct. The ideal CSV output would have the locations as the first row and the times for each location in the correct CSV column on the following rows.  Of course there are some hurdles here. In the PDF paste, there are two columns because the PDF was in a two column layout and whoever created it decided to just stretch one table across the page rather than created two separate tables. So, step one is to get the columns cleaned up. Easy enough, right? After all, <a href="http://xkcd.com/208/">I know VIM MACROS!!!</a></p>
<p>It&#8217;s macro time.</p>
<p>I actually didn&#8217;t know much about macros in Vim, but I&#8217;ve used them in other contexts. The basic idea is that a macro is a recording of some actions that can be replayed. There are some basic rules one has to know to get full value out of macros. As far as I can tell, there are two:</p>
<h3>Remember the computer has no intuition so it must be told exactly what to do</h3>
<p>Maybe this is obvious, but it&#8217;s worth reiterating that when you, as a human, say &#8220;select this text and do this with it&#8221; in your head, you&#8217;re glossing over tons and tons of steps that a computer will have to be told. To write a macro, you have to break down complex actions into each of the sub-actions until you arrive at steps simple enough for the computer to do it. Baby steps.</p>
<h3>Use the most general commands possible to get the effect you want</h3>
<p>Once you have simple steps the computer can handle, you have to figure out how to make those action work on all the text you want to modify. For instance, I could read the first line of my data and say, &#8220;There are 19 characters in the first column. Computer, go forward 19 characters to the start of the second column.&#8221; The problem is that this is a specific rather than a general command. It&#8217;s specific to the first line only. If you skip ahead to the fourth line, you can see it wouldn&#8217;t work correctly. Instead, I have to come up with a general way of getting to the position I want. This will require finding something that is true of <em>every</em> line of the data and using that consistency to my advantage. In this dataset, I know that every line has exactly two colons in it. I can also see that the second column always starts at the first space before the second colon. I can use this consistency to write general commands that always put me in the column space. As you can probably tell, this part of macros requires some cleverness.</p>
<p>Keeping these two rules in mind, here&#8217;s exactly how I think my macro should work:</p>
<ol>
<li>Move to the beginning of the line</li>
<li>Set a marker at the current line</li>
<li>Jump to the second column</li>
<li>Delete from the start of the second column to the end of the line</li>
<li>Jump to the bottom of the file</li>
<li>Create a new line and paste the column data</li>
<li>Jump back up to my marker</li>
<li>Go down to the next line</li>
</ol>
<p>These are pretty simple steps, but the real test will be converting them to Vim commands. As Vim commands, this is all pretty straight-forward except for the &#8220;jump to the second column&#8221; part, but I&#8217;ll explain it in detail below.</p>
<p>Here&#8217;s what the guts of my macro are going to look like, starting from visual mode (i.e., not Insert or Ex mode) on the first line of the data.</p>
<ol>
<li><kbd class="inline">0</kbd> Jump to the beginning of the line</li>
<li><kbd class="inline">mm</kbd> Create a marker labelled &#8220;m&#8221;</li>
<li><kbd class="inline">f:f:</kbd> Jump forward to the second colon; my cursor is now in the middle of the time string in the second column</li>
<li><kbd class="inline">bb</kbd> Go back to the beginning of the word, then go back to the word before that, this puts my cursor on the number character at the end of the first column</li>
<li><kbd class="inline">f&lt;space&gt;</kbd> Forward to the space; my cursor is now right where I want it, in between the two columns</li>
<li><kbd class="inline">d$</kbd> Delete from the cursor position to the end of the line; the text is in-memory and I can paste it somewhere else</li>
<li><kbd class="inline">G</kbd> Jump to the last line of the file</li>
<li><kbd class="inline">o</kbd> Create a new empty line, which puts me in Insert mode</li>
<li><kbd class="inline">&lt;escape&gt;</kbd> Go back to Edit mode</li>
<li><kbd class="inline">p</kbd> Paste the data into the line</li>
<li><kbd class="inline">0x</kbd> Jump to the beginning of the line and delete the space that I don&#8217;t need</li>
<li><kbd class="inline">'m</kbd> Jump back to the line I started the process with</li>
<li><kbd class="inline">j</kbd> Go down to the next line to start the process over</li>
</ol>
<p>Cool, I have steps that can be repeated line after line. Now it&#8217;s time to hit the docs. According to <a href="http://vim.wikia.com/wiki/Macros">the Macros page</a> on the Vim Tips Wiki, to record a macro, hit &#8220;q&#8221; and then a label key, just like with markers. So, I choose &#8220;s&#8221; as my label. It&#8217;s a good, memorable letter. I now type &#8220;qs&#8221;, which gives me the familiar &#8220;recording&#8221; text at the bottom left of my editor. Only this time, I actually intend for it to show up. Huzza. I&#8217;m a pro.</p>
<p>If I&#8217;ve designed my macro correctly, I should be able to follow it exactly, then hit the &#8220;q&#8221; key to stop recording. Of course, as a frail human, I botched the steps up the first time and had to start over. Sad panda. Life goes on. On the second try, I got the commands right. Hitting the &#8220;q&#8221; key finishes the macro.</p>
<p>It&#8217;s the moment of truth. I&#8217;m on the second line of my data set. The first line has already been converted. According to the macro documentation, if I want to replay my macro, I hit &#8220;@&#8221; and the macro label. Drumroll. I hit &#8220;@s&#8221; and instantly my cursor is on the third line and the second line has been converted in exactly the way I want. At the bottom of the file, the second column of the second line has been appended. It&#8217;s magic!</p>
<p>I know I started the process willing to do all that work myself, but now I&#8217;m spoiled. The computer is my bitch and I want it to get me some cookies and milk while I lay on the couch basking in my genius. Translation: I realize that I&#8217;m still going to have to type &#8220;@s&#8221; a bunch of times and I&#8217;m sad. There has to be a better way! And, reading the documentation further, it turns out that you can replay a macro as many times in a row as you want by declaring the number of times to repeat the &#8220;@&#8221; then the label key. So, doing some quick math, I see I have 116 more lines to convert. I type &#8220;116@qs&#8221; and BAM! The file is now one column of data. Macros are awesome.</p>
<p>When you think about it, if you&#8217;re doing work in Vim already, macros are pretty much just a recording of the steps you would take anyway. From now on, I&#8217;m going to think about every editing procedure I do in terms of simple, sequential actions and general commands.</p>
<p>The time it took me to devise the macro plus the time it took me to get it recorded was about 15 minutes, including the time it took to read the docs. Sure, that&#8217;s longer than it would&#8217;ve taken to do the conversion by hand. But, I got smarter in that time. Manual work would&#8217;ve made me more inclined to take the route of drudgery, which I&#8217;ll submit is the equivalent of getting dumber. Fifteen minutes spent learning and applying a new skill is definitely a more leveraged use of the time than reapplying a well-worn skill. If you factor in the damage done by the error I probably would&#8217;ve made doing it all manually as well as the time it would take to find and correct the error, learning to use automation is easily the cheaper way to go. The next time around it&#8217;ll be an even better option. Speaking of the next time around, now I need to create a macro to convert this one-column state I&#8217;m in into a multicolumn file using CSV format. This is going to be be fun.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/vim-macros.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>APKs Won&#8217;t Download In Android Browser</title>
		<link>http://jacksongabbard.com/apks-wont-download-in-android-browser.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=apks-wont-download-in-android-browser</link>
		<comments>http://jacksongabbard.com/apks-wont-download-in-android-browser.html#comments</comments>
		<pubDate>Sun, 27 Nov 2011 06:27:25 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=34</guid>
		<description><![CDATA[If you, like me, are attempting to setup a download end point for Android APK files that doesn&#8217;t include a .apk in the URL, you have probably butted your head against a myriad of issues. It&#8217;s worth noting that downloading &#8230; <a href="http://jacksongabbard.com/apks-wont-download-in-android-browser.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>If you, like me, are attempting to setup a download end point for Android APK files that doesn&#8217;t include a .apk in the URL, you have probably butted your head against a myriad of issues. It&#8217;s worth noting that downloading APKs in Dolphin, Firefox Mobile, or Opera all work fine with respect to using Content-Disposition headers alone. It&#8217;s just the native browser that gives us grief. For the native browser, my first pass at the endpoint&#8217;s download code was the naive approach:</p>
<p><code>header('Content-Disposition: attachment; filename='.$apk_name);<br />
echo file_get_contents($web_inaccessible_dir.'/'.$apk_name);</code></p>
<p>Testing this code in any desktop browser, I get the expected download notification and the file downloads like a champ. When I test drive this in the Android browser the result is different. Rather than getting a file download, I get the correct filename downloading but instead of using the necessary APK extension, the file is magically transformed into an HTM file, which is completely useless. This seems like a painfully bad decision for the content handling mechanisms of the browser, perhaps even a bug. Looking for some sign in the Android logs, I see this:</p>
<pre>D/MediaScannerService( 1386): IMediaScannerService.scanFile: /mnt/sdcard/download/that_apk_file.htm mimeType: text/html</pre>
<p>So while this is completely bad behavior, at least there&#8217;s a trail of hope in it &#8212; MIME type. Text/HTML is not the MIME type an APK file should have and apparently the browser is making us play by the rules. I can understand that. After all, if the MIME type is the source of truth then an APK file extension is only going to make the OS confused about what to do with the file. So, the question becomes what file type should an Android APK file have?</p>
<p>Doing some Googling about it, I find Wikipedia has the answer. The <a title="APK File Format entry" href="http://en.wikipedia.org/wiki/APK_(file_format)">APK file format entry</a> explains that Android APK files should have a MIME type of <strong>application/vnd.android.package-archive</strong>. Armed with this knowledge, I tried modifying my endpoint to send the MIME type I want:</p>
<p><code>header('Content-Disposition: attachment; filename='.$apk_name);<br />
header('Content-Type: application/vnd.android.package-archive');<br />
echo file_get_contents($web_inaccessible_dir.'/'.$apk_name);</code></p>
<p>Hitting the endpoint now, I find that the browser gets the APK file with the APK extension. Booyah. Just for rigor, I check logcat, and see what I was hoping to see:</p>
<pre>D/MediaScannerService( 1386): IMediaScannerService.scanFile: /mnt/sdcard/download/that_apk_file.apk mimeType: application/vnd.android.package-archive</pre>
<p>This is good, but I notice there are still flaws in the pearl. Namely, the file downloads without an indication of filesize or progress. The downloads page shows only that the download is &#8220;in progess&#8221; all the way up to completion. On a slow connection, this will seem like the file is stuck. Not ideal.</p>
<p>So, knowing a little about the HTTP spec, it seems like what the browser might need is a Content-Length header to get a handle on the progress towards completion. This is a straight-forward change, requiring just a single line.</p>
<p><code>header('Content-Disposition: attachment; filename='.$apk_name);<br />
header('Content-Type: application/vnd.android.package-archive');<br />
header('Content-Length: '.filesize($web_inaccessible_dir.'/'.$apk_name));<br />
echo file_get_contents($web_inaccessible_dir.'/'.$apk_name);</code></p>
<p>Giving this a whirl in the browser, I find that I now have both the correct file extension and a progress bar during the download. Huzza.</p>
<p>I believe I&#8217;ve got what I want, but with Android it&#8217;s not guaranteed to be that simple. Due to fragmentation, I can only assume this works on the devices I&#8217;ve tested. So far, that includes the T-Mobile G2X (2.3.3 + T-mo&#8217;s bloatware), the x86 Android Emulator running 2.2 (by the way, if you haven&#8217;t tried out the x86 Emulator for Android and are still using the ARM emulator, you need to check out <a title="http://www.android-x86.org/" href="http://www.android-x86.org/">http://www.android-x86.org/</a> &#8211; it will change your whole perspective on shit), and the HTC Sensation 4G running 2.3.4 + Sense UI. I feel like that&#8217;s a good enough start to write this post. Please comment if you have an OS version/phone that doesn&#8217;t work with this setup.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/apks-wont-download-in-android-browser.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Here We Go Again</title>
		<link>http://jacksongabbard.com/hello-world.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=hello-world</link>
		<comments>http://jacksongabbard.com/hello-world.html#comments</comments>
		<pubDate>Sun, 20 Nov 2011 19:09:03 +0000</pubDate>
		<dc:creator>jackson</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jacksongabbard.com/?p=1</guid>
		<description><![CDATA[This is the first post of the newest iteration of my corner of the web. Enjoy and converse. In case you&#8217;re curious, this is a WordPress blog. I&#8217;m not a WordPress fanboy, but it is the best tool for the &#8230; <a href="http://jacksongabbard.com/hello-world.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is the first post of the newest iteration of my corner of the web. Enjoy and converse. In case you&#8217;re curious, this is a WordPress blog. I&#8217;m not a WordPress fanboy, but it is the best tool for the job as far as I&#8217;m concerned. I rebuilt most of the blog twice before switching to WP. The first time, I wrote a directory parser/cacher system that would let me statically generate the blog from flat files. The second time, I wrote an even more sophisticated version that was still file-backed rather than database backed. I was doing a lot of fancy caching, minimizing, and dog fooding. Still, I wasn&#8217;t getting it done fast enough. Who cares who fast a blog loads if it isn&#8217;t done enough to launch. Exactly no one, including me. After spending probably fifteen hours on each I had the epiphany that just using WordPress will deliver significantly more bang for the time I spend. At the time of this writitng I have approximately 7 hours in the project and it&#8217;s nearly complete in terms of desired layout and features for desktops and tablets. The handheld mobile view sucks, but I haven&#8217;t spent any time optimizing for that yet. I&#8217;ll get there. Anyway, now that this bad boy is up and running, look for frequent updates with interesting things.</p>
]]></content:encoded>
			<wfw:commentRss>http://jacksongabbard.com/hello-world.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
